Странное поведение QScxmlStateMachine.connectToEvent в PySide2
Я пытаюсь написать настольное приложение, используя Python на бэкэнде, QML на внешнем интерфейсе и scxml для создания конечного автомата. Я использую PySide2. Я намерен описать логику приложения с помощью конечного автомата. Основываясь на состоянии, пользовательский интерфейс в QML должен реагировать соответствующим образом. Кроме того, бэкэнд должен выполнять методы, основанные на вводе состояний и т. Д.
Мой проект состоит из 3 файлов: main.py
с внутренней логикой, ui.qml
с пользовательским интерфейсом, stateMachine.scxml
с конечным автоматом.
содержание main.py
:
from PySide2.QtScxml import QScxmlStateMachine
from PySide2.QtWidgets import QApplication
from PySide2.QtCore import QUrl, QObject, Slot
from PySide2.QtQml import QQmlApplicationEngine
class BackEnd(QObject):
def __init__(self):
super().__init__()
@Slot()
def ev_slot(self):
'''method called on event t1'''
print('event t1')
@Slot(bool)
def st_slot(self, active):
'''method called on entering and exiting state s2'''
if active:
print('s2 entered')
else:
print('s2 exited')
app = QApplication([])
qml_url = QUrl("ui.qml")
engine = QQmlApplicationEngine()
# loading state machine
my_state_machine = QScxmlStateMachine.fromFile('stateMachine.scxml')
backend = BackEnd()
# registering state machine in QML context
engine.rootContext().setContextProperty("stateMachine", my_state_machine)
# connecting event of state machine to method of backend
conn1 = my_state_machine.connectToEvent("t1", backend, "aev_slot()")
# connecting state of state machine to method of backend
conn2 = my_state_machine.connectToState("s2", backend, "ast_slot(bool)")
my_state_machine.start()
engine.load(qml_url)
app.exec_()
Пользовательский интерфейс очень прост: он состоит только из 3 кнопок, которые отправляют события на конечный автомат (ui.qml
файл):
import QtQuick 2.0
import QtQuick.Controls 2.2
ApplicationWindow {
width: column.width
height: column.height
visible: true
Column {
id: column
spacing: 10
Button {
id: button0
text: qsTr("t1")
onClicked: stateMachine.submitEvent('t1')
}
Button {
id: button1
text: qsTr("t2")
onClicked: stateMachine.submitEvent('t2')
}
Button {
id: button2
text: qsTr("t3")
onClicked: stateMachine.submitEvent('t3')
}
}
}
Государственный аппарат stateMachine.scxml
состоит из 3 состояний: s1
, s2
, s3
и переходы t1
, t2
, t3
:
<?xml version="1.0" encoding="UTF-8"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" binding="early" xmlns:qt="http://www.qt.io/2015/02/scxml-ext" name="stateMachine" initial="s1">
<state id="s1">
<transition type="external" event="t1" target="s2"/>
<onentry>
<log label="entered" expr="s1"/>
</onentry>
</state>
<state id="s2">
<transition type="external" event="t2" target="s3"/>
<onentry>
<log label="entered" expr="s2"/>
</onentry>
</state>
<state id="s3">
<transition type="external" event="t3" target="s1">
</transition>
<onentry>
<log label="entered" expr="s3"/>
</onentry>
</state>
</scxml>
Проблема в том, что все работает. Линия my_state_machine.connectToEvent("t1", backend, "aev_slot()")
имеет ошибку: имя метода ev_slot()
не aev_slot()
, Но если я изменил его на правильное имя, я получаю следующую ошибку:
QObject::connect: No such slot BackEnd::v_slot()
Почему-то первая буква в имени метода игнорируется. Я делаю что-то неправильно? Я очень новичок в Qt и PySide2. В целом это хороший подход? Я использую PySide2 5.11.1a1.dev1530708810518
1 ответ
Вы должны использовать SLOT()
передать метод в виде строки (имейте в виду, что SLOT
отличается от Slot
декоратор).
from PySide2 import QtCore, QtGui, QtQml, QtScxml
class BackEnd(QtCore.QObject):
@QtCore.Slot()
def ev_slot(self):
'''method called on event t1'''
print('event t1')
@QtCore.Slot(bool)
def st_slot(self, active):
'''method called on entering and exiting state s2'''
if active:
print('s2 entered')
else:
print('s2 exited')
if __name__ == '__main__':
import sys
app = QtGui.QGuiApplication(sys.argv)
qml_url = QtCore.QUrl.fromLocalFile("ui.qml")
# loading state machine
my_state_machine = QtScxml.QScxmlStateMachine.fromFile('stateMachine.scxml')
backend = BackEnd()
conn1 = my_state_machine.connectToEvent("t1", backend, QtCore.SLOT("ev_slot()"))
conn2 = my_state_machine.connectToState("s2", backend, QtCore.SLOT("st_slot(bool)"))
my_state_machine.start()
engine = QtQml.QQmlApplicationEngine()
engine.rootContext().setContextProperty("stateMachine", my_state_machine)
engine.load(qml_url)
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
SLOT
только префиксы 1
на имя слота, и СИГНАЛ делает то же самое с 2
, так что если вы не хотите использовать его просто префикс 1: (..., backend, "1ev_slot()")
(Я не рекомендую делать это, так как это делает код менее читабельным)