.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "_gallery/other/text_with_qt.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code .. rst-class:: sphx-glr-example-title .. _sphx_glr__gallery_other_text_with_qt.py: Render Text with Qt =================== This example was created before we had text rendering in pygfx. It renders text using qt in an overlay. This overly technique can still be useful in specific use-cases. .. GENERATED FROM PYTHON SOURCE LINES 11-19 .. code-block:: default import numpy as np import pygfx as gfx from PySide6 import QtWidgets, QtCore, QtGui from wgpu.gui.qt import WgpuCanvas .. GENERATED FROM PYTHON SOURCE LINES 20-96 .. code-block:: default class CanvasWithOverlay(WgpuCanvas): """A Qt canvas with support for 2D overlay.""" overlay = None def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._text_labels = [] self.overlay = Overlay(self) self.overlay.setGeometry(self.geometry()) self.overlay.show() self.overlay.raise_() def resizeEvent(self, event): # noqa: N802 super().resizeEvent(event) if self.overlay: self.overlay.setGeometry(self.geometry()) def moveEvent(self, event): # noqa: N802 super().moveEvent(event) if self.overlay: self.overlay.setGeometry(self.geometry()) def set_text_labels(self, wobjects): """Set text labels to overlay. Must be a list of TextOverlay objects.""" self._text_labels = wobjects class Overlay(QtWidgets.QWidget): """Overlay that draws 2D featues using the canvas API. We cannot draw in the wgpu widget directly, because that widget has no paint engine (we have to remove it to prevent Qt from overwriting the screen that we draw with wgpu). We can also not use a normal widget overlaid over it, because Qt would then do the compositing, but the wgpu widget draws directly to screen, so the overlay widget would get a black background. Therefore, the overlay widget is a toplevel widget (a window) that we keep exactly on top of the actual widget. Not pretty, but it seems to work. I am not sure how well this holds up on other platforms. """ def __init__(self, *args, **kwargs): super().__init__( *args, **kwargs, ) # We want a tranlucent background, and no window frame. # Setting the Tool flag make it always on top of the parent widget. self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True) self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool) # No background, just in case. self.setAutoFillBackground(False) def paintEvent(self, event): # noqa: N802 painter = QtGui.QPainter() if not painter.begin(self): return # Draw text labels text_labels = self.parent()._text_labels features = None for label in text_labels: if features != (label.size, label.color): features = label.size, label.color painter.setFont(QtGui.QFont("Arial", label.size)) painter.setPen(QtGui.QColor(label.color)) painter.drawText(QtCore.QPointF(*label.ppos), label.text) painter.end() .. GENERATED FROM PYTHON SOURCE LINES 97-198 .. code-block:: default from pygfx.renderers import Renderer # noqa: E402 class TextOverlay(gfx.WorldObject): """A text label that can get overlaid on the visualization using e.q. Qt.""" def __init__(self, text, size=12, color="f000000", position=(0, 0, 0)): super().__init__() self.text = str(text) self.size = int(size) self.color = color self.position = gfx.linalg.Vector3(*position) # Other options: font, bold, background, pixel-offset, alignment class QtOverlayRenderer(Renderer): """A special renderer that can draw certain 2D overlays over a Qt canvas. Currently only text (TextOverlay objects). """ def __init__(self, canvas): self._canvas = canvas # todo: allow passing a normal qt canvas? # assert isinstance(canvas, wgpu.gui.qt.QtWgpuCanvas) assert isinstance(canvas, CanvasWithOverlay) def render(self, scene: gfx.WorldObject, camera: gfx.Camera): """Main render method, called from the canvas.""" logical_size = self._canvas.get_logical_size() # We assume that this call is preceded by a call to the wgpu renderer, # so we don't need to apply any updates. # scene.update_matrix_world() # camera.set_viewport_size(*logical_size) # camera.update_matrix_world() # camera.update_projection_matrix() # Get the list of objects to render def visit(wobject): if wobject.visible and isinstance(wobject, TextOverlay): q.append(wobject) q = [] scene.traverse(visit) # Set the pixel position of each text overlay object for wobject in q: pos = wobject.position.clone() pos = pos.apply_matrix4(wobject.matrix_world).project(camera) # I don't understand this 0.25 value. I would expect it to be 0.5 # but for some reason it needs to be 0.25. pos_pix = ( (+pos.x * 0.25 + 0.5) * logical_size[0], (-pos.y * 0.25 + 0.5) * logical_size[1], ) wobject.ppos = pos_pix # Store the labels for the overlay, and schedule a draw. self._canvas.set_text_labels(q) self._canvas.overlay.update() app = QtWidgets.QApplication([]) canvas = CanvasWithOverlay() renderer = gfx.WgpuRenderer(canvas) overlay_renderer = QtOverlayRenderer(canvas) scene = gfx.Scene() positions = np.random.normal(0, 1, (20, 3)).astype(np.float32) geometry = gfx.Geometry(positions=positions) material = gfx.PointsMaterial(size=10, color=(0, 1, 0.5, 0.7)) points = gfx.Points(geometry, material) scene.add(points) for point in positions: scene.add( TextOverlay( f"{point[0]:0.1f}, {point[1]:0.1f}", position=point, color="#ffffff" ) ) scene.add( gfx.Background(None, gfx.BackgroundMaterial((0.04, 0.0, 0, 1), (0, 0.0, 0.04, 1))) ) camera = gfx.OrthographicCamera(3, 3) def aninmate(): renderer.render(scene, camera) overlay_renderer.render(scene, camera) if __name__ == "__main__": canvas.request_draw(aninmate) app.exec() .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.000 seconds) .. _sphx_glr_download__gallery_other_text_with_qt.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: text_with_qt.py ` .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: text_with_qt.ipynb ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_