我正在尝试创建一个基于PyQt5和asyncio的新应用程序(使用python3.4,希望最终使用async/await升级到3.5)。我的目标是使用asyncio,这样即使在应用程序等待连接的硬件完成操作时,GUI也能保持响应。在
在研究如何合并Qt5和asyncio的事件循环时,我发现了一个mailing list posting,建议使用quamash。但是,在运行此示例(未修改)时
yield from fut
似乎再也不会回来了。我看到输出'Timeout',因此timer回调显然会触发,但是未来无法唤醒waiting方法。当手动关闭窗口时,它告诉我存在未完成的期货:
^{pr2}$我用python3.5在Ubuntu上测试了这一点,在Windows上测试了3.4,两个平台上的行为相同。在
总之,由于这不是我真正要实现的目标,我还测试了其他一些代码:
import quamash
import asyncio
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
@asyncio.coroutine
def op():
print('op()')
@asyncio.coroutine
def slow_operation():
print('clicked')
yield from op()
print('op done')
yield from asyncio.sleep(0.1)
print('timeout expired')
yield from asyncio.sleep(2)
print('second timeout expired')
def coroCallHelper(coro):
asyncio.ensure_future(coro(), loop=loop)
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
def btnCallback(obj):
#~ loop.call_soon(coroCallHelper, slow_operation)
asyncio.ensure_future(slow_operation(), loop=loop)
print('btnCallback returns...')
btn = QPushButton('Button', self)
btn.resize(btn.sizeHint())
btn.move(50, 50)
btn.clicked.connect(btnCallback)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('Async')
self.show()
with quamash.QEventLoop(app=QApplication([])) as loop:
w = Example()
loop.run_forever()
#~ loop = asyncio.get_event_loop()
#~ loop.run_until_complete(slow_operation())
程序应该显示一个窗口,其中有一个按钮(确实如此),按钮调用slow_operation()而不阻塞GUI。在运行这个例子时,我可以根据需要经常单击按钮,这样GUI就不会被阻塞。但是
yield from asyncio.sleep(0.1)
从未通过,终端输出如下所示:
btnCallback returns...
clicked
op()
op done
btnCallback returns...
clicked
op()
op done
这次我关窗时没有抛出异常。如果我直接用slow_operation()函数运行事件循环,它基本上可以工作:
#~ with quamash.QEventLoop(app=QApplication([])) as loop:
#~ w = Example()
#~ loop.run_forever()
loop = asyncio.get_event_loop()
loop.run_until_complete(slow_operation())
现在,有两个问题:
一般来说,这是实现长时间操作与GUI分离的明智方法吗?我的意图是按钮回调将协同例程调用发布到事件循环(有或没有额外的嵌套级别,cf.coroCallHelper()),然后在那里调度和执行。我不需要单独的线程,因为实际上只有I/O需要时间,没有实际的处理。
我怎样才能纠正这种行为?
谢谢, 菲利普
好吧,这是一个优点:写下一个问题会让你重新思考每件事。不知怎么的,我才明白:
再次查看quamash repo中的示例,我发现要使用的事件循环的获取方式有些不同:
关键似乎是
^{pr2}$asyncio.set_event_loop()
。还需要注意的是,这里提到的QEventLoop
是来自quamash包的,而不是Qt5。我的例子如下:它现在“管用”了:
也许这对其他人有些帮助。至少我很满意;) 当然,对一般模式的评论仍然是受欢迎的!在
附录:请注意,如果quamash被asyncqt取代,那么这可以用于python3.7.x之前的最新Python版本。但是,在python3.8中使用相同的代码会导致}失败。也许其他人知道该怎么做才能让它再次工作。可能只是asyncqt还不兼容python3.8。在
@coroutine
装饰器生成RuntimeWarning
s,并最终在asyncio.sleep()
中出现{谨致问候, 菲利普
相关问题 更多 >
编程相关推荐