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

2024-04-19 22:12:12 发布

您现在位置:Python中文网/ 问答频道 /正文

下面的代码执行后台操作(scan_value),同时更新ui中的进度条(progress)。scan_value遍历obj中的某个值,每次更改该值时都会发出信号(value_changed)。出于与此无关的原因,我必须将其包装在另一个线程中的对象(Scanner)中。当a按钮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()

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


Tags: the对象进度条objscanvalueconnect线程
3条回答

这与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函数中指定连接类型,则会自动检测该类型。

我的问题是通过movinf解决的,movinf连接到工作线程初始化的位置,在我的例子中,因为我正在访问一个对象,该对象只在另一个线程中的工作对象类实例化之后才存在。

只需在self.createWorkerThread()之后连接信号

问候

在将工作对象移动到另一个线程之前还是之后建立连接都不重要。引用Qt docs

Qt::AutoConnection - If the signal is emitted from a different thread than the receiving object, the signal is queued, behaving as Qt::QueuedConnection. Otherwise, the slot is invoked directly, behaving as Qt::DirectConnection. The type of connection is determined when the signal is emitted. [emphasis added]

因此,只要connecttype参数设置为QtCore.Qt.AutoConnection(这是默认值),Qt就应该确保以适当的方式发出信号。

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

from QtCore import pyqtSlot

class Scanner(QObject):

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

编辑

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

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

是这个代理对象导致了这个问题。创建代理后,PyQt只需执行以下操作:

    if (rx_qobj)
        proxy->moveToThread(rx_qobj->thread());

如果在接收对象被移动到其线程之后连接就可以了;但是如果在之前连接就可以了,代理将留在主线程中。

使用@pyqtSlotdecorator完全避免了这个问题,因为它更直接地创建了一个Qt槽,并且根本不使用代理对象。

最后,还应该指出,这个问题目前并不影响PySide。

相关问题 更多 >