I am trying to remove the banner effect from the pyqtgraph.opengl.GLTextItem
such that whenever the camera angle changes, the angle of the text item is fixed. I can get the size of the text item to be fixed, but I am unable to figure out how to stop the text item from facing the camera view.
import numpy as np
from PySide6 import QtWidgets, QtGui
import pyqtgraph as pg
from pyqtgraph.opengl import GLViewWidget, GLTextItem
def euler_matrix(azimuth_deg, elevation_deg):
a, e = np.radians([azimuth_deg, elevation_deg])
ca, sa = np.cos(a), np.sin(a)
ce, se = np.cos(e), np.sin(e)
Rz = np.array([[ ca, -sa, 0],
[ sa, ca, 0],
[ 0, 0, 1]])
Rx = np.array([[1, 0, 0],
[0, ce, -se],
[0, se, ce]])
return Rx @ Rz
class CustomText(GLTextItem):
def __init__(self, *args, worldTextHeight=0.1, **kwargs):
super().__init__(*args, **kwargs)
self.worldTextHeight=worldTextHeight
tf = np.eye(4)
tf[:3, :3] = euler_matrix(0, 0)
self.fixed=tf
self.setTransform(tf)
def update_text_rotation(self):
self.setTransform(self.fixed)
def update_text_size(self):
unit_per_pixel = self.view().pixelSize(np.array(self.pos))
pixel_height = max(1, int(self.worldTextHeight / unit_per_pixel))
f = QtGui.QFont(self.font)
f.setPixelSize(pixel_height)
self.font = f
def paint(self):
self.update_text_rotation()
self.update_text_size()
super().paint()
class CustomGLView(GLViewWidget):
def __init__(self):
super().__init__()
self.text_item = CustomText(pos=(0,0,0), text="text item", worldTextHeight=1)
self.addItem(self.text_item)
self.addItem(pg.opengl.GLAxisItem())
self.setCameraPosition(distance=5)
if __name__ == '__main__':
app = QtWidgets.QApplication([])
view = CustomGLView()
view.show()
app.exec()
Unfortunately, there is no built-in way to do this. I made a workaround for this, by first rendering the text as an image using PIL, then converting that image to a numpy array, and displaying that numpy array as a pyqtgraph.opengl.ImageItem
.
import numpy as np
import pyqtgraph.opengl as gl
from PySide6.QtWidgets import QApplication
from PIL import Image, ImageDraw, ImageFont
import sys
def string_to_image_array(text, font="arial.ttf", font_size=70, image_size=(400, 100), bg_color=(0, 0, 0, 0), text_color=(255, 255, 255, 255)):
"""
Convert a string into a numpy array representing an image of the text.
text (str): The string to render.
font_size (int): Font size for the text.
image_size (tuple): (width, height) of the output image.
bg_color (tuple): Background color (R, G, B).
text_color (tuple): Text color (R, G, B).
Returns:
numpy.ndarray: Image array (H x W x 3) for RGB.
"""
img = Image.new("RGBA", image_size, bg_color)
draw = ImageDraw.Draw(img)
# Specify a path to a .ttf file
font = ImageFont.truetype(font, font_size)
# Calculate text position (centered)
text_width, text_height = draw.textbbox((0, 0), text, font=font)[2:]
x = (image_size[0] - text_width) // 2
y = (image_size[1] - text_height) // 2
# Draw the text
draw.text((x, y), text, font=font, fill=text_color)
# Convert to numpy array
img_array = np.array(img)
return img_array
app = QApplication(sys.argv)
# Create a 3D GLViewWidget
view = gl.GLViewWidget()
view.setGeometry(0, 0, 800, 600)
view.show()
# Add grid
grid = gl.GLGridItem()
grid.setSize(10, 10)
grid.setSpacing(1, 1)
view.addItem(grid)
# Add text
text = "Hello, World!"
img_array = string_to_image_array(text)
text_img = gl.GLImageItem(img_array)
factor = 0.01
text_img.scale(factor, factor, factor)
text_img.rotate(90, 0, 1, 0)
text_img.translate(0, 0, 2)
view.addItem(text_img)
if __name__ == '__main__':
sys.exit(app.exec())
First a gl.GLViewWidget()
is created to which a grid is added for visualisation. text
specifies the text. Then img_array
is set to an RGBA image of the text using string_to_image_array
. This function creates an image of the text and converts it to a numpy array. Then this array is drawn as an image using gl.GLImageItem
. It is scaled so that it isn't huge, and rotated and translated to appear above the grid.
To get a better resolution, increase font_size
, and the x-coordinate of image_size
such that the image still contains the entire text. The image does of course get a bit bigger if you do this so change factor
to scale the image accordingly. The size of the text can be changed by adjusting factor
. To adjust the position and orientation change the arguments of text_img.rotate
and text_img.translate
accordingly. The color of the text can be changed by passing the desired color to the function. The font can be changed by passing a path to a ttf-file with a font.