PDA

View Full Version : Crashing QGraphicsTextItem when updating HTML in PyQt



illuzioner1
18th March 2020, 00:21
Hello!

I'm working in PyQt and I have created a QGraphicsTextItem that I need to update (the text) from time to time. I need to center the text, so I read that I have to use the TextOptions class and then apply that to the QGraphicsTextItem.document(). Kind of confusing to me why the document is necessary, but that's what I've done.

The problem is when I update the text with QGraphicsTextItem.document().setHTML function it crashes. It gives nothing but an error return value and no other indications why it's crashing. I update the html in on_press() when any key is pressed. I've tried several approaches in setTextString in the DisplayKeyText class, but all of them end up crashing.

Any ideas how to fix this? Thanks!


import sys
from PyQt5 import QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import QPainter, QColor
from pynput import keyboard

class Geo:
def __init__(self, x, y, w, h):
self.x = x
self.y = y
self.w = w
self.h = h

class DisplayKeyText(QtWidgets.QGraphicsTextItem):
def __init__(self, txt, font_size=60, color="white", parent=None):
super(DisplayKeyText, self).__init__(parent)

self.display = txt
self.color = color
self.font_size = font_size

font = QtGui.QFont("Helvetica",font_size) #, QtGui.QFont.Bold)
#self.setHtml("<font color='{}' >{}</font>".format(color, self.display))
self.setFont(font)

txt_width = 100
self.setTextWidth(txt_width)
self.document().setTextWidth(txt_width)
options = QtGui.QTextOption(Qt.AlignHCenter)
self.document().setDefaultTextOption(options)
self.document().setHtml("<font color='{}' >{}</font>".format(color, self.display))

def setTextProps(self, color, font_size, txt=None):
self.setHtml("<font color='{}' size={}>{}</font>"
.format(color, font_size, self.display if txt is None else txt))

def setTextString(self, txt):
self.display = txt
html_txt = "<font color='{}' size={}>{}</font>".format(self.color, self.font_size, txt)
#self.document().setHtml("hello")
self.document().setHtml("<div>hello</div>")
print("x = {}, y = {}, width = {}".format(self.x(), self.y(), self.document().textWidth()))


class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)

self.scene = QtWidgets.QGraphicsScene(self)
self.scene.setItemIndexMethod(QtWidgets.QGraphicsS cene.NoIndex)
self.txt = DisplayKeyText("X", 20)
self.scene.addItem(self.txt)

self.create_central_widget()
self.listener = keyboard.Listener(
on_press=self.on_press,
on_release=self.on_release)
self.listener.start()

def create_central_widget(self):
self.view = QtWidgets.QGraphicsView(self.scene)
self.view.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing)
self.view.setBackgroundBrush((QColor("bisque")))
self.setCentralWidget(self.view)
self.view.scale(3, 3)

def on_press(self, key):
self.txt.setTextString("*")

def on_release(self, key):
pass


def main():
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
ret = app.exec_()
sys.exit(ret)

if __name__ == "__main__":
main()

d_stranz
18th March 2020, 14:49
The QGraphicsTextItem does not have a default QTextDocument; you have to create one and set it before you can use it. So your call to self.document() is likely returning a null instance, thus the crash. QGraphicsTextItem does not take ownership of the document, allowing the document to be shared with multiple views.

illuzioner1
18th March 2020, 15:39
Ahh, thank you, I see. It's strange that it didn't then crash when it started as there is the setHTML in the constructor. If there's no associated text document it seems that would have crashed it too.

d_stranz
18th March 2020, 16:41
Probably QGraphicsTextItem is implemented with the optional ability to use a QTextDocument, and has a default means to simply use an HTML or simple text string if no document is set.

illuzioner1
24th March 2020, 01:35
Thank you for the help. I couldn't find why this was crashing, but I was able to convert it to a simpleTextItem which is sufficient for this particular purpose. I'll have to revisit later, though, because I'll need the more complex one later.