我正在使用asyncio和tkinter处理简单的图形网络应用程序。我遇到了将asyncio事件循环与Tk的主循环结合起来的问题。如果可能的话,我想在没有线程的情况下这样做,因为这些库(但特别是tkinter)都不是非常线程安全的。目前,我在asyncio协程中使用Tk.update,它只运行tk事件循环的一次迭代:
@asyncio.coroutine
def run_tk(tk, interval=0.1):
try:
while True:
tk.update()
yield from asyncio.sleep(interval)
except TclError as e:
if "application has been destroyed" not in e.args[0]:
raise
但是,为了探索所有选项,我想知道是否可以执行相反的操作 - 如果可以在tk回调中仅调用asyncio事件循环的单次迭代。
答案 0 :(得分:10)
像loop.run_once()
这样的公共方法缺失是故意的。
并非每个受支持的事件循环都有一个迭代一步的方法。通常,底层API具有创建事件循环并永久运行它的方法,但模拟单个步骤可能非常无效。
如果您真的需要它,您可以轻松实现单步迭代:
import asyncio
def run_once(loop):
loop.call_soon(loop.stop)
loop.run_forever()
loop = asyncio.get_event_loop()
for i in range(100):
print('Iteration', i)
run_once(loop)
答案 1 :(得分:1)
看看这个例子。
import asyncio
from tkinter import *
class asyncTk(Tk):
def __init__(self):
super().__init__()
self.running = True
self.protocol("WM_DELETE_WINDOW", self.on_closing)
def on_closing(self):
self.running = False
self.destroy()
def __await__(self):
while self.running:
self.update()
yield
async def asd():
for x in range(1,10):
await asyncio.sleep(1)
print(x)
async def main():
w = asyncTk()
asyncio.create_task(asd())
await w
asyncio.run(main())
答案 2 :(得分:0)
我使用以下过程创建自己的run_once()
和run_forever()
。
这是一个简化的示例:
import asyncio
async def worker(**kwargs):
id = kwargs.get('id', '0.0.0.0.0.0')
time = kwargs.get('time', 1)
try:
# Do stuff.
print('start: ' + id)
finally:
await asyncio.sleep(time)
async def worker_forever(**kwargs):
while True:
await worker(**kwargs)
def init_loop(configs, forever=True):
loop = asyncio.get_event_loop()
if forever:
tasks = [
loop.create_task(worker_forever(id=conf['id'], time=conf['time']))
for conf in config
]
else:
tasks = [
asyncio.ensure_future(worker(id=conf['id'], time=conf['time']))
for conf in configs
]
return loop, tasks
def run_once(configs):
print('RUN_ONCE')
loop, futures = init_loop(configs, forever=False)
result = loop.run_until_complete(asyncio.gather(*futures))
print(result)
def run_forever(configs):
print('RUN_FOREVER')
loop, _ = init_loop(configs, forever=True)
try:
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
print("Closing Loop")
loop.close()
if __name__ == '__main__':
configurations = [
{'time': 5, 'id': '4'},
{'time': 6, 'id': '5'},
{'time': 1, 'id': '6'},
] # TODO :: DUMMY
run_once(configurations)
run_forever(configurations)