PyQt:将信号连接到插槽以启动后台操作

时间:2013-12-23 22:30:38

标签: python multithreading pyqt signals-slots qthread

我有以下代码在更新ui(scan_value)中的进度条时执行后台操作(progress)。 scan_value遍历obj中的某个值,每次更改值时都会发出一个信号(value_changed)。由于这里不相关的原因,我必须将它包装在另一个线程中的对象(Scanner)中。当按钮scanclicked时,会调用扫描仪。这是我的问题...以下代码工作正常(即进度条按时更新)。

# I am copying only the relevant code here.

def update_progress_bar(new, old):
    fraction = (new - start) / (stop - start)
    progress.setValue(fraction * 100)

obj.value_changed.connect(update_progress_bar)

class Scanner(QObject):

    def scan(self):
        scan_value(start, stop, step)
        progress.setValue(100)

thread = QThread()
scanner = Scanner()
scanner.moveToThread(thread)
thread.start()

scan.clicked.connect(scanner.scan)

但如果我将最后一部分更改为:

thread = QThread()
scanner = Scanner()
scan.clicked.connect(scanner.scan) # This was at the end!
scanner.moveToThread(thread)
thread.start()

进度条仅在最后更新(我的猜测是所有内容都在同一个线程上运行)。如果在将对象接收对象移动到线程之前将信号连接到插槽,那么它是否无关紧要。

3 个答案:

答案 0 :(得分:10)

在将worker对象移动到另一个线程之前或之后进行连接无关紧要。引用Qt docs

  

Qt :: AutoConnection - 如果信号是从另一个发出的   线程比接收对象,信号排队,表现为    Qt的:: QueuedConnection 。否则,直接调用插槽,   表现为 Qt :: DirectConnection 连接类型是   确定何时发出信号。 [强调补充]

因此,只要type的{​​{1}}参数设置为connect(这是默认值),Qt就应该确保以适当的方式发出信号。

示例代码的问题更可能出在插槽而不是信号。信号连接到的python方法可能需要使用pyqtSlot decorator标记为Qt插槽:

QtCore.Qt.AutoConnection

修改

应该澄清的是,只有在最新版本的Qt中,才会在发出信号时确定连接类型。在4.4版本中引入了此行为(以及Qt的多线程支持中的其他一些更改)。

此外,可能值得进一步扩展PyQt特定问题。在PyQt中,信号可以连接到Qt插槽,另一个信号或任何python可调用。对于后一种情况,在内部创建一个代理对象,它包装python可调用并提供Qt信号/插槽机制所需的插槽。

这个代理对象是导致问题的原因。一旦创建了代理,PyQt就会这样做:

from QtCore import pyqtSlot

class Scanner(QObject):

    @pyqtSlot()
    def scan(self):
        scan_value(start, stop, step)
        progress.setValue(100)

如果接收对象移动到其线程后连接,则可以正常使用;但是如果它是之前的,那么代理将保留在主线程中。

使用 if (rx_qobj) proxy->moveToThread(rx_qobj->thread()); 装饰器可以完全避免这个问题,因为它可以更直接地创建Qt插槽,而根本不使用代理对象。

最后,还应该注意到此问题目前不会影响PySide。

答案 1 :(得分:0)

在我的情况下,通过访问到初始化工作线程的位置的连接,我的问题得以解决,因为我正在访问的对象仅在实例化另一个线程中的Worker Object类之后才存在。

self.createWorkerThread()

之后简单地连接信号

致谢

答案 2 :(得分:-1)

这与Qt的连接类型有关。

http://pyqt.sourceforge.net/Docs/PyQt5/signals_slots.html#connect

http://qt-project.org/doc/qt-4.8/qt.html#ConnectionType-enum

如果两个对象都存在于同一个线程中,则会生成标准连接类型,从而导致普通函数调用。在这种情况下,耗时的操作发生在GUI线程和接口块中。

如果连接类型是消息传递样式连接,则使用在另一个线程中处理的消息发出信号。 GUI线程现在可以自由更新用户界面。

如果未在connect函数中指定连接类型,则会自动检测类型。