QML对象类型是由QML文档定义的。QML文档是一个符合QML语法的字符串,每一个QML文档定义了一个QML对象类型(这是QT官方文档中的定义,但是我对这个定义存在一些疑问,对于含有内联组件的情况下,qml文档应该不是一个连续的字符串,其中包含内联组件定义的字符串应该属于内联组件文档)。通常QML文档是在.qml文件中定义的。但是.qml文件中不一定只包含一个QML文档。因为可能存在内联组件定义,因此一个.qml文件可能包含多个QML文档。
一、通过QML文件定义对象类型
通过QML文件定义对象类型的方式很简单,只要将.qml文件以<TypeName>.qml 格式进行命名,并在这个文件中输入QML文档就可以了。QML引擎会自动识别这个文件中定义的QML对象类型。如果另一个.qml文件与<TypeName>.qml 文件在同一个目录中,那么这个文件可以直接使用<TypeName>对象类型。一个QML文档还可以通过import语句引入一个目录,此时,QML文档可以使用这个被引入的目录中定义的所有QML对象类型。<TypeName>命名有一定要求:它必须由字母或下划线做成,且必须以一个大写字母开头。
以下是一个自定义QML对象类型的示例:
// SquareButton.qml
import QtQuick 2.0
Rectangle {
property int side: 100
width: side; height: side
color: "red"
MouseArea {
anchors.fill: parent
onClicked: console.log("Button clicked!")
}
}
// myapplication.qml
import QtQuick 2.0
SquareButton {}
在以上例子中,SquareButton.qml包含了一个QML文档,这个文档定义了SquareButton对象类型。myapplication.qml与SquareButton.qml文件在同一个目录中,因此它可以直接使用SquareButton对象类型。
二、以内联组件方式定义QML对象类型
有时,在一个新的文件中定义QML类型不太方便,那么,可以采用内联组件的方式在同一个.qml文件中定义多个QML对象类型。它的语法如下:
component <component name> : BaseType {
// declare properties and bindings here
}
以下是一个使用内联组件的示例:
// Images.qml
import QtQuick 2.15
Item {
component LabeledImage: Column {
property alias source: image.source
property alias caption: text.text
Image {
id: image
width: 50
height: 50
}
Text {
id: text
font.bold: true
}
}
Row {
LabeledImage {
id: before
source: "before.png"
caption: "Before"
}
LabeledImage {
id: after
source: "after.png"
caption: "After"
}
}
property LabeledImage selectedImage: before
}
在以上例子中,Images.qml文件定义了两个QML对象类型,一个是Images对象类型,另外一个是LabeledImage对象类型。在这个.qml文件中LabeledImage对象类型可以直接使用。在其他.qml文件中需要以Images.LabeledImage形式进行使用。
qt官方文档中这里说的有些模糊,除了以上方式外,还可以通过Component类型定义一个QML对象类型。 以下是一个使用Component类型定义对象类型的例子:
import QtQuick 2.0
Item {
width: 100; height: 100
Component {
id: redSquare
Rectangle {
color: "red"
width: 10
height: 10
}
}
Loader { sourceComponent: redSquare }
Loader { sourceComponent: redSquare; x: 20 }
}
在以上这个例子中,Component类型定义了一个名为redSquare的对象类型。将redSquare赋值给Loader类型子对象的sourceComponent属性,会创建一个RedSquare类型的对象。Component类型定义的对象类型在使用的时候无法增加属性,也无法在当前.qml文件以外的底方使用这个对象类型。但是,之前提到的那种内联组件方式定义的对象类型,可以在其他.qml文件中使用,也可以在使用时增加它的属性。
三、自定义对象类型的属性访问
一个QML对象类型的根对象包含的所有属性,对外是可见的。(类似于C++中的public成员)。可以被此类型的对象进行访问。
// SquareButton.qml
import QtQuick 2.0
Rectangle {
id: root
property bool pressed: mouseArea.pressed
signal buttonClicked(real xPos, real yPos)
function randomizeColor() {
root.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
}
property int side: 100
width: side; height: side
color: "red"
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: (mouse)=> root.buttonClicked(mouse.x, mouse.y)
}
}
// application.qml
import QtQuick 2.0
SquareButton {
id: squareButton
onButtonClicked: (xPos, yPos)=> {
console.log("Clicked", xPos, yPos)
randomizeColor()
}
Text { text: squareButton.pressed ? "Down" : "Up" }
}
在以上这个例子里,SquareButton对象类型的根对象是Rectangle类型的,因此,Rectangle类型的所有属性以及根对象自定义属性(pressed属性,side属性,buttonClicked信号,randomizeColor方法,)对外是可见的。在application.qml文件中可以对pressed属性进行访问,发送randomizeColor信号。
需要注意的是,所有定义在 SquareButton.qml的id不能被SquareButton类型对象访问。因此,在以上例子中通过squareButton.mouseArea.pressed形式访问mouseArea属性是错误的。