From c3dba791d15ad7109c5524be680da1a99ee6a299 Mon Sep 17 00:00:00 2001 From: Ogi Moore Date: Fri, 16 Apr 2021 22:57:43 -0700 Subject: [PATCH] Make equivalent to #807 --- pyqtgraph/opengl/GLGraphicsItem.py | 2 +- pyqtgraph/opengl/__init__.py | 21 ++-- pyqtgraph/opengl/items/GLGraphItem.py | 101 ++++++++++++++++++++ pyqtgraph/opengl/items/GLScatterPlotItem.py | 9 +- 4 files changed, 119 insertions(+), 14 deletions(-) create mode 100644 pyqtgraph/opengl/items/GLGraphItem.py diff --git a/pyqtgraph/opengl/GLGraphicsItem.py b/pyqtgraph/opengl/GLGraphicsItem.py index 66a21f32..7b53895f 100644 --- a/pyqtgraph/opengl/GLGraphicsItem.py +++ b/pyqtgraph/opengl/GLGraphicsItem.py @@ -32,7 +32,7 @@ class GLGraphicsItem(QtCore.QObject): _nextId = 0 def __init__(self, parentItem=None): - QtCore.QObject.__init__(self) + super().__init__() self._id = GLGraphicsItem._nextId GLGraphicsItem._nextId += 1 diff --git a/pyqtgraph/opengl/__init__.py b/pyqtgraph/opengl/__init__.py index 8db7c4a5..d39c8db9 100644 --- a/pyqtgraph/opengl/__init__.py +++ b/pyqtgraph/opengl/__init__.py @@ -4,16 +4,17 @@ from .GLViewWidget import GLViewWidget #from .. import importAll #importAll('items', globals(), locals()) -from .items.GLGridItem import * -from .items.GLBarGraphItem import * -from .items.GLScatterPlotItem import * -from .items.GLMeshItem import * -from .items.GLLinePlotItem import * -from .items.GLAxisItem import * -from .items.GLImageItem import * -from .items.GLSurfacePlotItem import * -from .items.GLBoxItem import * -from .items.GLVolumeItem import * +from .items.GLGraphItem import * +from .items.GLGridItem import * +from .items.GLBarGraphItem import * +from .items.GLScatterPlotItem import * +from .items.GLMeshItem import * +from .items.GLLinePlotItem import * +from .items.GLAxisItem import * +from .items.GLImageItem import * +from .items.GLSurfacePlotItem import * +from .items.GLBoxItem import * +from .items.GLVolumeItem import * from .items.GLTextItem import * from .items.GLGradientLegendItem import * diff --git a/pyqtgraph/opengl/items/GLGraphItem.py b/pyqtgraph/opengl/items/GLGraphItem.py new file mode 100644 index 00000000..1d7e0100 --- /dev/null +++ b/pyqtgraph/opengl/items/GLGraphItem.py @@ -0,0 +1,101 @@ +from OpenGL.GL import * +from ..GLGraphicsItem import GLGraphicsItem +from .GLScatterPlotItem import GLScatterPlotItem +from ...Qt import QtCore, QtGui +from ... import functions as fn + + +__all__ = ['GLGraphItem'] + +class GLGraphItem(GLGraphicsItem): + """A GLGraphItem displays graph information as + a set of nodes connected by lines (as in 'graph theory', not 'graphics'). + Useful for drawing networks, trees, etc. + """ + + def __init__(self, **kwds): + GLGraphicsItem.__init__(self) + + self.edges = None + self.edgeColor = QtGui.QColor(QtCore.Qt.GlobalColor.white) + self.edgeWidth = 1.0 + + self.scatter = GLScatterPlotItem() + self.scatter.setParentItem(self) + self.setData(**kwds) + + def setData(self, **kwds): + """ + Change the data displayed by the graph. + + Parameters + ---------- + edges : np.ndarray + 2D array of shape (M, 2) of connection data, each row contains + indexes of two nodes that are connected. Dtype must be integer + or unsigned. + edgeColor: QColor, array-like, optional. + The color to draw edges. Accepts the same arguments as + :func:`~pyqtgraph.mkColor()`. If None, no edges will be drawn. + Default is (1.0, 1.0, 1.0, 0.5). + edgeWidth: float, optional. + Value specifying edge width. Default is 1.0 + nodePositions : np.ndarray + 2D array of shape (N, 3), where each row represents the x, y, z + coordinates for each node + nodeColor : np.ndarray, QColor, str or array like + 2D array of shape (N, 4) of dtype float32, where each row represents + the R, G, B, A vakues in range of 0-1, or for the same color for all + nodes, provide either QColor type or input for + :func:`~pyqtgraph.mkColor()` + nodeSize : np.ndarray, float or int + Either 2D numpy array of shape (N, 1) where each row represents the + size of each node, or if a scalar, apply the same size to all nodes + **kwds + All other keyword arguments are given to + :meth:`GLScatterPlotItem.setData() ` + to affect the appearance of nodes (pos, color, size, pxMode, etc.) + + Raises + ------ + TypeError + When dtype of edges dtype is not unisnged or integer dtype + """ + + if 'edges' in kwds: + self.edges = kwds.pop('edges') + if self.edges.dtype.kind not in 'iu': + raise TypeError("edges array must have int or unsigned dtype.") + if 'edgeColor' in kwds: + edgeColor = kwds.pop('edgeColor') + self.edgeColor = fn.mkColor(edgeColor) if edgeColor is not None else None + if 'edgeWidth' in kwds: + self.edgeWidth = kwds.pop('edgeWidth') + if 'nodePositions' in kwds: + kwds['pos'] = kwds.pop('nodePositions') + if 'nodeColor' in kwds: + kwds['color'] = kwds.pop('nodeColor') + if 'nodeSize' in kwds: + kwds['size'] = kwds.pop('nodeSize') + self.scatter.setData(**kwds) + self.update() + + def initializeGL(self): + self.scatter.initializeGL() + + def paint(self): + if self.scatter.pos is None \ + or self.edges is None \ + or self.edgeColor is None: + return None + verts = self.scatter.pos + edges = self.edges.flatten() + glEnableClientState(GL_VERTEX_ARRAY) + try: + glColor4f(*self.edgeColor.getRgbF()) + glLineWidth(self.edgeWidth) + glVertexPointerf(verts) + glDrawElements(GL_LINES, edges.shape[0], GL_UNSIGNED_INT, edges) + finally: + glDisableClientState(GL_VERTEX_ARRAY) + return None diff --git a/pyqtgraph/opengl/items/GLScatterPlotItem.py b/pyqtgraph/opengl/items/GLScatterPlotItem.py index 81fc7400..a3d2726d 100644 --- a/pyqtgraph/opengl/items/GLScatterPlotItem.py +++ b/pyqtgraph/opengl/items/GLScatterPlotItem.py @@ -12,7 +12,7 @@ class GLScatterPlotItem(GLGraphicsItem): """Draws points at a list of 3D positions.""" def __init__(self, **kwds): - GLGraphicsItem.__init__(self) + super().__init__() glopts = kwds.pop('glOptions', 'additive') self.setGLOptions(glopts) self.pos = None @@ -138,9 +138,12 @@ class GLScatterPlotItem(GLGraphicsItem): norm[...,0] = self.size else: gpos = self.mapToView(pos.transpose()).transpose() - pxSize = self.view().pixelSize(gpos) + if self.view(): + pxSize = self.view().pixelSize(gpos) + else: + pxSize = self.parentItem().view().pixelSize(gpos) norm[...,0] = self.size / pxSize - + glNormalPointerf(norm) else: glNormal3f(self.size, 0, 0) ## vertex shader uses norm.x to determine point size