enthought / pyface

pyface: traits-capable windowing framework
Other
105 stars 55 forks source link

Integration with asyncio? #663

Open mdickinson opened 4 years ago

mdickinson commented 4 years ago

From some experimentation last week, integrating asyncqt into a TraitsUI GUI works almost seamlessly, making it easy to use other asyncio-aware packages like aiohttp from a TraitsUI GUI. I think this could be extremely valuable for things like making async requests from a running GUI.

The one sticking point is starting the event loop: currently, the Qt backend for TraitsUI/Pyface starts the Qt event loop directly, via a qt_app.exec_ call. To integrate with asyncqt, we instead need to be able to start the event loop through something like the asyncio get_event_loop().run_forever(). (Note that Pyface would not need any awareness of asyncqt for this; only awareness of asyncio.)

What's the easiest way to make it possible in Pyface to start the event loop through asyncio instead of directly through Qt?

[It wasn't clear to me whether this issue belongs in Pyface or TraitsUI; feel free to migrate if necessary]

mdickinson commented 4 years ago

Note that there's an existing workaround which isn't horrible: instead of using configure_traits to start the event loop, start the event loop the asyncio way, and then use edit_traits instead of configure_traits.

kitchoi commented 4 years ago

I think... with configure_traits, the event loop is started here for Qt: https://github.com/enthought/traitsui/blob/b23a4cc87b60b4abc777d33aa583ad3310cbb553/traitsui/qt4/view_application.py#L129-L131

Not sure if asyncio can work with wx, but here it the wx counterpart: https://github.com/enthought/traitsui/blob/b23a4cc87b60b4abc777d33aa583ad3310cbb553/traitsui/wx/view_application.py#L137-L141)

Perhaps we just need to provide another implementation of this view_application?

k2bd commented 4 years ago

Just dropping in to say this would be useful for us. As discussed offline, using async requests to keep an application updated with the status of a long-running job that's being run remotely is something we've been playing around with a little bit.

I've got a small version working as Mark describes, using the same workaround.

corranwebster commented 4 years ago

There's two places we'd need to make changes: Pyface GUI object and TraitsUI ViewApplication. This could be improved by having ViewApplication use Pyface GUI rather than the lower-level code it currently uses (which could probably then be removed).

I don't think I'd be in favour of having asyncqt be a required dependency of Pyface/TraitsUI (but I could be convinced otherwise, I guess). There may be an argument for pushing generic event loop code somewhere outside of Pyface: we have discussed dispatch="async" as a potential option for Traits handlers (it is almost trivial to implement), so I could see an argument where we go all-in that Traits event loops are always AsyncIO event loops, and Pyface and TraitsUI just make sure that they use one from the appropriate backend.

There is https://github.com/sirk390/wxasync but I haven't looked for code quality. In principle integrating any event loop with async should be feasible.

mdickinson commented 4 years ago

I don't think I'd be in favour of having asyncqt be a required dependency of Pyface/TraitsUI

No, me neither. I just want a way to tell Pyface / TraitsUI: "here's an asyncio-compatible event loop. use it!", or maybe "here's a factory that creates an asyncio-compatible event loop. use it!", or perhaps "grab the current asyncio event loop (yay global state!) and use that".

So the first step is probably to figure out how this would/could/should look to a user.

corranwebster commented 4 years ago

One possible approach would be to contribute an asyncqt toolkit via setup.py (possibly in a different library, such as Traits Futures) which is almost the same as the qt toolkit but has a different implementation of the init.py module (https://github.com/enthought/pyface/blob/master/pyface/ui/qt4/init.py) which sets up asyncio dispatch and special-cases the GUI object to pull in an async-aware version.

A similar thing would need to be done for TraitsUI and Enable (and possibly Mayavi), and I don't think we're quite at the point where we could pull it off.

But this would have minimal impact on code as written - just switch the toolkit and you suddenly have async support.