使用QEventLoop实现Qt5兼容Python协程是否合理?exec_()与yield的关系?
问题
我通过自定义QEventLoop实例模拟QDialog.exec_()函数,实现了在不冻结GUI的情况下暂停Python脚本,待用户手动交互后调用QEventLoop.quit()让程序从QEventLoop.exec_()调用处恢复执行的功能,这个行为和协程完全一致。示例代码如下:
from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QRadioButton, QButtonGroup, QDialogButtonBox from PySide2.QtCore import Qt, QTimer, QEventLoop recursion = 5 def laterOn(): # Request settings from user: dialog = SettingsForm() # Simulate a coroutine. # - Python interpreter is paused on this line. # - Other widgets code will still execute as they are connected from Qt5 side. dialog.exec_() # After the eventloop quits, the python interpreter will execute from where # it was paused: # Using the dialog results: if (dialog.result): # We can use the user's response somehow. In this simple example, I'm just # printing text on the console. print('SELECTED OPTION WAS: ', dialog.group.checkedButton().text()) class SettingsForm(QWidget): def __init__(self): super().__init__() vbox = QVBoxLayout() self.setLayout(vbox) self.eventLoop = QEventLoop() self.result = False a = QRadioButton('A option') b = QRadioButton('B option') c = QRadioButton('C option') self.group = QButtonGroup() self.group.addButton(a) self.group.addButton(b) self.group.addButton(c) bbox = QDialogButtonBox() bbox.addButton('Save', QDialogButtonBox.AcceptRole) bbox.addButton('Cancel', QDialogButtonBox.RejectRole) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) vbox.addWidget(a) vbox.addWidget(b) vbox.addWidget(c) vbox.addWidget(bbox) global recursion recursion -= 1 if (recursion > 0): QTimer.singleShot(0, laterOn) def accept(self): self.close() self.eventLoop.quit() self.result = True def reject(self): self.close() self.eventLoop.quit() self.result = False def exec_(self): self.setWindowModality(Qt.ApplicationModal) self.show() self.eventLoop.exec_() # Initialize app and main window app = QApplication() mwin = QWidget() mwin.show() QTimer.singleShot(0, laterOn) app.exec_()
代码中recursion变量控制QEventLoop实例的创建次数及exec_()方法的调用次数,实现了暂停Python解释器但不冻结其他组件的效果。看起来QEventLoop.exec_()的行为类似Python生成器函数中的yield关键字,想请教:每次调用QEventLoop.exec()时是否确实使用了yield?还是其本质与协程无关,后台另有机制?
回答
QEventLoop.exec_()并没有使用Python的yield关键字,它的底层实现和Python协程机制完全无关,核心是Qt的C++事件循环:
- 当调用
QEventLoop.exec_()时,Python解释器会进入Qt的C事件循环逻辑,此时Python线程会阻塞在这个C函数调用上,但Qt的事件循环会持续处理GUI事件(比如用户点击按钮、定时器触发),因此GUI不会冻结。 - 当调用
QEventLoop.quit()时,Qt的C++事件循环会终止,exec_()函数返回,Python解释器才会继续执行后续代码。 - 这种“暂停-恢复”的表现虽然和Python协程的
yield类似,但本质完全不同:协程是Python层面的上下文切换,由Python解释器负责调度;而Qt事件循环是C层面的阻塞与唤醒,控制权完全在Qt的事件循环机制中,Python线程只是等待C函数执行完毕后继续运行。
内容的提问来源于stack exchange,提问作者Carl HR
相关产品推荐
相关产品推荐

