Python 在PyQt中,在主窗口和线程之间共享数据的最佳方式是什么

Python 在PyQt中,在主窗口和线程之间共享数据的最佳方式是什么,python,multithreading,qt,pyqt,Python,Multithreading,Qt,Pyqt,我正在用PyQt4编写我的第一个GUI应用程序,我遇到了一个似乎非常基本的问题,但我似乎没有找到一个好的答案: 我使用一个线程连续执行一个重复的任务,而不阻塞主窗口。线程需要来自主窗口的一些信息(例如,spinbox的当前值),这些信息在线程运行时也会发生变化。在这种情况下,在主窗口和线程之间共享此类数据的正确方式是什么 天真地,我可以想出以下几种可能性: 将对主机窗口的引用传递给线程,并使用它检索相关变量的当前值(请参见下面的示例) 在线程中保留变量的副本,并通过在变量更改时发出信号使其与主窗

我正在用PyQt4编写我的第一个GUI应用程序,我遇到了一个似乎非常基本的问题,但我似乎没有找到一个好的答案:

我使用一个线程连续执行一个重复的任务,而不阻塞主窗口。线程需要来自主窗口的一些信息(例如,spinbox的当前值),这些信息在线程运行时也会发生变化。在这种情况下,在主窗口和线程之间共享此类数据的正确方式是什么

天真地,我可以想出以下几种可能性:

  • 将对主机窗口的引用传递给线程,并使用它检索相关变量的当前值(请参见下面的示例)
  • 在线程中保留变量的副本,并通过在变量更改时发出信号使其与主窗口保持同步
  • 使用全局变量
  • 这三个选项很可能适用于我的特定用例(虽然第二个有点复杂),但我觉得应该有一种更好的/更具Pythonic风格的/更类似Qt的方式

    下面是一个最低限度的工作示例,说明我想做什么,在这种情况下,使用选项1:

    从PyQt4导入QtGui,QtCore
    导入时间,sys
    类主窗口(QtGui.QWidget):
    定义初始化(自):
    超级(主窗口,自我)。\uuuu初始化
    self.layout=QtGui.QVBoxLayout(self)
    self.spinbox=QtGui.QSpinBox(self)
    self.spinbox.setValue(1)
    self.layout.addWidget(self.spinbox)
    self.output=QtGui.QLCDNumber(self)
    self.layout.addWidget(self.output)
    self.worker=worker(self)
    self.connect(self.worker、QtCore.SIGNAL('beep')、self.update)
    self.worker.start()
    def更新(自我,编号):
    self.output.display(数字)
    类工作程序(QtCore.QThread):
    定义初始化(自,主机窗口):
    超级(工作者,自我)。\uuuu初始化
    self.host=host\u窗口
    self.running=False
    def运行(自):
    self.running=True
    i=0
    自运行时:
    i+=1
    自我发射(QtCore.SIGNAL('beep'),i)
    sleep\u time=self.host.spinbox.value()
    时间。睡眠(睡眠时间)
    def停止(自):
    self.running=False
    app=QtGui.QApplication(sys.argv)
    窗口=主窗口()
    window.show()
    app.exec()
    

    PS:因为我对PyQt完全没有经验,所以代码不太可能有其他问题,或者问题不清楚。在这种情况下,请随意评论或编辑问题。

    小部件不是线程安全的,请参阅:

    尽管QObject是可重入的,但GUI类,尤其是QWidget和 它的所有子类都不是可重入的。它们只能从以下位置使用: 主线程

    请参见此处的更多定义:


    您应该只在主线程中使用小部件,并使用信号和插槽与其他线程通信

    我不认为全局变量会起作用,但我真的不知道为什么


    本例中如何使用信号:

    #in main
    self.worker = Worker(self.spinbox.value())
    self.worker.beep.connect(self.update)
    self.spinbox.valueChanged.connect(self.worker.update_value)
    
    class Worker(QtCore.QThread):
        beep=QtCore.pyqtSignal(int)
    
        def __init__(self,sleep_time):
            super(Worker, self).__init__()
            self.running = False
            self.sleep_time=sleep_time
    
        def run(self):
            self.running = True
            i = 0
            while self.running:
                i += 1
                self.beep.emit(i)
                time.sleep(self.sleep_time)
    
        def stop(self):
            self.running = False
    
        def update_value(self,value):
            self.sleep_time=value
    

    注意:我使用了新样式的信号和插槽

    尽管我给出了答案,但您的代码似乎工作正常,所以可能是我错了,或者有什么我不明白的地方。@tmoreau。在这种特殊情况下,工作线程只需从自旋框访问值。如果它试图更改该值,则更有可能导致问题(因为它可能导致GUI更新)。但是,调用
    value()
    仍然不是线程安全的,因为用户可以同时更改自旋框的值,而这些更改可能不是原子操作。即便如此,最糟糕的情况可能是工作线程可能会从spin-box中获得一个“过时”值。所以你是说不应该使用选项1和3。您能否给出一些提示,说明如何仅使用线程之间的信号来实现所需的行为?对我来说,这似乎是一个相当基本的任务的可怕的复杂性(至少当我处理许多我想分享的不同信息时)。嗯,我觉得使用线程从来都不是基本的。但我仍然感到惊讶的是,当我发现的每个文档都重复“不要从主线程以外的任何地方更改/访问小部件”时,上面的代码工作得非常好。不管怎样,请参见“编辑”中的信号示例。再次感谢您提供的其他示例!这看起来还不错。:)我仍然认为,在两个地方保存同一信息的两份副本对我来说似乎很奇怪,但这就是我现在所做的。我还修改了代码,以便在主线程中保留尽可能多的信息处理,这样我只需要在线程之间传递最少数量的值。