【问题标题】:How to bind async method to a keystroke in Tkinter?如何将异步方法绑定到 Tkinter 中的击键?
【发布时间】:2018-05-17 14:18:18
【问题描述】:

考虑以下示例:

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))

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

 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))

...和一堆其他的组合,但无济于事。

【问题讨论】:

    标签: python tkinter python-3.5 python-3.6 python-asyncio


    【解决方案1】:

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

    但是,如果您尝试坚持使用 asyncio 也是可能的,但首先让我们考虑一下您尝试了什么。

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

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

    试试这个例子:

    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))
    

    另外,我认为值得一提的是this 问题,因为您使用的是update 方法。

    【讨论】:

    • Cyn 你解释一下为什么你使用update() 而不是update_idletasks()?见Tkinter-Docs
    • @buhtz,这条线直接来自问题。我还在回答中提到了该“问题”的主题。实际上,在这个问题的背景下,没有区别。
    猜你喜欢
    • 2020-04-08
    • 1970-01-01
    • 2013-03-23
    • 2014-03-23
    • 1970-01-01
    • 2018-05-20
    • 2019-07-09
    • 2012-10-30
    • 1970-01-01
    相关资源
    最近更新 更多