From c3dba791d15ad7109c5524be680da1a99ee6a299 Mon Sep 17 00:00:00 2001 From: Ogi Moore Date: Fri, 16 Apr 2021 22:57:43 -0700 Subject: [PATCH 1/3] 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 From 13ab4de209e4f462d075482d8328911b60053b86 Mon Sep 17 00:00:00 2001 From: Ogi Moore Date: Fri, 16 Apr 2021 23:26:20 -0700 Subject: [PATCH 2/3] Add really basic GLGraphItem example --- examples/GLGraphItem.py | 47 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 examples/GLGraphItem.py diff --git a/examples/GLGraphItem.py b/examples/GLGraphItem.py new file mode 100644 index 00000000..a2b75a92 --- /dev/null +++ b/examples/GLGraphItem.py @@ -0,0 +1,47 @@ +""" +Demonstrates use of GLGraphItem + +""" + +## Add path to library (just for examples; you do not need this) +import initExample + +import pyqtgraph as pg +import pyqtgraph.opengl as gl +import numpy as np + +app = pg.mkQApp("GLGraphItem Example") +w = gl.GLViewWidget() +w.setCameraPosition(distance=20) +w.show() + +edges = np.array([ + [0, 2], + [0, 3], + [1, 2], + [1, 3], + [2, 3] +]) + +nodes = np.array( + [ + [0, 0, 0], + [1, 0, 0], + [0, 1, 0], + [1, 1, 1] + ] +) + +edgeColor=pg.glColor("w") + +gi = gl.GLGraphItem( + edges=edges, + nodePositions=nodes, + edgeWidth=1., + nodeSize=10. +) + +w.addItem(gi) + +if __name__ == "__main__": + pg.exec() From d54da71138926f0e23f4ee5922dd2709408e39b9 Mon Sep 17 00:00:00 2001 From: Ogi Moore Date: Sun, 1 Aug 2021 22:21:22 -0700 Subject: [PATCH 3/3] Add docs page --- doc/source/3dgraphics/glgraphitem.rst | 7 +++++++ doc/source/3dgraphics/index.rst | 1 + 2 files changed, 8 insertions(+) create mode 100644 doc/source/3dgraphics/glgraphitem.rst diff --git a/doc/source/3dgraphics/glgraphitem.rst b/doc/source/3dgraphics/glgraphitem.rst new file mode 100644 index 00000000..026cda50 --- /dev/null +++ b/doc/source/3dgraphics/glgraphitem.rst @@ -0,0 +1,7 @@ +GLGraphItem +=========== + +.. autoclass:: pyqtgraph.opengl.GLGraphItem + :members: + + .. automethod:: pyqtgraph.opengl.GLGraphItem.__init__ diff --git a/doc/source/3dgraphics/index.rst b/doc/source/3dgraphics/index.rst index 08202d31..e0b2294b 100644 --- a/doc/source/3dgraphics/index.rst +++ b/doc/source/3dgraphics/index.rst @@ -19,6 +19,7 @@ Contents: glviewwidget glgriditem + glgraphitem glsurfaceplotitem glvolumeitem glimageitem