Das unbarmherzige pyqtSignal()

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Hier nochmal auf Threadsicherheit getestet mit signals/slots in beide Richtungen und mit ein paar Änderungen am Interface:
- MySignal muss zwingend auf Exemplarebene liegen, nicht klassenweit (sonst werden die Signale dupliziert)
- MySignal braucht das Exemplar mit dem Signal als parent (sonst kann pyconnect nicht die korrekte Threadaffinität herausfinden)

Code: Alles auswählen

from PyQt4 import QtGui
from PyQt4.QtCore import pyqtSignal, QObject, QThread

class Wrapper(QObject):
    def __init__(self, f):
        QObject.__init__(self, None)
        self.f = f
    def wrap(self, *args):
        print self.thread().currentThreadId(), 'should be equal to:',
        return self.f(*args[0], **args[1])

class MySignal(QObject):
    sig = pyqtSignal(tuple, dict)
    def __init__(self, parent):
        QObject.__init__(self, parent)
        self.wrappers = []
    def pyconnect(self, f):
        wrapper = Wrapper(f)
        if (f.im_self.thread() != self.thread()):
            wrapper.moveToThread(f.im_self.thread())
        self.wrappers.append(wrapper)
        self.sig.connect(wrapper.wrap)
    def connect(self, f):
        self.sig.connect(f)
    def emit(self, args, kwargs):
        self.sig.emit(args, kwargs)
    def pyemit(self, *args, **kwargs):
        self.sig.emit(args, kwargs)

class Work(QObject):
    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self.done = MySignal(self)

    def work(self, a, b, c):
        print self.thread().currentThreadId(), 'should be subthread'
        self.done.pyemit('done', 'with', z='work')

class Widget(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        # MySignal must be instance not class wide and must have parent set!
        self.do = MySignal(self)

        # some work for a thread
        self.work = Work()
        self.mythread = QThread(self)
        self.work.moveToThread(self.mythread)

        # signal for do work and work done
        # always do signal slot connections after the thread binding
        self.do.pyconnect(self.work.work)
        self.work.done.pyconnect(self.done)

        # start thread
        self.mythread.start()

        # some action to trigger everything
        self.button = QtGui.QPushButton(self)
        self.button.clicked.connect(lambda : self.do.pyemit('a', 'b', c='c'))

    def done(self, x, y, z):
        print self.thread().currentThreadId(), 'should be mainthread'

    def closeEvent(self, ev):
        # shutdown thread gracefully
        self.mythread.quit()
        self.mythread.wait()

if __name__ == '__main__':
    app = QtGui.QApplication([])
    print app.thread().currentThreadId(), 'mainthread'
    win = Widget()
    win.show()
    app.exec_()
Antworten