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.
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:
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 local directory as the engine searches within the immediate directory when resolving QML type names.
注意: The QML engine does not automatically search remote directories this way. You have to add a qmldir file if your documents are loaded over the network. See Importing QML Document Directories .
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
对象。
The
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.
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.
若
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
模块
了解更多细节。
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
类型:
// 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.
You can prepend global instructions to a QML document using the
pragma
keyword. The following pragmas are supported:
pragma Singleton
declares the component defined in the QML document as singleton. Singletons are created only once per QML engine. In order to use a QML-declared singleton you also have to register it with its module. See
qt_target_qml_sources
for how to do this with CMake.
With this pragma you can define how assignments to list properties shall be handled in components defined in the QML document. By default, assigning to a list property appends to the list. You can explicitly request this behavior using the value
追加
. Alternatively, you can request the contents of list properties to always be replaced using
替换
, or replaced if the property is not the default property using
ReplaceIfNotDefault
。例如:
pragma ListPropertyAssignBehavior: ReplaceIfNotDefault
The same declaration can also be given for C++-defined types. See QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_APPEND , QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE ,和 QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE_IF_NOT_DEFAULT
With this pragma you can restrict components defined in this file to only create objects within their original context. This holds for inline components as well as Component elements explicitly or implicitly created as properties. If a component is bound to its context, you can safely use IDs from the rest of the file within the component. Otherwise, the engine and the QML tooling cannot know in advance what type, if any, such IDs will resolve to at run time.
In order to bind the components to their context specify the
Bound
自变量:
pragma ComponentBehavior: Bound
默认为
Unbound
. You can also specify it explicitly. In a future version of Qt the default will change to
Bound
.
Delegate components bound to their context don't receive their own private contexts on instantiation. This means that model data can only be passed via required properties in this case. Passing model data via context properties will not work. This concerns delegates to e.g. Instantiator , Repeater , ListView , TableView , GridView , TreeView and in general anything that uses DelegateModel internally.
For example, the following will not work:
pragma ComponentBehavior: Bound import QtQuick ListView { delegate: Rectangle { color: model.myColor } }
The
delegate
property of
ListView
is a component. Therefore, a
Component
is implicitly created around the
Rectangle
here. That component is bound to its context. It doesn't receive the context property
model
provided by
ListView
. To make it work, you'd have to write it this way:
pragma ComponentBehavior: Bound import QtQuick ListView { delegate: Rectangle { required property color myColor color: myColor } }
You can nest components in a QML file. The pragma holds for all components in the file, no matter how deeply nested.
With this pragma you can change the way type annotations on functions are handled. By default the interpreter and JIT ignore type annotations, but the QML 脚本编译器 enforces them when compiling to C++.
Specifying
Enforce
as value makes sure the type annotations are always enforced. The resulting type coercions increase the overhead of calling typed JavaScript functions.
Specifying
Ignore
as value makes the
QML 脚本编译器
ignore any JavaScript functions when compiling the document to C++. This means less code is compiled to C++ ahead of time, and more code has to be interpreted or JIT-compiled.
With this pragma you can change the way value types and sequences are handled.
Value types and sequences are generally treated as references. This means, if you retrieve a value type instance from a property into a local value, and then change the local value, the original property is also changed. Furthermore, if you write the original property explicitly, the local value is also updated. This behavior is rather unintuitive in many places, and you should not rely on it. The
Copy
and
参考
values for the
ValueTypeBehavior
pragma are experimental options to change this behavior. You should not use them. Specifying
Copy
causes all value types to be treated as actual copies. Specifying
参考
explicitly states the default behavior.
Rather than using
Copy
you should explicitly re-load references to value types and sequences any time they can have been affected by side effects. Side effects can happen whenever you call a function or imperatively set a property.
qmllint
provides guidance on this. For example, in the following code the variable
f
is affected by side effects after writing
width
. This is because there may be a binding in a derived type or in a
绑定
element that updates
font
当
width
改变。
import QtQuick Text { function a() : real { var f = font; width = f.pixelSize; return f.pointSize; } }
In order to address this, you can avoid holding
f
across the write operation on
width
:
import QtQuick Text { function a() : real { var f = font; width = f.pixelSize; f = font; return f.pointSize; } }
This, in turn can be shortened to:
import QtQuick Text { function a() : real { width = font.pixelSize; return font.pointSize; } }
You might assume that re-retrieving the
font
property is costly, but actually the QML engine automatically refreshes value type references each time you read from them. So this is not more expensive than the first version, but a clearer way to express the same operations.