PyQt:将信号连接到一个插槽以开始后台操作

我有以下代码执行后台操作( scan_value ),同时更新ui( progress )中的进度条。 scan_value迭代obj某个值,每当该值发生变化时都会发出一个信号( value_changed )。 由于在这里不相关的原因,我不得不把它包装在另一个线程中的对象( Scanner )。 扫描器在clickedbuttonscan时被调用。 这里是我的问题…下面的代码工作正常(即进度条按时更新)。

 # 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 。 [着重点]

所以,只要connecttype参数设置为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。