【零基礎學QT】【047】Qml自定義控件

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)
	        }
	    }
	}
	

運行效果

一個帶圖標和標題的按鈕,點擊時打印鼠標位置,沒啥特殊功能,越簡單越方便演示代碼
在這裏插入圖片描述

發佈了429 篇原創文章 · 獲贊 43 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章