Python: 如何将 tornado read_message() 的结果传递给 QT5 UI 元素

Python: How to pass tornado read_message()'s result to QT5 UI element

我有一个 python QT5 应用程序,它在另一个线程中启动 tornado 套接字客户端。我想知道的是,每当我在龙卷风的 websocket.read_message() 中得到结果时,如何将该值传递给 QT5 GUI 标签。

这里 Main.py 在新线程中启动 QT5 GUI 应用程序和 tornado websocket 客户端。

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

这里是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")

tornado 线程可以从原始线程访问内存中的对象。因此,一个非常简单的解决方案是将 QT 应用程序中的 QApplication 或其他对象传递到您的 Client 对象中。然后当你收到一条消息时,你可以调用QT对象上的任何方法。

但是,目前还不清楚这是否会安全运行。 QT 对象可能不设计为从两个不同的线程访问。在不知道 class 是如何使用的情况下,您不应该假设它是 线程安全的 。所以简单的解决方案可能意味着在主线程上有一些 QT 代码 运行 的同时,Tornado 线程调用一些 QT 代码来改变 QT 对象的状态。另一段代码并不期望那个东西会改变,所以你会遇到崩溃或其他故障。

您可能会发现 QT 库 设计用于处理此问题的。但是无论如何有更好的方法 - producer-consumer queue.

这意味着您有一个线程间共享的队列对象。这可能是您已经在使用的 threading 模块中的 threading.Queue。每次收到 Websocket 消息时,您 put 队列中的内容。这可以是任何 Python 对象 - 如果您只需要消息字符串,也可以是一些自定义对象。

然后在另一个线程上,您有一个循环,它只从队列中读取,并在每次收到消息时调用 QT。

编辑 - QT 可能可以处理这个问题

我对QT不是很了解。然而,大多数窗口系统实际上都有自己的事件队列,它们在其中管理点击、按键等。如果调用 QT 更新某些东西是在这个事件队列上处理的,这可能意味着它们是线程安全的。这是因为您将从 Tornado 线程调用的函数不会直接对 GUI 进行更改,而只是将一些内容添加到队列中,以便在 QT 轮到它时进行处理。

我快速浏览了一些关于 QT 的内容,似乎这可能就是 QT 的工作方式,您应该可以从 Tornado 线程中找到更新它而无需实现您自己的队列。参见 and http://code.activestate.com/recipes/415311-qt-event-processing-python-threads/