在不冻结 GUI 的情况下一起使用 asyncio 和 Tkinter(或其他 GUI 库) [英] Use asyncio and Tkinter (or another GUI lib) together without freezing the GUI
问题描述
我想结合使用 asyncio
使用 tkinter
GUI.我是 asyncio
的新手,我对它的理解不是很详细.此处的示例在单击第一个按钮时启动 10 个任务.任务只是用 sleep()
模拟工作几秒钟.
I want to use asyncio
in combination with a tkinter
GUI.
I am new to asyncio
and my understanding of it is not very detailed.
The example here starts 10 task when clicking on the first button. The task are just simulating work with a sleep()
for some seconds.
示例代码在 Python 3.6.4rc1
上运行良好.但是问题是 GUI 被冻结了.当我按下第一个按钮并启动 10 个异步任务时,在完成所有任务之前,我无法按下 GUI 中的第二个按钮.GUI 永远不应该冻结 - 这是我的目标.
The example code is running fine with Python 3.6.4rc1
. But
the problem is that the GUI is freezed. When I press the first button and start the 10 asyncio-tasks I am not able to press the second button in the GUI until all tasks are done. The GUI should never freeze - that is my goal.
#!/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: {} sec: {}'.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('
'.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()
一个_方面的问题
...是因为这个错误,我无法第二次运行任务.
A _side problem
...is that I am not able to run the task a second time because of this error.
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
多线程
多线程是一种可能的解决方案吗?只有两个线程——每个循环都有自己的线程?
Multithreading
Whould multithreading be a possible solution? Only two threads - each loop has it's own thread?
编辑:在查看此问题和答案后,它几乎与所有 GUI 库(例如 PygObject/Gtk、wxWidgets、Qt 等)相关.
EDIT: After reviewing this question and the answers it is related to nearly all GUI libs (e.g. PygObject/Gtk, wxWidgets, Qt, ...).
推荐答案
对您的代码稍作修改,我在主线程中创建了 asyncio event_loop
并将其作为参数传递给 asyncio线.现在,在获取 url 时,Tkinter 不会冻结.
In a slight modification to your code, I created the asyncio event_loop
in the main thread and passed it as an argument to the asyncio thread. Now Tkinter won't freeze while the urls are fetched.
from tkinter import *
from tkinter import messagebox
import asyncio
import threading
import random
def _asyncio_thread(async_loop):
async_loop.run_until_complete(do_urls())
def do_tasks(async_loop):
""" Button-Event-Handler starting the asyncio part. """
threading.Thread(target=_asyncio_thread, args=(async_loop,)).start()
async def one_url(url):
""" One task. """
sec = random.randint(1, 8)
await asyncio.sleep(sec)
return 'url: {} sec: {}'.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('
'.join(results))
def do_freezed():
messagebox.showinfo(message='Tkinter is reacting.')
def main(async_loop):
root = Tk()
Button(master=root, text='Asyncio Tasks', command= lambda:do_tasks(async_loop)).pack()
Button(master=root, text='Freezed???', command=do_freezed).pack()
root.mainloop()
if __name__ == '__main__':
async_loop = asyncio.get_event_loop()
main(async_loop)
这篇关于在不冻结 GUI 的情况下一起使用 asyncio 和 Tkinter(或其他 GUI 库)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!