【发布时间】:2019-01-30 01:54:10
【问题描述】:
在学习更多关于Signal/Slot mechanic in Qt的过程中,我很困惑一个槽是在哪个上下文中执行的,所以我写了下面的例子来测试它:
from PyQt5.Qt import * # I know this is bad, but I want a small example
import threading
def slot_to_output_something ( something ):
print( 'slot called by', threading.get_ident(), 'with', something )
class Object_With_A_Signal( QObject ):
sig = pyqtSignal( str )
class LoopThread( QThread ):
def __init__ ( self, object_with_a_signal ):
self.object_with_a_signal = object_with_a_signal
super().__init__()
def run ( self ):
print( 'loop running in', threading.get_ident() )
import time
for i in range( 5 ):
self.object_with_a_signal.sig.emit( str( i ) )
time.sleep( 1 )
print( 'main running in', threading.get_ident() )
app = QApplication( [] )
mainw = QMainWindow( None )
mainw.show()
obj = Object_With_A_Signal()
# connection in main-thread
obj.sig.connect(slot_to_output_something, Qt.QueuedConnection )
loop = LoopThread( obj )
loop.start()
app.exec()
输出:
主要运行在 57474
循环在 57528 中运行
由 57474 以 0 调用的插槽
插槽由 57474 调用,带有 1
...
到目前为止还不错 - 但现在我找到了answer of Sebastian Lange,他说:
您的插槽将始终在调用线程中执行,除非您创建一个
Qt::QueuedConnection来在拥有该插槽的对象所属的线程中运行该插槽。
槽的所有权在 Python 中是如何工作的? 就我的下一次尝试显示,槽连接到信号的线程是执行槽的线程,当信号获取时发出:
# connection in main-thread
# obj.sig.connect(slot_to_output_something, Qt.QueuedConnection )
# loop = LoopThread( obj )
# loop.start()
# connection in helper-thread
class Thread_In_Between( QThread ):
def __init__ ( self, object_with_a_signal ):
super().__init__()
self.object_with_a_signal = object_with_a_signal
def run ( self ):
print( 'helper thread running in', threading.get_ident() )
self.object_with_a_signal.sig.connect( slot_to_output_something, Qt.QueuedConnection)
loop = LoopThread( self.object_with_a_signal )
loop.start()
loop.exec() # without -> ERROR: QThread: Destroyed while thread is still running
print( 'end helper thread' ) # never reached ??
helper_thread = Thread_In_Between( obj )
helper_thread.start()
输出:
主要运行在 65804
在 65896 中运行的辅助线程
循环运行在 65900
插槽由 65896 调用,带有 0
插槽由 65896 调用,带有 1
...
所以..我说得对吗? 插槽是由线程执行的,它们在其中连接还是我只是想出了一个不好的例子?
此外,GUI 更改应该只在主线程中执行,但如果我将这些行添加到我的代码中
# use QListwidget for output instead
lis = QListWidget( None )
print = lambda *args: lis.addItem( str( ' '.join( str( x ) for x in args ) ) )
mainw.setCentralWidget( lis )
输出被重定向到一个 QListWidget,但表明它没有在主线程中调用。是否可以选择将插槽移动到另一个线程(转移“所有权” - 我刚刚找到 QObject::moveToThread)?
它们是关于使用 pyqt 执行调用槽(通过发出的信号)的一般规则吗?
编辑:
这整个问题只是关于QueuedConnection 或BlockingQueuedConnection。我知道DirectConnection 和other options。
【问题讨论】:
-
为什么要在另一个线程中创建线程?循环被破坏,因为它是一个局部变量
-
否 - 使用QueuedConnection,槽在接收者的线程中执行。
-
@eyllanesc:我创建了另一个线程来查看,如果他是执行该插槽的人,情况确实如此。此外,
loop不会被破坏,因为代码不会越过loop.exec()行,它不会离开事件循环(不知道为什么?)。 -
@ekhumoro: 并且是他们的一套规则,决定了谁是接收线程?
标签: python multithreading pyqt signals-slots qthread