在不冻结 GUI 的情况下一起使用 asyncio 和 Tkinter(或其他 GUI 库) [英] Use asyncio and Tkinter (or another GUI lib) together without freezing the GUI

查看:17
本文介绍了在不冻结 GUI 的情况下一起使用 asyncio 和 Tkinter(或其他 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆