透过 QML 文档定义对象类型

One of the core features of QML is that it enables QML object types to be easily defined in a lightweight manner through QML documents to suit the needs of individual QML applications. The standard Qt Quick module provides various types like Rectangle , Text and Image for building a QML application; beyond these, you can easily define your own QML types to be reused within your application. This ability to create your own types forms the building blocks of any QML application.

采用 QML 文件定义对象类型

Naming Custom QML Object Types

To create an object type, a QML document should be placed into a text file named as <TypeName>.qml where <TypeName> is the desired name of the type. The type name has the following requirements:

  • It must be comprised of alphanumeric characters or underscores.
  • It must begin with an uppercase letter.

This document is then automatically recognized by the engine as a definition of a QML type. Additionally, a type defined in this manner is automatically made available to other QML files within the same directory as the engine searches within the immediate directory when resolving QML type names.

Custom QML Type Definition

For example, below is a document that declares a Rectangle with a child MouseArea . The document has been saved to file named SquareButton.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!")
    }
}
					

Since the file is named SquareButton.qml , this can now be used as a type named SquareButton by any other QML file within the same directory . For example, if there was a myapplication.qml file in the same directory, it could refer to the SquareButton 类型:

// myapplication.qml
import QtQuick 2.0
SquareButton {}
					

This creates a 100 x 100 red Rectangle with an inner MouseArea , as defined in SquareButton.qml . When this myapplication.qml document is loaded by the engine, it loads the SquareButton.qml document as a component and instantiates it to create a SquareButton 对象。

SquareButton type encapsulates the tree of QML objects declared in SquareButton.qml . When the QML engine instantiates a SquareButton object from this type, it is instantiating an object from the Rectangle tree declared in SquareButton.qml .

注意: the letter case of the file name is significant on some (notably UNIX) filesystems. It is recommended the file name case matches the case of the desired QML type name exactly - for example, Box.qml 和不 BoX.qml - regardless of the platform to which the QML type will be deployed.

Inline Components

Sometimes, it can be inconvenient to create a new file for a type, for instance when reusing a small delegate in multiple views. If you don't actually need to expose the type, but only need to create an instance, Component is an option. But if you want to declare properties with the component types, or if you want to use it in multiple files, 组件 is not an option. In that case, you can use inline components . Inline components declare a new component inside of a file. The syntax for that is

component <component name> : BaseType {
    // declare properties and bindings here
}
					

Inside the file which declares the inline component, the type can be referenced simply by its name.

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

In other files, it has to be prefixed with the name of its containing component.

// LabeledImageBox.qml
import QtQuick 2.15
Rectangle {
    property alias caption: image.caption
    property alias source: image.source
    border.width: 2
    border.color: "black"
    Images.LabeledImage {
        id: image
    }
}
					

注意: Inline components don't share their scope with the component they are declared in. In the following example, when A.MyInlineComponent in file B.qml gets created, a ReferenceError will occur, as root does not exist as an id in B.qml. It is therefore advisable not to reference objects in an inline component which are not part of it.

// A.qml
import QtQuick 2.15
Item {
    id: root
    property string message: "From A"
    component MyInlineComponent : Item {
        Component.onCompleted: console.log(root.message)
    }
}
// B.qml
import QtQuick 2.15
Item {
    A.MyInlineComponent {}
}
					

注意: Inline components cannot be nested.

Importing Types Defined Outside the Current Directory

SquareButton.qml was not in the same directory as myapplication.qml SquareButton type would need to be specifically made available through an import statement in myapplication.qml . It could be imported from a relative path on the file system, or as an installed module; see 模块 了解更多细节。

自定义类型的可访问属性

root object definition in a .qml file defines the attributes that are available for a QML type . All properties, signals and methods that belong to this root object - whether they are custom declared, or come from the QML type of the root object - are externally accessible and can be read and modified for objects of this type.

For example, the root object type in the SquareButton.qml file above is Rectangle . This means any properties defined by the Rectangle type can be modified for a SquareButton object. The code below defines three SquareButton objects with customized values for some of the properties of the root Rectangle object of the SquareButton 类型:

// application.qml
import QtQuick 2.0
Column {
    SquareButton { side: 50 }
    SquareButton { x: 50; color: "blue" }
    SquareButton { radius: 10 }
}
					

The attributes that are accessible to objects of the custom QML type include any custom properties , methods and signals that have additionally been defined for an object. For example, suppose the Rectangle in SquareButton.qml had been defined as follows, with additional properties, methods and signals:

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

任何 SquareButton object could make use of the pressed property, buttonClicked signal and randomizeColor() method that have been added to the root Rectangle :

// application.qml
import QtQuick 2.0
SquareButton {
    id: squareButton
    onButtonClicked: (xPos, yPos)=> {
        console.log("Clicked", xPos, yPos)
        randomizeColor()
    }
    Text { text: squareButton.pressed ? "Down" : "Up" }
}
					

Note that any of the id values defined in SquareButton.qml are not accessible to SquareButton objects, as id values are only accessible from within the component scope in which a component is declared. The SquareButton object definition above cannot refer to mouseArea in order to refer to the MouseArea child, and if it had an id of root 而不是 squareButton , this would not conflict with the id of the same value for the root object defined in SquareButton.qml as the two would be declared within separate scopes.