diff --git a/examples/GLTextItem.py b/examples/GLTextItem.py new file mode 100644 index 00000000..ab6e7b31 --- /dev/null +++ b/examples/GLTextItem.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +""" +Simple examples demonstrating the use of GLTextItem. + +""" + +import initExample + +import pyqtgraph as pg +from pyqtgraph.Qt import QtCore, QtGui, mkQApp +import pyqtgraph.opengl as gl + +app = mkQApp("GLTextItem Example") + +gvw = gl.GLViewWidget() +gvw.show() +gvw.setWindowTitle('pyqtgraph example: GLTextItem') + +griditem = gl.GLGridItem() +griditem.setSize(10, 10) +griditem.setSpacing(1, 1) +gvw.addItem(griditem) + +axisitem = gl.GLAxisItem() +gvw.addItem(axisitem) + +txtitem1 = gl.GLTextItem(pos=(0.0, 0.0, 0.0), text='text1') +gvw.addItem(txtitem1) + +txtitem2 = gl.GLTextItem() +txtitem2.setData(pos=(1.0, -1.0, 2.0), color=(127, 255, 127, 255), text='text2') +gvw.addItem(txtitem2) + +if __name__ == '__main__': + pg.exec() diff --git a/examples/utils.py b/examples/utils.py index 346c6bca..c506b5d4 100644 --- a/examples/utils.py +++ b/examples/utils.py @@ -69,6 +69,7 @@ examples = OrderedDict([ ('Line Plot', 'GLLinePlotItem.py'), ('Mesh', 'GLMeshItem.py'), ('Image', 'GLImageItem.py'), + ('Text', 'GLTextItem.py'), ])), ('Widgets', OrderedDict([ ('PlotWidget', 'PlotWidget.py'), diff --git a/pyqtgraph/opengl/__init__.py b/pyqtgraph/opengl/__init__.py index 931003e4..8e53e81e 100644 --- a/pyqtgraph/opengl/__init__.py +++ b/pyqtgraph/opengl/__init__.py @@ -14,6 +14,7 @@ from .items.GLImageItem import * from .items.GLSurfacePlotItem import * from .items.GLBoxItem import * from .items.GLVolumeItem import * +from .items.GLTextItem import * from .MeshData import MeshData ## for backward compatibility: diff --git a/pyqtgraph/opengl/items/GLTextItem.py b/pyqtgraph/opengl/items/GLTextItem.py new file mode 100644 index 00000000..0dc7205d --- /dev/null +++ b/pyqtgraph/opengl/items/GLTextItem.py @@ -0,0 +1,97 @@ +from OpenGL.GL import * +import numpy as np +from pyqtgraph.Qt import QtCore, QtGui +from pyqtgraph.opengl.GLGraphicsItem import GLGraphicsItem +import pyqtgraph.functions as fn + +__all__ = ['GLTextItem'] + +class GLTextItem(GLGraphicsItem): + """Draws text in 3D.""" + + def __init__(self, **kwds): + """All keyword arguments are passed to setData()""" + GLGraphicsItem.__init__(self) + glopts = kwds.pop('glOptions', 'additive') + self.setGLOptions(glopts) + + self.pos = np.array([0.0, 0.0, 0.0]) + self.color = QtCore.Qt.white + self.text = '' + self.font = QtGui.QFont('Helvetica', 16) + + self.setData(**kwds) + + def setData(self, **kwds): + """ + Update the data displayed by this item. All arguments are optional; + for example it is allowed to update text while leaving colors unchanged, etc. + + ==================== ================================================== + **Arguments:** + ------------------------------------------------------------------------ + pos (3,) array of floats specifying text location. + color QColor or array of ints [R,G,B] or [R,G,B,A]. (Default: Qt.white) + text String to display. + font QFont (Default: QFont('Helvetica', 16)) + ==================== ================================================== + """ + args = ['pos', 'color', 'text', 'font'] + for k in kwds.keys(): + if k not in args: + raise ArgumentError('Invalid keyword argument: %s (allowed arguments are %s)' % (k, str(args))) + for arg in args: + if arg in kwds: + value = kwds[arg] + if arg == 'pos': + if isinstance(value, np.ndarray): + if value.shape != (3,): + raise ValueError('"pos.shape" must be (3,).') + elif isinstance(value, (tuple, list)): + if len(value) != 3: + raise ValueError('"len(pos)" must be 3.') + elif arg == 'color': + value = fn.mkColor(value) + elif arg == 'font': + if isinstance(value, QtGui.QFont) is False: + raise TypeError('"font" must be QFont.') + setattr(self, arg, value) + self.update() + + def paint(self): + if len(self.text) < 1: + return + self.setupGLState() + + modelview = glGetDoublev(GL_MODELVIEW_MATRIX) + projection = glGetDoublev(GL_PROJECTION_MATRIX) + viewport = glGetIntegerv(GL_VIEWPORT) + + text_pos = self.__project(self.pos, modelview, projection, viewport) + text_pos[1] = viewport[3] - text_pos[1] + + text_pos /= self.view().devicePixelRatio() + + painter = QtGui.QPainter(self.view()) + painter.setPen(self.color) + painter.setFont(self.font) + painter.setRenderHints(QtGui.QPainter.Antialiasing | QtGui.QPainter.TextAntialiasing) + painter.drawText(text_pos[0], text_pos[1], self.text) + painter.end() + + def __project(self, obj_pos, modelview, projection, viewport): + obj_vec = np.append(np.array(obj_pos), [1.0]) + + view_vec = np.matmul(modelview.T, obj_vec) + proj_vec = np.matmul(projection.T, view_vec) + + if proj_vec[3] == 0.0: + return + + proj_vec[0:3] /= proj_vec[3] + + return np.array([ + viewport[0] + (1.0 + proj_vec[0]) * viewport[2] / 2.0, + viewport[1] + (1.0 + proj_vec[1]) * viewport[3] / 2.0, + (1.0 + proj_vec[2]) / 2.0 + ])