Пользовательский сигнал PyQt, никогда не полученный подклассом QSignalTransition

#python #python-3.x #pyqt #pyqt5 #qstatemachine

#python #python-3.x #pyqt #pyqt5 #qstatemachine

Вопрос:

Пытаюсь изучить PyQt5 QStateMachine с переходами, полученными из QSignalTransition следования этому примеру, написанному для PySide. Чтобы использовать PyQt, я просто изменил определение сигнала и способ ссылки на него в QSignalTransition инициализаторе. Это полный код:

 from PyQt5.QtCore import QObject, pyqtSignal, QSignalTransition, QCoreApplication, QStateMachine, QState, QFinalState


class Factorial(QObject):
    xChanged = pyqtSignal(int)
    def __init__(self):
        super(Factorial, self).__init__()
        self.xval = -1
        self.facval = 1
    def getX(self):
        return self.xval
    def setX(self, x):
        if self.xval == x:
            return
        self.xval = x
        self.xChanged.emit(x)
    x = property(int, getX, setX)
    def getFact(self):
        return self.facval
    def setFact(self, fac):
        self.facval = fac
    fac = property(int, getFact, setFact)

class FactorialLoopTransition(QSignalTransition):
    def __init__(self, fact):
        super(FactorialLoopTransition, self).__init__(fact.xChanged[int])
        self.fact = fact
    def eventTest(self, e):
        if not super(FactorialLoopTransition, self).eventTest(e):
            return False
        return e.arguments()[0] > 1
    def onTransition(self, e):
        x = e.arguments()[0]
        fac = self.fact.fac
        self.fact.fac = x * fac
        self.fact.x = x - 1

class FactorialDoneTransition(QSignalTransition):
    def __init__(self, fact):
        super(FactorialDoneTransition, self).__init__(fact.xChanged[int])
        self.fact = fact
    def eventTest(self, e):
        if not super(FactorialDoneTransition, self).eventTest(e):
            return False
        return e.arguments()[0] <= 1
    def onTransition(self, e):
        print(self.fact.fac)

if __name__ == '__main__':
    import sys
    app = QCoreApplication(sys.argv)
    factorial = Factorial()
    machine = QStateMachine()

    compute = QState(machine)
    compute.assignProperty(factorial, 'fac', 1)
    compute.assignProperty(factorial, 'x', 6)
    compute.addTransition(FactorialLoopTransition(factorial))

    done = QFinalState(machine)
    doneTransition = FactorialDoneTransition(factorial)
    doneTransition.setTargetState(done)
    compute.addTransition(doneTransition)

    machine.setInitialState(compute)
    machine.finished.connect(app.quit)
    machine.start()
    sys.exit(app.exec_())
  

Этот конечный автомат просто вычисляет факториал 6 и печатает его.

Моя проблема в том, что в обоих переходах этот фрагмент кода:

 def eventTest(self, e):
    if not super(FactorialDoneTransition, self).eventTest(e):
        return False
    return e.arguments()[0] <= 1
  

возвращается False при первом и единственном вызове. Я предполагаю, что это связано с тем, что переданное событие имеет неправильный тип, и возвращается суперкласс False . При таком поведении переходы никогда не выполняются, и конечный автомат остается в своем начальном состоянии.

Кто-нибудь может объяснить, в чем проблема? Является ли неправильным мое преобразование из PySide в PyQt?

Ответ №1:

assignProperty() Метод ожидает Q_PROPERTY, который в случае PySide /PySide2 равен свойству (которое отличается от свойства, предоставленного python изначально), а в случае PyQt4 / PyQt5 вы должны использовать pyqtProperty, и это является причиной ошибки, поэтому решение состоит в замене на следующее:

 from PyQt5.QtCore import QObject, pyqtSignal, QSignalTransition, QCoreApplication, QStateMachine, QState, QFinalState, pyqtProperty


class Factorial(QObject):
    xChanged = pyqtSignal(int)
    def __init__(self):
        super(Factorial, self).__init__()
        self.xval = -1
        self.facval = 1

    def getX(self):
        return self.xval

    def setX(self, x):
        if self.xval == x:
            return
        self.xval = x
        self.xChanged.emit(x)

    x = pyqtProperty(int, getX, setX) # <---

    def getFact(self):
        return self.facval

    def setFact(self, fac):
        self.facval = fac

    fac = pyqtProperty(int, getFact, setFact) # <---

# ...
  

Комментарии:

1. Спасибо за прекрасное объяснение, это именно то решение. Я не понимаю, почему это привело к отправке события неправильного типа.

2. @mins Проблема в том, что из-за неправильной передачи значения конечный автомат запускается, но так и не был завершен, поэтому сигнал QStateMachine finished так и не был отправлен, и, следовательно, не было вызвано ни app.quit(), который закрывает приложение.

3. На самом деле, я вижу, что первое отправленное событие не является SignalEvent , даже если код правильный. Итак, ок, до исправления никаких других событий не отправлялось, потому что значение x никогда не изменялось.