Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Emit signal in standard python thread

I have a threaded application where I do have a network thread. The UI-part passes a callback to this thread. The thread is a normal python thread - it's NO QThread.

Is it possible to emit PyQT Slot within this thread?

like image 925
Razer Avatar asked Sep 29 '13 12:09

Razer


People also ask

What is emit in Python?

Emit is a Python library for realtime data processing. It can distribute work with Celery or RQ, coordinate execution in other languages, and let you swing from the trees of your graph with Tarzan-like precision.

Is threading a standard Python library?

The Python standard library provides threading , which contains most of the primitives you'll see in this article. Thread , in this module, nicely encapsulates threads, providing a clean interface to work with them. When you create a Thread , you pass it a function and a list containing the arguments to that function.

Is Python threading efficient?

The threading is efficient in CPython, but threads can not run concurrently on different processors/cores. This is probably what was meant. It only affects you if you need to do shared memory concurrency. Other Python implementations does not have this problem.


1 Answers

No, it is not possible to emit a PyQt signal from a python thread like this.

However, a possible solution is to use an additional object shared by both threads, making the necessary operations to finally emit a thread-safe PyQt signal.

Here is an implementation of a "SafeConnector" class, making use of a pair of connected sockets and a Queue to exchange data between the two threads, and using a QSocketNotifier to get back in Qt's loop. A QObject is used to make it possible to emit a proper Qt signal:

from PyQt4 import Qt, QtCore, QtGui
import threading
import socket
import Queue
import time

# Object of this class has to be shared between
# the two threads (Python and Qt one).
# Qt thread calls 'connect',   
# Python thread calls 'emit'.
# The slot corresponding to the emitted signal
# will be called in Qt's thread.
class SafeConnector:
    def __init__(self):
        self._rsock, self._wsock = socket.socketpair()
        self._queue = Queue.Queue()
        self._qt_object = QtCore.QObject()
        self._notifier = QtCore.QSocketNotifier(self._rsock.fileno(),
                                                QtCore.QSocketNotifier.Read)
        self._notifier.activated.connect(self._recv)

    def connect(self, signal, receiver):
        QtCore.QObject.connect(self._qt_object, signal, receiver)

    # should be called by Python thread
    def emit(self, signal, args):
        self._queue.put((signal, args))
        self._wsock.send('!')

    # happens in Qt's main thread
    def _recv(self):
        self._rsock.recv(1)
        signal, args = self._queue.get()
        self._qt_object.emit(signal, args)

class PythonThread(threading.Thread):
    def __init__(self, connector, *args, **kwargs):
        threading.Thread.__init__(self, *args, **kwargs)
        self.connector = connector
        self.daemon = True

    def emit_signal(self):
        self.connector.emit(QtCore.SIGNAL("test"), str(time.time()))

    def run(self):
        while True:
            time.sleep(1)
            self.emit_signal()

if __name__ == '__main__':
    app = QtGui.QApplication([])
    mainwin = QtGui.QMainWindow()
    label = QtGui.QLabel(mainwin)
    mainwin.setCentralWidget(label)

    connector = SafeConnector()
    python_thread = PythonThread(connector)
    connector.connect(QtCore.SIGNAL("test"), label.setText)
    python_thread.start()

    mainwin.show()
    app.exec_()
like image 151
mguijarr Avatar answered Oct 02 '22 07:10

mguijarr