【问题标题】:combining ZMQ event loop with QT / Pyforms event loop将 ZMQ 事件循环与 QT / Pyforms 事件循环相结合
【发布时间】:2018-08-15 14:43:51
【问题描述】:

我正在尝试同时实现 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)

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

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

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

pyzmq readthedocs

zeromq org

pyforms readthedocs

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

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

【问题讨论】:

    标签: python event-handling zeromq pyforms


    【解决方案1】:

    对于含糊的答案,请提前道歉,但也许它可以作为一个潜在的起点。

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

    恐怕我不知道 PyZMQ 和 PyForms 是否公开了这些。

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-28
        • 1970-01-01
        • 2020-09-06
        • 2010-12-19
        • 2014-10-31
        相关资源
        最近更新 更多