PyQt:将信号连接到一个插槽以开始后台操作
我有以下代码执行后台操作( scan_value
),同时更新ui( progress
)中的进度条。 scan_value
迭代obj
某个值,每当该值发生变化时都会发出一个信号( value_changed
)。 由于在这里不相关的原因,我不得不把它包装在另一个线程中的对象( Scanner
)。 扫描器在clicked
buttonscan
时被调用。 这里是我的问题…下面的代码工作正常(即进度条按时更新)。
# 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()
进度条只在最后更新(我猜是所有东西都在同一个线程上运行)。 如果在将对象接收对象移动到线程之前,将信号连接到一个插槽之前,这应该是无关紧要的。
将工作对象移动到另一个线程之前或之后进行连接无关紧要。 引用Qt文档 :
Qt :: AutoConnection – 如果信号是从与接收对象不同的线程发出的,则信号排队,performance为Qt :: QueuedConnection 。 否则,直接调用该插槽,performance为Qt :: DirectConnection 。 信号发射时确定连接的types 。 [着重点]
所以,只要connect
的type
参数设置为QtCore.Qt.AutoConnection
(这是默认值),Qt应该确保信号以适当的方式发出。
示例代码的问题更可能是槽比信号 。 信号连接到的python方法可能需要使用pyqtSlot装饰器标记为Qt插槽:
from QtCore import pyqtSlot class Scanner(QObject): @pyqtSlot() def scan(self): scan_value(start, stop, step) progress.setValue(100)
编辑 :
应该明确的是,只有在相当新的Qt版本中,连接types才会在信号发出时被确定。 在4.4版本中引入了这种行为(以及对Qtmultithreading支持的其他更改)。
而且,在PyQt特定的问题上可能还需要进一步扩展。 在PyQt中,一个信号可以连接到一个Qt插槽,另一个信号或任何可调用的python。 对于后一种情况,代理对象是在内部创build的,包装python可调用并提供Qt信号/插槽机制所需的插槽。
这是代理对象,是问题的原因。 一旦代理被创build,PyQt将简单地做到这一点:
if (rx_qobj) proxy->moveToThread(rx_qobj->thread());
如果在接收对象已经移动到它的线程之后进行连接的话,这是很好的; 但是如果之前做了,代理将停留在主线程中。
使用@pyqtSlot
装饰器完全避免了这个问题,因为它直接创build一个Qt插槽,根本不使用代理对象。
最后,还应该指出,这个问题目前不影响PySide。
这与Qt的连接types有关。
http://pyqt.sourceforge.net/Docs/PyQt5/signals_slots.html#connect
http://qt-project.org/doc/qt-4.8/qt.html#ConnectionType-enum
如果两个对象都存在于同一个线程中,则会创build一个标准的连接types,这会导致一个普通的函数调用。 在这种情况下,耗时的操作发生在GUI线程和接口块中。
如果连接types是消息传递样式连接,则使用在另一个线程中处理的消息来发出信号。 GUI线程现在可以自由更新用户界面。
如果不在连接function中指定连接types,则会自动检测types。