-
-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Core: Add async support to kivy App #6368
Conversation
It seems the whitespace in line 58 was causing Travis CI to [fail while building](https://travis-ci.org/kivy/kivy/builds/542022321) due to a "style guide violation" during PEP8 Verification.
Removed whitespace from newline
How is this coming? Any progress? |
It works nicely, but I haven't found the time yet to add the docs and tests. |
I believe the PR is complete. I updated the first comment with more details. The osx tests are failing for reasons unrelated to this PR. |
Anxiously awaiting this - have a project with existing substantial async Python3.7 code base where customer now wants to add a touch screen. Kivy + async will be awesome for this! |
I plan on merging in a week if no feedback or objections is forthcoming. But you should go ahead and try this branch to see if you run into issues and things need to be fixed. Although I tested with a fairly complex app and things seemed to work fine. |
I doubt I'll have time to get it rolling in the next week, but will definitely post back if I do. Thanks again for your work on this! |
Running tests with both
Tests always fail if Versions
|
Thanks for testing! Hmm, strange. I didn't see this behavior, maybe because I had a I'll try to reproduce in a clean venv. |
Ah sorry, it was my mistake 😅 . I forgot about that (
the tests passed. |
Hm, I do understand better now. I'm not familiar enough with the way kivy
initializes things or why you need to initialize anything about asyncio
before creating a loop. It normally really doesn't do anything with
sideeffects or blocking unless it is waiting for events on file
descriptors. The difference between Windows and Linux for asyncio is mainly
which event loop class is initialized and those classes have different ways
of blocking for events.
Two years ago I integrated asyncio into a kivy application the same way I
did it with Blender and IPython, the latter of which now has better
facilities for that. I basically run the _run_once member function of the
loop from some idle callback.
Now, normally that function blocks until the next event occurs, but you can
stop it two ways: One is by providing some event to be called soon/1ms
later, or subclass the loop class you will be using (which is different
depending on the platform) and override the _run_once method with a copy
that calls the "select" function with a short timeout. Now I can run Kivy
unmolested, I can use all of asyncio and third-party libraries and I can
even interact with the loop from kivy events. I have to look some more into
the pull request how things work now.
Please don't take my comments as criticism or attacks, I'm just trying to
give my opinion and understand Kivy better.
… |
This error might have to do with
( I'm not sure |
Is the main loop supposed to exit when the app finishes? I am running into the situation that when closing the application by ESC, the loop seems to shut down before other running tasks get a chance to clean up after them. |
The problem about async_run closing the loop was caused by "run_until_complete". "run_forever" works better, if another coroutine stops the loop later. Next issue: I'm trying to set widget properties (like label texts) and that seems to break something. At the very least the changed properties don't show up on the screen. |
Hi, it's just an example. Kivy itself doesn't force you to use
Can you show me the code that reproduce that issue? |
I had these issues in an app that uses aiohttp's Websocket client to read Flightgear simulation data. So this wouldn't be easy to share/reproduce. Basically aiohttp complained that the websocket/client session was still running when the loop had been shut down. Originally I tried to schedule coroutines from kivy event handlers, and that didn't work at all. Now it works, so it's possible to use An example I can share is this: This example starts a That sleep task won't yield while the app is running, so I run the loop again with that task. Two disadvantages: The main block of the Python program has to know about that task. Secondly, any exceptions from the main task are only presented after the app closes. I'll probably figure out another way. It's probably just me not understanding the complete picture... |
So you want to start |
@gottadiveintopython yes, there are multiple solutions. I settled on overriding "async_run" in a custom Application. |
@akloster ah ok, so the issues you had were already solved? I thought you currently have some issues. |
I've publicly added the But the environment variable still works like before, it's just now optional. I also updated the examples and docs for this. I'm pretty happy with this now, and would merge maybe next weekend if there's no additional feedback. |
I've just run tests. That error ( |
This is awesome as I was just looking for a way to integrate asyncio and kivy. I apologize for the ignorance but how can I use this PR as it's not included in the latest stable release? |
@intgsull I think you can do EDIT: better just follow the complete guide https://kivy.org/doc/stable/installation/installation-devel.html#installation-devel |
Also, look in the install docs and depending on the platform there may be a nightly wheel or daily PPA to try. |
Big thanks for this major PR. By the way there is a minor issue with the Asyncio example given on this page: |
@matham I'm just porting to mainline (since I just found out your old branch was put to rest) and I'm not sure how to deal with the lack of |
@matham Thank you for this great work. Can you provide an example of how to call an async method without the |
@goodboy Did you find an alternative solution to |
This adds async support to kivy. It is a continuation from #5241.
It only adds support for async, without support for the
async_bind()
convenience method as requested. The event/property binding will (may) come later in a different PR.PR info copied from the docs:
Background
Normally, when a Kivy app is run, it blocks the thread that runs it until the app exits. Internally, at each clock iteration it executes all the app callbacks, handles graphics and input, and idles by sleeping for any remaining time.
To be able to run asynchronously, the Kivy app may not sleep, but instead must release control of the running context to the asynchronous event loop running the Kivy app. We do this when idling by calling the appropriate functions of the async package being used instead of sleeping.
Async configuration
To run an async app, both the
KIVY_EVENTLOOP
environmental variable must be set appropriately, and :func:async_runTouchApp
or :meth:App.async_run
must be scheduled to run in the external async package's event loop. The variable tells kivy which async library to use when idling and:func:
async_runTouchApp
or :meth:App.async_run
run the actual app.The environmental variable
KIVY_EVENTLOOP
determines which async library to use, if at all. It can be set to one of"sync"
when it should be run synchronously like a normal app,"async"
when the standard libraryasyncio
should be used, or"trio"
if the trio library should be used. If not set itdefaults to
"sync"
.In the
"async"
or"trio"
case, one schedules :func:async_runTouchApp
or :meth:App.async_run
to run within the given library's async event loop as in the examples shown below. Kivy is then treated as just another coroutine that the given library runs in its event loop.For a fuller basic and more advanced examples, see the demo apps in
examples/async
.Asyncio example
Trio example
Interacting with Kivy app from other coroutines
It is fully safe to interact with any kivy object from other coroutines running within the same async event loop. This is because they are all running from the same thread and the other coroutines are only executed when Kivy is idling.
Similarly, the kivy callbacks may safely interact with objects from other coroutines running in the same event loop. Normal single threaded rules apply to both case.
Examples
I added a few full examples to
examples/async
, and the docs are pretty complete. Please see those examples to get a fuller understanding of how this would be used.Testing
With this PR, I also added a new way of graphically testing apps. Using pytest, I added a pytest
kivy_app
fixture that takes a function that returns a Kivy app, and creates a self-contained kivy app from it. With it, we can test apps super easily and they get re-created for each test. I hope this will make it much easier for people to add tests to test complex widgets. I created aUnitKivyApp
base class to help with this testing as it provides some convenient methods.Please see
kivy/tests/test_app.py
for various example tests andkivy/tests/async_common
for theUnitKivyApp
base class. I actually am using this to help test a very large app I made, which I'm not sure how I would have tested the GUI without this kind of thing.One note, these tests are skipped for python 3.5 because support for yielding from a
async def
method was only added in 3.6 and we need this to help with testing.Here's a couple of the sample tests to show how easy async make testing actual full kivy apps:
And another: