How to signal slots in a GUI from a different process?

前端 未结 5 2110
囚心锁ツ
囚心锁ツ 2021-01-31 19:52

Context: In Python a main thread spawns a 2nd process (using multiprocessing module) and then launches a GUI (using PyQt4). At this point the main thread blocks until the GUI is

5条回答
  •  别跟我提以往
    2021-01-31 20:26

    One should first look how Signals/Slots work within only one Python process:

    If there is only one running QThread, they just call the slots directly.

    If the signal is emitted on a different thread it has to find the target thread of the signal and put a message/ post an event in the thread queue of this thread. This thread will then, in due time, process the message/event and call the signal.

    So, there is always some kind of polling involved internally and the important thing is that the polling is non-blocking.

    Processes created by multiprocessing can communicate via Pipes which gives you two connections for each side.

    The poll function of Connection is non-blocking, therefore I would regularly poll it with a QTimer and then emit signals accordingly.

    Another solution might be to have a Thread from the threading module (or a QThread) specifically just waiting for new messages from a Queue with the get function of the queue. See the Pipes and Queues part of multiprocessing for more information..

    Here is an example starting a Qt GUI in another Process together with a Thread who listens on a Connection and upon a certain message, closes the GUI which then terminates the process.

    from multiprocessing import Process, Pipe
    from threading import Thread
    import time
    from PySide import QtGui
    
    class MyProcess(Process):
    
        def __init__(self, child_conn):
            super().__init__()
            self.child_conn = child_conn
    
        def run(self):
            # start a qt application
            app = QtGui.QApplication([])
            window = QtGui.QWidget()
            layout = QtGui.QVBoxLayout(window)
            button = QtGui.QPushButton('Test')
            button.clicked.connect(self.print_something)
            layout.addWidget(button)
            window.show()
    
            # start thread which listens on the child_connection
            t = Thread(target=self.listen, args = (app,))
            t.start()
    
            app.exec_() # this will block this process until somebody calls app.quit
    
        def listen(self, app):
            while True:
                message = self.child_conn.recv()
                if message == 'stop now':
                    app.quit()
                    return
    
        def print_something(self):
            print("button pressed")
    
    if __name__ == '__main__':
        parent_conn, child_conn = Pipe()
        s = MyProcess(child_conn)
        s.start()
        time.sleep(5)
        parent_conn.send('stop now')
        s.join()
    

提交回复
热议问题