混合asyncio和Kivy:如何同时启动asyncio循环和Kivy应用程序?

时间:2017-12-14 14:24:10

标签: python kivy python-asyncio

迷失在asyncio中。

我正在同时学习Kivy和asyncio,并且坚持解决运行Kivy和运行asyncio循环的问题,无论我怎样转动它,都是阻塞调用并且需要按顺序执行(好吧,我希望我错了),例如

loop = asyncio.get_event_loop()
loop.call_soon(MyAsyncApp().run())
loop.run_forever()

我当前的尝试导致应用程序正在启动,但是没有正在执行的协程,例如当我点击“连接”按钮时,我应该开始使用loop.call_soon来安排和执行任务,但没有任何反应。

有人可以查看我的代码并提出正确解决问题的方法吗?

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()

2 个答案:

答案 0 :(得分:2)

  

我当前的尝试导致应用程序正在启动,但没有   协程正在执行

发生这种情况是因为MyAsyncApp().run()阻止了执行流程,控制永远不会返回到asyncio的事件循环。这就是所有事件循环的工作方式。

不是手动尝试跨越两个循环,而是使用现有的尝试:

https://github.com/kivy/kivy/pull/5241

此PR来自Kivy的一位开发人员,包含有解释和用法示例的工作实现。

然而它还没有合并到主人:你需要手动用这个PR建立Kivy。

答案 1 :(得分:1)

我最近在 Kivy 的文档中发现了 this。现在 Kivy 支持异步循环库,比如 asyncio 和 trio。在链接中,您可以找到以下示例:

异步示例 ~~~~~~~~~~~~~–

import asyncio

from kivy.app import async_runTouchApp
from kivy.uix.label import Label


loop = asyncio.get_event_loop()
loop.run_until_complete(
    async_runTouchApp(Label(text='Hello, World!'), async_lib='asyncio'))
loop.close()

三重奏~~~~~~~~~~-

import trio

from kivy.app import async_runTouchApp
from kivy.uix.label import Label

from functools import partial

# use functools.partial() to pass keyword arguments:
async_runTouchApp_func = partial(async_runTouchApp, async_lib='trio')

trio.run(async_runTouchApp_func, Label(text='Hello, World!'))

抱歉英语不好。希望它有所帮助。