Search code examples
pythonpyqt4qt4signals-slotsqstatemachine

how to use a custom signal with QStateMachine addtransition


'm trying to figure out how to use my own custom signals in combination with QStateMachine. I started with a simple example from here. Now I'm trying to create a new signal mysignal and trigger a transition off of it. But I can't figure out how to structure the call to addtransition, or how to use the SIGNAL("clicked()") syntax to refer to mysignal.

from PyQt4.QtGui import *
from PyQt4.QtCore import *

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    button = QPushButton()
    machine = QStateMachine()

    off = QState()
    off.assignProperty(button, 'text', 'Off')
    off.setObjectName('off')

    on = QState()
    on.setObjectName('on')
    on.assignProperty(button, 'text', 'On')

    mysignal = pyqtSignal()

    off.addTransition(mysignal, on)
    # Let's use the new style signals just for the kicks.
    on.addTransition(button.clicked, off)

    machine.addState(off)
    machine.addState(on)
    machine.setInitialState(off)
    machine.start()
    mysignal.emit()
    button.resize(100, 50)
    button.show()
    sys.exit(app.exec_())

Solution

  • A custom signal must be defined as a class attribute (see New-style Signal and Slot Support in the PyQt docs). So the code in your example needs to be refactored so that all the setup happens in the __init__ of a widget subclass.

    Below is a demo that does that (in order to trigger the custom signal and its state transition, you must type "foo" in the line-edit whenever the button text shows "Foo"):

    from PyQt4 import QtCore, QtGui
    
    class Window(QtGui.QWidget):
        customSignal = QtCore.pyqtSignal()
    
        def __init__(self):
            QtGui.QWidget.__init__(self)
            self.edit = QtGui.QLineEdit(self)
            self.edit.textChanged.connect(self.handleTextChanged)
            self.button = QtGui.QPushButton(self)
            layout = QtGui.QVBoxLayout(self)
            layout.addWidget(self.edit)
            layout.addWidget(self.button)
            self.machine = QtCore.QStateMachine()
            self.off = QtCore.QState()
            self.off.assignProperty(self.button, 'text', 'Off')
            self.on = QtCore.QState()
            self.on.assignProperty(self.button, 'text', 'On')
            self.foo = QtCore.QState()
            self.foo.assignProperty(self.button, 'text', 'Foo')
            self.off.addTransition(self.button.clicked, self.on)
            self.on.addTransition(self.button.clicked, self.foo)
            self.foo.addTransition(self.customSignal, self.off)
            self.machine.addState(self.off)
            self.machine.addState(self.on)
            self.machine.addState(self.foo)
            self.machine.setInitialState(self.off)
            self.machine.start()
    
        def handleTextChanged(self, text):
            if text == 'foo':
                self.edit.clear()
                self.customSignal.emit()
    
    if __name__ == '__main__':
    
        import sys
        app = QtGui.QApplication(sys.argv)
        window = Window()
        window.show()
        window.setGeometry(500, 300, 100, 100)
        sys.exit(app.exec_())