Python 使用QRunnable执行线程-发送双向回调的正确方式
,我已经看到可以使用信号和插槽将工作线程回调到主GUI线程,但我不确定如何使用信号和插槽建立双向通信。以下是我的工作内容:Python 使用QRunnable执行线程-发送双向回调的正确方式,python,multithreading,pyqt,pyqt5,signals-slots,Python,Multithreading,Pyqt,Pyqt5,Signals Slots,,我已经看到可以使用信号和插槽将工作线程回调到主GUI线程,但我不确定如何使用信号和插槽建立双向通信。以下是我的工作内容: class RespondedToWorkerSignals(QObject): callback_from_worker = pyqtSignal() class RespondedToWorker(QRunnable): def __init__(self, func, *args, **kwargs): super(Responded
class RespondedToWorkerSignals(QObject):
callback_from_worker = pyqtSignal()
class RespondedToWorker(QRunnable):
def __init__(self, func, *args, **kwargs):
super(RespondedToWorker, self).__init__()
self._func = func
self.args = args
self.kwargs = kwargs
self.signals = RespondedToWorkerSignals()
self.kwargs['signal'] = self.signals.callback_from_worker
print("Created a responded-to worker")
@pyqtSlot()
def run(self):
self._func(*self.args, **self.kwargs)
@pyqtSlot()
def acknowledge_callback_in_worker(self):
print("Acknowledged Callback in Worker")
class MainWindow(QMainWindow):
# Signal meant to connect to a slot present within a worker
mainthread_callback_to_worker = pyqtSignal()
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
# Quick UI setup
w, lay = QWidget(), QVBoxLayout()
w.setLayout(lay)
self.setCentralWidget(w)
self.timer_label = QLabel("Timer Label")
lay.addWidget(self.timer_label)
self.btn_thread_example = QPushButton("Push Me")
self.btn_thread_example.pressed.connect(self.thread_example)
lay.addWidget(self.btn_thread_example)
self.threadpool = QThreadPool()
self.show()
# Set up QTimer to continue in the background to help demonstrate threading advantage
self.counter = 0
self.timer = QTimer()
self.timer.setInterval(1000)
self.timer.timeout.connect(self.recurring_timer)
self.timer.start()
@pyqtSlot()
def do_something(self, signal):
# signal argument will be the callback_from_worker and it will emit to acknowledge_callback_in_mainthread
print("do_something is sleeping briefly. Try to see if you get a locked widget...")
time.sleep(7)
signal.emit()
@pyqtSlot()
def acknowledge_callback_in_mainthread_and_respond(self):
# this function should respond to callback_from_worker and emit a response
print("Acknowledged Callback in Main")
self.mainthread_callback_to_worker.emit()
def thread_example(self):
print("Beginning thread example")
worker = RespondedToWorker(self.do_something)
worker.signals.callback_from_worker.connect(self.acknowledge_callback_in_mainthread_and_respond)
# self.mainthread_callback_to_worker.connect(worker.acknowledge_callback_in_worker) # <-- causes crash
def recurring_timer(self):
self.counter += 1
self.timer_label.setText(f"Counter: {self.counter}")
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
app.setStyle("Fusion")
win.show()
sys.exit(app.exec())
类响应的工作信号(QObject):
从\u worker=pyqtSignal()回调\u
类响应的ToWorker(QRunnable):
定义初始化(self,func,*args,**kwargs):
超级(响应的工作人员,自我)。\uuuu init\uuuu()
self._func=func
self.args=args
self.kwargs=kwargs
self.signals=响应的工作信号()
self.kwargs['signal']=self.signals.callback\u来自\u worker
打印(“创建了一个响应的工作人员”)
@pyqtSlot()
def运行(自):
self._func(*self.args,**self.kwargs)
@pyqtSlot()
def确认\u回拨\u输入\u工作者(自我):
打印(“工作进程中已确认的回调”)
类主窗口(QMainWindow):
#用于连接到工作机中存在的插槽的信号
mainthread\u callback\u to\u worker=pyqtSignal()
定义初始化(self,*args,**kwargs):
超级(主窗口,自我)。\uuuuu初始化(*args,**kwargs)
#快速用户界面设置
w、 lay=QWidget(),QVBoxLayout()
w、 设置布局(布局)
self.setCentralWidget(w)
self.timer_label=QLabel(“计时器标签”)
lay.addWidget(self.timer\u标签)
self.btn_thread_示例=QPushButton(“推我”)
self.btn\u线程\u示例。按下。连接(self.thread\u示例)
lay.addWidget(self.btn\u线程\u示例)
self.threadpool=QThreadPool()
self.show()
#设置QTimer以在后台继续,以帮助演示线程优势
self.counter=0
self.timer=QTimer()
自动定时器设置间隔(1000)
self.timer.timeout.connect(self.recurtive\u timer)
self.timer.start()
@pyqtSlot()
def do_某事(自身、信号):
#signal参数将是来自\u worker的回调\u,它将在\u主线程中发出以确认\u回调\u
打印(“do_某事正在短暂休眠。尝试查看是否有锁定的小部件…”)
时间。睡眠(7)
signal.emit()
@pyqtSlot()
主线程中的def确认回调和响应(自我):
#此函数应响应来自\u worker的回调\u并发出响应
打印(“主屏幕中已确认的回调”)
self.mainthread\u回调\u到\u worker.emit()
def螺纹_示例(自):
打印(“开始线程示例”)
worker=响应的worker(self.do\u something)
worker.signals.callback\u来自\u worker.connect(self.acknowledge\u callback\u在\u主线程中\u和\u响应)
#self.mainthread_callback_to_worker.connect(worker.acknowledge_callback_in_worker)#要了解错误原因,您必须在终端中运行代码,并会收到以下错误消息:
QObject::connect: Cannot connect MainWindow::mainthread_callback_to_worker() to (nullptr)::acknowledge_callback_in_worker()
Traceback (most recent call last):
File "main.py", line 72, in thread_example
self.mainthread_callback_to_worker.connect(worker.acknowledge_callback_in_worker) # <-- causes crash
TypeError: connect() failed between MainWindow.mainthread_callback_to_worker[] and acknowledge_callback_in_worker()
Aborted (core dumped)
谢谢你的解释。在进一步研究之后,我还想让任何读者知道,根据我所犯的另一个错误,就是遗漏了一个细节,即QRunnable+QThreadPool不支持“通过信号进行数据更新”,也不支持“使用信号进行控制”。您似乎没有阅读网站的文档,因此我建议您检查并通过。此处的标题不会通过添加已解决的问题来修改,或者编辑问题以添加可能的解决方案,您必须在此处将答案标记为正确,并在“答案”部分中发布答案。注意:您应该始终阅读所使用技术的文档(例如Qt和StackOverflow文档)