狀態機,簡寫為FSM(Finite State Machine),狀態機由狀態暫存器和組合邏輯電路構成,能夠根據控制訊號按照預先設定的狀態進行狀態轉移,是協調相關訊號動作、完成特定操作的控制中心。

    在GUI開發的時候,介面複雜的邏輯往往令人抓狂,邏輯都不清晰,程式碼寫起來更加費勁。使用者介面設計中採用狀態驅動,就可以使得GUI的邏輯更加清晰。根據當前狀態的不同,顯示不同的介面。程式介面可以被看作顯示對應不同場景,或者是通過改變外觀響應使用者的互動。通常情況下,介面中很多個元件的改變是併發進行的,這樣的介面可以看作從一個狀態改變到另外一個狀態。

    今天我們來看看qt中的狀態機框架,qt把它作為QtCore中的一個模組,也足見其重要性:

    Qt狀態機框架提供了一些類來建立執行狀態圖,狀態圖為一個系統如何對外界激勵進行反應提供了一個圖形化模型,該模型是通過定義一些系統可能進入的狀態以及系統怎樣從一個狀態切換到另一個狀態來實現的。事件驅動的系統的一個關鍵特性就是它的行為不總是僅僅依賴於前一個或者當前的事件,而且也依賴於將要執行的事件。通過使用狀態圖,這些資訊會非常容易進行表達。狀態機框架提供了一套API以及一種執行模型,可以有效地將狀態圖的元素和語義嵌入到Qt應用程式中。該框架與Qt的元物件系統結合緊密:例如,不同狀態之間的轉化可由訊號觸發,訊號驅動,且狀態可配置用於QObject的屬性和方法。


在進入今天的主題之前,我們先來了解三個概念:

狀態(State):是靜態的東西,對一系列物件屬性的一組靜態描述和配置。

過渡(Transition):是一段時間或者空間的描述,狀態之間切換的整個過程,或者描述的是不同屬性值之間變化的一個過程。

動畫(Animation):是動態的一個東西,可以看作是過渡的執行者,通過在屬性值上應用動畫型別來建立。動畫型別會對屬性值進行插值,從而創建出平滑的過渡效果。要建立動畫,需要為某個屬性使用恰當的動畫型別;應用的動畫也依賴於需要實現的行為型別。

這三者之間的聯絡較為密切,只定義了狀態,它是一個死(靜態)的東西,所以就需要過渡,來讓它活起來。但是過渡這個過程是如何實現,就需要動畫來執行,從而讓過的的平滑,給使用者良好的視覺效果。

在Qt中,這三者之間的關係有沒有那麼明確,但是明確這三個概念,對於我們理解qt中的狀態機制會有很大的幫助。

一、狀態

對於初學者來說,我建議從qml中去學習瞭解狀態機的概念會更快。它描述性的語言,會讓人很快的去接受這些概念和使用。在你瞭解了qml中狀態機的使用後,再來了解qt中的狀態機就會發現很簡單。今天我們就先從qml中瞭解一些狀態機的魅力。


在qml中,繼承自Item的控制元件都會有一個states屬性,該屬性有使用者自定義的屬性組組成。可以通過PropertyChanges、ParentChange、StateChangeScript、 AnchorChanges這些控制元件對這個狀態中的屬性做定義和描述。下面是一個簡單的程式碼示例:

import QtQuick 2.0


Rectangle {
    id: root
    width: 100; height: 100


    states: [
        State {
            name: "red_color"
            PropertyChanges { target: root; color: "red" }
        },
        State {
            name: "blue_color"
            PropertyChanges { target: root; color: "blue" }
        }
    ]
}

二、漸變


在狀態改變的過程中,我們可以指定一個過渡,可以採用Transition 、Behavior這兩個控制元件,可以在這個過渡中指定各種動畫,從而達到想要的互動效果。

import QtQuick 2.0

Rectangle {
    id: rect
    width: 100; height: 100
    color: "red"

    MouseArea {
        id: mouseArea
        anchors.fill: parent
    }

    states: State {
        name: "moved"; when: mouseArea.pressed
        PropertyChanges { target: rect; x: 50; y: 50 }
    }

    transitions: Transition {
        NumberAnimation { properties: "x,y"; easing.type: Easing.InOutQuad }
    }
}
這個小示例藉助Item的transitions屬性,qt幫助文件上的描述,This property holds the list of transitions for this item. These define the transitions to be applied to the item whenever it changes its state.,只有在狀態屬性發生改變,才會觸發這個漸變,再運用NumberAnimation,完成這個漸變。
import QtQuick 2.0

Rectangle {
    width: 400
    height: 400

    Rectangle {
        id: coloredRect
        width: 100
        height: 100
        anchors.centerIn: parent

        color: "red"
        Behavior on color {
            ColorAnimation {}
        }

        MouseArea {
            id: mouser
            anchors.fill: parent
            hoverEnabled: true
        }

        states: State {
            name: "GreenState"
            when: mouser.containsMouse

            PropertyChanges {
                target: coloredRect
                color: "green"
            }
        }
    }
}
這串程式碼也實現了狀態的漸變,區別在於Behavior這個控制元件不僅可以用於狀態的改變,在沒有使用狀態,只要安裝Behavoir監控的屬性發生了改變,就會運用相應的動畫平滑的完成這個漸變,使用起來相當方便。

以上的兩部分講的就是狀態和漸變,藉助qml這種描述性語言,我們也可很方便的完成GUI的開發,很容易做出漂亮的效果。在上面的例項中,大家發現也有很多動畫,所以說,狀態、漸變、動畫三者關係是很密切的,下面,我們來看看動畫。

三、動畫


上面的圖是qtquick中的所有用到的動畫的一個關係圖,他們都繼承自Animation這個最基礎的控制元件,這個控制元件提供了動畫的控制,開始,暫停,重播,播放次數。此外它還提供了兩個訊號,started() 和 stopped(),以供其他控制元件去捕捉該訊號去響應其他的槽函式。在平時開發中運用較多的是 PropertyAnimation這個控制元件。Qt系統中強大的元物件系統,使得對屬性的操作很方便,所以這個PropertyAnimation就可以對這些屬性做動畫。而且它還帶Easing Curve緩和曲線,qt中提供了四十多種緩和曲線,讓開發者更方便的定義動畫的效果。此外還有ParallelAnimation 、 SequentialAnimation 這兩個控制元件,一個是平行動畫組,一個是序列動畫組,這兩個又可以相互的巢狀,使用非常的方便。在使用的時候,可能又會遇到想要在兩個動畫之間加一段空白暫停的時間,這時候 PauseAnimation 就可以達到你想要的效果。

總結:今天主要講解的是qt QML中的狀態機制,這個對於剛接觸qt的新手會比較好上手,瞭解了這些概念和控制元件的使用,我們基本上就可以完成一些基本的互動介面的開發。

後續 詳解Qt中的狀態機機制(二)中會講解qt C++中的狀態機制以及和QML中的狀態機制的比較。