I am prototyping an application and i would like to control QML UI transitions from a QStateMachine
on the C++ side of the app. To make things easier, we can say that the QML UI consists in several pages which contains buttons that should trigger a transition from one page to another.
// main.qml
Window {
// ..
StackLayout {
id: layout
anchors.fill: parent
currentIndex: uiController.currentPage // binding with the C++ side
Page0 {
id: page0
}
Page1 {
id: page1
}
Page2 {
id: page2
}
Page3 {
id: page3
}
}
// ..
}
}
Now each Page
has a Button
so that the user can to go to another page:
// example of Page0.qml
Page {
id: root
// ..
Button {
text: "Page 1"
width: 100
height: 100
anchors.top: text.bottom
anchors.horizontalCenter: text.horizontalCenter
anchors.horizontalCenterOffset: 10
onClicked: {
console.log("Button clicked")
backend.msg = "Button clicked !"
uiController.buttonClicked = 1; // binding with the C++ side
}
}
// ..
}
On the C++ side i have a controller that internally use a statemachine to control the transitions :
class UIController : public QObject
{
Q_OBJECT
Q_PROPERTY(int buttonClicked READ buttonClicked WRITE setButtonClicked NOTIFY buttonClickedChanged)
Q_PROPERTY(int currentPage READ currentPage WRITE setCurrentPage NOTIFY currentPageChanged)
public:
// ..
private:
QStateMachine m_machine;
int m_buttonClicked;
int m_currentPage;
};
Now the important part is the set up of the QStateMachine :
UIController::UIController()
: m_buttonClicked(0)
{
QState *page1 = new QState();
QState *page2 = new QState();
QState *page3 = new QState();
QState *page4 = new QState();
// ButtonTransition rely on a ButtonEvent
ButtonTransition *tr1 = new ButtonTransition(1);
ButtonTransition *tr2 = new ButtonTransition(2);
ButtonTransition *tr3 = new ButtonTransition(3);
ButtonTransition *tr4 = new ButtonTransition(4);
// the current page is a state property
page1->assignProperty(this, "currentPage", 0);
page2->assignProperty(this, "currentPage", 1);
page3->assignProperty(this, "currentPage", 2);
page4->assignProperty(this, "currentPage", 3);
tr1->setTargetState(page2);
tr2->setTargetState(page3);
tr3->setTargetState(page4);
tr4->setTargetState(page1);
page1->addTransition(tr1);
page2->addTransition(tr2);
page3->addTransition(tr3);
page4->addTransition(tr4);
m_machine.addState(page1);
m_machine.addState(page2);
m_machine.addState(page3);
m_machine.addState(page4);
m_machine.setInitialState(page1);
m_machine.start();
}
And finally for the transitions to occurs :
/* this setter function is called everytime the QML side change the
buttonClicked property of the UiController */
void UIController::setButtonClicked(int button)
{
if (m_buttonClicked != button) {
m_buttonClicked = button;
m_machine.postEvent(new ButtonEvent(button));
emit buttonClickedChanged();
}
}
It actually works but i am asking if there are better ways to do that : i think this approach is a bit "clumsy".
Especially is it possible to bind the state machine transition directly to QML signals ? (as for QSignalTransition
)
Thank you.
Especially is it possible to bind the state machine transition directly to QML signals ?
Yes. You can connect the entered()
signal from any sub-state to eg buttonClickedChanged()
.
Instead of QStateMachine, use the declarative state machine framework in QML projects. It is concise, readable and has features not available in QStateMachine such as the TimeoutTransition .
Your sample QML code needs few changes to use the declarative state machine.
Add a top-level signal to your "Page" components:
// Page1.qml
import QtQuick 2.0
import QtQuick.Controls 2.12
Page {
id: root
signal clicked() // <- new signal
// ..
Button {
text: "Page 1"
width: 100; height: 100
onClicked: {
console.log("Button clicked")
root.clicked() // emit new signal
}
}
// ..
}
And here's the entire main.qml with the declarative state machine:
// main.qml
import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.12
import QtQml.StateMachine 1.12 as DSM
ApplicationWindow {
visible: true
width: 640; height: 480
StackLayout {
id: layout
anchors.fill: parent
Page0 { id: page0 }
Page1 { id: page1 }
Page2 { id: page2 }
Page3 { id: page3 }
}
DSM.StateMachine {
initialState: p0
running: true
DSM.State {
id: p0
onEntered: layout.currentIndex = 0
DSM.SignalTransition { targetState: p1; signal: page0.clicked }
}
DSM.State {
id: p1
onEntered: layout.currentIndex = 1
DSM.SignalTransition { targetState: p2; signal: page1.clicked }
}
DSM.State {
id: p2
onEntered: layout.currentIndex = 2
DSM.SignalTransition { targetState: p3; signal: page2.clicked }
}
DSM.State {
id: p3
onEntered: layout.currentIndex = 3
DSM.SignalTransition { targetState: p0; signal: page3.clicked }
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.