首页 > 解决方案 > 将 ZMQ 事件循环与 QT / Pyforms 事件循环相结合

问题描述

我正在尝试同时实现 zmq 和 Pyforms GUI,它们都需要有自己的事件循环。任务是拥有一个带有文本字段的 Pyforms GUI,显示传入的 zmq 消息。这是我试图开始工作的简化代码。

import pyforms
from   pyforms          import BaseWidget
from   pyforms.controls import ControlTextArea
from   pyforms.controls import ControlButton
import threading
import zmq
from zmq.asyncio import Context
from zmq.eventloop.zmqstream import ZMQStream
from zmq.eventloop import ioloop


class SimpleExample1(BaseWidget):

    def __init__(self):
        super(SimpleExample1,self).__init__('Simple example 1')

        #Definition of the forms fields
        self._controltextarea     = ControlTextArea('textarea to show incoming zmq messages')
        self._button        = ControlButton('Press this button')

        def echo(msg):
            self._controltextarea.__add__(msg) #this should add a line in the Textbox with the message "msg"


        context = Context.instance()
        s = context.socket(zmq.PULL)
        s.connect('tcp://127.0.0.1:5014')
        stream = ZMQStream(s)
        stream.on_recv(echo)  #this calls the function echo from the zmq Ioloop when something is recived

#Execute the application
if __name__ == "__main__":
    #here is where I have tried a lot to make both loops work simultaniously, without success
    guiThread = threading.Thread(target=pyforms.start_app( SimpleExample1 ))
    zmqThread = threading.Thread(target=lambda: ioloop.IOLoop.current().start())
    zmqThread.setDaemon(True)
    guiThread.start()
    zmqThread.start()

这是 ZMQ 发送方。

import zmq
import time

context = zmq.Context()
publisher = context.socket(zmq.PUSH)
publisher.bind('tcp://127.0.0.1:5014')

while True:
    publisher.send_string('something')
    #print('sended')
    time.sleep(1)

我看到了两种可能的解决方案。首先,它可以与上面代码中的线程一起工作。但是我还没有找到一种方法来启动两个事件循环。当我不使用 lamda 等时,一个语句会阻塞另一个语句,或者我收到错误消息。或者它只是不工作。- 这是我尝试实现但没有成功的参考,描述了类似的任务:github maartenbreddels

第二种选择是将 echo() 的 zmq 函数调用添加到 Pyforms 的事件循环中(据我所知,它基于 QT)。这可能是最优雅的,但我不知道如何实现或添加一些东西到 GUI 的事件循环中。

我对这两种解决方案都进行了很多尝试,但都没有成功。
我能找到的最有价值的信息在这里:

pyzmq 阅读文档

zeromq 组织

pyforms 阅读文档

我没有太多经验,并试图理解诸如期货、承诺和协程之类的东西,但也了解诸如 asyncio 之类的框架,到目前为止,python 中的绿色还没有成功。一旦收到消息,对“echo”的简单函数调用就是我正在寻找的。

任何想法如何使它工作?我在做傻事吗?

标签: pythonevent-handlingzeromqpyforms

解决方案


谢谢巴扎的意见。您的回答帮助我找到了解决问题的方法。在搜索了如何发出 Qevent 之后;我找到了以下示例解决了问题。最终代码如下所示:

import pyforms
from   pyforms          import BaseWidget
from   pyforms.controls import ControlTextArea
from   pyforms.controls import ControlButton
import threading
import zmq
from PyQt5 import QtCore

class ZeroMQ_Listener(QtCore.QObject):

    message = QtCore.pyqtSignal(str)

    def __init__(self):

        QtCore.QObject.__init__(self)

        # Socket to talk to server
        context = zmq.Context()
        self.socket = context.socket(zmq.PULL)
        self.socket.connect('tcp://127.0.0.1:5014')
        print('connected!')
        self.running = True

    def loop(self):
        while self.running:
            string = self.socket.recv_string()
            self.message.emit(string)


class SimpleExample1(BaseWidget):

    def __init__(self):
        super(SimpleExample1,self).__init__('Simple example 1')

        #Definition of the forms fields
        self._controltextarea     = ControlTextArea('textarea to show incoming zmq messages')
        self._button        = ControlButton('Press this button')

        message = QtCore.pyqtSignal(str)
        self.thread = QtCore.QThread()
        self.zeromq_listener = ZeroMQ_Listener()

        self.zeromq_listener.moveToThread(self.thread)

        self.thread.started.connect(self.zeromq_listener.loop)
        self.zeromq_listener.message.connect(self.signal_received)

        QtCore.QTimer.singleShot(0, self.thread.start)


    def signal_received(self, message):
        self._controltextarea.__add__(message)

#Execute the application
if __name__ == "__main__":
    guiThread = threading.Thread(target=pyforms.start_app( SimpleExample1 ))

    guiThread.start()

非常感谢和最好的问候!!!


推荐阅读