[Quick module of Qt] 6. Detailed explanation of QML syntax_3 QML object characteristics

Overview

Each QML object type contains a defined set of properties. When instantiated, a set of properties are included, which are defined in the object type.
An object type in a QML document declares a new type, that is, instantiates a type.
The following features are included.

  • the id attribute: id attribute
  • property attributes: attribute characteristics
  • signal attributes: signal attributes
  • signal handler attributes: signal handler characteristics
  • method attributes: method attributes
  • attached properties and attached signal handler attributes: attached properties and attached signal handler attributes
  • enumeration attributes: enumeration attributes

id characteristics

Every QML object type has a unique id attribute. This property is provided by the language itself and cannot be redefined or overridden by any QML object type.
You can assign a value to the id attribute of an object instance so that other objects can identify and reference the object. The id must start with a lowercase letter or underscore, and cannot contain characters other than letters, numbers, and underscores.
Below is a TextInput object and a Text object. The id value of the TextInput object is set to "myTextInput". The Text object sets its text property to the same value as the TextInput's text property, by referencing myTextInput.text. Now, both items will display the same text:

 import QtQuick 2.0

 Column {
    
    
     width: 200; height: 200

     TextInput {
    
     id: myTextInput; text: "Hello World" }

     Text {
    
     text: myTextInput.text }
 }

In component scope, an object declared in the component can be referenced by id from anywhere. Therefore, the id value must always be unique within its component scope.
Once an object instance is created, the value of its id attribute cannot be changed. Although it looks like an ordinary property, the id attribute is not an ordinary property, it has special semantics; for example, myTextInput.id cannot be accessed in the above example.

Properties

Properties are characteristics of an object that can be assigned static values ​​or bound to dynamic expressions. The property's value can be read by other objects. In general, it can also be modified by another object, unless the specific QML type explicitly prohibits modification of a specific property.

Define attribute properties

Properties can be defined for a type in C++ by registering the Q_PROPERTY macro of the class, which is then registered in the QML type system. In addition, custom properties of object types can be defined in the object declaration in the QML document, with the following syntax:

[default] property <propertyType> <propertyName>

In this way, object declarations make it easier to expose specific values ​​to external objects or maintain some internal state.
Property names must start with a lowercase letter and can contain only letters, numbers, and underscores. JavaScript reserved words are not valid property names. The default keyword is optional and is used to modify the semantics of the declared attribute.
Implicitly declaring a custom property creates a value change signal for the property, and an on<PropertyName>Changedassociated signal handler named, where <PropertyName>is the name of the property, with the first letter capitalized.
For example, the following object declaration defines a new type derived from the Rectangle base type. It has two new properties and implements a signal handler for one of the new properties:

Rectangle {
    
     property color previousColor property color nextColor onNextColorChanged: console.log("The next color will be: " + nextColor.toString()) }

Valid types in custom property definitions

In addition to enumeration types, any QML primitive type can be used as a custom property type. For example, these are valid property declarations:

Item {
    
    
      property int someNumber
      property string someString
      property url someUrl
  }

(Enumeration values ​​are simple integer values ​​that can be replaced by int types.)
Some basic types are provided by the QtQuick module and therefore cannot be used as property types unless the module is imported.
Note that the var basic type is a general placeholder type that can hold any type of value, including lists and objects:

property var someNumber: 1.5
  property var someString: "abc"
  property var someBool: true
  property var someList: [1, 2, "three", "four"]
  property var someObject: Rectangle {
    
     width: 100; height: 100; color: "red" }

Additionally, any QML object type can be used as a property type. For example:

property Item someItem
  property Rectangle someRectangle

This also works with custom QML types. If the QML type is defined in a file called ColorfulButton. qml (in the directory subsequently imported by the client), then properties of type ColorfulButton will also be valid.

Property ColorfulButton colorBtn

Assign a value to a property

Property values ​​for object instances can be specified in two different ways:

  • Assignment during initialization
  • Imperative value assignment
    In both cases, the value can be a static value or a bound expression value.

initialization assignment

The syntax for assigning a value to a property during initialization is:

<propertyName> : <value> 

If desired, initialization value assignment can be combined with property definitions in the object declaration. In this case, the syntax of the property definition becomes:

[default] property <propertyType> <propertyName> : <value> 

For example:

import QtQuick 2.0 
Rectangle 
{
    
     
color: "red" 
property color nextColor: "blue" // 初始化并定义 
}

imperative assignment

Imperative value assignment is the assignment of a property value (either a static value or a binding expression) from imperative JavaScript code to a property. The syntax for imperative value assignment is the JavaScript assignment operator, as shown below:

[<objectId>.]<propertyName> = value

For example:

import QtQuick 2.0 
Rectangle {
    
     
id: rect
Component.onCompleted: {
    
     rect.color = "red" }
} 

Static value and property binding

As mentioned before, there are two types of values ​​that can be assigned to properties: static values ​​and bind expression values. The latter is also called property binding.
Property binding:
An object's properties can be assigned a static value that remains unchanged until a new value is explicitly assigned. However, to take full advantage of QML and its built-in support for dynamic object behavior, most QML objects use property binding.
Property binding is a core feature of QML that allows developers to specify relationships between different object properties. When the value of a property's dependency changes, the property is automatically updated based on the specified relationship.
Behind the scenes, the QML engine monitors the dependencies of properties (i.e. variables in binding expressions). When a change is detected, the QML engine re-evaluates the binding expression and applies the new result to the property.

type describe
Static Value A JavaScript expression that describes the relationship between a property and other properties. The variables in this expression are called dependencies of the property.
Binding Expression The QML engine enforces relationships between properties and their dependencies. When the value of any dependency changes, the QML engine automatically re-evaluates the binding expression and assigns the new result to the property.

Examples are as follows:

import QtQuick 2.0

  Rectangle {
    
    
      // 静态值初始化
      width: 400
      height: 200

      Rectangle {
    
    
          // 属性绑定,当parent.width变化时,width会自动变化
          width: parent.width / 2
          height: parent.height
      }
  }

Note: To assign a binding expression imperatively, the binding expression must be included in the function passed to Qt.binding(), and the value returned by Qt.binding() must then be assigned to the property. Conversely, Qt.binding() cannot be used when assigning a binding expression at initialization time.
Properties with bindings are automatically updated as needed. However, if you later assign a static value to the property from within a JavaScript statement, the binding will be removed.
For example, the rectangle below initially ensures that its height is always twice its width. However, when the space bar is pressed, the current value of width*3 will be assigned to height as a static value. After this, the height will remain fixed at this value even if the width changes. Assignment of a static value will remove the binding.

import QtQuick 2.0

  Rectangle {
    
    
      width: 100
      height: width * 2

      focus: true
      Keys.onSpacePressed: {
    
    
          height = width * 3
      }
  }

If the purpose is to give the rectangle a fixed height and stop automatic updating, then this isn't a problem. However, if the purpose is to establish a new relationship between width and height, then the new binding expression must be wrapped in the Qt.binding() function:

import QtQuick 2.0

  Rectangle {
    
    
      width: 100
      height: width * 2

      focus: true
      Keys.onSpacePressed: {
    
    
          height = Qt.binding(function() {
    
     return width * 3 })
      }
  }

Now, after pressing the space bar, the height of the rectangle will continue to automatically update, always being three times its width.

type safety

Properties are type safe. Properties can only be assigned values ​​that match the property type.
For example, if an attribute is a real number and you try to assign a string to it, an error will be reported:

property int volume: "four" // 该属性的对象将不会被加载

Likewise, if a property is assigned a value of the wrong type at runtime, the new value will not be assigned and an error will be generated.
Some attribute types have no natural value representation. For these attribute types, the QML engine automatically performs string to type value conversion. So, for example, you can assign the string "red" to the color property without an error being reported, even though a property of type color stores a color rather than a string.
Additionally, any available QML object type can also be used as a property type.

Special attribute types

list object attribute type

A list type property can be assigned a list of QML object type values. The syntax for defining object list values ​​is a comma-separated list enclosed in square brackets:

[ <item 1>, <item 2>, ... ]

For example, the Item type has a states property that holds a list of State type objects. The following code initializes the value of this property to a list of three State objects:

import QtQuick 2.0

  Item {
    
    
      states: [
          State {
    
     name: "loading" },
          State {
    
     name: "running" },
          State {
    
     name: "stopped" }
      ]
  }

If the list contains only one item, the square brackets can be omitted:

import QtQuick 2.0

  Item {
    
    
      states: State {
    
     name: "running" }
  }

List type properties can be specified in an object declaration using the following syntax:

[default] property list<<objectType>> propertyName

And, like other property declarations, property initialization can be used in conjunction with property declarations using the following syntax:

[default] property list<<objectType>> propertyName: <value>

Here is an example of a list property declaration:

import QtQuick 2.0

  Rectangle {
    
    
      // 声明,但不初始化
      property list<Rectangle> siblingRects

      // 声明并初始化
      property list<Rectangle> childRects: [
          Rectangle {
    
     color: "red" },
          Rectangle {
    
     color: "blue"}
      ]
  }

If you wish to declare a property to store a list of values ​​that are not necessarily QML object type values, then you should declare a var attribute.

group object attribute type

In some cases, properties contain logical groups of subproperty properties. These subproperties can be assigned using either point notation or group notation.
For example, the Text type has a FontGroup property. Below, the first Text object initializes its font value using dot notation, while the second uses group notation:

Text {
    
    
      //dot notation
      font.pixelSize: 12
      font.b: true
  }

  Text {
    
    
      //group notation
      font {
    
     pixelSize: 12; b: true }
  }

Grouped property types are basic types with sub-properties. Some of these basic types are provided by the QML language, while others are only available when importing the Qt Quick module.

attribute alias

A property alias is a property that contains a reference to another property. Unlike normal property definitions, which allocate a new, unique storage space for the property, property aliases concatenate the newly declared property (called the alias property) as a direct reference to the existing property (the alias property).
An attribute alias declaration looks like a normal attribute definition, except that it requires the alias keyword instead of the attribute type, and the right side of the attribute declaration must be a valid alias reference:

[default] property alias <name>: <alias reference>

Unlike ordinary properties, aliases have the following restrictions:

  • It can only refer to objects or properties of objects within the scope of the type in which the alias is declared.
  • It cannot contain arbitrary JavaScript expressions
  • It cannot refer to an object declared outside the scope of its type.
  • Unlike the optional default value of a normal property, an alias reference is not optional; an alias reference must be provided when the alias is first declared.
  • It cannot reference grouping properties; the following code does not work:
property alias color: rectangle.border.color

  Rectangle {
    
    
      id: rectangle
  }

However, aliases for value type properties are still useful:

property alias rectX: object.rectProperty.x Item
{
    
    
id: object 
property rect rectProperty
}

For example, here is a Button type with a buttonText alias property connected to the text object of the Text child element:

// Button.qml
  import QtQuick 2.0

  Rectangle {
    
    
      property alias buttonText: textItem.text

      width: 100; height: 30; color: "yellow"

      Text {
    
     id: textItem }
  }

The following code will create a Button with a defined text string as a child Text object:

Button {
    
     buttonText: "Click Me" }

Here, modifying buttonText directly modifies the textItem.text value; it does not change some other value and then update textItem.text. If buttonText is not an alias, then changing its value will not actually change the displayed text at all, because property binding is not two-way: if textItem.text changes, the buttonText value will change. On the contrary, it will not.

Things to note about attribute aliases

The alias will only be activated after the component is fully initialized. An error will be generated when referencing an uninitialized alias. Likewise, aliasing an aliased property again will result in an error.

property alias widgetLabel: label

  //下面代码将会报错
  //widgetLabel.text: "Initial text"

  //下面代码将会报错
  //property alias widgetLabelText: widgetLabel.text

  Component.onCompleted: widgetLabel.text = "Alias completed Initialization"

However, when importing a QML object type with a property alias in the root object, the property appears as a regular Qt property and can therefore be used in alias references.
An alias property may have the same name as an existing property, effectively overwriting the existing property. For example, the following QML type has a color alias property named the same as the built-in Rectangle::color property:

Rectangle {
    
    
      id: coloredrectangle
      property alias color: bluerectangle.color
      color: "red"

      Rectangle {
    
    
          id: bluerectangle
          color: "#1234ff"
      }

      Component.onCompleted: {
    
    
          console.log (coloredrectangle.color)    //prints "#1234ff"
          setInternalColor()
          console.log (coloredrectangle.color)    //prints "#111111"
          coloredrectangle.color = "#884646"
          console.log (coloredrectangle.color)    //prints #884646
      }

      //internal function that has access to internal properties
      function setInternalColor() {
    
    
          color = "#111111"
      }
  }

Any object that uses this type and references its color property will refer to the alias instead of the normal Rectangle::color property. Internally, however, the rectangle sets its color property correctly, referencing the actual defined property rather than the alias.

Default properties

Object definitions can have a default property. If an object is declared within the definition of another object without declaring it as the value of a specific property, the default property is the property for which it was assigned a value.
Declaring a property using the optional default keyword marks it as a default property. For example, assume there is a file MyLabel. Use the default attribute someText:

// MyLabel.qml
  import QtQuick 2.0

  Text {
    
    
      default property var someText

      text: "Hello, " + someText.text
  }

The someText value can be assigned in the MyLabel object definition as follows:

MyLabel {
    
    
      Text {
    
     text: "world!" }
  }

This has the exact same effect as:

MyLabel {
    
    
      someText: Text {
    
     text: "world!" }
  }

However, since the someText property has been marked as the default property, there is no need to explicitly assign a Text object to the property.
You should note that child objects can be added to any item-based type without explicitly adding them to the children property. This is because the Item's default property is its data property, and any items added to this list for Item are automatically added to its sublists.
Default attributes can be used to reassign the item's child elements.

read-only attribute

Object declarations can use the readonly keyword to define read-only properties. The syntax is as follows:

readonly property <propertyType> <propertyName> : <initialValue>

Read-only properties must be assigned a value during initialization. Once a read-only property is initialized, it is no longer possible to assign a value to it, whether through imperative code or otherwise.
For example, code in components. The following onCompleted block is invalid:

Item {
    
    
      readonly property int someNumber: 10

      Component.onCompleted: someNumber = 20  // doesn't work, causes an error
  }

Note: Read-only properties cannot also be default properties.

property modifier object

Properties can have property value modifier objects associated with them. The syntax for declaring an instance of a property modifier type associated with a specific property is as follows:

<PropertyModifierTypeName> on <propertyName> {
    
    
      // attributes of the object instance
  }

It should be noted that the above syntax is actually an object declaration, which will instantiate an object that acts on existing properties.
Some property modifier types may only apply to specific property types, but the language does not enforce this. For example, the NumberAnimation type provided by QtQuick will only animate properties of numerical types (such as int or real). Trying to use a NumberAnimation with a non-numeric property will not cause an error, but the non-numeric property will not be animated. The behavior of a property modifier type when associated with a specific property type is defined by its implementation.

signal characteristics

A signal is a notification from an object that a certain event has occurred: for example, a property has changed, an animation has started or stopped, or when an image has been downloaded. For example, the MouseArea type has a click signal that is emitted when the user clicks within the mouse area.
An object can be notified via a signal handler whenever a specific signal is emitted. The declaration syntax for a signal handler is on<Signal>; <Signal>is the name of the signal, with the first letter capitalized. The signal handler must be declared in the definition of the object that emits the signal, and the handler should contain a block of JavaScript code to be executed when the signal handler is called.
For example, the following onClicked signal handler is declared in the MouseArea object definition and is called when the MouseArea is clicked, thus printing a console message:

import QtQuick 2.0

Rectangle {
    
    
    visible: true
    width: 640
    height: 480
    border.color: "red"

    Rectangle {
    
    
        anchors.centerIn: parent

        visible: true
        width: 100
        height: 100
        border.color: "red"


        MouseArea {
    
    
            anchors.fill: parent
            onClicked: {
    
    
                console.log("Click!")
            }
        }
    }
}

Insert image description here

Define signal properties

You can define a signal for a type in C++ by registering Q_SIGNAL of a class and then registering the class in the QML type system. In addition, custom signals of object types can be defined in the object declaration in the QML document, with the following syntax:

signal <signalName>[([<type> <parameter name>[, ...]])]

It is an error to attempt to declare two signals or methods with the same name in the same type block. However, new signals can reuse the names of existing signals on that type. (This should be done with caution, as existing signals may be hidden and inaccessible.)
Here are three examples of signal declarations:

import QtQuick 2.0

  Item {
    
    
      signal clicked
      signal hovered()
      signal actionPerformed(string action, var actionResult)
  }

If the signal has no parameters, the "()" brackets are optional. If parameters are used, the parameter types must be declared, just like the string and var parameters of the actionPerformed signal above. The allowed parameter types are the same as those listed under Define Attribute Properties on this page.
To emit a signal, call it as a method. When a signal is emitted, any relevant signal handlers will be called, and the handlers can access the corresponding parameters using the defined signal parameter names.

property change signal

QML types also provide built-in property change signals, which are emitted whenever a property value changes, as described in the previous section on property properties.

Signal processing characteristics

A signal handler is a special type of method attribute that is called by the QML engine whenever the relevant signal is emitted. Adding a signal to an object definition in QML will automatically add an associated signal handler to the object definition, which has an empty implementation by default. The client can provide an implementation to implement program logic.
This is shown in the following QML file, where the signal is activated and deactivated:

import QtQuick 2.0


Rectangle {
    
    
    visible: true
    width: 640
    height: 480
    border.color: "red"



    Rectangle {
    
    
        id: aaaaaa

        border.color: "blue"
        anchors.centerIn: parent

        signal activated(real xPosition, real yPosition)
        signal deactivated

        property int side: 100
        width: side; height: side

        MouseArea {
    
    
            anchors.fill: parent
            onPressed: aaaaaa.activated(mouse.x, mouse.y)
            onReleased: aaaaaa.deactivated()
        }

        onActivated: {
    
    
            console.log("onActivated")
        }
        onDeactivated: {
    
    
            console.log("onDeactivated")
        }
    }
}

Insert image description here

Property change signal handling

The syntax for a signal handler for a property change signal is on<Property>Changed; where <Property>is the name of the property, with the first letter capitalized. For example, although the TextInput type documentation does not document the textChanged signal, this signal is implicitly available because TextInput has a text property, so you can write an onTextChanged signal handler to be called when that property changes:

import QtQuick 2.0

  TextInput {
    
    
      text: "Change this!"

      onTextChanged: console.log("Text has changed to:", text)
  }

#Method attribute
A method of an object type is a function that can be called to perform some processing or trigger further events. You can connect a method to a signal so that it is automatically called when the signal is emitted.

Define method attributes

In C++, you can define methods for a type by marking the class's functions (and then registering them with the QML type system with Q_INVOKABLE) or by registering them as Q_SLOT of the class. Alternatively, custom methods can be added to the object declaration in the QML document using the following syntax:

function <functionName>([<parameterName>[, ...]]) {
    
     <body> }

Methods can be added to QML types to define independent, reusable blocks of JavaScript code. These methods can be called both internally and by external objects.
Unlike signals, method parameter types do not have to be declared as they default to type var.
It is an error to attempt to declare two methods or signals with the same name in the same type block. However, new methods can reuse the names of existing methods on the type. (This should be done with caution, as existing methods may be hidden and inaccessible.)
Here is a rectangle with the calculateHeight() method called when assigning a height value:

import QtQuick 2.0
  Rectangle {
    
    
      id: rect

      function calculateHeight() {
    
    
          return rect.width / 2;
      }

      width: 100
      height: calculateHeight()
  }

If the method has parameters, they can be accessed by name within the method. Below, when the MouseArea is clicked, it calls the moveTo() method, which can reference the newX and newY parameters it receives to reposition the text:

import QtQuick 2.0

  Item {
    
    
      width: 200; height: 200

      MouseArea {
    
    
          anchors.fill: parent
          onClicked: label.moveTo(mouse.x, mouse.y)
      }

      Text {
    
    
          id: label

          function moveTo(newX, newY) {
    
    
              label.x = newX;
              label.y = newY;
          }

          text: "Move me!"
      }
  }

Additional properties and additional signal handling

Attached properties and additional signal handlers are a mechanism to annotate an object with extra properties or signal handlers that are not available on the object. In particular, they allow objects to access properties or signals that are particularly relevant to a single object.
QML type implementations can optionally create additional types in C++ with specific properties and signals. Instances of this type can then be created at runtime and attached to specific objects, allowing those objects to access the properties and signals of the attached type. These properties can be accessed by prefixing the name of the property and the respective signal handler with the attached type.
References to attached properties and handlers take the following syntax:

<AttachingType>.<propertyName>
  <AttachingType>.on<SignalName>

For example, the ListView type has an additional property ListView.isCurrentItem available
on each delegate object. This can be used by each individual delegate object to determine whether it is the currently selected item in the view:

import QtQuick 2.0

  ListView {
    
    
      width: 240; height: 320
      model: 3
      delegate: Rectangle {
    
    
          width: 100; height: 30
          color: ListView.isCurrentItem ? "red" : "yellow"
      }
  }

In this case, the name of the attached type is ListView and the property in question is isCurrentItem, so the attached property is called ListView.isCurrentItem.
Additional signal handlers are referenced in the same way. For example, components. The onCompleted attached signal handler is usually used to execute some JavaScript code when the component creation process is completed. In the example below, once the ListModel is fully created, its components. The onCompleted signal handler will be automatically called to populate the model:

import QtQuick 2.0

  ListView {
    
    
      width: 240; height: 320
      model: ListModel {
    
    
          id: listModel
          Component.onCompleted: {
    
    
              for (var i = 0; i < 10; i++)
                  listModel.append({
    
    "Name": "Item " + i})
          }
      }
      delegate: Text {
    
     text: index }
  }

Since the name of the attached type is Component and that type has a completed signal, the attached signal handler is called Component.oncompleted.

Notes on accessing attached properties and signal handling

A common mistake is to assume that attached properties and signal handlers are directly accessible from sub-objects of the object to which these properties are attached. But in fact, it's not. Instances of attached types are attached only to a specific object, not to the object and all of its sub-objects.
For example, below is a modified version of the previous example involving attached properties. This time, the delegate is an Item and the Rectangle is a child of that Item:

import QtQuick 2.0

  ListView {
    
    
      width: 240; height: 320
      model: 3
      delegate: Item {
    
    
          width: 100; height: 30

          Rectangle {
    
    
              width: 100; height: 30
              color: ListView.isCurrentItem ? "red" : "yellow"    // WRONG! This won't work.
          }
      }
  }

This doesn't work as expected because ListView.isCurrentItem only attaches to the root delegate object, not its children. Because the rectangle is a child of the delegate, not the delegate itself, it cannot access the isCurrentItem attached property like ListView.isCurrentItem. Therefore, the rectangle should access isCurrentItem through the root delegate:

ListView {
    
    
      //....
      delegate: Item {
    
    
          id: delegateItem
          width: 100; height: 30

          Rectangle {
    
    
              width: 100; height: 30
              color: delegateItem.ListView.isCurrentItem ? "red" : "yellow"   // correct
          }
      }
  }

delegateItem.ListView.isCurrentItem now correctly references the delegate's isCurrentItem attached property.

Guess you like

Origin blog.csdn.net/MrHHHHHH/article/details/135210769