PyQt:将信号连接到插槽以启动后台操作 [英] PyQt: Connecting a signal to a slot to start a background operation

查看:152
本文介绍了PyQt:将信号连接到插槽以启动后台操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have the following code that performs a background operation (scan_value) while updating a progress bar in the ui (progress). scan_value iterates over some value in obj, emitting a signal (value_changed) each time that the value is changed. For reasons which are not relevant here, I have to wrap this in an object (Scanner) in another thread. The Scanner is called when the a button scan is clicked. And here comes my question ... the following code works fine (i.e. the progress bar gets updated on time).

# 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)

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

But if I change the last part to this:

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

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

The progress bar gets updated only at the end (my guess is that everything is running on the same thread). Should it be irrelevant if I connect the signal to a slot before of after moving the object receiving object to the Thread.

推荐答案

是在将worker对象移动到另一个线程之前还是之后进行连接都没有关系.引用 Qt文档:

It shouldn't matter whether the connection is made before or after moving the worker object to the other thread. To quote from the Qt docs:

Qt :: AutoConnection -如果信号是从另一个发射的 线程比接收对象线程多,信号排队,表现为 Qt :: QueuedConnection .否则,将直接调用该广告位, 表现为 Qt :: DirectConnection . 连接类型为 确定何时发出信号. [加重]

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就应确保以适当的方式发出信号.

So, as long as the type argument of connect is set to QtCore.Qt.AutoConnection (which is the default), Qt should ensure that signals are emitted in the appropriate way.

示例代码的问题更有可能是插槽而不是 signal .信号所连接的python方法可能需要使用 pyqtSlot装饰器:

The problem with the example code is more likely to be with the slot than the signal. The python method that the signal is connected to probably needs to be marked as a Qt slot, using the pyqtSlot decorator:

from QtCore import pyqtSlot

class Scanner(QObject):

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

编辑:

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

It should be clarified that it's only in fairly recent versions of Qt that the type of connection is determined when the signal is emitted. This behaviour was introduced (along with several other changes in Qt's multithreading support) with version 4.4.

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

Also, it might be worth expanding further on the PyQt-specific issue. In PyQt, a signal can be connected to a Qt slot, another signal, or any python callable (including lambda functions). For the latter case, a proxy object is created internally that wraps the python callable and provides the slot that is required by the Qt signal/slot mechanism.

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

It is this proxy object that is the cause of the problem. Once the proxy is created, PyQt will simply do this:

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

如果在接收对象已移动到其线程之后建立连接,这很好.但是如果在之前创建,则代理将保留在主线程中.

which is fine if the connection is made after the receiving object has been moved to its thread; but if it's made before, the proxy will stay in the main thread.

使用@pyqtSlot装饰器可以完全避免此问题,因为它可以更直接地创建Qt插槽并且完全不使用代理对象.

Using the @pyqtSlot decorator avoids this issue altogether, because it creates a Qt slot more directly and does not use a proxy object at all.

最后,还应注意,此问题当前不影响PySide.

Finally, it should also be noted that this issue does not currently affect PySide.

这篇关于PyQt:将信号连接到插槽以启动后台操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆