将ZMQ事件循环与QT / Pyforms事件循环结合在一起

时间:2018-08-15 14:43:52

标签: python event-handling zeromq pyforms

我试图同时实现zmq和Pyforms GUI,这两个都需要有自己的事件循环。任务是创建一个带有文本字段的Pyforms GUI,该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)

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

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

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

pyzmq readthedocs

zeromq org

pyforms readthedocs

我经验不足,试图了解诸如Future,Promise和Coroutines之类的东西,但也了解asyncio之类的框架,python中的green至今没有成功。我正在寻找一个简单的函数,即在收到消息后立即调用“ echo”。

任何想法如何使其起作用?我在做傻事吗?

2 个答案:

答案 0 :(得分:0)

事先道歉,以获得模糊的答案,但也许可以作为一个潜在的起点。

PyForms最终看起来是基于Qt的。 Qt我认为可以使用套接字(很好,文件描述符)作为输入甚至源。 ZeroMQ(至少是C版本)公开了一个文件描述符,当接收到ZMQ消息时,该文件描述符可以立即读取。因此,原则上,Qt可以使用此文件描述符来调用一个回调,该回调读取ZMQ套接字收到的任何消息,并在Qt事件循环的线程上处理该消息(这可能还有其他好处!)。

恐怕我不知道PyZMQ和PyForms是否暴露了其中任何一个。

答案 1 :(得分:0)

谢谢Bazza的投入。您的回答帮助我找到了解决问题的方法。在搜索如何发出Qevent之后;我找到以下示例example并解决了该问题。 最终代码如下:

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

非常感谢!!