I have a python QT5 application, which starts tornado socket client in another thread. What I want to know is, whenever I get the result in tornado's websocket.read_message(), how to pass that value to QT5 GUI label.
Here is Main.py which starts QT5 GUI application and tornado websocket client in a new thread.
import sys
import threading
from PyQt5.QtWidgets import QApplication
from app.ui.components import MainWindow
from app.client import Client
from config import SERVER_URL
def main():
app = QApplication(sys.argv)
form = MainWindow()
form.show()
sys.exit(app.exec_())
if __name__ == '__main__':
client = Client("ws://{0}/socket".format(SERVER_URL), 5)
thread = threading.Thread(target=lambda : client.ioloop.start())
thread.setDaemon(True)
thread.start()
main()
Here is Client.py
from tornado import gen
from tornado.ioloop import IOLoop, PeriodicCallback
from tornado.websocket import websocket_connect
class Client(object):
def __init__(self, url, timeout):
self.url = url
self.timeout = timeout
self.ioloop = IOLoop.instance()
self.ws = None
self.connect()
self.periodic_callback = PeriodicCallback(self.keep_alive, 20000, io_loop=self.ioloop)
@gen.coroutine
def connect(self):
try:
self.ws = yield websocket_connect(self.url)
self.periodic_callback.start()
except Exception as e:
pass
else:
self.run()
@gen.coroutine
def run(self):
while True:
msg = yield self.ws.read_message()
if msg is None:
self.ws = None
break
print("Received: {0}".format(msg)) # HOW TO PASS msg to GUI LABEL WHENEVER IT RECEIVES A NEW VALUE
def keep_alive(self):
if self.ws is None:
self.connect()
else:
self.ws.write_message("Hello from client")
The tornado thread has access to objects in memory from the original thread. So a very simple solution would be to pass QApplication
or some other object from the QT app into your Client object. Then when you receive a message, you can call whatever method on the QT object.
However, it's far from clear that this will work safely. QT objects might not be designed to be accessed from two different threads. Without knowing how a class is meant to be used, you shouldn't assume it's thread-safe. So the simple solution might mean that at the same time that some QT code is running on your main thread, the Tornado thread calls some piece of QT code which changes the state of the QT objects. The other piece of code wasn't expecting that thing to change, and so you get a crash or other failure.
It's possible you find out that the QT library is designed to cope with this. But's there's a better way in any case - a producer-consumer queue.
This means that you have a Queue object, which is shared between threads. This could be threading.Queue
from the threading
module you're already using. Every time a Websocket message is received, you put
something on the queue. This could be any Python object - just the message string if it's all you need, or some custom object if you want.
Then on another thread, you have a loop which just reads from the queue, and calls QT each time it gets a message.
I don't know much about QT. However, most windowing systems actually have their own event queue, where they manage clicks, key presses, etc. If calls to QT to update something are handled on this event queue, it probably means they are thread safe. This is because the function you would call from the Tornado thread doesn't directly make changes to the GUI, rather it simply adds something to the queue to be processed when QT gets round to it.
I had a quick look to some stuff about QT, and it seems that this probably is how QT works, and you should be find update it from the Tornado thread without implementing your own queue. See https://stackoverflow.com/a/11793684/1737957 and http://code.activestate.com/recipes/415311-qt-event-processing-python-threads/