我想将 asyncio
与 tkinter
GUI 结合使用。我是新来的 asyncio
,我对它的理解不是很详细。单击第一个按钮时,此处的示例将启动 10 个任务。该任务只是模拟使用 sleep()
工作几秒钟。
示例代码在 Python 3.6.4rc1
中运行良好。 但问题 是 GUI 被冻结了。当我按下第一个按钮并启动 10 个异步任务时,在完成所有任务之前,我无法按下 GUI 中的第二个按钮。 GUI 永远不应该冻结——这是我的目标。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from tkinter import *
from tkinter import messagebox
import asyncio
import random
def do_freezed():
""" Button-Event-Handler to see if a button on GUI works. """
messagebox.showinfo(message='Tkinter is reacting.')
def do_tasks():
""" Button-Event-Handler starting the asyncio part. """
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(do_urls())
finally:
loop.close()
async def one_url(url):
""" One task. """
sec = random.randint(1, 15)
await asyncio.sleep(sec)
return 'url: {}\tsec: {}'.format(url, sec)
async def do_urls():
""" Creating and starting 10 tasks. """
tasks = [
one_url(url)
for url in range(10)
]
completed, pending = await asyncio.wait(tasks)
results = [task.result() for task in completed]
print('\n'.join(results))
if __name__ == '__main__':
root = Tk()
buttonT = Button(master=root, text='Asyncio Tasks', command=do_tasks)
buttonT.pack()
buttonX = Button(master=root, text='Freezed???', command=do_freezed)
buttonX.pack()
root.mainloop()
一个_side问题
…是因为这个错误,我无法再次运行该任务。
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.6/tkinter/__init__.py", line 1699, in __call__
return self.func(*args)
File "./tk_simple.py", line 17, in do_tasks
loop.run_until_complete(do_urls())
File "/usr/lib/python3.6/asyncio/base_events.py", line 443, in run_until_complete
self._check_closed()
File "/usr/lib/python3.6/asyncio/base_events.py", line 357, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
多线程
多线程是一个可能的解决方案吗?只有两个线程 - 每个循环都有自己的线程?
编辑:在审查了这个问题和它与几乎所有 GUI 库(例如 PygObject/Gtk、wxWidgets、Qt 等)相关的答案之后。
原文由 buhtz 发布,翻译遵循 CC BY-SA 4.0 许可协议
在对您的代码稍作修改后,我在主线程中创建了 asyncio
event_loop
并将其作为参数传递给 asyncio 线程。现在,在获取 url 时 Tkinter 不会冻结。