1. 程式人生 > >QML:第一個QML專案分析

QML:第一個QML專案分析

QML是一種宣告式語言,它提供了一組介面用來描述視覺化元件以及他們之間的互動。它是一個高度可讀的語言,並且被設計成使元件以一個動態的方式相互連線。同時它使元件很容易被複用以及建立定製的使用者介面。使用QtQuick模組,設計者和開發者可以很容易使用QML建立帶有流暢動畫的使用者介面,並將這些介面連線到後端的C++庫上面。

QML是一個使用者介面規範和程式語言。它允許開發者和設計者建立高效能的,流暢的動畫和視覺效果的應用。QML提供了一個高度可讀的,宣告式的,類似JSON語法的,並支援與JavaScript表示式相結合來達到動態屬性的繫結。

專案檔案結構如下圖:
專案結構

先看看工程檔案demo01.pro

TEMPLATE = app

QT += qml quick # 這裡需要新增qml和quick模組
CONFIG += c++11 # c++11標準支援

SOURCES += main.cpp

RESOURCES += qml.qrc

# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =

# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH = # The following define makes your compiler emit warnings if you use # any feature of Qt which as been marked deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine; /// QML引擎
    engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); /// 載入QML檔案

    return app.exec();
}

補充:QStringLiteral巨集建立一個QString在編譯的時候,如果編譯器支援這種操作。這個巨集避免了執行時動態分配空間。

main.qml

QML基本介紹

qml的檔案結構分為三部分:import宣告、物件宣告和註釋。

import宣告

一個QML檔案在檔案頂部可能含有一個或多個import宣告. 一個import宣告可能為以下幾種型別:

  • 註冊的是哪個版本的名稱空間。
  • a relative directory which contains type-definitions as QML documents
  • 一個JavaScript檔案

JavaScript file imports must be qualified when imported, so that the properties and methods they provide can be accessed.
import宣告有以下幾種形式:

  • import Namespace VersionMajor.VersionMinor
  • import Namespace VersionMajor.VersionMinor as SingletonTypeIdentifier
  • import “directory”
  • import “file.js” as ScriptIdentifier

例子:

import QtQuick 2.0
import QtQuick.LocalStorage 2.0 as Database
import "../privateComponents"
import "somefile.js" as Script

物件宣告

物件的幾個常用屬性

id屬性

QML物件的id屬性是唯一的,不同物件的id屬性值不能相同,我們可通過某個物件的id屬性來訪問這個物件。id屬性是QML語言內建的一個屬性,在物件例項化完成後其值是不能被修改的,不同於其它的普通屬性。id屬性值必須以小寫字母或下劃線開頭,且不能包含字母、數字、下劃線以外的其它字元,如下所示:

Rectangle {  
    id: root  
    width: 800; height: 480  
    color: "lightblue"  

    Column {  
        Text { id: myText; text: "Hello World" }  
        Text { text: myText.text }  
    }  
}  

通過物件的id來訪問這個物件:

自定義屬性

在QML檔案中我們可自定義屬性,語法如下:

property <propertyType> <propertyName> [ : <value> ]  

自定義屬性完成後會自動為這個屬性建立一個屬性值改變的訊號和相應的訊號處理器onChanged,這裡的ProperName是這個屬性的名字,首字母大寫,如下所示:

Rectangle {  
    id: root  

    property string someText  
    onSomeTextChanged: console.log("The someText will be: " + someText)  

    width: 800; height: 480  
    color: "lightblue"  

    MouseArea {  
        anchors.fill: parent  
        onClicked: someText = "click"  
    }  
}  

properName以一個小寫字母開頭,只能包括字母、數字和下劃線。propertyType可以是QML基本型別,enumeration以int來代替,也可以是QML物件型別,神奇的var型別是返型的,支援任何型別的屬性值,如下所示:

Item {  
    property int theNumber  
    property string theString  
    property url theUrl  

    property Item someItem  
    property Rectangle someRectangle  

    property var someNumber: 1.5  
    property var someString: "abc"  
    property var someBool: true  
    property var someList: [1, 2, "three", "four"]  
    property var someObject: Rectangle { width: 100; height: 100; color: "red" }  
}  

屬性值可以被初始化,也可以使用JavaScript表示式來賦值,通過這兩種方式賦值時,可以是一個靜態值,也可以是一個與其它屬性繫結的值。

Rectangle {  
    id: rootRect  

    property color theColor: "green"  
    property color previousColor: rootRect.color  
    property color nextColor  

    width: 100; height: 100  
    color: "red"  

    Component.onCompleted: {  
        rootRect.nextColor = rootRect.color  
        console.log(theColor, previousColor, nextColor, rootRect.color)  
    }  
} 

屬性是型別安全的,賦值時屬性值必須與屬性型別匹配,否則會出錯,如下所示:

// Invalid property assignment: int expected  
property int theNumber: "one"  
列表屬性

先看如下程式碼:

Item {  
    children: [  
        Text { text: "textOne" },  
        Text { text: "textTwo" },  
        Text { text: "textThree" }  
    ]  

    Component.onCompleted: {  
        for(var i = 0; i < children.length; i++)  
            console.log("text of lable", i, ":", children[i].text)  
    }  
}  

上面例子中的children屬性就是一個列表屬性,包含在一對方括號中,裡面的元素必須是QML物件型別而不能是QML基本型別,並以逗號分隔。列表內的元素可通過陣列下標[index]訪問,元素個數由length屬性提供。若列表內只有一個元素,方括號可省略。

自定義列表屬性,程式碼如下:

Item {  
    property list<Rectangle> siblingRects  
    property list<Rectangle> childRects: [  
        Rectangle { color: "red" },  
        Rectangle { color: "green" },  
        Rectangle { color: "blue"}  
    ]  

    Component.onCompleted: {  
        for(var i = 0; i < childRects.length; i++)  
            console.log("color of lable", i, ":", childRects[i].color)  
    }  
}  
分組屬性

在某些情況下,屬性可以是由若干個子屬性構成的一個邏輯組,我們可以用“.”符號或者分組符號對其進行賦值。


    Text {  
        //dot notation  
        font.pixelSize: 12  
        font.bold: true  
    }  

    Text {  
        //group notation  
        font { pixelSize: 12; bold: true }  
    }  
屬性別名

屬性別名引用其它的屬性,語法如下:

[default] property alias <name>: <alias reference>  

name是我們自定以的屬性名,alias reference是屬性別名所引用的那個屬性或物件,也就是說屬性別名可以引用自一個單一的屬性,也可以引用自一個複雜的物件。屬性繫結是動態的,但不是雙向的,而通過屬性別名可達到動態、雙向的效果,即修改name和aliasreference中任一個的值,與之關聯的另一個屬性值也會隨之改變。還有一點需要注意的是,如果屬性別名與已有的屬性名相同,就會把已有的屬性覆蓋掉,當我們再使用這個屬性的時候,實際上使用的是我們自定義的那個屬性,如下所示:

Rectangle {  
    id: rootRect  

    property alias color: blueRect.color  
    property alias theRect: blueRect  

    color: "red"  

    Rectangle {  
        id: blueRect  
        color: "#1234ff"  
    }  

    Component.onCompleted: {  
        console.log ("color:", color, // "#1234ff"  
                     "blueRect.color:", blueRect.color, // "#1234ff"  
                     "theRect.color:", theRect.color, // "#1234ff"  
                     "rootRect.color:", rootRect.color) // "#1234ff"  

        color = "#884646"  
        console.log ("color:", color, // "#884646"  
                     "blueRect.color:", blueRect.color, // "#884646"  
                     "theRect.color:", theRect.color, // "#884646"  
                     "rootRect.color:", rootRect.color) // "#884646"  

        blueRect.color = "#123456"  
        console.log ("color:", color, // "#123456"  
                     "blueRect.color:", blueRect.color, // "#123456"  
                     "theRect.color:", theRect.color, // "#123456"  
                     "rootRect.color:", rootRect.color) // "#123456"  

        theRect.color = "#ff00ff"  
        console.log ("color:", color, // "#ff00ff"  
                     "blueRect.color:", blueRect.color, // "#ff00ff"  
                     "theRect.color:", theRect.color, // "#ff00ff"  
                     "rootRect.color:", rootRect.color) // "#ff00ff"  
    }  
}  
預設屬性

任何基於Item的型別都有一個data列表屬性,這個屬性就是該型別的預設屬性,儲存了所有的孩子物件,其中可視的孩子物件又儲存在了children列表屬性中,不可視的孩子物件儲存在了resources列表屬性中,在新增子物件時children、resources屬性可寫可不寫,都會自動新增到對應的屬性列表中,所以在之前介紹列表屬性的例子中可省略children關鍵字,修改後如下:

Item {  
    Text { text: "textOne" }  
    Text { text: "textTwo" }   
    Text { text: "textThree" }   
    Component.onCompleted: {  
        for(var i = 0; i < children.length; i++)  
            console.log("text of lable", i, ":", children[i].text)  
    }  
}  

另外,我們還可以在自定義屬性時新增default關鍵字,將這個屬性作為預設屬性。

只讀屬性

使用readonly關鍵字可定義一個只讀屬性,語法如下:

readonly property <propertyType> <propertyName> : <initialValue>

只讀屬性必須初始化,且不能修改,也不能是default屬性和alias屬性,如下所示:

Item {  
    readonly property int someNumber: 10  
    Component.onCompleted: someNumber = 20  // TypeError: Cannot assign to read-only property   "someNumber"  
}  
附加屬性

QML語言還包括了一些附加屬性和訊號處理器,由相應的附加型別提供,使用語法如下:

<AttachingType>.<propertyName>  
<AttachingType>.on<SignalName> 

anchors錨佈局

QML的佈局方式一般採用兩種,一種就是直接設定,X與Y座標的值。一種是採用相對位置佈局,anchors錨佈局。使用錨佈局的能夠使介面更緊湊,更有整體化。錨佈局相當於Qt的 Grid,HBox,VBox layout。

錨佈局包含器個屬性:left, horizontalCenter, right, top, verticalCenter, baseline, and bottom.
錨佈局

The Qt Quick anchoring system allows you to define relationships between the anchor lines of different items. For example, you can write:

  Rectangle { id: rect1; ... }
  Rectangle { id: rect2; anchors.left: rect1.right; ... }

這裡寫圖片描述
You can specify multiple anchors. For example:

  Rectangle { id: rect1; ... }
  Rectangle { id: rect2; anchors.left: rect1.right; anchors.top: rect1.bottom; ... }

這裡寫圖片描述

By specifying multiple horizontal or vertical anchors you can control the size of an item. Below, rect2 is anchored to the right of rect1 and the left of rect3. If either of the blue rectangles are moved, rect2 will stretch and shrink as necessary:

  Rectangle { id: rect1; x: 0; ... }
  Rectangle { id: rect2; anchors.left: rect1.right; anchors.right: rect3.left; ... }
  Rectangle { id: rect3; x: 150; ... }

這裡寫圖片描述

錨佈局的Margins和Offsets

這裡寫圖片描述

  Rectangle { id: rect1; ... }
  Rectangle { id: rect2; anchors.left: rect1.right; anchors.leftMargin: 5; ... }

這裡寫圖片描述

注意:處於效能原因,你僅被允許相對它的兄弟物件或者父物件來錨定一個物件。

程式碼

import QtQuick 2.6
import QtQuick.Window 2.2
/*
Window物件為Qt Quick 視窗建立一個頂層的視窗.
要使用這個物件,必須增加如下import宣告:
  import QtQuick.Window 2.2
*/

Window {
    visible: true // 是否可見
    width: 640
    height: 480
    title: qsTr("Hello World")

    /*
        MouseArea是一個不可見的物件,通常用來關聯一個可見的物件,以便為它提供滑鼠操作。
    */
    MouseArea {
        /*
        anchors.centerIn:parent,是將子控制元件放在父控制元件的正中心,子控制元件的寬高是自己設定的;anchors.fill:parent, 是在子控制元件的大小設定與父控制元件大小一樣,特別是mouseArea中經常使用anchors.fill:parent,這是為了指定滑鼠事件接受的範圍。如果是兩個矩形控制元件,顏色不同,那麼子控制元件會完全覆蓋父控制元件,全是子控制元件的顏色顯示。
        還有一點,就是parent是指父類,直接相關的父類。anchors.fill指定的父類是必須是直接的父類,如果不是,會報錯的。
        */
        anchors.fill: parent
        /*
        滑鼠位置資訊和按鍵點選事件通過signals提供.使用頻率最高的滑鼠按下和點選處理事件:onClicked, onDoubleClicked, onPressed, onReleased and onPressAndHold.
        還可以使用onWheel訊號去操作塑標滾動事件。
        */
        onClicked: {
            /*
              console.log, console.debug, console.info, console.warn和console.error可以被用來列印除錯資訊到控制檯上.
            */
            console.log(qsTr('Clicked on background. Text: "' + textEdit.text + '"'))
        }
    }

    TextEdit {
        /*
          ID屬性:
        QML物件的id屬性是唯一的,不同物件的id屬性值不能相同,我們可通過某個物件的id屬性來訪問這個物件。
        id屬性是QML語言內建的一個屬性,在物件例項化完成後其值是不能被修改的,不同於其它的普通屬性。
        id屬性值必須以小寫字母或下劃線開頭,且不能包含字母、數字、下劃線以外的其它字元。

        */
        id: textEdit
        text: qsTr("Enter some text...")
        /*

        horizontalAlignment和verticalAlignment。
        Valid values for horizontalAlignment are:
            TextEdit.AlignLeft (default)
            TextEdit.AlignRight
            TextEdit.AlignHCenter
            TextEdit.AlignJustify
        Valid values for verticalAlignment are:
            TextEdit.AlignTop (default)
            TextEdit.AlignBottom
            TextEdit.AlignVCenter
        */
        verticalAlignment: Text.AlignVCenter
        anchors.top: parent.top
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.topMargin: 20
        Rectangle {
            anchors.fill: parent
            anchors.margins: -10
            color: "transparent"
            border.width: 1
        }
    }
}