如何将异步方法绑定到 Tkinter 中的击键? [英] How to bind async method to a keystroke in Tkinter?

查看:118
本文介绍了如何将异步方法绑定到 Tkinter 中的击键?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下示例:

导入异步将 tkinter 作为 tk 导入类应用程序(tk.Tk):def __init__(self):super().__init__()self.create_widgets()self._configure_bindings() # 我相信这是不可能的# 如果方法需要,则执行此操作# 也是异步的def create_widgets(self):经过def _configure_bindings(self):self.bind('', self.spam) # 正确的方法是什么?# 这个方法也需要异步吗?异步定义垃圾邮件(自我,事件):等待 self.do_something()异步定义 do_something():经过async def run_tk(root):尝试:为真:根.更新()等待 asyncio.sleep(.01)除了 tk.TclError 作为 e:如果应用程序已被销毁"不在 e.args[0] 中:增加如果 __name__ == '__main__':应用程序 = 应用程序()asyncio.get_event_loop().run_until_complete(run_tk(app))

在 tkinter 中将异步方法绑定到击键的正确方法是什么?我试过类似的东西:

 self.bind('', self.spam)self.bind('', await self.spam)self.bind('', await self.spam())self.bind('', lambda 事件:等待 self.spam(event))

...以及一堆其他组合,但无济于事.

解决方案

tkinter 本身是异步的,这要归功于事件循环,after 方法和 绑定.

但是,如果您尝试使用 asyncio 也有可能,但首先让我们考虑一下您尝试过的内容.

您的第一次尝试显然失败了,因为您尝试将 spam 作为通用函数调用,而它是 coroutine.您的其他尝试比第一次更正确,但是 await coroutineyield from coroutine 只能用于从另一个协程启动协程,因此它再次失败.>

因此,启动该野兽的正确方法是调度其执行,并使用不言自明的方法 ensure_future(或旧的 async,这只是一个已弃用的别名.

试试这个例子:

导入异步将 tkinter 作为 tk 导入类应用程序(tk.Tk):def __init__(self):super().__init__()self._configure_bindings()def _configure_bindings(self):self.bind('', lambda 事件:asyncio.ensure_future(self.spam(event)))异步定义垃圾邮件(自我,事件):等待 self.do_something()等待 asyncio.sleep(2)print('%s 执行了!' % self.spam.__name__)async def do_something(self):print('%s 执行了!' % self.do_something.__name__)async def run_tk(root):尝试:为真:根.更新()等待 asyncio.sleep(.01)除了 tk.TclError 作为 e:如果应用程序已被销毁"不在 e.args[0] 中:增加如果 __name__ == '__main__':应用程序 = 应用程序()asyncio.get_event_loop().run_until_complete(run_tk(app))

另外,我认为值得一提的是这个 问题,因为您使用了 update 方法.

Consider the following example:

import asyncio
import tkinter as tk

class App(tk.Tk):

    def __init__(self):
        super().__init__()
        self.create_widgets()
        self._configure_bindings() # I believe it is not possible 
                                   # to do this if the method needs 
                                   # to be async as well

    def create_widgets(self):
        pass

    def _configure_bindings(self):
        self.bind('<F5>', self.spam) # what's the proper way?
                                     # does this method need to be async as well?

    async def spam(self, event):
        await self.do_something()

    async def do_something():
        pass

async def run_tk(root):
    try:
        while True:
            root.update()
            await asyncio.sleep(.01)
    except tk.TclError as e:
        if "application has been destroyed" not in e.args[0]:
            raise

if __name__ == '__main__':
    app = App()
    asyncio.get_event_loop().run_until_complete(run_tk(app))

What is the proper way to bind async method to a keystroke in tkinter? I've tried something like:

 self.bind('<F5>', self.spam)
 self.bind('<F5>', await self.spam)
 self.bind('<F5>', await self.spam())
 self.bind('<F5>', lambda event: await self.spam(event))

...and a bunch of other combinations, but to no avail.

解决方案

tkinter itself is asynchronous thanks to event loop, the after method and the bindings.

However, if you trying to stick with asyncio it's also possible, but first let's consider what you tried.

Your first try is obviously a fail, because you trying to call spam as a generic function, when it's a coroutine. Your other tries are more correct than a first, but await coroutine or yield from coroutine can be used to start a coroutine from another coroutine only, so it fails again.

So the proper way of start that beast is a scheduling of its execution with a self-explanatory method ensure_future (or old async, which is just a deprecated alias).

Try this example:

import asyncio
import tkinter as tk


class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self._configure_bindings()

    def _configure_bindings(self):
        self.bind('<F5>', lambda event: asyncio.ensure_future(self.spam(event)))

    async def spam(self, event):
        await self.do_something()
        await asyncio.sleep(2)
        print('%s executed!' % self.spam.__name__)

    async def do_something(self):
        print('%s executed!' % self.do_something.__name__)

async def run_tk(root):
    try:
        while True:
            root.update()
            await asyncio.sleep(.01)
    except tk.TclError as e:
        if "application has been destroyed" not in e.args[0]:
            raise

if __name__ == '__main__':
    app = App()
    asyncio.get_event_loop().run_until_complete(run_tk(app))

Also, I think that it's worth to mention this question, since you use an update method.

这篇关于如何将异步方法绑定到 Tkinter 中的击键?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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