简体   繁体   中英

combining ZMQ event loop with QT / Pyforms event loop

I'm trying to implement both, zmq and a Pyforms GUI which both requires there own event loop. The task is to have a Pyforms GUI with a textfield, that displays the incoming zmq messages. This is the simplified code that I'm trying to get to work.

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

This is the ZMQ sender.

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)

I see 2 possible solutions. First it could work with threads like in the code above. But I haven't found a way to start both event loops. Either one statement blocks the other or I get error messages when I don't use lamda etc. Or it's just not working. - here is a reference I tried to implement for this without success, describing a similar task: github maartenbreddels

The second option is to add the zmq function call of echo() into the eventloop of Pyforms (which is based on QT as far as I know). This could be the most elegant but I don't know how to implement or add something to the GUI's event loop.

I've tried a lot for both solutions without success.
The most valuable information I could find is here:

pyzmq readthedocs

zeromq org

pyforms readthedocs

I've not a lot experience and try to understand things like futures, promises and coroutines but also frameworks like asyncio, green in python without success so far. A simple function call for "echo", as soon as a message is received, is what I'm looking for.

Any Ideas how to make it work? Am I doing something silly ?

Apologies in advance for a vague answer, but perhaps it can act as a potential starting point.

PyForms looks like, ultimately, it's based on Qt. Qt I think can use a socket (well, a file descriptor) as an input even source. ZeroMQ, at least the C version, exposes a file descriptor that becomes to ready-to-read when a ZMQ message has been received. So in principal, Qt could use this file descriptor to call a callback that reads whatever ZMQ socket has received a message, and handle the message on the Qt event loop's thread (which may have other benefits!).

Whether or not any of this is exposed by PyZMQ and PyForms I'm afraid I don't know.

Thank you Bazza for your input. Your answer helped me to find a solution for my problem. After searching how I can emit a Qevent; I found the following example example and solved the problem. The final code looks like this:

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

Thanks a lot and best regards!!!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM