diff --git a/README.txt b/README.txt index 85e2b24a..d03c6c77 100644 --- a/README.txt +++ b/README.txt @@ -13,6 +13,7 @@ Contributors: Michael Cristopher Hogg Ulrich Leutner Felix Schill + Guillaume Poulin Requirements: PyQt 4.7+ or PySide diff --git a/examples/GLBarGraphItem.py b/examples/GLBarGraphItem.py new file mode 100644 index 00000000..d14eba87 --- /dev/null +++ b/examples/GLBarGraphItem.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +""" +Demonstrate use of GLLinePlotItem to draw cross-sections of a surface. + +""" +## Add path to library (just for examples; you do not need this) +import initExample + +from pyqtgraph.Qt import QtCore, QtGui +import pyqtgraph.opengl as gl +import pyqtgraph as pg +import numpy as np + +app = QtGui.QApplication([]) +w = gl.GLViewWidget() +w.opts['distance'] = 40 +w.show() +w.setWindowTitle('pyqtgraph example: GLBarGraphItem') + +gx = gl.GLGridItem() +gx.rotate(90, 0, 1, 0) +gx.translate(-10, 0, 10) +w.addItem(gx) +gy = gl.GLGridItem() +gy.rotate(90, 1, 0, 0) +gy.translate(0, -10, 10) +w.addItem(gy) +gz = gl.GLGridItem() +gz.translate(0, 0, 0) +w.addItem(gz) + +# regular grid of starting positions +pos = np.mgrid[0:10, 0:10, 0:1].reshape(3,10,10).transpose(1,2,0) +# fixed widths, random heights +size = np.empty((10,10,3)) +size[...,0:2] = 0.4 +size[...,2] = np.random.normal(size=(10,10)) + +bg = gl.GLBarGraphItem(pos, size) +w.addItem(bg) + + +## Start Qt event loop unless running in interactive mode. +if __name__ == '__main__': + import sys + if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): + QtGui.QApplication.instance().exec_() diff --git a/pyqtgraph/PlotData.py b/pyqtgraph/PlotData.py index 0bf13ca8..e5faadda 100644 --- a/pyqtgraph/PlotData.py +++ b/pyqtgraph/PlotData.py @@ -15,6 +15,7 @@ class PlotData(object): - removal of nan/inf values - option for single value shared by entire column - cached downsampling + - cached min / max / hasnan / isuniform """ def __init__(self): self.fields = {} diff --git a/pyqtgraph/__init__.py b/pyqtgraph/__init__.py index c1b62041..12a4f90f 100644 --- a/pyqtgraph/__init__.py +++ b/pyqtgraph/__init__.py @@ -316,7 +316,7 @@ def dbg(): Create a console window and begin watching for exceptions. """ mkQApp() - import console + from . import console c = console.ConsoleWidget() c.catchAllExceptions() c.show() diff --git a/pyqtgraph/multiprocess/remoteproxy.py b/pyqtgraph/multiprocess/remoteproxy.py index 7622b6e7..702b10bc 100644 --- a/pyqtgraph/multiprocess/remoteproxy.py +++ b/pyqtgraph/multiprocess/remoteproxy.py @@ -205,7 +205,11 @@ class RemoteEventHandler(object): fnkwds[k] = np.fromstring(byteData[ind], dtype=dtype).reshape(shape) if len(fnkwds) == 0: ## need to do this because some functions do not allow keyword arguments. - result = obj(*fnargs) + try: + result = obj(*fnargs) + except: + print("Failed to call object %s: %d, %s" % (obj, len(fnargs), fnargs[1:])) + raise else: result = obj(*fnargs, **fnkwds) @@ -932,9 +936,9 @@ class ObjectProxy(object): def __ilshift__(self, *args): return self._getSpecialAttr('__ilshift__')(*args, _callSync='off') - #def __eq__(self, *args): - # return self._getSpecialAttr('__eq__')(*args) - + def __eq__(self, *args): + return self._getSpecialAttr('__eq__')(*args) + def __ne__(self, *args): return self._getSpecialAttr('__ne__')(*args) @@ -1010,6 +1014,10 @@ class ObjectProxy(object): def __rmod__(self, *args): return self._getSpecialAttr('__rmod__')(*args) + def __hash__(self): + ## Required for python3 since __eq__ is defined. + return id(self) + class DeferredObjectProxy(ObjectProxy): """ This class represents an attribute (or sub-attribute) of a proxied object. diff --git a/pyqtgraph/opengl/GLGraphicsItem.py b/pyqtgraph/opengl/GLGraphicsItem.py index 59bc4449..9680fba7 100644 --- a/pyqtgraph/opengl/GLGraphicsItem.py +++ b/pyqtgraph/opengl/GLGraphicsItem.py @@ -40,6 +40,7 @@ class GLGraphicsItem(QtCore.QObject): self.__glOpts = {} def setParentItem(self, item): + """Set this item's parent in the scenegraph hierarchy.""" if self.__parent is not None: self.__parent.__children.remove(self) if item is not None: @@ -98,9 +99,11 @@ class GLGraphicsItem(QtCore.QObject): def parentItem(self): + """Return a this item's parent in the scenegraph hierarchy.""" return self.__parent def childItems(self): + """Return a list of this item's children in the scenegraph hierarchy.""" return list(self.__children) def _setView(self, v): @@ -124,10 +127,15 @@ class GLGraphicsItem(QtCore.QObject): return self.__depthValue def setTransform(self, tr): + """Set the local transform for this object. + Must be a :class:`Transform3D ` instance. This transform + determines how the local coordinate system of the item is mapped to the coordinate + system of its parent.""" self.__transform = Transform3D(tr) self.update() def resetTransform(self): + """Reset this item's transform to an identity transformation.""" self.__transform.setToIdentity() self.update() @@ -148,9 +156,12 @@ class GLGraphicsItem(QtCore.QObject): self.setTransform(tr * self.transform()) def transform(self): + """Return this item's transform object.""" return self.__transform def viewTransform(self): + """Return the transform mapping this item's local coordinate system to the + view coordinate system.""" tr = self.__transform p = self while True: @@ -190,16 +201,24 @@ class GLGraphicsItem(QtCore.QObject): def hide(self): + """Hide this item. + This is equivalent to setVisible(False).""" self.setVisible(False) def show(self): + """Make this item visible if it was previously hidden. + This is equivalent to setVisible(True).""" self.setVisible(True) def setVisible(self, vis): + """Set the visibility of this item.""" self.__visible = vis self.update() def visible(self): + """Return True if the item is currently set to be visible. + Note that this does not guarantee that the item actually appears in the + view, as it may be obscured or outside of the current view area.""" return self.__visible @@ -237,6 +256,10 @@ class GLGraphicsItem(QtCore.QObject): self.setupGLState() def update(self): + """ + Indicates that this item needs to be redrawn, and schedules an update + with the view it is displayed in. + """ v = self.view() if v is None: return diff --git a/pyqtgraph/opengl/MeshData.py b/pyqtgraph/opengl/MeshData.py index 12a9b83b..71e566c9 100644 --- a/pyqtgraph/opengl/MeshData.py +++ b/pyqtgraph/opengl/MeshData.py @@ -247,9 +247,9 @@ class MeshData(object): return self._faceNormals elif indexed == 'faces': if self._faceNormalsIndexedByFaces is None: - norms = np.empty((self._faceNormals.shape[0], 3, 3)) - norms[:] = self._faceNormals[:,np.newaxis,:] - self._faceNormalsIndexedByFaces = norms + norms = np.empty((self._faceNormals.shape[0], 3, 3)) + norms[:] = self._faceNormals[:,np.newaxis,:] + self._faceNormalsIndexedByFaces = norms return self._faceNormalsIndexedByFaces else: raise Exception("Invalid indexing mode. Accepts: None, 'faces'") diff --git a/pyqtgraph/opengl/items/GLMeshItem.py b/pyqtgraph/opengl/items/GLMeshItem.py index 66d54361..5b245e64 100644 --- a/pyqtgraph/opengl/items/GLMeshItem.py +++ b/pyqtgraph/opengl/items/GLMeshItem.py @@ -69,7 +69,11 @@ class GLMeshItem(GLGraphicsItem): self.update() def shader(self): - return shaders.getShaderProgram(self.opts['shader']) + shader = self.opts['shader'] + if isinstance(shader, shaders.ShaderProgram): + return shader + else: + return shaders.getShaderProgram(shader) def setColor(self, c): """Set the default color to use when no vertex or face colors are specified.""" diff --git a/pyqtgraph/opengl/shaders.py b/pyqtgraph/opengl/shaders.py index 8f0d6e1b..8922cd21 100644 --- a/pyqtgraph/opengl/shaders.py +++ b/pyqtgraph/opengl/shaders.py @@ -223,7 +223,7 @@ class Shader(object): try: self.compiled = shaders.compileShader(self.code, self.shaderType) except NullFunctionError: - raise Exception("This OpenGL implementation does not support shader programs; many features on pyqtgraph will not work.") + raise Exception("This OpenGL implementation does not support shader programs; many OpenGL features in pyqtgraph will not work.") except RuntimeError as exc: ## Format compile errors a bit more nicely if len(exc.args) == 3: diff --git a/pyqtgraph/widgets/RawImageWidget.py b/pyqtgraph/widgets/RawImageWidget.py index a780f463..517f4f99 100644 --- a/pyqtgraph/widgets/RawImageWidget.py +++ b/pyqtgraph/widgets/RawImageWidget.py @@ -1,6 +1,7 @@ from pyqtgraph.Qt import QtCore, QtGui try: from pyqtgraph.Qt import QtOpenGL + from OpenGL.GL import * HAVE_OPENGL = True except ImportError: HAVE_OPENGL = False @@ -59,7 +60,6 @@ class RawImageWidget(QtGui.QWidget): p.end() if HAVE_OPENGL: - from OpenGL.GL import * class RawImageGLWidget(QtOpenGL.QGLWidget): """ Similar to RawImageWidget, but uses a GL widget to do all drawing.