C++ type is not supported as a pyqtSignal() type argument type

1.2k Views Asked by At

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.

1

There are 1 best solutions below

5
On BEST ANSWER

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()