Skip to main content
 首页 » 编程设计

python之改变QLabel大小时PyQt5 "Timers cannot be started from another thread"错误

2025年05月04日47qlqwjy

我在 Python 3.5 中遇到 PyQt5 的奇怪问题。 我有两个类,FrontEnd(QWidget) 和 TimerThread(Thread)。我在 FrontEnd 的初始化函数中定义了一些 QLabel,它们都可以正常工作。

显示的是FrontEnd的几个相关函数:

def update_ui(self): 
    ret, frame = self.cam_capture.read() 
 
    if self.results_pending: 
        if not path.isfile('output.jpg'): 
            self.results_pending = False 
            with open('.out') as content_file: 
                content = content_file.readlines()[2:-2] 
            system('rm .out') 
            self.handle_image_classification(content) 
 
    if self.take_picture: 
        cv2.imwrite('output.jpg', frame) 
        self.user_prompt.setText('Please wait...') 
        system('./classifyimage.py --mean mean.binaryproto --nogpu --labels labels.txt model.caffemodel deploy.prototxt output.jpg > .out && rm output.jpg') 
        self.take_picture = False 
        self.results_pending = True 
 
    image = QImage(frame, frame.shape[1], frame.shape[0], QImage.Format_RGB888).rgbSwapped() 
    pix = QPixmap.fromImage(image) 
    self.video_frame.setPixmap(pix) 
 
def update_bar_graph(self, data): 
    palette = QPalette() 
    palette.setColor(QPalette.Background, Qt.white) 
    for i in range(0, 8): 
        self.bar_graph_labels[i].setText(str(data[i]) + "%") 
        height = int(data[i] * 5) 
        self.bar_graph[i].setFixedSize(self.bar_width, height) 
        self.bar_graph[i].move(1280 + (i * (self.bar_width + self.bar_spacing)), 640 - height) 
 
def handle_image_classification(self, raw_output): 
    data = [None] * 8 
    for i in range(0, len(raw_output)): 
        raw_output[i] = raw_output[i].strip() 
        data[int(raw_output[i][-2]) - 1] = float(raw_output[i][:-10]) 
    self.update_bar_graph(data) 

以及整个 TimerThread 类:

class TimerThread(Thread): 
    front_end = None 
 
    def __init__(self, event): 
        Thread.__init__(self) 
        self.stopped = event 
 
    def run(self): 
        while not self.stopped.wait(0.02):     
            FrontEnd.update_ui(self.front_end) 

(TimerThread 的front_end 元素设置在FrontEnd 的init 上)

问题出在 update_bar_graph 函数中。当 setFixedSize 调用被注释掉时,程序运行正常,尽管在我的应用程序中没有正确显示条形图的条形图(它们是 QLabels)。 move 函数似乎运行正常。但是,setFixedSize 调用会导致此错误:

QObject::startTimer: Timers cannot be started from another thread 
QObject::startTimer: Timers cannot be started from another thread 
QObject::killTimer: Timers cannot be stopped from another thread 
QObject::startTimer: Timers cannot be started from another thread 

我完全不知道为什么会发生这种情况,也不知道为什么 move 函数(看似本质上相似)却能正常工作。任何帮助将非常感激。 (如果我应该使用不同类型的计时器类或不同的方法在 PyQt 中绘制大矩形,我愿意接受任何此类建议)。

编辑:

这是一些奇怪的东西。第二天我运行了两次,没有更改代码。 (我认为......)有一次条形图没有显示但没有抛出错误。另一次我得到这个:

7fdfaf931000-7fdfaf932000 r--p 0007a000 08:07 655633                     /usr/lib/x86_64-linux-gnu/libQt5DBus.so.5.5.1 
7fdfaf932000-7fdfaf933000 rw-p 0007b000 08:07 655633                     /usr/lib/x86_64-linux-gnu/libQt5DBus.so.5.5.1 
7fdfaf933000-7fdfaf934000 rw-p 00000000 00:00 0  
7fdfaf934000-7fdfaf971000 r-xp 00000000 08:07 667112                     /usr/lib/x86_64-linux-gnu/libxkbcommon.so.0.0.0 
7fdfaf971000-7fdfafb70000 ---p 0003d000 08:07 667112                     /usr/lib/x86_64-linux-gnu/libxkbcommon.so.0.0.0 
7fdfafb70000-7fdfafb72000 r--p 0003c000 08:07 667112                     /usr/lib/x86_64-linux-gnu/libxkbcommon.so.0.0.0 
7fdfafb72000-7fdfafb73000 rw-p 0003e000 08:07 667112                     /usr/lib/x86_64-linux-gnu/libxkbcommon.so.0.0.0 
7fdfafb73000-7fdfafb7a000 r-xp 00000000 08:07 667110                     /usr/lib/x86_64-linux-gnu/libxkbcommon-x11.so.0.0.0 
7fdfafb7a000-7fdfafd79000 ---p 00007000 08:07 667110                     /usr/lib/x86_64-linux-gnu/libxkbcommon-x11.so.0.0.0 

我想我可能发现了 PyQt5 中的错误。

请您参考如下方法:

如@mata所述您的代码不是线程安全的。这很可能是错误行为的来源,在进一步调试之前肯定应该修复(this 是相关的)。

它是线程不安全的原因是因为您直接从辅助线程与 GUI 对象进行交互。相反,您应该从您的线程发出一个信号到主线程中的一个插槽,您可以在其中安全地更新您的 GUI。但是,这需要您使用 QThread,根据 this post 建议使用它。 .

这需要进行以下更改:

class TimerThread(QThread): 
    update = pyqtSignal() 
 
    def __init__(self, event): 
        QThread.__init__(self) 
        self.stopped = event 
 
    def run(self): 
        while not self.stopped.wait(0.02):     
            self.update.emit() 
 
class FrontEnd(QWidget): 
    def __init__(self): 
        super().__init__() 
 
        ... # code as in your original 
 
        stop_flag = Event()     
        self.timer_thread = TimerThread(stop_flag) 
        self.timer_thread.update.connect(self.update_ui) 
        self.timer_thread.start() 

我还修改了您的代码,以便它在 FrontEnd 对象中存储对 TimerThread 的引用,这样线程就不会被垃圾回收。

我还要补充一点,这是一种每 0.02 秒触发一次更新的过于复杂的方法。您可以使用 QTimer 来调用 update_ui 方法并完全放弃线程,但我采用的方法是您稍后可能希望对线程执行更复杂的操作,所以已经演示了如何安全地进行操作!