4

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?

illright
  • 3,991
  • 2
  • 29
  • 54
  • I think the approach taken with twisted is to have Kivy's eventloop drive Twisted's eventloop. Perhaps you can achieve the same thing with Tornado. – inclement Dec 10 '16 at 12:09
  • @inclement it's not clear how the Twisted event loop gets driven as it is a package specifically for Twisted. I tried a backwards approach instead: have the Kivy event loop be run as a slave and Tornado event loop to call `EventLoop.idle()` on every iteration. The window successfully loads but then the app freezes. More details in the edit – illright Dec 10 '16 at 13:31
  • I think running two event loops with two separate threads might be much easier to handle than trying to integrate them both. – Nykakin Dec 11 '16 at 10:35
  • @Nykakin yeah, I tried that and it was easier. The problem was that during some heavy computations on the Tornado thread, the Kivy application stops responding. Can this be fixed? – illright Dec 11 '16 at 10:37
  • @Leva7 heavy computation on a separate thread shouldn't affect GUI event loop in the main thread. My guess is you handle communication between both threads in a blocking way. You should probably use `queue.Queue` class and call `get` method with soume timeout set. Consider putting your code in a separate question. – Nykakin Dec 11 '16 at 10:47

1 Answers1

0

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.

gens
  • 972
  • 11
  • 22