From f13002b2517aff72f10a6a3b31852e25e09d99ba Mon Sep 17 00:00:00 2001 From: shikishima-TasakiLab <55086542+shikishima-TasakiLab@users.noreply.github.com> Date: Sun, 6 Jun 2021 00:19:54 +0900 Subject: [PATCH] Add "GLTextItem" (#1776) * Add GLTextItem * Fixed DocString. * Delete unnecessary function. * Add `from .items.GLTextItem import *` * Add an example. * [Combines two `isinstance()` into one.](https://github.com/pyqtgraph/pyqtgraph/pull/1776#discussion_r633046120) * [Combines two `isinstance()` into one.](https://github.com/pyqtgraph/pyqtgraph/pull/1776#discussion_r633046120) * [The long code has been broken up into separate lines.](https://github.com/pyqtgraph/pyqtgraph/pull/1776#discussion_r633046234) * [Moved `pos`, `color`, `text`, and `font` to the `__init__` method.](https://github.com/pyqtgraph/pyqtgraph/pull/1776#discussion_r633047216) * Add `import initExample` and fix `mkQApp().exec_()` to `pg.exec()` (https://github.com/pyqtgraph/pyqtgraph/pull/1776#discussion_r633046878, https://github.com/pyqtgraph/pyqtgraph/pull/1776#discussion_r633046781) * Fix `pg.exec()` to `pg.mkQApp().exec()` * Revert "Fix `pg.exec()` to `pg.mkQApp().exec()`" This reverts commit 67d397d8033f3fe0cc27468ea96fe61dc29cc78b. * Remove type-hints. * Fix `glColor4d(float(self.color[0]), float(self.color[1]), float(self.color[2]), float(self.color[3]))` to `glColor4d(*self.color)` * Add `value = fn.glColor(value)` * Remove debug print. * Add GLGridItem and GLAxisItem * Remove if-check for "color" argument * Draw text without using GLUT. * Divide the text position by the device pixel ratio * Fixed bare exceptions to ValueError and TypeError. * Add 'GLTextItem.py' to utils.py. * Fixed a bare exception to ArgumentErrror. * Add `__all__ = ['GLTextItem']` --- examples/GLTextItem.py | 35 ++++++++++ examples/utils.py | 1 + pyqtgraph/opengl/__init__.py | 1 + pyqtgraph/opengl/items/GLTextItem.py | 97 ++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+) create mode 100644 examples/GLTextItem.py create mode 100644 pyqtgraph/opengl/items/GLTextItem.py 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 + ])