Defining Object Types through QML Documents

QML的核心特性之一是轻松定义QML对象类型. 标准的Qt Quick模块提供多种类型, 如Rectangle, TextImage. 此外, 你也可以自定义QML类型.

在QML文件中定义对象类型

自定义对象类型

为了创建一个对象类型, 应将QML文档命名为 <TypeName>.qml, <TypeName> 是类型名称, 它的要求如下:

  • 必须由字母数字字符或下划线组成.
  • 必须以大写字母开头.

QML引擎自动将文档识别为QML类型定义. 此外, 当解析QML类型名称时, 对象类型自动用于与同级目录的其他QML文件.

自定义QML类型

如下所示, 声明一个具有子对象MouseAreaRectangle对象. 文档保存为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!")
      }
  }

现在, 你可以在同级目录的其他QML文件中使用名为SquareButton类型. 如下所示, 如果myapplication.qml文件在同级目录, 它可以引用SquareButton类型:


  // myapplication.qml
  import QtQuick 2.0

  SquareButton {}

上述代码创建一个SquareButton.qml对象, 它定义一个100*100的红色Rectangle, 并带有一个MouseArea. 当QML引擎加载myapplication.qml时, 也会以组件方式加载SquareButton.qml文件, 并实例化一个SquareButton对象.

SquareButton.qml中声明的SquareButton类型, 封装了一个QML对象树. QML引擎实例化SquareButton对象时, 它也会实例化一个Rectangle对象树.

注意: 文件名的大小写在某些文件系统(尤其是UNIX)中非常重要. 无论部署在哪个平台, 建议文件名与QML类型名称完全匹配, 如Box.qml, 而不是BoX.qml.

内联组件

有时, 你不方便为一个类型创建一个新文件, 如多个视图中复用的委托部件. 如果你不需要一个公开类型, 只想创建一个实例, Component可以满足要求. 但是, 如果你想使用组件类型声明属性, 或者在多个文件使用它, Component无法满足要求. 在这种情况下, 你可以使用内联组件. 内联组件是在文件中声明一个新组件. 语法是:

 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
 }

其他文件中, 你必须使用组件的名称作为前缀.

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

注意: 内联组件与其内声明的组件共享作用域. 如下所示, B.qml中创建A.MyInlineComponent时, 会发生ReferenceError, 因为在B.qml中, root不能作为id存在. 因此, 建议不要引用内联组件中不属于它的对象.

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

注意: 内联组件不能嵌套.

导入在当前目录之外定义的类型

如果SquareButton.qmlmyapplication.qml不在同级目录, 则myapplication.qml必须在导入语句中提供SquareButton类型. 它可以从文件系统的相对路径导入, 也可以作为已安装的模块导入; 详见module.

自定义类型的可访问属性

The 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 type:


  // application.qml
  import QtQuick 2.0

  Column {
      SquareButton { side: 50 }
      SquareButton { x: 50; color: "blue" }
      SquareButton { radius: 10 }
  }

自定义QML类型的对象可访问的属性包括为对象额外定义的任何 自定义属性, 方法信号. 例如, 假设SquareButton.qml中的Rectangle定义如下, 并定义额外的属性, 方法和信号:


  // 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: root.buttonClicked(mouse.x, mouse.y)
      }
  }

任何SquareButton对象都可以使用pressed属性, buttonClicked信号, randomizeColor()方法:


  // application.qml
  import QtQuick 2.0

  SquareButton {
      id: squareButton

      onButtonClicked: {
          console.log("Clicked", xPos, yPos)
          randomizeColor()
      }

      Text { text: squareButton.pressed ? "Down" : "Up" }
  }

注意: SquareButton.qml中定义的id不能访问SquareButton对象, id只能从声明组件的作用域访问. SquareButton对象不能引用MouseArea子对象, 如果它的idroot, 而不是SquareButton, 则不会与SquareButton.qml的根对象id冲突, 因为两者不在同一作用域声明.