-->

How to run the Tornado event loop alongside a Kivy

2019-05-10 06:13发布

问题:

My client application uses a Kivy GUI (Kivy has its own event loop) and connects to the server using the WebSocket protocol with Tornado (Tornado also has an event loop). That's why the connection part is asynchronous.
I want the user to interact with the UI while a Tornado client is running an infinite asynchronous loop of listening for server messages.

Here's some example code:
client_test.py

from tornado.ioloop import IOLoop
from tornado.websocket import websocket_connect

class RequestSender:
    url = 'server url here (no scheme)'

    async def _connect(self):
        self.conn = await websocket_connect('wss://' + self.url, io_loop=self.ioloop)
        self.ioloop.add_callback(self._listen)

    async def _listen(self):
        while True:
            print(await self.conn.read_message())

    def __init__(self):
        self.ioloop = IOLoop.current()
        self.ioloop.add_callback(self._connect)

    def run(self):
        self.ioloop.start()

GUI

from kivy.app import App
from kivy.uix.label import Label
from client_test import RequestSender

class TestApp(App):
    def build(self):
        RequestSender().run()
        return Label(text = "hello")

TestApp().run()

Apparently, since the Tornado's event loop has started earlier, it has taken over the program flow and now no GUI window appears.
I execute the GUI file and the execution hangs after the RequestSender().run(), so build never returns.

Searching on this case provided little to no information, except for this Google Groups post. Kivy's documentation only mentions Twisted.

I tried putting the Kivy event loop into slave mode and running GUI updates from Tornado's event loop, but that didn't work because apparently a call EventLoop.idle() of Kivy's event loop isn't enough to keep the GUI application running.

What else could be done here?

回答1:

I found this question trying to do the same thing, and opted for two separate processes instead; one Kivy GUI and one Tornado (Server, in my case). I have the two communicate using multiprocessing.connection as explained well in this SO answer

If you have large and complex data to pass between the two, perhaps this is less than ideal, but for simple messages it works well. You also have the advantage of running without the UI, and running the UI on a separate machine.