从C ++ QStateMachine控制QML UI

时间:2019-01-15 11:38:36

标签: c++ qt qml

我正在为应用程序制作原型,我想从应用程序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

谢谢。

2 个答案:

答案 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 }
        }
    }
}