diff --git a/documentation/source/3dgraphics/glimageitem.rst b/documentation/source/3dgraphics/glimageitem.rst new file mode 100644 index 00000000..ca40ff41 --- /dev/null +++ b/documentation/source/3dgraphics/glimageitem.rst @@ -0,0 +1,8 @@ +GLImageItem +=========== + +.. autoclass:: pyqtgraph.opengl.GLImageItem + :members: + + .. automethod:: pyqtgraph.opengl.GLImageItem.__init__ + diff --git a/documentation/source/graphicsItems/legenditem.rst b/documentation/source/graphicsItems/legenditem.rst new file mode 100644 index 00000000..e94b0995 --- /dev/null +++ b/documentation/source/graphicsItems/legenditem.rst @@ -0,0 +1,8 @@ +LegendItem +========== + +.. autoclass:: pyqtgraph.LegendItem + :members: + + .. automethod:: pyqtgraph.LegendItem.__init__ + diff --git a/examples/GLImageItem.py b/examples/GLImageItem.py new file mode 100644 index 00000000..6e8c96bd --- /dev/null +++ b/examples/GLImageItem.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +## Add path to library (just for examples; you do not need this) +import sys, os +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) + +from pyqtgraph.Qt import QtCore, QtGui +import pyqtgraph.opengl as gl +import pyqtgraph as pg +import numpy as np +import scipy.ndimage as ndi + +app = QtGui.QApplication([]) +w = gl.GLViewWidget() +w.opts['distance'] = 200 +w.show() + +## create volume data set to slice three images from +shape = (100,100,70) +data = ndi.gaussian_filter(np.random.normal(size=shape), (4,4,4)) +data += ndi.gaussian_filter(np.random.normal(size=shape), (15,15,15))*15 + +## slice out three planes, convert to ARGB for OpenGL texture +levels = (-0.08, 0.08) +tex1 = pg.makeRGBA(data[shape[0]/2], levels=levels)[0] # yz plane +tex2 = pg.makeRGBA(data[:,shape[1]/2], levels=levels)[0] # xz plane +tex3 = pg.makeRGBA(data[:,:,shape[2]/2], levels=levels)[0] # xy plane + +## Create three image items from textures, add to view +v1 = gl.GLImageItem(tex1) +v1.translate(-shape[1]/2, -shape[2]/2, 0) +v1.rotate(90, 0,0,1) +v1.rotate(-90, 0,1,0) +w.addItem(v1) +v2 = gl.GLImageItem(tex2) +v2.translate(-shape[0]/2, -shape[2]/2, 0) +v2.rotate(-90, 1,0,0) +w.addItem(v2) +v3 = gl.GLImageItem(tex3) +v3.translate(-shape[0]/2, -shape[1]/2, 0) +w.addItem(v3) + +ax = gl.GLAxisItem() +w.addItem(ax) + +## Start Qt event loop unless running in interactive mode. +if sys.flags.interactive != 1: + app.exec_() diff --git a/examples/Legend.py b/examples/Legend.py new file mode 100644 index 00000000..f615a6eb --- /dev/null +++ b/examples/Legend.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +import initExample ## Add path to library (just for examples; you do not need this) + +import pyqtgraph as pg +from pyqtgraph.Qt import QtCore, QtGui + +plt = pg.plot() + +l = pg.LegendItem((100,60), (60,10)) # args are (size, position) +l.setParentItem(plt.graphicsItem()) # Note we do NOT call plt.addItem in this case + +c1 = plt.plot([1,3,2,4], pen='r') +c2 = plt.plot([2,1,4,3], pen='g') +l.addItem(c1, 'red plot') +l.addItem(c2, 'green plot') + + +## Start Qt event loop unless running in interactive mode or using pyside. +import sys +if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): + QtGui.QApplication.instance().exec_() diff --git a/examples/__main__.py b/examples/__main__.py index 19514ae1..bfcc6de4 100644 --- a/examples/__main__.py +++ b/examples/__main__.py @@ -31,6 +31,7 @@ examples = OrderedDict([ ('ImageItem - draw', 'Draw.py'), ('Region-of-Interest', 'ROIExamples.py'), ('GraphicsLayout', 'GraphicsLayout.py'), + ('LegendItem', 'Legend.py'), ('Text Item', 'text.py'), ('Linked Views', 'linkedViews.py'), ('Arrow', 'Arrow.py'), @@ -39,6 +40,8 @@ examples = OrderedDict([ ('3D Graphics', OrderedDict([ ('Volumetric', 'GLVolumeItem.py'), ('Isosurface', 'GLMeshItem.py'), + ('Image', 'GLImageItem.py'), + ('Scatter Plot', 'GLScatterPlotItem.py'), ])), ('Widgets', OrderedDict([ ('PlotWidget', 'PlotWidget.py'), diff --git a/graphicsItems/LegendItem.py b/graphicsItems/LegendItem.py new file mode 100644 index 00000000..9443a8d4 --- /dev/null +++ b/graphicsItems/LegendItem.py @@ -0,0 +1,67 @@ +from .GraphicsWidget import GraphicsWidget +from .LabelItem import LabelItem +from ..Qt import QtGui, QtCore +from .. import functions as fn + +__all__ = ['LegendItem'] + +class LegendItem(GraphicsWidget): + """ + Displays a legend used for describing the contents of a plot. + + Note that this item should not be added directly to a PlotItem. Instead, + Make it a direct descendant of the PlotItem:: + + legend.setParentItem(plotItem) + + """ + def __init__(self, size, offset): + GraphicsWidget.__init__(self) + self.setFlag(self.ItemIgnoresTransformations) + self.layout = QtGui.QGraphicsGridLayout() + self.setLayout(self.layout) + self.items = [] + self.size = size + self.offset = offset + self.setGeometry(QtCore.QRectF(self.offset[0], self.offset[1], self.size[0], self.size[1])) + + def addItem(self, item, title): + """ + Add a new entry to the legend. + =========== ======================================================== + Arguments + item A PlotDataItem from which the line and point style + of the item will be determined + title The title to display for this item. Simple HTML allowed. + =========== ======================================================== + """ + label = LabelItem(title) + sample = ItemSample(item) + row = len(self.items) + self.items.append((sample, label)) + self.layout.addItem(sample, row, 0) + self.layout.addItem(label, row, 1) + + def boundingRect(self): + return QtCore.QRectF(0, 0, self.size[0], self.size[1]) + + def paint(self, p, *args): + p.setPen(fn.mkPen(255,255,255,100)) + p.setBrush(fn.mkBrush(100,100,100,50)) + p.drawRect(self.boundingRect()) + + +class ItemSample(GraphicsWidget): + def __init__(self, item): + GraphicsWidget.__init__(self) + self.item = item + + def boundingRect(self): + return QtCore.QRectF(0, 0, 20, 20) + + def paint(self, p, *args): + p.setPen(fn.mkPen(self.item.opts['pen'])) + p.drawLine(2, 18, 18, 2) + + + diff --git a/opengl/items/GLImageItem.py b/opengl/items/GLImageItem.py new file mode 100644 index 00000000..b292a7b7 --- /dev/null +++ b/opengl/items/GLImageItem.py @@ -0,0 +1,87 @@ +from OpenGL.GL import * +from .. GLGraphicsItem import GLGraphicsItem +from pyqtgraph.Qt import QtGui +import numpy as np + +__all__ = ['GLImageItem'] + +class GLImageItem(GLGraphicsItem): + """ + **Bases:** :class:`GLGraphicsItem ` + + Displays image data as a textured quad. + """ + + + def __init__(self, data, smooth=False): + """ + + ============== ======================================================================================= + **Arguments:** + data Volume data to be rendered. *Must* be 3D numpy array (x, y, RGBA) with dtype=ubyte. + (See functions.makeRGBA) + smooth (bool) If True, the volume slices are rendered with linear interpolation + ============== ======================================================================================= + """ + + self.smooth = smooth + self.data = data + GLGraphicsItem.__init__(self) + + def initializeGL(self): + glEnable(GL_TEXTURE_2D) + self.texture = glGenTextures(1) + glBindTexture(GL_TEXTURE_2D, self.texture) + if self.smooth: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) + else: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER) + #glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER) + shape = self.data.shape + + ## Test texture dimensions first + glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA, shape[0], shape[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, None) + if glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH) == 0: + raise Exception("OpenGL failed to create 2D texture (%dx%d); too large for this hardware." % shape[:2]) + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, shape[0], shape[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, self.data.transpose((1,0,2))) + glDisable(GL_TEXTURE_2D) + + #self.lists = {} + #for ax in [0,1,2]: + #for d in [-1, 1]: + #l = glGenLists(1) + #self.lists[(ax,d)] = l + #glNewList(l, GL_COMPILE) + #self.drawVolume(ax, d) + #glEndList() + + + def paint(self): + + glEnable(GL_TEXTURE_2D) + glBindTexture(GL_TEXTURE_2D, self.texture) + + glEnable(GL_DEPTH_TEST) + #glDisable(GL_CULL_FACE) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable( GL_BLEND ) + glEnable( GL_ALPHA_TEST ) + glColor4f(1,1,1,1) + + glBegin(GL_QUADS) + glTexCoord2f(0,0) + glVertex3f(0,0,0) + glTexCoord2f(1,0) + glVertex3f(self.data.shape[0], 0, 0) + glTexCoord2f(1,1) + glVertex3f(self.data.shape[0], self.data.shape[1], 0) + glTexCoord2f(0,1) + glVertex3f(0, self.data.shape[1], 0) + glEnd() + glDisable(GL_TEXTURE_3D) +