我正在为应用程序制作原型,我想从应用程序C ++侧的QStateMachine
控制QML UI转换。为了使事情变得简单,我们可以说QML UI包含几个页面,其中包含应该触发从一个页面到另一页面的转换的按钮。
// 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
}
}
// ..
}
}
现在每个Page
都有一个Button
,以便用户可以转到另一个页面:
// 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
}
}
// ..
}
在C ++方面,我有一个控制器,该控制器在内部使用状态机来控制转换:
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;
};
现在重要的部分是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();
}
最后要进行过渡:
/* 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();
}
}
它确实有效,但是我问是否有更好的方法可以做到这一点:我认为这种方法有点“笨拙”。
尤其可以将状态机转换直接绑定到QML信号吗? (关于QSignalTransition
)
谢谢。
答案 0 :(得分:2)
尤其可以将状态机转换直接绑定到QML信号吗?
是的。您可以将entered()
信号从任何子状态连接到例如buttonClickedChanged()
。
答案 1 :(得分:0)
在QML项目中使用declarative state machine framework代替QStateMachine。它简洁,易读,并且具有QStateMachine中不可用的功能,例如TimeoutTransition。
您的示例QML代码只需进行少量更改即可使用声明式状态机。
向“页面”组件添加顶层信号:
// 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
}
}
// ..
}
这是声明状态机的整个main.qml:
// 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 }
}
}
}