1. 程式人生 > >Qt Quick QML

Qt Quick QML

Qt Quick簡介

Qt Quick是一個用於幫助開發者設計直觀,現代,流暢的使用者介面的技術集,近年來被廣泛應用於手機,媒體播放器,機頂盒和其他手提裝置.Qt Quick中包含了大量的使用者介面元素,及描述這些使用者介面的陳述性(declarative)語言,和一個語言執行時.在典型的Qt應用程式中有一系列C++ API與這些高層次特性整合.Qt Creator2.1的整合開發環境(IDE)包含了開發Qt Quick應用程式的必要工具.

QML語言
QML是高層次的描述性語言.其命令和得體的元素可以平衡Qt庫的功能和效率,使用可執行直觀功能的命令非常容易.繪製一個矩形,展示圖片,及應用程式事件--都可以在這個陳述性(declarative)程式中完成.

這種語言也允許使用JavaScript靈活控制這些命令,實現高層次的使用者介面邏輯.

為方便定義元素通常QML元素都帶有各種屬性.例如,如果要定義一個圓,那麼可能就有一個半徑作為其屬性.使用匯入的元素構建使用者介面是QML和Qt Quick的一大特點.

QtDeclarative模組

為建立Qt Quick,Qt引入了QtDeclarative模組.模組建立了一個JavaScript執行時, Qt在後端支援QML的執行.由於QtDeclarative和QML都基於Qt構建的,他們繼承了很多Qt技術,即訊號槽(signals and slots) 機制和元物件(meta-object)系統.在C++中建立的資料物件可直接在QML中訪問,QML物件也可直接在C++程式碼中進行操作.

QtDeclarative模組協調QML語言,將介面邏輯與C++的應用邏輯相隔離.

Creator工具

Qt Creator是一個完整的基於Qt Quick和Qt應用程式框架的整合開發工具(IDE).

Qt Creator的主要目的是滿足Qt Quick開發者的需要,使其簡單,易用,高效,可擴充套件,開放,降低Qt和Qt Quick新人的入門門檻.Qt Creator的主要特性是允許UI設計人員和開發者完成如下任務:

§  使用範例和教程及專案嚮導可快速學會使用Qt Quick開發應用程式.

§  使用整合的編輯器Qt Quick Designer設計應用程式使用者介面,或使用繪圖軟體設計使用者介面在使用指令碼將設計匯入到Qt Quick Designer.

§  使用高階的程式碼編輯器開發應用程式,提供了強大的程式碼完成功能,重構程式碼功能,及檢視QML檔案元素繼承層次功能.

§  開發針對多種桌面或移動平臺的Qt Quick應用程式,如Microsoft Windows, Mac OS X, Linux, Symbian, 和 Maemo.

§  在當前上下文中除錯JavaScript函式並執行JavaScript表示式,在執行時檢視QML的物件結構,動畫除錯資訊和顏色資訊.

§  向移動裝置部署應用程式併為Symbian和Maemo裝置建立安裝包,通過Ovi儲存裝置和其他通道釋出.

§  可與上下文敏感的QT幫助系統整合輕鬆訪問幫助資訊.

可跳轉頁面

Qt Quick頁面連結了各種Qt Quick主題如QML features, addons, 和tools.
QML Examples and Demos頁面是學習QML應用程式的捷徑.
授權資訊

§  Qt Quick Licensing Information

QML語法


QML是一種宣告式的語言,用來描述程式的使用者介面:兩個方面--外觀和行為.在QML中使用者介面被描述為帶有屬性的物件的樹.

QML中使用JavaScript作為指令碼語言,因此深入學習QML前應先多瞭解一下Javascript.
QML基本語法

QML是這個樣子的:

 import QtQuick 1.0

 Rectangle {
     width: 200
     height: 200
     color: "blue"

     Image {
         source: "pics/logo.png"
         anchors.centerIn: parent
     }
 }

宣告物件時,先指定其型別,後面是一對大括號.物件型別名稱的首字母總是大寫的.上例中,有兩個物件,一個Rectangle,一個Image.在大括號中間可以設定物件的資訊,如屬性.

設定屬性使用 propertyname: value的方式.上例中,設定了Image物件的source屬性值為 "pics/logo.png".屬性和值中間用冒號間隔.

屬性可在一行設定:

 Rectangle {
     width: 100
     height: 100
 }

或在一行中設定多個屬性:

 Rectangle { width: 100; height: 100 }

當在一行中設定多個屬性,需要使用分號間隔.

import語句匯入Qt模組,其中包含了所有標準的QML元素.沒有匯入語句,Rectangle和Image元素都是非法的.
表示式

也可將JavaScript表示式賦值給屬性.

 Rotation {
     angle: 360 * 3
 }

表示式中可以包含其他物件和屬性的引用,這樣就建立了一種繫結.當表示式的值變化時,對應的屬性值會自動更新.

 Item {
     Text {
         id: text1
         text: "Hello World"
     }
     Text {
         id: text2
         text: text1.text
     }
 }

上例中,text2顯示的內容總與text1相同.如果text1被修改了,text2也會自動修改為同樣的值.

注意通過id屬性指定的物件名稱來引用其他物件 (更多id屬性資訊見下面的描述.)
QML註釋

QML中的註釋與JavaScript相同.

    單行註釋使用//.
    多行註釋使用 /*    */

 Text {
     text: "Hello world!"    //a basic greeting
     /*
         We want this text to stand out from the rest so
         we give it a large size and different font.
      */
     font.family: "Helvetica"
     font.pointSize: 24
 }

註釋被引擎忽略.用於解釋做了什麼,便於日後查閱,也有助於其他人閱讀QML檔案.

註釋也可防止程式碼執行,便於跟蹤問題.

 Text {
     text: "Hello world!"
     //opacity: 0.5
 }

上例中,Text物件具有正常的透明度,因為opacity: 0.5被註釋掉了.

QML元素


這是Qt Quick子部分的QML元素的功能分組列表.

These are the functionally grouped lists of QML elements as part of Qt Quick.

宣告元素時要帶有名稱和兩個花括號.元素可能被嵌入到其他元素中,從而在兩個元素間建立了父子關係.

Elements are declared with the their name and two curly braces. Elements may be nested in elements, thereby creating a parent-child relationship between the two elements.

要檢視QML元素的功能列表,請見Groups Of Related QML Elements

To see the QML elements listed by functional area, see the Groups Of Related QML Elements page.

基本QML元素

    Item -被其他QML元素繼承的基本項 Basic item element inherited by QML elements
    Component - 匯入時封裝了QML元素Encapsulates QML elements during importing
    QtObject - 僅容納一個objectName屬性的基本元素Basic element containing only the objectName property

圖形

    Rectangle - 一個矩形元素A rectangle element
    Image -螢幕上的一個位圖 For incorporating bitmaps into a scene
    BorderImage - 可使用圖片作為邊緣 Allows the use of images as borders
    AnimatedImage - 未播放動畫而儲存的一系列幀 For playing animations stored in a series of frames
    Gradient - 定義顏色漸變 For defining a color gradient
    GradientStop -  為Gradient定義一個顏色 Used to define a color within a Gradient
    SystemPalette -  提供訪問Qt調色盤的介面 Provides access to the Qt palettes

文書處理

    Text -  向螢幕上插入格式化的文字 For inserting formatted text into a scene
    TextInput -  獲取使用者鍵盤輸入 Captures user key input
    TextEdit - 顯示多行可編輯的格式化文字 Displays multiple lines of editable formatted text
    IntValidator -  驗證整數Validates values as integers
    DoubleValidator - 驗證實數 Validates real values
    RegExpValidator - 字串正則表示式驗證器 Validator for string regular expressions
    FontLoader - 按名稱或URL載入字型 Loads fonts by name or URL

滑鼠和互動區域

    MouseArea - 設定一塊用於滑鼠互動的區域 Sets up an area for mouse interaction
    Keys - 提供一個帶有附加屬性的元件用於處理鍵盤輸入Provides components with attached properties to handle key input.
    FocusScope - 傳遞鍵盤焦點變化的元素 Element that mediate keyboard focus changes
    Flickable - 提供實現"flicked"(彈性)效果的表面元件 Provides a surface that can be "flicked"
    Flipable - 提供處理”flipping”(翻轉)效果的表面元件 Provides a surface that produces "flipping" effects
    PinchArea - 簡單的擠壓手勢處理 Enables simple pinch gesture handling

定位器和重複器(Repeater)

    Column - 垂直排列子元素 Arranges its children vertically
    Row -  水平排列子元素 Arranges its children horizontally
    Grid -  在網格中定位子元素 Positions its children in a grid
    Flow - 按座標定位子元素 Positions its children with wrapping support
    Repeater - 使用一個模型建立多個元件 Uses a model to create multiple components

轉換Transformations

    Scale - 設定item的縮放行為 Assigns item scaling behaviors
    Rotation - 設定item的選擇行為 Assigns item rotation behaviors
    Translate - 設定item的移動行為 Assigns item translation behaviors

狀態States

    State - 定義物件和屬性的一系列配置值 Defines sets of configurations of objects and properties
    PropertyChanges - 描述在狀態中的屬性變化 Describes property changes within a state
    StateGroup - 包含一系列的狀態和狀態變換 Contains a set of states and state transitions
    StateChangeScript - 時指令碼繫結到一個狀態上 Allows script binding in a state
    ParentChange - 在狀態改變時重定義item的父項 Re-parent an Item in a state change
    AnchorChanges - 在狀態中改變item的描點 Change the anchors of an item in a state

動畫和過度(Transitions)

    Transition - 狀態改變時動畫的過度 Animates transitions during state changes
    SequentialAnimation - 順序執行動畫 Runs animations sequentially
    ParallelAnimation - 並行執行動畫 Runs animations in parallel
    Behavior - 為屬性變換指定預設動畫 Specifies a default animation for property changes
    PropertyAction - 在動畫中設定直接的屬性變化 Sets immediate property changes during animation
    PauseAnimation - 暫停動畫 Introduces a pause in an animation
    SmoothedAnimation - 使屬性平滑的到達指定值 Allows a property to smoothly track a value
    SpringAnimation - 使屬性向彈簧效果一樣達到指定值 Allows a property to track a value in a spring-like motion
    ScriptAction -  動畫時執行指令碼 Runs scripts during an animation

基於資料型別的元素動畫屬性 Elements that animate properties based on data types

    PropertyAnimation - 基於屬性變化的動畫 Animates property changes
    NumberAnimation - 基於greal型別屬性的動畫 Animates properties of type qreal
    Vector3dAnimation - 基於QVector3d型別屬性的動畫 Animates properties of type QVector3d
    ColorAnimation - 基於顏色屬性的動畫 Animates color changes
    RotationAnimation - 基於旋轉的動畫 Animates rotations
    ParentAnimation - 基於parent屬性變化的動畫 Animates parent changes
    AnchorAnimation - 基於描點變化的動畫 Animates anchor changes

模型和資料處理 Models and Data Handling

    ListModel - 定義資料列表 Defines a list of data
    ListElement - 在ListModel中定義資料項 Defines a data item in a ListModel
    VisualItemModel - 包含定義在其視覺化代理中的項 Contains items that already defines its own visual delegate
    VisualDataModel - 封裝一個模型和一個代理 Encapsulates a model and a delegate
    XmlListModel - 使用XPath表示式指定一個模型 Specifies a model using XPath expressions
    XmlRole - 為XmlListModel指定一個角色 Specifies a role for an XmlListModel
    Binding - 在任意屬性上繫結任意值 Binds any value to any property
    Package - 可在不同檢視中共享的項的集合 Collection that enables sharing of items within different views

檢視Views

    ListView - 提供一個視覺化列表模型Provides a list visualization of a model
    GridView - 提供一個視覺化網格模型 Provides a grid visualization of a model
    PathView - 假設模型的內容中含有一個路徑.更多資訊見Path Elements.Visualizes a model's contents along a path. See Path Elements for more information.

路徑定義Path Definition

    Path - 使用PathView定義一個路徑 Defines a path used by PathView
    PathLine - 在Path中定義一條線 Defines a line in Path
    PathQuad - 在Path中定義一條二次方的貝塞爾曲線 Defines a quadratic Bezier curve in a Path
    PathCubic - 在Path中定義一條三次方的貝塞爾曲線 Defines a cubic Bezier curve in a Path
    PathAttribute - 允許設定Path的屬性 Allows the setting of attributes along a Path
    PathPercent - 修改Path中item的分佈 Modifies the item distribution along a Path

功能Utility

    Connections - 顯示連線訊號和槽 Explicitly connects signals and signal handlers
    Timer - 提供定時器Provides timed triggers
    Qt - QML中全域性的Qt物件提供Qt中有用的列舉型別和函式.The QML global Qt object provides useful enums and functions from Qt.
    WorkerScript - 可在QML中使用執行緒 Enables the use of threads in QML
    Loader - 控制item或元件的載入 Controls the loading of items or components
    LayoutItem - 允許在Qt的Graphics View佈局中宣告UI元素 Allows declarative UI elements inside Qt's Graphics View layouts

影象效果 Graphical Effects

    粒子(Particles) - 生成並播放粒子動畫Generates and animates particles
    ParticleMotionLinear - 向粒子增加直線運動行為 Adds linear motion behavior to Particles
    ParticleMotionGravity - 向粒子增加自由落體運動行為 Adds gravitational motion to Particles
    ParticleMotionWander - 向粒子增加不同的運動行為Adds varied motions to Particles
    ShaderEffectItem - 允許在QML中使用OpenGL Shading Language(OpenGL陰影描述語言) Enables the use of OpenGL Shading Language together with QML
    ShaderEffectSource - 封裝QML物件樹作為ShaderEffectItem 的元item Encapsulates QML item tree as a source item for ShaderEffectItem

擴充套件元素 Add-On Elements

這些元素不包括在QtQuick1.0模組中.要使用他們必須先獲取並安裝.These elements are not included in the QtQuick 1.0 module. Their respective QML bindings should first be obtained and installed.

    QtWebKit QML模組 - WebView元素 - 顯示web內容
    Mobility QML Plugins
    Qt Quick Components

使用QML檢視顯示資料

檢視是包含專案的集合.他們富有特色,可自定義風格和行為

Qt Quick圖形元素提供了幾個標準的檢視:

    ListView 水平或垂直列表中排列專案
    GridView 在一個有效空間的網格內排列專案
    PathView 在路徑上排列專案
    WebView - 可在QtWebKit QML Module中使用.

與其他檢視不同,WebView 不具有全部檢視特性,需要與Flickable組合建立一個像Web瀏覽器一樣執行的專案.

這些元素具有的屬性和行為相互獨立.更多資訊見他們的文件.
模型

檢視在螢幕上顯示模型(models).模型可以是簡單的整形列表或一個C++模型.

要給檢視設定模型,需要給檢視的model屬性繫結到一個模型.

 ListModel {
     id: petlist
     ListElement { type: "Cat" }
     ListElement { type: "Dog" }
     ListElement { type: "Mouse" }
     ListElement { type: "Rabbit" }
     ListElement { type: "Horse" }
 }
 ListView {
     id: view
     anchors.fill: parent

     model: petlist
     delegate: petdelegate
 }

更多資訊見QML Data Models 文件.
檢視代理

檢視需要使用代理(delegate)來視覺化的表現列表中的項.檢視以代理作為模版顯示列表中的每個項.模型中的項使用index屬性來訪問.

 Component {
     id: petdelegate
     Text {
         id: label
         font.pixelSize: 24
         text: if (index == 0)
             label.text = type + " (default)"
         else
             text: type
     }
 }

美化檢視

可使用decoration屬性來自定義檢視的header,footer,section屬性.通過向這些屬性繫結其他可視物件,就可美化檢視.footer中可能包含一個Rectangle元素作為邊框,或在header中顯示列表的logo圖示.

假如一個俱樂部要使用它們的商標顏色來修飾其成員列表.成員列表包含在一個模型中,代理顯示模型中的內容.

 ListModel {
     id: nameModel
     ListElement { name: "Alice" }
     ListElement { name: "Bob" }
     ListElement { name: "Jane" }
     ListElement { name: "Harry" }
     ListElement { name: "Wendy" }
 }
 Component {
     id: nameDelegate
     Text {
         text: name;
         font.pixelSize: 24
     }
 }

可以向header和footer屬性繫結可視物件來美化這個俱樂部的成員列表.這個可視物件可以直接定義,或在其他檔案中定義,或在元件元素中定義..

 ListView {
     anchors.fill: parent
     clip: true
     model: nameModel
     delegate: nameDelegate
     header: bannercomponent
     footer: Rectangle {
         width: parent.width; height: 30;
         gradient: clubcolors
     }
     highlight: Rectangle {
         width: parent.width
         color: "lightgray"
     }
 }

 Component {     //instantiated when header is processed
     id: bannercomponent
     Rectangle {
         id: banner
         width: parent.width; height: 50
         gradient: clubcolors
         border {color: "#9EDDF2"; width: 2}
         Text {
             anchors.centerIn: parent
             text: "Club Members"
             font.pixelSize: 32
         }
     }
 }
 Gradient {
     id: clubcolors
     GradientStop { position: 0.0; color: "#8EE2FE"}
     GradientStop { position: 0.66; color: "#7ED2EE"}
 }

 
ListView的小節

ListView 可在sections中包含很多分組,相關的列表項按他們所在的小節進行標記.而且小節還可以指定代理(delegates).

如下列表中包含了人員姓名和所在小組的資訊.

 ListModel {
     id: nameModel
     ListElement { name: "Alice"; team: "Crypto" }
     ListElement { name: "Bob"; team: "Crypto" }
     ListElement { name: "Jane"; team: "QA" }
     ListElement { name: "Victor"; team: "QA" }
     ListElement { name: "Wendy"; team: "Graphics" }
 }
 Component {
     id: nameDelegate
     Text {
         text: name;
         font.pixelSize: 24
         anchors.left: parent.left
         anchors.leftMargin: 2
     }
 }

ListView 元素具有一個叫做section的附加屬性(attached property) 可在一個小節內組合臨近或相關的元素.section的property屬性指明列表元素中的一個屬性作為小節名稱.criteria屬性指明如何顯示小節的名稱,而delegate與檢視的delegate相同.

 ListView {
     anchors.fill: parent
     model: nameModel
     delegate: nameDelegate
     focus: true
     highlight: Rectangle {
         color: "lightblue"
         width: parent.width
     }
     section {
         property: "team"
         criteria: ViewSection.FullString
         delegate: Rectangle {
             color: "#b0dfb0"
             width: parent.width
             height: childrenRect.height + 4
             Text { anchors.horizontalCenter: parent.horizontalCenter
                 font.pixelSize: 16
                 font.bold: true
                 text: section
             }
         }
     }
 }

QML範例詳解

研究了一段時間QML,現在對Qt中的一個計算器範例的程式碼進行分析,並總結一下前面學習的內容.Qt這種語言大多數還是被用於嵌入式裝置上,而QML則是專為嵌入式裝置而生的.Qt在桌面開發上佔據的比例很小,而且已被Nokia出售,未來的前景如何誰也不好說.但Qt確實很棒,祝福一下吧,如果以後Qt支援android和蘋果的開發了,在繼續深入研究.

上圖是執行效果圖,介面風格確實很漂亮.滑鼠點選按鈕後還有一個變灰的反應,整體來說介面簡潔大氣.而且實現了計算器的基本功能,這裡要說明的是,所有功能都是由QML獨立完成的,沒有任何qt外掛參與.而且調整介面的尺寸後,還會使介面發生旋轉.這樣的功能這樣的介面效果要是使用Qt或Delphi,VC來實現的話,相信還是有一點的工作量的.正如前面翻譯的文章中所說的那樣,QML適合於介面上有大量簡單動態元素的情形.像這種計算器程式或時鐘程式使用QML實現就太方便了.

在總結一下以前翻譯的幾篇文章中的要點:QML中的核心是屬性繫結,物件的屬性發生了變化不一定就一定有函式在給屬性賦值.可能是其他的屬性與其有繫結關係,當這些屬性發生變化時,QML引擎會自動為屬性重新計算值.動畫效果的實現依靠State和Transition.閒話少說,直接分析程式碼吧.

計算器程式的組織結構

在core目錄中,定義了按鈕元件Button和顯示計算器輸入資訊及計算結果的Display元件.core/images目錄中是按鈕的圖片和Display元件的背景圖片.

還有一個qmldir檔案,這個檔案沒有後綴.其中儲存了目錄中元件的名稱和位置.

按鈕元件

先來看看Button.qml檔案的定義.這個檔案定義了按鈕元件,為了分析方便,我將原碼直接拷貝過來,每行程式碼後面加上註釋.

import QtQuick 1.0  //匯入QML基本元素 版本號為1.0

BorderImage {  //宣告一個BorderImage元素 BorderImage一般用來作為邊界影象.這裡直接用來顯示按鈕影象
    id: button       //設定其唯一標識

    property alias operation: buttonText.text  //定義一個屬性別名供外部使用,當給operation賦值或讀取operation時,實際上在操作buttonText.text的值 buttonText元素在後面定義
    property string color: ""                               //定義字串屬性color,預設值為""

    signal clicked                                               //定義一個訊號,這裡的訊號和Qt中的訊號概念上相同,用法上也一致

    //source屬性指定其圖片的地址,注意這裡使用了屬性繫結,最終的值與color有關,
    //如果color的值發生了變化,source的值自動變化.最終計算的source值正好是上圖中幾個按鈕的背景圖片的名稱

    source: "images/button-" + color + ".png"; clip: true
    border { left: 10; top: 10; right: 10; bottom: 10 }          //設定邊界 定義了影象距離外邊框的距離 這裡上下左右都空閒10個畫素

    Rectangle {  //聲明瞭一個矩形,這個矩形在滑鼠點選的時候設定opacity為0.4,使按鈕變灰.但不會影響按鈕上顯示的文字,因為文字是在其下方宣告的.
        id: shade  //設定唯一標示
        anchors.fill: button; /*完全平鋪到button上*/radius: 10;/*定義圓角半徑*/ color: "black"; opacity: 0/*定義了透明度,0為完全透明,1為完全不透明*/
    }

    Text {  //宣告按鈕上的文字
        id: buttonText  //設定唯一標識 上面定義屬性property alias operation: buttonText.text就引用了這個標識.Text上顯示的文字就是text屬性的值
        anchors.centerIn: parent;/*居中顯示*/ anchors.verticalCenterOffset: -1/*垂直居中偏移-1畫素*/
        font.pixelSize: parent.width > parent.height ? parent.height * .5 : parent.width * .5  //計算字型大小,為按鈕寬高最小值的一半
        style: Text.Sunken;/*設定文字風格*/ color: "white"; styleColor: "black"; smooth: true
    }

    MouseArea {  //設定滑鼠響應區域
        id: mouseArea
        anchors.fill: parent  //整個按鈕區域都可響應滑鼠
        onClicked: {
            doOp(operation)  //定義doOp函式,注意doOp在calculator.qml中定義,這個qml引用了Button.qml,由於qml是宣告式的,因此可先引用後宣告(定義).
            button.clicked()    //觸發button的click訊號
        }
    }

    states: State {  //定義State實現動畫效果 這個State實現當mouseArea是滑鼠按下狀態時,修改shade的屬性opacity的值為0.4,也就是當按鈕被按下時看到一層淡淡的灰色.
        name: "pressed"; when: mouseArea.pressed == true  //when關鍵字定義狀態觸發的條件
        PropertyChanges { target: shade; opacity: .4 }                //改變shade的opacity屬性
    }
}

Display元件

import QtQuick 1.0  //匯入1.0版本的QtQuick模組

BorderImage {  //定義顯示背景圖片元素
    id: image       //唯一標識

    property alias text : displayText.text  //屬性別名 設定text就是給displayText.text賦值
    property alias currentOperation : operationText  //屬性別名 這是一個Text元素型別的屬性

    source: "images/display.png"   //裝置背景圖片
    border { left: 10; top: 10; right: 10; bottom: 10 }  //設定圖片與邊框的距離

    Text {
        id: displayText
        anchors {  //定位
            right: parent.right;/*右側與父物件的右側對齊*/ verticalCenter: parent.verticalCenter;/*垂直居中*/ verticalCenterOffset: -1/*垂直偏移量-1 顯示稍偏下*/
            rightMargin: 6; /*右邊界間隔6個畫素*/left: operationText.right/*左側與operationText的右側對齊*/
        }
        font.pixelSize: parent.height * .6; text: "0"; horizontalAlignment: Text.AlignRight; elide: Text.ElideRight
        color: "#343434"; smooth: true; font.bold: true
    }
    Text {
        id: operationText
        font.bold: true;/*粗體*/ font.pixelSize: parent.height * .7
        color: "#343434"; smooth: true
        anchors { left: parent.left;/*靠左顯示*/ leftMargin: 6;/*左側邊距6畫素*/ verticalCenterOffset: -3; verticalCenter: parent.verticalCenter }
    }
}

Display元件定義了一個背景圖,上面有兩個Text,這兩個Text一個靠左,一個靠右,平鋪在Display元件上,而且兩個Text直接具有描點關係:anchors{displayText.left: operationText.right}.displayText的左側總與operationText的右側相連.說明在改變大小時operationText不變,而displayText是可伸展的.

calculator定義

兩個共用元件介紹完了,現在看看calculator.qml.這是計時器的定義檔案.

import QtQuick 1.0
import "Core"  //匯入Core目錄中定義的元件 引擎查詢目錄中的qmldir檔案(無後綴),根據其中的內容匯入定義的元件.
import "Core/calculator.js" as CalcEngine  //匯入javaScript檔案內容 也可作為一個元件來看,並定義了元件別名,下面使用檔案中定義的函式時可用:別名.方法名

Rectangle {
    id: window

    width: 360; height: 480  //定義視窗尺寸
    color: "#282828"

    property string rotateLeft: "\u2939"
    property string rotateRight: "\u2935"
    property string leftArrow: "\u2190"
    property string division : "\u00f7"
    property string multiplication : "\u00d7"
    property string squareRoot : "\u221a"
    property string plusminus : "\u00b1"

    function doOp(operation) { CalcEngine.doOperation(operation) }  //定義了個函式,供下面呼叫.這個函式又呼叫了js檔案中的doOperation函式,注意引數operation是按鈕上的文字內容.

    Item {
        id: main
        state: "orientation " + runtime.orientation  //runtime.orienttation返回介面的顯示方向. 如果方向改變,就會重新設定state的值,其屬性也會按state定義的相應更改.

        property bool landscapeWindow: window.width > window.height  
        property real baseWidth: landscapeWindow ? window.height : window.width  //取寬高中最小的那個值
        property real baseHeight: landscapeWindow ? window.width : window.height //取寬高中最大的那個值
        property real rotationDelta: landscapeWindow ? -90 : 0

        rotation: rotationDelta  //根據視窗寬與高的大小來調整旋轉角度,只用一行程式碼搞定介面旋轉
        width: main.baseWidth
        height: main.baseHeight
        anchors.centerIn: parent
        //定義一個Column元素,單列排布其中的子元素.上面是Display 下面是多個按鈕的區域
        Column {
            id: box; spacing: 8

            anchors { fill: parent; topMargin: 6; bottomMargin: 6; leftMargin: 6; rightMargin: 6 }
            //顯示Display元件
            Display {
                id: display
                width: box.width-3
                height: 64
            }
            //定義按鈕區域 應使用Column元素宣告 其中的子元素垂直分佈 共分三個區域按鈕 介面中紫色,綠色,及下面的其他按鈕三個部分
            Column {
                id: column; spacing: 6

                property real h: ((box.height - 72) / 6) - ((spacing * (6 - 1)) / 6)//計算出每個按鈕的高度
                property real w: (box.width / 4) - ((spacing * (4 - 1)) / 4)           //計算出每個按鈕的寬度
                //定義紫色按鈕區域 按鈕之所以顯示為紫色,因為Button的color屬性設定為purple,在Button按鈕元件定義中,其背景圖片的source屬性與color繫結,確定了顯示哪個圖片
                Row {  //Row元素定義一行,其中包含的元素水平佈局
                    spacing: 6
                    Button { width: column.w; height: column.h; color: 'purple'; operation: "Off" }
                    Button { width: column.w; height: column.h; color: 'purple'; operation: leftArrow }
                    Button { width: column.w; height: column.h; color: 'purple'; operation: "C" }
                    Button { width: column.w; height: column.h; color: 'purple'; operation: "AC" }
                }
               //定義綠色按鈕區域
                Row {
                    spacing: 6
                    property real w: (box.width / 4) - ((spacing * (4 - 1)) / 4)

                    Button { width: column.w; height: column.h; color: 'green'; operation: "mc" }
                    Button { width: column.w; height: column.h; color: 'green'; operation: "m+" }
                    Button { width: column.w; height: column.h; color: 'green'; operation: "m-" }
                    Button { width: column.w; height: column.h; color: 'green'; operation: "mr" }
                }
                //定義其他按鈕
                Grid {  //Grid元素定義一個網格,其中的元素都佔據一個小格
                    id: grid; rows: 5;/*指定網格的行數*/ columns: 5;/*指定網格的列數*/ spacing: 6

                    property real w: (box.width / columns) - ((spacing * (columns - 1)) / columns)

                    Button { width: grid.w; height: column.h; operation: "7"; color: 'blue' }
                    Button { width: grid.w; height: column.h; operation: "8"; color: 'blue' }
                    Button { width: grid.w; height: column.h; operation: "9"; color: 'blue' }
                    Button { width: grid.w; height: column.h; operation: division }
                    Button { width: grid.w; height: column.h; operation: squareRoot }
                    Button { width: grid.w; height: column.h; operation: "4"; color: 'blue' }
                    Button { width: grid.w; height: column.h; operation: "5"; color: 'blue' }
                    Button { width: grid.w; height: column.h; operation: "6"; color: 'blue' }
                    Button { width: grid.w; height: column.h; operation: multiplication }
                    Button { width: grid.w; height: column.h; operation: "x^2" }
                    Button { width: grid.w; height: column.h; operation: "1"; color: 'blue' }
                    Button { width: grid.w; height: column.h; operation: "2"; color: 'blue' }
                    Button { width: grid.w; height: column.h; operation: "3"; color: 'blue' }
                    Button { width: grid.w; height: column.h; operation: "-" }
                    Button { width: grid.w; height: column.h; operation: "1/x" }
                    Button { width: grid.w; height: column.h; operation: "0"; color: 'blue' }
                    Button { width: grid.w; height: column.h; operation: "." }
                    Button { width: grid.w; height: column.h; operation: plusminus }
                    Button { width: grid.w; height: column.h; operation: "+" }
                    Button { width: grid.w; height: column.h; operation: "="; color: 'red' }
                }
            }
        }
        //定義狀態,main元素的state屬性指定為如下狀態名稱時,其屬性值就會發生改變 通常為了具有動畫效果,states要與transitions配合使用
        states: [
            State {
                name: "orientation " + Orientation.Landscape
                PropertyChanges { target: main; rotation: 90 + rotationDelta; width: main.baseHeight; height: main.baseWidth }
            },
            State {
                name: "orientation " + Orientation.PortraitInverted
                PropertyChanges { target: main; rotation: 180 + rotationDelta; }
            },
            State {
                name: "orientation " + Orientation.LandscapeInverted
                PropertyChanges { target: main; rotation: 270 + rotationDelta; width: main.baseHeight; height: main.baseWidth }
            }
        ]
        //定義動畫效果
        transitions: Transition {
            SequentialAnimation {  //定義一個順序執行的動畫
                RotationAnimation { direction: RotationAnimation.Shortest; duration: 300; easing.type: Easing.InOutQuint  }  //旋轉動畫效果屬性
                NumberAnimation { properties: "x,y,width,height"; duration: 300; easing.type: Easing.InOutQuint } //在x,y,width,height屬性發生變化時的動畫屬性
            }
        }
    }
}

演算法

計時器的演算法定義在一個單獨的JavaScript檔案中.


var curVal = 0
var memory = 0
var lastOp = ""
var timer = 0

function disabled(op) {
    if (op == "." && display.text.toString().search(/\./) != -1) {
        return true
    } else if (op == squareRoot &&  display.text.toString().search(/-/) != -1) {
        return true
    } else {
        return false
    }
}

function doOperation(op) {
    if (disabled(op)) {
        return
    }

    if (op.toString().length==1 && ((op >= "0" && op <= "9") || op==".") ) {
        if (display.text.toString().length >= 14)
            return; // No arbitrary length numbers
        if (lastOp.toString().length == 1 && ((lastOp >= "0" && lastOp <= "9") || lastOp == ".") ) {
            display.text = display.text + op.toString()
        } else {
            display.text = op
        }
        lastOp = op
        return
    }
    lastOp = op

    if (display.currentOperation.text == "+") {  //已經按下了+號
        display.text = Number(display.text.valueOf()) + Number(curVal.valueOf())
    } else if (display.currentOperation.text == "-") {
        display.text = Number(curVal) - Number(display.text.valueOf())
    } else if (display.currentOperation.text == multiplication) {
        display.text = Number(curVal) * Number(display.text.valueOf())
    } else if (display.currentOperation.text == division) {
        display.text = Number(Number(curVal) / Number(display.text.valueOf())).toString()//開始計算
    } else if (display.currentOperation.text == "=") {
    }

    if (op == "+" || op == "-" || op == multiplication || op == division) {
        display.currentOperation.text = op
        curVal = display.text.valueOf()
        return
    }

    curVal = 0
    display.currentOperation.text = ""

    if (op == "1/x") {
        display.text = (1 / display.text.valueOf()).toString()
    } else if (op == "x^2") {
        display.text = (display.text.valueOf() * display.text.valueOf()).toString()
    } else if (op == "Abs") {
        display.text = (Math.abs(display.text.valueOf())).toString()
    } else if (op == "Int") {
        display.text = (Math.floor(display.text.valueOf())).toString()
    } else if (op == plusminus) {
        display.text = (display.text.valueOf() * -1).toString()
    } else if (op == squareRoot) {
        display.text = (Math.sqrt(display.text.valueOf())).toString()
    } else if (op == "mc") {
        memory = 0;
    } else if (op == "m+") {
        memory += display.text.valueOf()
    } else if (op == "mr") {
        display.text = memory.toString()
    } else if (op == "m-") {
        memory = display.text.valueOf()
    } else if (op == leftArrow) {
        display.text = display.text.toString().slice(0, -1)
        if (display.text.length == 0) {
            display.text = "0"
        }
    } else if (op == "Off") {
        Qt.quit();
    } else if (op == "C") {
        display.text = "0"
    } else if (op == "AC") {
        curVal = 0
        memory = 0
        lastOp = ""
        display.text ="0"
    }
}

QML負責GUI,C++負責業務邏輯的範例


在declarative目錄中,有個minehunt範例,實現了在C++中載入QML介面,並用C++來處理QML介面上的滑鼠動作.這種思路和傳統的GUI相似,感覺比較順暢.否則執行一個QML,還要使用qmlviewer,上面帶一大堆選單按鈕,看著夠彆扭的.

在main函式中,建立了一個QDeclarativeView例項,這個例項負責顯示QML介面.接著建立負責處理業務邏輯的MinehuntGame例項,並在view載入QML檔案後,將其設定為引擎的上下文物件.這樣就可以直接在QML中使用MinehuntGame類中的屬性和方法了.感覺設定上下文後,將上下文類例項與QML介面做了融合,QML中的滑鼠點選等事件就可以呼叫類中方法進行處理,並可以繫結到例項的屬性.

#include <QtGui/QApplication>
#include <QtDeclarative/QDeclarativeView>
#include <QtDeclarative/QDeclarativeContext>
#include <QtDeclarative/QDeclarativeEngine>

#include "minehunt.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QDeclarativeView canvas;
    qmlRegisterType<TileData>();//註冊TileData類,這個類不需要在QML中例項化.
    MinehuntGame* game = new MinehuntGame();
    canvas.engine()->rootContext()->setContextObject(game);        
    canvas.setSource(QString("qrc:minehunt.qml"));
    QObject::connect(canvas.engine(), SIGNAL(quit()), &app, SLOT(quit()));  //QML退出,應用程式也退出
    canvas.setGeometry(QRect(100, 100, 450, 450));
    canvas.show();
    return app.exec();
}

在程式中定義了代表掃雷介面中每個方塊的TileData類,用來記錄這個方塊的狀態,如做標記的,被點開的,或有雷的.那麼類的例項是如何與QML進行關聯同步的呢?在MinehuntGame中定義了一個屬性QDeclarativeListProperty<TileData> tiles(),與_tiles成員繫結,並在類構造時向tiles中填充多個TileData例項.在QML中可以看到如下宣告:

    Grid {
        anchors.horizontalCenter: parent.horizontalCenter
        columns: 9; spacing: 1

        Repeater {
            id: repeater
            model: tiles
            delegate: Tile {}
        }
    }

這樣就按MinehuntGame例項中的tiles屬性進行構造Grid中的元素了,每個TileData例項生成一個Tile元件.在操作Tile元件時又會反過來呼叫MinehuntGame中的方法.先看看那個表情圖示,點選時會重置介面.

    Image {
        anchors.bottom: field.bottom; anchors.bottomMargin: 15
        anchors.right: field.right; anchors.rightMargin: 20
        source: isPlaying ? 'MinehuntCore/pics/face-smile.png' ://isPlaying,hasWon是MinehuntGame例項中的屬性,這裡做了繫結,如果例項中的屬性發生變化,則圖示自動變化
        hasWon ? 'MinehuntCore/pics/face-smile-big.png': 'MinehuntCore/pics/face-sad.png'

        MouseArea { anchors.fill: parent; onPressed: reset() }//點選的時候呼叫MinehuntGame例項的reset方法,重置雷區,上面也有個關於雷區的繫結,重置後介面自動更新
    }

在看看點選雷區的一個方塊時的動作觸發過程.這裡有兩個成員物件,index和modelData,都是和模型繫結有關的,index表示當前選中的模型元素索引,modelData表示當前選中子模型對應的元素.在QML中可通過modelData訪問TileData元素屬性.下面的宣告中指定,滑鼠左鍵點選呼叫MinehuntGame的flip方法,右擊呼叫flag方法.在這兩個C++函式中判斷是否觸雷等邏輯,並修改類例項的狀態,通過繫結,介面元素自動更新.

    MouseArea {
        anchors.fill: parent
        acceptedButtons: Qt.LeftButton | Qt.RightButton
        onClicked: {
            field.clickx = flipable.x//滑鼠點選時設定主介面的clickx,clicky屬性
            field.clicky = flipable.y
            var row = Math.floor(index / 9)
            var col = index - (Math.floor(index / 9) * 9)
            if (mouse.button == undefined || mouse.button == Qt.RightButton) {
                flag(row, col)
            } else {
                flip(row, col)
            }
        }
        onPressAndHold: {
            field.clickx = flipable.x
            field.clicky = flipable.y
            var row = Math.floor(index / 9)
            var col = index - (Math.floor(index / 9) * 9)
            flag(row, col)
        }
    }

那麼觸雷後的例子系統是如何宣告的呢?

    transitions: Transition {
        SequentialAnimation {
            ScriptAction {
                script: {
                    var ret = Math.abs(flipable.x - field.clickx)
                        + Math.abs(flipable.y - field.clicky);
                    if (modelData.hasMine && modelData.flipped)
                        pauseDur = ret * 3
                    else
                        pauseDur = ret
                }
            }
            PauseAnimation {
                duration: pauseDur
            }
            RotationAnimation { easing.type: Easing.InOutQuad }
            ScriptAction { script: if (modelData.hasMine && modelData.flipped) { expl.explode = true } }//這裡指定如果modelData即TileData資料物件屬性hasMine或flipped為true,則觸發例子效果.
        }
    }

expl的定義:Explosion { id: expl }.

Explosion的定義:

import QtQuick 1.0
import Qt.labs.particles 1.0

Item {
    property bool explode : false

    Particles {//定義了一個粒子物件
        id: particles
        width: 40
        height: 40
        lifeSpan: 1000
        lifeSpanDeviation: 0
        source: "pics/star.png"//粒子影象
        count: 0
        angle: 270
        angleDeviation: 360
        velocity: 100
        velocityDeviation: 20
        z: 100
    }
    states: State { name: "exploding"; when: explode
        StateChangeScript {script: particles.burst(200); }
    }

}

總結:要向將QML和C++結合起來,實現傳統的開發方式,核心的程式碼就是canvas.engine()->rootContext()->setContextObject(game);這樣就可以在QML中呼叫game例項的屬性和函數了.

**************測試如何在C++中獲取TextInput元素中輸入的內容**************

根據上面的範例分析可以在QML中訪問C++中定義的函式或屬性,那麼C++中怎麼獲取到QML中的介面內容呢?網上說有三種方法,一種是在C++的外掛獲取,一種是在C++中解析QML中的元素,最後一種是在C++中定義帶引數的槽函式,在QML中的事件中呼叫這個槽,並將TextInput中的內容作為引數傳到C++中.感覺最後一種方法最方便了,因此做了如下測試:

定義QML檔案:

import QtQuick 1.0

Rectangle{
    id: window
    width: 240; height: 250
    BorderImage{
        y:0
        id:button
        source:"pics/back.png"
        MouseArea{
            id:mouseArea
            anchors.fill:parent
            onClicked:buttonClicked(textInput.text)//按鈕點選的時候呼叫C++中的buttonClicked槽函式
        }
        Rectangle{
            id:shade
            anchors.fill:button; radius: 10; color: "black"; opacity: 0
        }
        states:State{
            name:"pressed";when: mouseArea.pressed == true
            PropertyChanges{ target: shade; opacity: 0.4}
        }
    }
    BorderImage
    {
        source: "pics/back.png";width:82;height:22;y:50
        TextInput{
            id:textInput
            text: qsTr("text")
            y:1;x:1;width:80;height:20;
        }
    }
}

C++類:

標頭檔案

#ifndef QMLCONTROL_H
#define QMLCONTROL_H

#include <QObject>

class QmlControl : public QObject
{
 Q_OBJECT//必須加上Q_OBJECT巨集,否則在QML中無法觸發buttonClicked函式
public:
 QmlControl(void);
 ~QmlControl(void);
public slots:
 Q_INVOKABLE void buttonClicked(QString textInput);
};
#endif

cpp檔案

#include "QmlControl.h"
#include <QMessageBox>

QmlControl::QmlControl(void)
{
 setObjectName("mainObject");
}

QmlControl::~QmlControl(void)
{
}

void QmlControl::buttonClicked(QString textInput)
{
 QMessageBox::information(NULL, "", textInput);
}

介面上放一個QDeclarativeView控制元件,並在建構函式中載入QML,設定引擎中的物件:

GetQMLInputTextValue::GetQMLInputTextValue(QWidget *parent, Qt::WFlags flags)
 : QMainWindow(parent, flags)
{
 ui.setupUi(this);
 qmlRegisterType<QmlControl>();
 QmlControl *ctrl = new QmlControl();
 ui.declarativeView->engine()->rootContext()->setContextObject(ctrl);
 ui.declarativeView->setSource(QString("qrc:/Resources/test.qml"));
 connect(ui.declarativeView->engine(),SIGNAL(quit()), qApp, SLOT(quit()));
}

注意我將QML和其中的圖片放入資源中,這裡引用QML的時候需要以qrc開頭,否則訪問不到圖片檔案.

 QT程式設計師使用QML

使用QML並不需要Qt的知識,如果你已經熟悉Qt,那麼很多知識都可以直接用於學習和使用QML.當然,使用QML定義UI的應用程式還是需要使用Qt實現非UI邏輯的.

熟悉的概念

QML直接支援如下Qt中的概念:

    QAction -action 型別
    QObject 訊號槽 - 可用於呼叫JavaScript函式
    QObject 屬性- 在JavaScript中當做變數使用
    QWidget - QDeclarativeView 是一個QML顯示部件
    Qt 模型 - 可直接用在資料繫結中(QAbstractItemModel)

Extending QML Functionalities using C++以及Integrating QML Code with existing Qt UI code中需要Qt知識

QML項與QWidget比較

QML中的item與QWidget很相似:他們都定義了使用者介面的外觀.(注意通常QWidget並不是用來定義檢視代理外觀的,QML項也可這樣使用.)

有三不同種結構的QWidget:

    不能作為父部件的簡單部件(QLabel, QCheckBox, QToolButton等)
    常作為其他部件的父部件(QGroupBox, QStackedWidget, QTabWidget等)
    由子部件組成的組合部件(QComboBox, QSpinBox, QFileDialog, QTabWidget等)

QML項也可這樣分類.分類如下.

簡單部件

最主要的原則是要記住當在C++中繼承一個新的QDeclarativeItem類時不要定義任何的外觀策略--留到QML使用元素時再定義.

作為範例,假設你要重用按鈕專案.因此需要定義一個QDeclarativeItem子類實現按鈕功能,與QToolButton繼承於QWidget 一樣,按上面的原則, QDeclarativeButton 類不應該有任何與外觀相關的程式碼--只需要處理使能,觸發等操作.

但這些已經被Qt中的QAction實現了.

QAction是UI無關的,可繫結到QPushButton, QCheckBox, QMenu,QToolButton,以及其他可視部件.

因此QML中以及具有了複選框功能--利用QAction.僅在QML中定義--按鈕外觀,狀態的過度,如何精確的響應滑鼠,鍵盤,或觸控輸入.

為說明這點,請注意QDeclarativeTextEdit構建於QTextControl, QDeclarativeWebView構建於QWebPage,ListView 構建於QAbstractItemModel,QTextEdit, QWebView,和 QListView構建於UI無關的元件.

獨立封裝外觀對QWidget是很重要的,QML中的元件概念也保留了這個觀點.如果生成一個完整的應用程式,需要由一致的外觀風格,需要建立一系列可重用的具有期望外觀的元件.

為實現這個可重用按鈕,需要簡單的建立一個QML元件.

父部件

父部件提供了通用方法訪問任意的子部件.QTabWidget 提供可訪問多個頁面(pages)的介面,同時只有一個page被顯示,以及切換page的機制(QTabBar).QScrollArea 具有位於部件邊緣的滾動條,可在有限的空間內瀏覽超大部件.

這些元件幾乎都可以在QML中直接建立.只有幾個物件需要特殊的事件處理,如Flickable,需要在C++中實現.

例如,假設要建立可大量用於應用程式中的一般的標籤部件(tab widget),根據資料量判斷是否需要分頁顯示.

QML元件和QWidget的parent概念最明顯區別在於,子項位置是相對於父項的,但不會要求子項完全包含在父項中(當然可在必要時設定子項的clipped屬性).這個差異具有深遠的影響,例如:

    圍繞部件的陰影或高亮可作為部件的子項.
    粒子效果可以漂移到其發起的物件之外.
    過度動畫可以將專案移動到螢幕範圍之外隱藏他們.

組合部件

一些部件支援組合其他部件作為其實現細節,併為組合體提供高層次的API.例如QSpinBox 由一個QLineEdit和運算元值的向上向下按鈕組成的.QFileDialog 作為一個完整的部件為使用者提供查詢和選擇檔名稱的功能.

開發可重用QML時,通常都是這樣做的,使用已定義的item組合出新的item.

唯一需要注意的是,要考慮到使用組合體的使用者可能希望採用動畫和過度.例如,一個spinbox可能需要平滑過度到任意值,因此這個spinbox項需要由足夠靈活,以允許這樣的動畫.

QML項與QGraphicsWidget比較

QML項和QGraphicWidget的主要不同點是使用方式.技術實現大致相同的,但實際上QML元素是可宣告和可組合的,而QGraphicWidget是一個基本元素,用於協調QGraphicScene和部件.QML項和QGraphicWidget都從QGraphicsObject繼承,可以共存.在佈局系統中和與其他元件互動上是不同的.注意QGraphicWidget更傾向於要求在一個包中定義,而與QGraphicWidget等價的QML項可能由跨多個QML檔案的QML項組合而成,但還是可以載入到C++的單個QGraphicsObject 物件中.

QGraphicsWidget通常使用QGraphicLayout來佈局.QML不使用QGraphicLayout,因為Qt的佈局對動畫和UI的流暢性不太友好,因此幾何上的介面是主要的不同點.當定義QML元素時,允許設計者使用絕對幾何位置,繫結或描點(從QDeclarativeItem繼承而來)定位其外邊框,而不是使用佈局或指定尺寸.如果適合指定尺寸就將其放置在QML文件中,讓設計者知道如何更好的使用這個元素,但仍可完全控制介面外觀.

其他主要不同在於QGraphicWidget用於佈局模型,其具有獨立的UI和邏輯.相反,QML實體通常是具有單一目標的項,不會在所有者中履行使用者用例,而是在QML檔案中組成等價的部件,要避免在項定義中涉及UI邏輯和組成視覺化元素.而是嘗試定義更加通用的實體,以便於在QML中定義介面外觀(包括UI邏輯).

這兩點不同決定了不同的互動方式. QGraphicsWidget是 QGraphicsObject的子類,用於在C++中輕鬆定義流暢的UI介面,而 QDeclarativeItem 是 QGraphicsObject 的子類用於在QML中定義流暢的UI介面.因此主要的不同是其暴露的介面,及設計時與其互動的物件(為QML宣告實體,QGraphicWidget則不用如此,因為你需要在子類中定義UI邏輯)

如果希望同時使用QML和C++定義UI,例如要進行過度,推薦使用 QDeclarativeItem子類(也可同時使用QGraphicWidget).允許在C++中輕鬆的為每個C++元件建立一個根項 LayoutItem,向場景中載入獨立的QML(可能定義在不同檔案中,組成獨立的UI和邏輯)代表的部件,替代個別的QGraphicWidget.

 使用C++擴充套件QML功能


使用C++擴充套件QML功能

分類:Qt QuickQML跨平臺-QT2012-08-30 23:1436人閱讀評論(0)收藏編輯刪除

QML語法宣告性的描述如何在記憶體中構建物件樹.在Qt中QML主要用於描述視覺化場景圖,但是其不僅限於此:QML格式可抽象描述任意物件樹.QT中包含的所有QML元素型別都按本文中描述的機制由C++擴充套件而來的.開發者可以使用這些API函式擴充套件新的型別與Qt已存型別進行互動,或為特殊目更改QML.

新增型別

 import People 1.0

 

 Person {

     name: "Bob Jones"

     shoeSize: 12

 }

上面的QML片段定義了一個Person例項及其name和shoeSize屬性.本質上QML中的每條語句都是在定義一個物件例項或設定屬性的值.

QML依賴於Qt的元物件系統,僅可例項化QObject的子類.對於視覺化元素型別,都是 QDeclarativeItem的子類;檢視中元素的模型,都是 QAbstractItemModel的子類;任意帶有屬性的物件都是QObject的直接子類.

QML引擎不知道任何型別資訊.需要程式設計師使用QML中所用的名稱來註冊C++類.

自定義C++型別使用模版函式來註冊:

 template<typename T>

 int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)

呼叫qmlRegisterType()在QML系統中註冊型別T,在QML中以qmlName引用型別,庫的版本為versionMajor.versionMinor.qmlName可以與C++型別同名.

型別T必須是QObject的子類,必須有預設建構函式.

#include <QtDeclarative>才可呼叫qmlRegisterType().

型別可在庫,應用程式程式碼,或外掛中註冊(見QDeclarativeExtensionPlugin).

註冊後,類中所有屬性都在QML中可用.QML原生支援的屬性型別列表在QML Basic Types 文件中描述,包括:

·        bool, unsigned int, int, float, double, qreal

·        QString, QUrl, QColor

·        QDate, QTime, QDateTime

·        QPoint, QPointF, QSize, QSizeF, QRect, QRectF

·        QVariant

在QML中元素是基於C++類支援的,當一個屬性加入到C++類,會自動生成一個對應的value-changed訊號.見下面的 Signal Support .

QML是型別安全的.試圖向屬性賦非法值會生成一個錯誤.例如,Person元素的name屬性是QString型別的,下面程式碼將引起錯誤:

 Person {

     // Will NOT work

     name: 12

 }

Extending QML - Adding Types Example 展示了建立Person型別的完整程式碼.

QML型別版本

在C++中向類新增新的方法或屬性不會影響原來的應用程式.然而在QML中,新新增的方法或屬性可能會改變以前的屬性引用方式.

例如考慮到如下兩個QML檔案

 // main.qml

 import QtQuick 1.0

 Item {

     id: root

     MyComponent {}

 }

 // MyComponent.qml

 import MyModule 1.0

 CppItem {

     value: root.x

 }

Cpp