Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to run only a single step of the asyncio event loop

I'm working on a simple graphical network application, using asyncio and tkinter. I'm running into the problem of combining the asyncio event loop with Tk's mainloop. If possible, I'd like to do it without threads, because both these libraries (but especially tkinter) aren't very thread safe. Currently, I'm using Tk.update in an asyncio coroutine, which runs only a single iteration of the tk event loop:

@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

However, in the interest of exploring all options, I was wondering if it was possible to do the reverse- if it was possible to invoke only a single iteration of an asyncio event loop inside a tk callback.

like image 468
Lucretiel Avatar asked Apr 21 '15 20:04

Lucretiel


2 Answers

Take a look at this example.

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())
like image 88
The Matrix Avatar answered Sep 28 '22 04:09

The Matrix


The missing of public method like loop.run_once() is intentional. Not every supported event loop has a method to iterate one step. Often underlying API has methods for creating event loop and running it forever but emulating single step may be very ineffective.

If you really need it you may implement single-step iteration easy:

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)
like image 30
Andrew Svetlov Avatar answered Sep 28 '22 03:09

Andrew Svetlov