1

I'm trying to implement a state machine in my GUI (using python3.8 and Qt5 (using PyQt5 and not PySides!)). The problem I encounter with this is when adding a transistion based on an event.

Using the code below, the interpreter complains at line 26:

s1.addTransition(self.btn_start, pyqtSignal("clicked()"), s2)
TypeError: C++ type 'clicked()' is not supported as a pyqtSignal() type argument type
import sys
import logging
from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, QApplication
from PyQt5.QtCore import QStateMachine, QState, QFinalState, pyqtSignal


class TabTest(QWidget):

    def __init__(self, parent=None):
        super().__init__(parent)

        self.logger = logging.getLogger("TABTEST")

        self.btn_start = QPushButton("START")
        # self.btn_start.clicked.connect(self.btn_start_clicked)

        layout = QVBoxLayout()
        layout.addWidget(self.btn_start)
        self.setLayout(layout)

        machine = QStateMachine()
        s1 = QState()
        s1.assignProperty(self.btn_start, "TEXT", "CLICK ME")

        s2 = QFinalState()
        s1.addTransition(self.btn_start, pyqtSignal("clicked()"), s2)

        machine.addState(s1)
        machine.addState(s2)
        machine.setInitialState(s1)
        machine.start()

    # def btn_start_clicked(self):
    #     self.logger.info("klikked")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    main_window = TabTest()
    main_window.show()
    sys.exit(app.exec_())

I googled before and read on stackoverflow but all answers are for Qt4 or are using PySides. Lots of changes between Qt5 and Qt4. And PySides has PySides.QtCore.SIGNAL, I found the equivalent in PyQt5 is PyQt5.QtCore.pyqtSignal, but can't seem to have it working.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Swedgin
  • 815
  • 1
  • 11
  • 20

1 Answers1

1

Your code has 3 errors:

  • QStateMachine is a local variable that will be destroyed instantly preventing the desired behavior from being observed, given that there are 2 solutions: set a parent(change to QStateMachine(self)) or make it a member of the class(change all machine to self.machine).

  • If you want to assign a property then the name must be respected, in this case the property is "text" and not "TEXT".

  • In PyQt5 you must pass the signal, that is, obj.foo_signal, in your case self.btn_start.clicked.

Considering the above, the solution is:

class TabTest(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.btn_start = QPushButton("START")

        layout = QVBoxLayout(self)
        layout.addWidget(self.btn_start)

        machine = QStateMachine(self)

        s1 = QState()
        s1.assignProperty(self.btn_start, "text", "CLICK ME")

        s2 = QFinalState()

        s1.addTransition(self.btn_start.clicked, s2)

        # Check if QFinalState was entered
        machine.finished.connect(lambda: print("finished"))

        machine.addState(s1)
        machine.addState(s2)
        machine.setInitialState(s1)
        machine.start()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thanks you! 1) ok, good catch, that would've burned me later. 2) yeah, I wasn't quite sure what that parameter did, so I was still experimenting with that. 3) Do you know where I can find this in the docs? That's quite an important thing I missed. – Swedgin May 18 '20 at 13:57
  • 1
    @Swedgin There are no docs unfortunately but some time ago I tried it, and I extracted it as a translation of [this method](https://doc.qt.io/qt-5/qstate.html#addTransition-2) from C++ to python that does not use the SIGNAL() macro in addition to checking the source code (qstate.sip) – eyllanesc May 18 '20 at 14:04
  • Ok, that's too bad. In the meantime I also searched back, but now more focused and found the definition of the function using pyqtBoundSignal. And indeed, no docs as it has a `TODO` below it haha. https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtcore/qstate.html?highlight=addtransition##addTransition – Swedgin May 18 '20 at 14:15
  • @Swedgin `self.btn_start.clicked` is actually a pyqtBoundSignal, you found the docs :-). It seems that the PyQt5 page has been updated because a few months ago that link did not exist. – eyllanesc May 18 '20 at 14:17
  • Yeah I figured now as much. I'm used to check https://doc.qt.io/qt-5 for documentation, but now apperantly that one is behind and I should use the `riverbankcomputing` one – Swedgin May 18 '20 at 14:31