Qml自定義控件其實比較容易,簡單來說,只需將自定義控件的界面佈局寫在qml文件裏面,在其它地方,直接用這個qml的文件名作爲類名,就可以使用這個自定義控件了
這裏我們主要講解一些自定義控件細節和可維護性方面的東西
下面我們以一個帶圖標的按鈕控件爲例,來講解自定義控件的完整過程
新建Qml文件
ui.qml文件和qml文件的關係
可以看到,Qt Creator爲我們創建了兩個qml文件,一個是IconButton.qml,另一個是IconButtonForm.ui.qml
打開代碼我們可以看到,IconButton裏面什麼內容都沒,只有一個名爲IconButtonForm的根節點
實際上,IconButton就是IconButtonForm,之所有要分成兩個qml文件,是爲了將界面代碼和業務代碼分離開來
IconButtonForm.ui.qml中定義界面佈局,IconButton.qml以IconButtonForm作爲根節點,然後再爲它添加業務代碼
其實我們不使用IconButtonForm.ui.qml,直接在IconButton.qml編寫界面代碼和業務代碼,也是可以的
只是這樣一來,界面代碼和方法代碼全部混在一起,就很難閱讀了,一般只在一些簡單的控件中,我們纔會這麼做
編寫界面文件(IconButtonForm.ui.qml)
import QtQuick 2.4
Item {
id: _root
//使用一個Rectangle作爲按鈕背景
Rectangle {
id: _background
anchors.fill: parent
gradient: Gradient {
GradientStop {
position: 0.0
color: "#999999"
}
GradientStop {
position: 1.0
color: "#CCCCCC"
}
}
border.color: "grey"
border.width: 1
radius: 5
//圖標
Image {
id: _icon
width: _background.width
height: _background.height - 50
source: "images/1.png"
fillMode: Image.PreserveAspectFit
}
//文字
Text {
id: _text
anchors.fill: parent
anchors.topMargin: _icon.height
anchors.horizontalCenter: _background.horizontalCenter
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: "Security"
color: "white"
font.pointSize: 18
font.bold: true
}
}
}
編寫業務文件(IconButton.qml)
ui.qml雖然本質上也是qml文件,但是爲了規範,Qt Creator限制了在Form.ui.qml中無法添加方法,包括onClicked這些內置方法,因爲這些都屬於業務代碼,因此我們需要在IconButton.qml中來編寫這些代碼
定義別名屬性(IconButtonForm.ui.qml)
Item中的節點在其它文件中是不可訪問的,我們必須在Item中定義一個屬性,指向其它節點
這和我們定義一個普通的新屬性性質是一樣的,只不過它指向了其它節點,起到了別名作用
接下來我們要給IconButton添加,由於控件存在邊距之類的無效區域,我們希望把點擊區域加在background上,而不是Item上,又由於在其它文件中是沒法修改子節點的,只能修改指定屬性,所以我們必須讓別名屬性指向MouseArea節點,而不是Rectangle節點
Item {
id: _root
//定義別名屬性,指向點擊區域
property alias clickRect: _mouse_area
//使用一個Rectangle作爲按鈕背景
Rectangle {
id: _background
anchors.fill: parent
anchors.margins: 20
//點擊區域
MouseArea {
id: _mouse_area
anchors.fill: parent
}
}
}
添加信號(IconButton.qml)
到此爲止,我們已經可以在IconButton.qml中直接訪問Form.MouseArea
但是由於我們這是自定義控件,點擊事件應該是由用戶去實現的,所以不應該在IconButton.qml實現
我們可以在main.qml實現clickRect.onClicked方法,但這樣等於將clickRect這種控件細節暴漏給了用戶,不是很規範
我們可以通過信號槽機制,來改善這種設計,當clickRect被點擊時,發出一個信號,main.qml響應這個信號就可以了
信號槽機制一個很大的好處就是,外部文件使用IconButton時,只需響應IconButton的信號即可,不必去管IconButton內部有哪些節點,它們是怎樣合作的
import QtQuick 2.4
IconButtonForm {
id: _root
signal clicked(int x, int y)
clickRect.onClicked: {
_root.clicked(mouse.x, mouse.y)
}
}
實現槽函數(main.qml)
那麼問題來了,我們該如何在main.qml中響應IconButton發出的信號呢
還記得我們學習Qt Widget時提到過的嗎,Qt可以自動將名爲[signalName]的信號與名爲on[ObjectName][SignalName]的連接起來
在Qml中也是如此,我們定義了一個clicked(int x, int y)信號,就會自動生成一個onClicked(int x, int y)槽函數,雖然函數名和參數都不是顯式可見的,但在外部都可以直接使用
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.12
//窗口節點
Window {
id: root
visible: true
width: 640
height: 480
title: "Qt Quick Demo"
//使用自定義控件
IconButton {
anchors.fill: parent
anchors.margins: 100
onClicked: {
console.debug(x, y)
}
}
}
信號處理器
在Qml中,我們一般不把響應信號的函數成爲槽函數,而是稱爲信號處理器(Signal Handler),其實它們功能是一樣的
自定義函數(IconButton.qml)
這個比較簡單,定義一個函數,在IconButton節點下即可直接調用使用,我們簡單測試下
IconButtonForm {
id: _root
function showWindowSize() {
//注意:root這個id現在是不存在的,但是也可以在調用它的文件裏定義,只要id在Window中存在即可
console.debug("window size:", root.width, root.height)
}
}
完整代碼
我們再完善下,然後給出全部代碼,由於控件圖標和文本是可變的,也應當作爲屬性提取出來,由用戶設定
//IconButtonForm.ui.qml
import QtQuick 2.13
Item {
id: _root
//定義別名屬性
property alias clickRect: _mouse_area
property alias icon: _icon.source
property alias text: _text.text
//使用一個Rectangle作爲按鈕背景
Rectangle {
id: _background
anchors.fill: parent
anchors.margins: 20
gradient: Gradient {
GradientStop {
position: 0.0
color: "#999999"
}
GradientStop {
position: 1.0
color: "#CCCCCC"
}
}
border.color: "grey"
border.width: 1
radius: 5
//點擊區域
MouseArea {
id: _mouse_area
anchors.fill: parent
}
//圖標
Image {
id: _icon
width: _background.width
height: _background.height - 50
source: "images/1.png"
fillMode: Image.PreserveAspectFit
}
//文字
Text {
id: _text
anchors.fill: parent
anchors.topMargin: _icon.height
anchors.horizontalCenter: _background.horizontalCenter
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: "Security"
color: "white"
font.pointSize: 18
font.bold: true
}
}
}
//IconButton.qml
import QtQuick 2.13
IconButtonForm {
id: _root
//點擊信號
signal clicked(var mouse)
//點擊事件
clickRect.onClicked: {
_root.clicked(mouse)
}
//打印鼠標位置
function printMousePosition(mouse) {
console.debug("mouse position:", mouse.x, mouse.y)
}
}
//main.qml
import QtQuick 2.13
import QtQuick.Window 2.13
//窗口節點
Window {
id: root
visible: true
width: 640
height: 480
title: "Qt Quick Demo"
//使用自定義控件
IconButton {
anchors.fill: parent
anchors.margins: 100
icon: "images/1.png"
text: "Print Mouse Position"
//點擊時打印鼠標點擊位置
onClicked: {
printMousePosition(mouse)
}
}
}
運行效果
一個帶圖標和標題的按鈕,點擊時打印鼠標位置,沒啥特殊功能,越簡單越方便演示代碼