一起使用asyncio和Tkinter(或另一个GUI库),而无需冻结GUI

发布于 2021-01-29 17:16:30

我想asynciotkinterGUI结合使用。我是新手asyncio,对它的理解不是很详细。单击第一个按钮时,此处的示例启动10个任务。任务只是用a模拟工作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等)。

关注者
0
被浏览
98
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    在对您的代码进行稍作修改后,我event_loop在主线程中创建了asyncio并将其作为参数传递给asyncio线程。现在,在提取网址时,Tkinter不会冻结。

    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)
    


知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看