Lost in asyncio.
I'm learning Kivy and asyncio at the same time, and got stuck into solving the problem of running the Kivy and running the asyncio loop, as whatever the way I turn it, both are blocking calls and need to be executed sequentially (well, I hope I'm wrong), e.g.
loop = asyncio.get_event_loop()
loop.call_soon(MyAsyncApp().run())
loop.run_forever()
My current attempt results in the application being launched, but no coroutine is being executed, e.g. when I click the "Connect" button, I should start scheduling and executing tasks using loop.call_soon
, but nothing happens.
Could someone have a look at my code and suggest a proper approach to the problem?
import asyncio
import random
import time
from kivy.app import App
from kivy.lang import Builder
ui = Builder.load_string('''
BoxLayout:
orientation: 'vertical'
GridLayout:
rows: 2
cols: 2
Label:
text: 'Status:'
size_hint: 0.3, 1
Label:
id: status
text: ''
Label:
text: 'Data:'
size_hint: 0.7, 1
Label:
id: data
text: ''
BoxLayout:
direction: 'horizontal'
Button:
text: 'Get Data'
on_press: app.connect()
Button:
text: 'Stop Data'
on_press: pass
''')
class MyAsyncApp(App):
def __init__(self):
super(self.__class__, self).__init__()
self.x_connected = None
self.x_widget_data = None
self.x_widget_status = None
self.x_loop = asyncio.get_event_loop()
def build(self):
return ui
def connect(self):
# Get widget
self.x_widget_status = self.root.ids.status
# Update status
self.x_widget_status.text = 'Preparing to connect...'
# Connect using asyncio
# --> But the loop must be already running <---
self.x_loop.call_soon(self.do_connect)
async def do_connect(self):
# Connect asynchronously
# Get widget
self.x_widget_data = self.root.ids.data
# Update status
self.x_connected = False
self.x_widget_status.text = 'Connecting...'
# Perform actual actions
try:
result = await self.feed_kivy()
if result:
self.x_widget_status.text = 'Service not available: ' + result
return
except Exception as e:
self.x_widget_status.text = 'Error while connecting'
return
# Update status
self.x_connected = True
self.x_widget_status.text = 'Connected'
async def feed_kivy(self):
# Deliver fresh data at random interval
# Some slow process to get data
result = await asyncio.sleep(random.randint(1, 5), time.time())
self.x_widget_data.text = result
# Reschedule ourselves
await self.x_loop.call_soon(self.feed_kivy())
def main():
# If loop started here, app is never started
loop = asyncio.get_event_loop()
loop.call_soon(MyAsyncApp().run())
loop.run_forever()
loop.close()
if __name__ == '__main__':
main()
My current attempt results in the application being launched, but no coroutine is being executed
It happens because MyAsyncApp().run()
blocks execution flow and control never returns to asyncio's event loop. This is how all event loops work.
Instead of trying cross two loops manually shorter way would be to use existing attempt:
https://github.com/kivy/kivy/pull/5241
This PR is from one of Kivy's developers and contains working implementation with explanation and usage examples.
However it's not merged into master yet: you would need to build Kivy with this PR manually.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With