一起使用asyncio和Tkinter(或另一个GUI库),而无需冻结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: {}\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问题
...是因为这个错误,我无法再次运行任务.
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线程.现在,在提取网址时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: {}\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))
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()
buttonX = Button(master=root, text='Freezed???', command=do_freezed).pack()
root.mainloop()
if __name__ == '__main__':
async_loop = asyncio.get_event_loop()
main(async_loop)
这篇关于一起使用asyncio和Tkinter(或另一个GUI库),而无需冻结GUI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!