一、概括
學習qt已有2年多的時間,從qt4.7開始使用直到現在正在使用的qt5.6,基本都在windows機器上做開發。最近有意向看了下qt的qml部分,覺着還是挺不錯的,畢竟可以做嵌入式移動端產品的部分,還是值的一學。后來在網上看了一些資料,算是初步了解了下qml,所以想就自己學習的過程做以記錄,也方便自己理解,如果你有機會看到這篇文章,那么我認為你也是來學習qml的,如果你已經是一個有很強qml開發經驗的老手,那么這篇文章和接下來的qml學習系列的文章你都不用看下去了,呵呵。。。
關於qml的由來,個人覺着Qt的Script、Quick、QML的關系與總結講的不錯,有興趣的同學可以去看下。
qml的學習過程我主要是以Qt 學習之路 2博客和QmlBook-In-Chinese這本書為主,同時在做小示例的時候查閱幫助文檔。每個人的學習方式都不太一樣,如果你有更好的辦法可以留言。
二、效果預覽
如下有4張效果圖,分別是4個小示例,關於demo后續章節會有解說,但是都是以代碼中的注解為主,有興趣的同學也可以直接下載示例程序,使用qt提供的qmlscene.exe來直接執行qml文件,或者qmlviewer.exe也可以預覽qml文件。

圖1 轉動的組件

圖2 紅綠燈1

圖3 紅綠燈2

圖4 GridView使用
三、學習qml必備
- 基本元素
- 組件,基本元素的復合
- 定位器(布局)
- 元素布局,錨
- 輸入元素,一行和多行
- quick現有組件
- 模型和視圖
- Canvas元素
1、基本元素
QML 基本元素可以分為可視元素和不可視元素兩類。可視元素:Item、Rectangle、Text、Image;不可見元素:MouseArea。關於MouseArea是不可見元素這一點我需要強調一下,因為上邊我提到的兩篇學習文章都沒有說清楚,圖5是qt5.7的幫助文檔截圖,從圖中我們一眼就能看出結果,MouseArea確實是不可見元素。

圖5 MouseArea幫助文檔
關於基本元素我覺着qmlbook這本書相關章節的最后一段說的很有意思,特此說明,如圖6所示
圖6 qml顯示和交互分開
理解這些基本元素,你可以認為他們是一個個被封裝好的類,而且他們有非常之多的屬性,這里我就不介紹了,因為幫助文檔說的太清楚了。
2、組件
組件其實就是基本元素的復合,放到一個單獨的文件,方便我們以后重用,關於怎么創建組件,本節的后續我會給出自己做的示例程序,代碼很簡單只是為了說明問題
3、定位器
定位器主要有 Row、Column、Grid和Flow等。
4、元素布局
除了定位器,我們還可以使用錨(anchor)來布局元素
5、輸入元素
鍵盤輸入的兩個元素:TextInput和TextEdit。TextInput為一行輸入,TextEdit為多行輸入
6、quick組件
如表1是Qt Quick Controls 1.1 提供的組件
| ApplicationWindow | 對應QMainWindow,提供頂層應用程序窗口 |
| MenuBar | 對應QMenuBar,提供窗口頂部橫向的菜單欄 |
| StatusBar | 對應QStatusBar,提供狀態欄 |
| ToolBar | 對應QToolBar,提供工具欄,可以添加ToolButton和其它組件 |
| Action | 對應QAction,提供能夠綁定到導航和視圖的抽象的用戶界面動作 |
| 導航和視圖 | |
| 方便用戶在一個布局中管理和顯示其它組件 | |
| ScrollView | 對應QScrollView,提供滾動視圖 |
| SplitView | 對應QSplitter,提供可拖動的分割視圖布局 |
| StackView | 對應QStackedWidget,提供基於棧的層疊布局 |
| TabView | 對應QTabWidget,提供帶有標簽的基於棧的層疊布局 |
| TableView | 對應QTableWidget,提供帶有滾動條、樣式和表頭的表格 |
| 控件 | |
| 控件用於表現或接受用戶輸入 | |
| BusyIndicator | 提供忙等示意組件 |
| Button | 對應QPushButton,提供按鈕組件 |
| CheckBox | 對應QCheckBox,提供復選框 |
| ComboBox | 對應QComboBox,提供下拉框 |
| GroupBox | 對應QGroupBox,提供帶有標題、邊框的容器 |
| Label | 對應QLabel,提供標簽組件 |
| ProgressBar | 對應QProgressBar,提供進度條組件 |
| RadioButton | 對應QRadioButton,提供單選按鈕 |
| Slider | 對應QSlider,提供滑動組件 |
| SpinBox | 對應QSpinBox,提供微調組件 |
| Switch | 提供類似單選按鈕的開關組件 |
| TextArea | 對應QTextEdit,提供能夠顯示多行文本的富文本編輯框 |
| TextField | 對應QTextLine,提供顯示單行文本的純文本編輯框 |
| ToolButton | 對應QToolButton,提供在工具欄上顯示的工具按鈕 |
| ExclusiveGroup | 提供互斥 |
| 菜單 | |
| 用於構建菜單的組件 | |
| Menu | 對應QMenu,提供菜單、子菜單、彈出菜單等 |
| MenuSeparator | 提供菜單分隔符 |
| MenuItem | 提供添加到菜單欄或菜單的菜單項 |
| StatusBar | 對應QStatusBar,提供狀態欄 |
| ToolBar | 對應QToolBar,提供工具欄,可以添加ToolButton和其它組件 |
表1 Qt Quick Controls 1.1組件
7、模型和視圖
模型和視圖其實屬於qml的高級使用部分了,但是為了能早些理解qml的東西,我提前拿出一些簡單的東西,預先學習下。
8、canvas畫布
在早些qt4時代,qml只提供了幾種基礎元素,第一小節也說明了,有很多人期望的圓角矩形,橢圓和圓,但是最終官方沒有給出具體的元素,如果是要做這些組件,那么就需要設計師給切圖。到了qt5,官方提供了canvas畫布,這個畫布可以實現復雜的繪圖操作,並且畫布元素是基於HTML5的畫布元素來完成的。支持畫筆,填充,漸變,文本和繪制路徑創建命令。
四、小示例
接下里就是第二節所展示的效果圖對於代碼講解了,那我也就按照上圖展示的順序一個個講解代碼
1、基礎組件講解
在開始示例講解之前,我先說下我自己封裝的一個小組件,代碼量很少,只為說明問題,具體請看diamante
1 import QtQuick 2.5 2 3 // 圓角矩形框矩形框,支持點擊 4 Rectangle { 5 property alias text: name.text;//導出文本變量 6 property alias textColor: name.color;//導出文本顏色 7 8 id: root; 9 width: 120; 10 height: 120; 11 radius:60; 12 antialiasing: true; 13 signal clicked();//自定義信號 外部可以通過onClicked接收 14 15 MouseArea 16 { 17 width: root.width; 18 height: root.height; 19 20 onClicked: 21 { 22 //鼠標點擊時發送消息 並輸入日志 23 root.clicked(); 24 console.log("rectangle clicked"); 25 } 26 } 27 28 Text 29 { 30 id: name; 31 text: ""; 32 color: "black"; 33 anchors.centerIn: parent; 34 } 35 }
2、旋轉的風車,代碼里有多種方式實現矩形旋轉,具體使用那一種就由個人喜好了
import QtQuick 2.0 import QtQuick.Window 2.0 import QtGraphicalEffects 1.0 import "../contrl" //導入自定義組件模塊 Window { id:root; visible: true; width: 600; height: 400; //背景色窗口 Rectangle { id: bg; color:"lightsteelblue"; width: root.width; height:root.height; } //鼠標點擊背景色時停止旋轉圖形 MouseArea { width: bg.width; height: bg.height; onClicked: { ro.pause(); } } //自定義控件 通過import導入 Rect { id: roundItem; anchors.centerIn: parent; //漸變填充矩形 ConicalGradient { anchors.fill: parent gradient: Gradient { GradientStop { position: 0.0; color: "lightsteelblue" } GradientStop { position: 1.0; color: "blue" } } } //旋轉動畫1 程序剛啟動會執行 原因未知 // NumberAnimation on rotation { // loops:Animation.Infinite; // from:0; // to:360; // duration: 1000; // } //旋轉動畫2 配合wheel.rotation = 360;使用 動畫 不能循環執行 // Behavior on rotation { // NumberAnimation { // loops:Animation.Infinite;//無效 // duration: 1000; // } // } //旋轉動畫3 相比於動畫1 在屬性中主動指明了target和property // NumberAnimation { // id:ro; // loops:Animation.Infinite; // property: "rotation"; // target:roundItem; // from:0; // to:360; // duration: 1000; // } //旋轉動畫4 和動畫1是一樣的 因為RotationAnimation和NumberAnimation都是繼承自PropertyAcimation //因此RotationAnimation動畫可以實現和動畫2一樣的效果,使用RotationAnimation // RotationAnimation on rotation { // loops: Animation.Infinite; // from: 0; // to: 360; // duration: 1000; // } //旋轉動畫5 RotationAnimation { id:ro; target:roundItem; loops: Animation.Infinite; from: 0; to: 360; duration: 1000; } onClicked: { if (ro.paused) { ro.resume(); } else { ro.start(); } } } }
3、紅綠燈,下述代碼紅色的的切換時通過鼠標單擊進行
1 import QtQuick 2.0 2 import QtQuick.Window 2.0 3 import QtGraphicalEffects 1.0 4 5 import "../contrl" 6 7 Window 8 { 9 function dosomething()//測試script腳本運行效果 10 { 11 console.log("do something"); 12 } 13 14 id:root; 15 visible: true; 16 width: 370; 17 height: 130; 18 19 Rectangle 20 { 22 id:rootRect; 23 width: root.width; 24 height:root.height; 25 anchors.centerIn:parent; 26 27 Row 28 { 29 id:ligheGroup; 30 spacing: 2; 31 states: 32 [ 33 State { 34 name: "red" 35 // StateChangeScript {name: "myScript"; script: dosomething(); } //可以正常調用 36 PropertyChanges { 37 target: redLight; color:"red"; 38 } 39 PropertyChanges { 40 target: greenLight; color:"black"; 41 } 42 PropertyChanges { 43 target: yellowLight; color:"black"; 44 } 45 }, 46 State { 47 name: "green" 48 PropertyChanges { 49 target: redLight; color:"black"; 50 } 51 PropertyChanges { 52 target: greenLight; color:"green"; 53 } 54 PropertyChanges { 55 target: yellowLight; color:"black"; 56 } 57 }, 58 State { 59 name: "yellow" 60 PropertyChanges { 61 target: redLight; color:"black"; 62 } 63 PropertyChanges { 64 target: greenLight; color:"black"; 65 } 66 PropertyChanges { 67 target: yellowLight; color:"yellow"; 68 } 69 } 70 ] 71 72 anchors.centerIn:parent; 73 Rect//紅燈 74 { 75 id:redLight; 76 color:"black"; 77 radius: width/2; 78 } 79 Rect//綠燈 80 { 81 id:greenLight; 82 color:"black"; 83 radius: width/2; 84 } 85 Rect//黃燈 86 { 87 id:yellowLight; 88 color:"black"; 89 radius: width/2; 90 } 91 92 transitions: 93 [ 94 Transition //提供從red狀態到yellow狀態的漸變過程 95 { 96 from: "red" 97 to: "yellow" 98 // ScriptAction { script: dosomething(); } //可以正常調用 99 ColorAnimation{ target: redLight; properties: "color";duration: 1000;} 100 ColorAnimation{ target: yellowLight; properties: "color";duration: 1000;} 101 } 102 ] 103 } 104 property bool m_bIsRed : false; 105 MouseArea 106 { 107 anchors.fill: parent; 108 onClicked://鼠標點擊時,狀態切換 109 { 110 if (ligheGroup.state == "red" 111 || ligheGroup.state == "green") 112 { 113 ligheGroup.state = "yellow"; 114 } 115 else 116 { 117 if (parent.m_bIsRed == false) 118 { 119 ligheGroup.state = "red"; 120 parent.m_bIsRed = true; 121 } 122 else 123 { 124 ligheGroup.state = "green"; 125 parent.m_bIsRed = false; 126 } 127 } 128 } 129 } 130 } 131 }
4、紅綠燈,不同於上述紅綠燈,次紅綠燈只需要鼠標單擊觸發運行,狀態是由定時器來控制,紅燈運行60秒,綠燈20秒,黃燈3秒,為了程序的迅速反應,在紅燈和綠燈的時候定時器觸發頻率所有提高,具體請看代碼,此處我只貼出定時器部分,如果需要整個運行程序,可自行下載demo。
1 property bool m_bIsRed : false;//是否是紅燈亮 2 property int m_iTicker : 0; 3 4 Timer 5 { 6 id:redState; 7 interval: 50;//每隔50毫秒觸發一次,真實情況下本應該是1000毫秒一次 8 repeat: true; 9 triggeredOnStart: true; 10 property int count : 60;//紅燈秒數 11 12 onTriggered: { 13 if (lightGroup.state != "red") 14 { 15 lightGroup.state = "red"; 16 root.m_bIsRed = true; 17 } 18 19 ++m_iTicker; 20 redLight.text = count - m_iTicker; 21 if (count <= m_iTicker)//到達指定時間 重置計數器,並切換到黃燈定時器,關閉自身定時器 22 { 23 m_iTicker = 0; 24 yellowState.start(); 25 redState.stop(); 26 } 27 } 28 } 29 Timer 30 { 31 id:yellowState; 32 interval: 1000; 33 repeat: true; 34 triggeredOnStart: true; 35 property int count : 3;//黃燈秒數 36 37 onTriggered: { 38 if (lightGroup.state != "yellow") 39 { 40 lightGroup.state = "yellow"; 41 } 42 ++m_iTicker; 43 yellowLight.text = count - m_iTicker; 44 if (count <= m_iTicker)//到達指定時間 重置計數器,並切換到綠燈/紅燈定時器,關閉自身定時器 45 { 46 m_iTicker = 0; 47 if (m_bIsRed) 48 { 49 greenState.start(); 50 } 51 else 52 { 53 redState.start(); 54 } 55 stop(); 56 } 57 } 58 } 59 Timer 60 { 61 id: greenState; 62 interval: 150;//每隔150毫秒觸發一次,真實情況下本應該是1000毫秒一次 63 repeat: true; 64 triggeredOnStart: true; 65 property int count : 20;//綠燈秒數 66 67 onTriggered: { 68 if (lightGroup.state != "green") 69 { 70 lightGroup.state = "green"; 71 root.m_bIsRed = false; 72 } 73 74 ++m_iTicker; 75 greenLight.text = count - m_iTicker; 76 if (count <= m_iTicker)//到達指定時間 重置計數器,並切換到黃燈定時器,關閉自身定時器 77 { 78 m_iTicker = 0; 79 yellowState.start(); 80 greenState.stop(); 81 } 82 } 83 }
5、日歷窗口,代碼量不大,有興趣的可以看看,主要就是界面展示,如果想要做到動態的日歷,需要對模型動態的增刪,這個功能后續我們在完善。
1 import QtQuick 2.6 2 import QtQuick.Window 2.0 3 import QtGraphicalEffects 1.0 4 5 import "../contrl" 6 7 Window 8 { 9 visible: true; 10 width: 300; 11 height: 300; 12 13 Rectangle 14 { 15 id:root; 16 anchors.fill: parent; 17 width: root.width; 18 height: root.height; 19 color: "yellow"; 20 21 //日期頭 22 Row 23 { 24 id: weekname; 25 spacing: 2; 26 padding: 5; 27 28 Repeater 29 { 30 model: ["周天", "周一", "周二", "周三", "周四", "周五", "周六"] 31 Rectangle 32 { 33 width: (root.width - 6 * weekname.spacing - 10) / 7; 34 height: 30 35 radius: 3 36 color: "lightBlue" 37 Text 38 { 39 anchors.centerIn: parent 40 text: modelData 41 } 42 } 43 } 44 } 45 46 //天 47 GridView 48 { 49 id: weekday; 50 boundsBehavior: Flickable.StopAtBounds; 51 anchors//布局 52 { 53 top: weekname.bottom; 54 left:root.left; 55 leftMargin:5; 56 right: root.right; 57 rightMargin:5; 58 bottom: root.bottom; 59 } 60 model: 42;//天數 61 62 cellWidth: (root.width - 10) / 7; 63 cellHeight: (root.width - 10) / 7; 64 // Repeater 65 // { 66 // Rectangle 67 // { 68 // radius: 8; 69 // color: "lightBlue"; 70 // Text 71 // { 72 // anchors.centerIn: parent; 73 // text: modelData; 74 // } 75 // } 76 // } 77 delegate: numberDelegate; 78 focus: true;//可以獲取焦點 79 } 80 81 Component//繪制代理 82 { 83 id: numberDelegate; 84 Rectangle 85 { 86 width: weekday.cellWidth; 87 height: weekday.cellHeight; 88 color: GridView.isCurrentItem ? "green" : "lightGreen"//根據是否是當前項設置顏色 89 border.color: Qt.lighter("green"); 90 Text 91 { 92 anchors.centerIn: parent; 93 font.pixelSize: 10; 94 text: index + 1;//文本取索引值 95 } 96 } 97 } 98 } 99 }
補充:示例代碼中:GridView中的repeater元素是不需要的,repeater是配合定位器使用的模型,因為每一個repeater都包含一個默認的繪制代理。
五、下載鏈接
注:這是qml學習系列的第一篇文章,后邊我還會以這種示例的形式繼續更新更多學習的進度,希望大家多多支持,有問題的小伙伴可以私信我。謝謝。。。


