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.
来源:https://stackoverflow.com/questions/47627900/how-to-bind-async-method-to-a-keystroke-in-tkinter