定时刷新QGraphicsTextItem的文字时报错

基于QGraphcisview做了个绘图软件,上面用QGraphicsTextItem显示了几个文本图元,在用线程定时器定时刷新文本内容时(方法:self.setPlainText(newText)),提示:QObject: Cannot create children for a parent that is in a different thread.;
但是调用self.setRotation(self._angle)可以旋转文本,
改变文本值与旋转实现方法有何不同呢,self.setPlainText是重新赋值,不是新建呀。

这是因为Qt的GUI部分不是线程安全的。只有主线程才能更改UI元素。如果你想在非主线程中更改UI元素,你需要使用Qt提供的线程信号和槽机制。

在你的情况下,调用 self.setRotation(self._angle) 不会引发错误,因为它只更改了QGraphicsTextItem的旋转属性,并不涉及到文本图元在场景中的父对象。而调用 self.setPlainText(newText) 则涉及到重新创建文本图元,因此会引发错误。

为了解决这个问题,你可以在主线程中使用QTimer来定时更改文本图元的属性。在定时器的回调函数中,使用信号和槽机制将更新后的文本信息发送给主线程,然后在主线程中更新QGraphicsTextItem的文本属性。具体实现方式如下:


from PyQt5.QtCore import QObject, pyqtSignal, QTimer
from PyQt5.QtGui import QGraphicsTextItem

class MyTextItem(QGraphicsTextItem):
    update_text = pyqtSignal(str)
    
    def __init__(self, text, parent=None):
        super().__init__(text, parent)
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.updateText)
        self.timer.start(1000)  # 1秒钟刷新一次

    def updateText(self):
        # 在这里更新文本信息
        new_text = "Hello World"
        self.update_text.emit(new_text)

class MainWindow(QObject):
    def __init__(self):
        super().__init__()
        self.textItem = MyTextItem("Hello", parent=None)
        self.textItem.update_text.connect(self.updateText)

    def updateText(self, new_text):
        # 在这里更新QGraphicsTextItem的文本属性
        self.textItem.setPlainText(new_text)

在这个示例中,我们创建了一个名为 MyTextItem 的QGraphicsTextItem的子类,并使用QTimer定期更新文本信息。在定时器回调函数中,我们发出了一个信号,将新的文本信息发送到主线程。然后在主线程中,我们连接了 update_text 信号到 updateText 槽函数中,并在 updateText 函数中更新了QGraphicsTextItem的文本属性。

“Devil组”引证GPT后的撰写:

  • 这个问题是由于Qt的线程模型导致的。Qt中的对象有父子关系,如果一个对象是在主线程中创建的,它的子对象也应该在主线程中创建,否则就会出现上述错误。
  • 在你的情况下,当你在一个不同的线程中尝试创建一个QGraphicsTextItem对象时,它会尝试将这个新创建的对象作为子对象添加到主线程中已有的对象上。由于这个新对象不是在主线程中创建的,因此会导致上述错误。
  • 而对于setRotation方法,它不会创建新的QGraphicsTextItem对象,而只是更改已有对象的属性,因此不会出现上述错误。
  • 解决这个问题的方法是,将文本更新的操作放到主线程中执行。你可以使用Qt提供的信号和槽机制来实现这个目的。在你的线程中,发出一个信号,然后在主线程中连接这个信号,将信号与一个槽函数关联。在槽函数中,执行setPlainText操作,这样就可以避免线程问题了。

我现在是用class MyTextItem(QGraphicsTextItem):创建了一个文本类,
在主页面窗口中调用文本类建立了10个文本图元,
然后在主窗体中建立了一个线程定时器,在其中定时分别修改10个文本图元的文字。
不是在文本类中用定时器,
每个文本图元要能分别赋予不同的值,定时器中要用遍历查找各图元并赋予相应的不同值(图元及对应值存储于一个字典中)