用C++编写QML扩展

本文介绍了如何使用C++扩展QML,通过创建新类型、连接C++方法和信号、添加属性绑定,以及定义自定义类型和列表属性。最后,演示了如何通过插件将扩展部署到QML应用中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Writing QML Extensions with C++

用C++编写QML扩展

Tutorial about extending QML with Qt C++.

关于使用Qt C++扩展QML的教程。

The Qt QML module provides a set of APIs for extending QML through C++ extensions. You can write extensions to add your own QML types, extend existing Qt types, or call C/C++ functions that are not accessible from ordinary QML code.

​Qt-QML模块提供了一组用于通过C++扩展QML的api。您可以编写扩展来添加自己的QML类型,扩展现有的Qt类型,或者调用普通QML代码无法访问的C/C++函数。

This tutorial shows how to write a QML extension using C++ that includes core QML features, including properties, signals and bindings. It also shows how extensions can be deployed through plugins.

本教程介绍如何使用C++编写QML扩展,该扩展包括QML的核心功能,包括属性、信号和绑定。它还展示了如何通过插件部署扩展。

Many of the topics covered in this tutorial are documented in further detail in Integrating QML and C++ and its documentation sub-topics. In particular, you may be interested in the sub-topics Exposing Attributes of C++ Classes to QML and Defining QML Types from C++.

​本教程中涉及的许多主题将在集成QML和C++及其文档子主题中详细介绍。特别是,您可能对向QML暴露C++类的属性和从C++定义QML类型的子主题感兴趣。

Running the Tutorial Examples

运行教程示例

The code in this tutorial is available as an example project with subprojects associated with each tutorial chapter. In Qt Creator, open the Welcome mode and select the tutorial from Examples. In Edit mode, expand the extending-qml project, right-click on the subproject (chapter) you want to run and select Run.

​本教程中的代码作为示例项目提供,其中包含与每个教程章节相关联的子项目。在Qt Creator中,打开欢迎模式并从示例中选择教程。在编辑模式下,展开扩展qml项目,右键单击要运行的子项目(章节),然后选择运行。

Chapter 1: Creating a New Type

第一章:创造一种新类型

extending-qml/chapter1-basics

A common task when extending QML is to provide a new QML type that supports some custom functionality beyond what is provided by the built-in Qt Quick types. For example, this could be done to implement particular data models, or provide types with custom painting and drawing capabilities, or access system features like network programming that are not accessible through built-in QML features.

​扩展QML时的一个常见任务是提供一个新的QML类型,该类型支持内置Qt Quick类型之外的一些自定义功能。例如,可以这样做来实现特定的数据模型,或提供具有自定义绘制和绘图功能的类型,或访问系统功能,如无法通过内置QML功能访问的网络编程。

In this tutorial, we will show how to use the C++ classes in the Qt Quick module to extend QML. The end result will be a simple Pie Chart display implemented by several custom QML types connected together through QML features like bindings and signals, and made available to the QML runtime through a plugin.

在本教程中,我们将展示如何使用Qt Quick模块中的C++类来扩展QML。最终结果将是一个简单的饼图显示,由几个定制的QML类型实现,通过绑定和信号等QML功能连接在一起,并通过插件提供给QML运行时。

To begin with, let's create a new QML type called "PieChart" that has two properties: a name and a color. We will make it available in an importable type namespace called "Charts", with a version of 1.0.

首先,让我们创建一个名为“PieChart”的新QML类型,它有两个属性:名称和颜色。我们将在一个名为“Charts”的可导入类型名称空间中提供它,版本为1.0。

We want this PieChart type to be usable from QML like this:

我们希望此PieChart类型可以从QML中使用,如下所示:

import Charts 1.0

PieChart {
    width: 100; height: 100
    name: "A simple pie chart"
    color: "red"
}

To do this, we need a C++ class that encapsulates this PieChart type and its two properties. Since QML makes extensive use of Qt's meta object system, this new class must:

​为此,我们需要一个C++类来封装这个PieChart类型及其两个属性。由于QML广泛使用了Qt的元对象系统,这个新类必须:

  • Inherit from QObject
  • 继承自QObject
  • Declare its properties using the Q_PROPERTY macro
  • ​使用Q_PROPERTY宏声明其属性

Here is our PieChart class, defined in piechart.h:

这是我们在PieChart.h中定义的PieChart类:

#include <QtQuick/QQuickPaintedItem>
#include <QColor>

class PieChart : public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(QString name READ name WRITE setName)
    Q_PROPERTY(QColor color READ color WRITE setColor)
    QML_ELEMENT

public:
    PieChart(QQuickItem *parent = nullptr);

    QString name() const;
    void setName(const QString &name);

    QColor color() const;
    void setColor(const QColor &color);

    void paint(QPainter *painter) override;

private:
    QString m_name;
    QColor m_color;
};

The class inherits from QQuickPaintedItem because we want to override QQuickPaintedItem::paint() in perform drawing operations with the QPainter API. If the class just represented some data type and was not an item that actually needed to be displayed, it could simply inherit from QObject. Or, if we want to extend the functionality of an existing QObject-based class, it could inherit from that class instead. Alternatively, if we want to create a visual item that doesn't need to perform drawing operations with the QPainter API, we can just subclass QQuickItem.

​该类继承自QQuickPaintedItem,因为我们希望在使用QPaint API执行绘图操作时重写QQuickPaintedItem::paint()。如果类只是表示某种数据类型,而不是实际需要显示的项,那么它可以简单地从QObject继承。或者,如果我们想扩展现有基于QObject的类的功能,它可以从该类继承。或者,如果我们想要创建一个不需要使用QPaint API执行绘图操作的可视化项目,我们可以子类化QQuickItem。

The PieChart class defines the two properties, name and color, with the Q_PROPERTY macro, and overrides QQuickPaintedItem::paint(). The PieChart class is registered using the QML_ELEMENT macro, to allow it to be used from QML. If you don't register the class, app.qml won't be able to create a PieChart.

​PieChart类使用Q_PROPERTY宏定义了两个属性,名称name和颜色color,并重写了QQuickPaintedItem::paint()。PieChart类是使用QML_ELEMENT宏注册的,以允许从QML使用它。如果你没有注册该类,app.qml将无法创建PieChart。

For the registration to take effect, the qmltypes option is added to CONFIG in the project file and a QML_IMPORT_NAME and QML_IMPORT_MAJOR_VERSION are given:

为了使注册生效,qmltypes选项被添加到项目文件中的CONFIG中,并给出QML_IMPORT_NAMEQML_IMPORT_MAJOR_VERSION

CONFIG += qmltypes
QML_IMPORT_NAME = Charts
QML_IMPORT_MAJOR_VERSION = 1

The class implementation in piechart.cpp simply sets and returns the m_name and m_color values as appropriate, and implements paint() to draw a simple pie chart. It also turns off the QGraphicsItem::ItemHasNoContents flag to enable painting:

​piechart.cpp中的类实现,只需根据需要设置并返回m_namem_color,并实现paint()来绘制简单的饼图。它还关闭QGraphicsItem::ItemHasNoContents标志以启用绘制:

PieChart::PieChart(QQuickItem *parent)
    : QQuickPaintedItem(parent)
{
}
...
void PieChart::paint(QPainter *painter)
{
    QPen pen(m_color, 2);
    painter->setPen(pen);
    painter->setRenderHints(QPainter::Antialiasing, true);
    painter->drawPie(boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16);
}

Now that we have defined the PieChart type, we will use it from QML. The app.qml file creates a PieChart item and display the pie chart's details using a standard QML Text item:

​现在我们已经定义了PieChart类型,我们将从QML中使用它。app.qml文件创建饼图项,并使用标准文本项显示饼图的详细信息:

import Charts 1.0
import QtQuick 2.0

Item {
    width: 300; height: 200

    PieChart {
        id: aPieChart
        anchors.centerIn: parent
        width: 100; height: 100
        name: "A simple pie chart"
        color: "red"
    }

    Text {
        anchors { bottom: parent.bottom; horizontalCenter: parent.horizontalCenter; bottomMargin: 20 }
        text: aPieChart.name
    }
}

Notice that although the color is specified as a string in QML, it is automatically converted to a QColor object for the PieChart color property. Automatic conversions are provided for various other basic types; for example, a string like "640x480" can be automatically converted to a QSize value.

​请注意,尽管颜色在QML中指定为字符串,但它会自动转换为PieChart color属性的QColor对象。为各种其他基本类型提供自动转换;例如,像“640x480”这样的字符串可以自动转换为QSize值。

We'll also create a C++ application that uses a QQuickView to run and display app.qml.

​我们还将创建一个C++应用程序,它使用QQuickView来运行和显示app.qml。

Here is the application main.cpp:

这是应用程序的main.cpp:

#include "piechart.h"
#include <QtQuick/QQuickView>
#include <QGuiApplication>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQuickView view;
    view.setResizeMode(QQuickView::SizeRootObjectToView);
    view.setSource(QUrl("qrc:///app.qml"));
    view.show();
    return QGuiApplication::exec();
}

We write a .pro project file that includes the files and the qml library, and defines a type namespace called "Charts" with a version of 1.0 for any types exposed to QML:

我们编写了一个.pro项目文件,其中包括文件和qml库,并定义了一个名为“Charts”的类型命名空间,对于任何暴露于qml的类型,其版本为1.0:

QT += qml quick

CONFIG += qmltypes
QML_IMPORT_NAME = Charts
QML_IMPORT_MAJOR_VERSION = 1

HEADERS += piechart.h
SOURCES += piechart.cpp \
           main.cpp

RESOURCES += chapter1-basics.qrc

DESTPATH = $$[QT_INSTALL_EXAMPLES]/qml/tutorials/extending-qml/chapter1-basics
target.path = $$DESTPATH
INSTALLS += target

Now we can build and run the application:

现在,我们可以构建并运行应用程序:

Note: You may see a warning Expression ... depends on non-NOTIFYable properties: PieChart::name. This happens because we add a binding to the writable name property, but haven't yet defined a notify signal for it. The QML engine therefore cannot update the binding if the name value changes. This is addressed in the following chapters.

注意:您可能会看到一个警告表达式。。。取决于不可通知的属性:PieChart::name。这是因为我们向可写入的name属性添加了一个绑定,但尚未为其定义通知信号。因此,如果名称值更改,QML引擎无法更新绑定。这将在以下章节中讨论。

The source code from the following files are referred to in this chapter:

本章引用了以下文件中的源代码:

Chapter 2: Connecting to C++ Methods and Signals

第2章:连接到C++方法和信号

extending-qml/chapter2-methods

Suppose we want PieChart to have a "clearChart()" method that erases the chart and then emits a "chartCleared" signal. Our app.qml would be able to call clearChart() and receive chartCleared() signals like this:

假设我们希望PieChart有一个“clearChart()”方法,该方法擦除图表,然后发出“chartCleared”信号。我们的app.qml将能够调用clearChart()并接收如下chartCleared()信号:

import Charts 1.0
import QtQuick 2.0

Item {
    width: 300; height: 200

    PieChart {
        id: aPieChart
        anchors.centerIn: parent
        width: 100; height: 100
        color: "red"

        onChartCleared: console.log("The chart has been cleared")
    }

    MouseArea {
        anchors.fill: parent
        onClicked: aPieChart.clearChart()
    }

    Text {
        anchors { bottom: parent.bottom; horizontalCenter: parent.horizontalCenter; bottomMargin: 20 }
        text: "Click anywhere to clear the chart"
    }
}

To do this, we add a clearChart() method and a chartCleared() signal to our C++ class:

为此,我们向C++类添加了一个clearChart()方法和一个chartCleared()信号:

class PieChart : public QQuickPaintedItem
{
    ...
public:
    ...
    Q_INVOKABLE void clearChart();

signals:
    void chartCleared();
    ...
};

The use of Q_INVOKABLE makes the clearChart() method available to the Qt Meta-Object system, and in turn, to QML. Note that it could have been declared as a Qt slot instead of using Q_INVOKABLE, as slots are also callable from QML. Both of these approaches are valid.

​Q_INVOKABLE的使用使clearChart()方法可用于Qt元对象系统,进而可用于QML。注意,它可以被声明为Qt插槽,而不是使用Q_INVOKABLE,因为插槽也可以从QML调用。这两种方法都是有效的。

The clearChart() method simply changes the color to Qt::transparent, repaints the chart, then emits the chartCleared() signal:

​clearChart()方法只需将颜色更改为Qt::transparent,重新绘制图表,然后发出chartCleared()信号:

void PieChart::clearChart()
{
    setColor(QColor(Qt::transparent));
    update();

    emit chartCleared();
}

Now when we run the application and click the window, the pie chart disappears, and the application outputs:

现在,当我们运行应用程序并单击窗口时,饼图消失,应用程序输出:

qml: The chart has been cleared

The source code from the following files are referred to in this chapter:

本章引用了以下文件中的源代码:

Chapter 3: Adding Property Bindings

第3章:添加属性绑定

extending-qml/chapter3-bindings

Property binding is a powerful feature of QML that allows values of different types to be synchronized automatically. It uses signals to notify and update other types' values when property values are changed.

属性绑定是QML的一个强大功能,它允许自动同步不同类型的值。当属性值更改时,它使用信号通知和更新其他类型的值。

Let's enable property bindings for the color property. That means if we have code like this:

让我们为color属性启用属性绑定。这意味着如果我们有这样的代码:

import Charts 1.0
import QtQuick 2.0

Item {
    width: 300; height: 200

    Row {
        anchors.centerIn: parent
        spacing: 20

        PieChart {
            id: chartA
            width: 100; height: 100
            color: "red"
        }

        PieChart {
            id: chartB
            width: 100; height: 100
            color: chartA.color
        }
    }

    MouseArea {
        anchors.fill: parent
        onClicked: { chartA.color = "blue" }
    }

    Text {
        anchors { bottom: parent.bottom; horizontalCenter: parent.horizontalCenter; bottomMargin: 20 }
        text: "Click anywhere to change the chart color"
    }
}

The "color: chartA.color" statement binds the color value of chartB to the color of chartA. Whenever chartA's color value changes, chartB's color value updates to the same value. When the window is clicked, the onClicked handler in the MouseArea changes the color of chartA, thereby changing both charts to the color blue.

​“color:chartA.color”语句将chartB的颜色值color绑定到chartA的颜色。每当chartA的颜色值更改时,chartB的颜色值都会更新为相同的值。单击窗口时,MouseArea中的onClicked处理程序会更改chartA的颜色,从而将两个图表都更改为蓝色。

It's easy to enable property binding for the color property. We add a NOTIFY feature to its Q_PROPERTY() declaration to indicate that a "colorChanged" signal is emitted whenever the value changes.

​属性的颜色绑定很容易。我们在其Q_PROPERTY()声明中添加了一个NOTIFY特性,以指示每当值发生变化时都会发出“colorChanged”信号。

class PieChart : public QQuickPaintedItem
{
    ...
    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
public:
    ...
signals:
    void colorChanged();
    ...
};

Then, we emit this signal in setPieSlice():

然后,我们在setPieSicle()中发出这个信号:

void PieChart::setColor(const QColor &color)
{
    if (color != m_color) {
        m_color = color;
        update();   // repaint with the new color
        emit colorChanged();
    }
}

It's important for setColor() to check that the color value has actually changed before emitting colorChanged(). This ensures the signal is not emitted unnecessarily and also prevents loops when other types respond to the value change.

对于setColor(),在发出colorChanged()之前,检查颜色值是否已实际更改非常重要。这确保了信号不会不必要地发出,也防止了其他类型响应值变化时出现循环。

The use of bindings is essential to QML. You should always add NOTIFY signals for properties if they are able to be implemented, so that your properties can be used in bindings. Properties that cannot be bound cannot be automatically updated and cannot be used as flexibly in QML. Also, since bindings are invoked so often and relied upon in QML usage, users of your custom QML types may see unexpected behavior if bindings are not implemented.

绑定的使用对QML至关重要。如果可以实现属性,则应该始终为属性添加通知信号,以便在绑定中使用属性。无法绑定的属性无法自动更新,也无法在QML中灵活使用。此外,由于绑定在QML使用中被频繁调用和依赖,如果未实现绑定,自定义QML类型的用户可能会看到意外行为。

The source code from the following files are referred to in this chapter:

本章引用了以下文件中的源代码:

Chapter 4: Using Custom Property Types

第4章 :使用自定义属性类型

extending-qml/chapter4-customPropertyTypes

The PieChart type currently has a string-type property and a color-type property. It could have many other types of properties. For example, it could have an int-type property to store an identifier for each chart:

PieChart类型当前有一个字符串类型属性和一个颜色类型属性。它可能有许多其他类型的属性。例如,它可以有一个int-type属性来存储每个图表的标识符:

// C++
class PieChart : public QQuickPaintedItem
{
    Q_PROPERTY(int chartId READ chartId WRITE setChartId NOTIFY chartIdChanged)
    ...

public:
    void setChartId(int chartId);
    int chartId() const;
    ...

signals:
    void chartIdChanged();
};

// QML
PieChart {
    ...
    chartId: 100
}

Aside from int, we could use various other property types. Many of the Qt data types such as QColorQSize and QRect are automatically supported from QML. (See Data Type Conversion Between QML and C++ documentation for a full list.)

​除了int之外,我们还可以使用其他各种属性类型。QML自动支持许多Qt数据类型,如QColor、QSize和QRect。(有关完整列表,请参阅QML和C++文档之间的数据类型转换。)

If we want to create a property whose type is not supported by QML by default, we need to register the type with the QML engine.

如果我们想要创建一个属性,其类型在默认情况下不受QML支持,我们需要向QML引擎注册该类型。

For example, let's replace the use of the property with a type called "PieSlice" that has a color property. Instead of assigning a color, we assign an PieSlice value which itself contains a color:

例如,让我们用一个名为“PieSlice”的类型替换属性的用法,该类型具有颜色属性color。我们不指定颜色color,而是指定一个本身包含颜色的PieSlice值:

import Charts 1.0
import QtQuick 2.0

Item {
    width: 300; height: 200

    PieChart {
        id: chart
        anchors.centerIn: parent
        width: 100; height: 100

        pieSlice: PieSlice {
            anchors.fill: parent
            color: "red"
        }
    }

    Component.onCompleted: console.log("The pie is colored " + chart.pieSlice.color)
}

Like PieChart, this new PieSlice type inherits from QQuickPaintedItem and declares its properties with Q_PROPERTY():

​与PieChart一样,这个新的PieSicle类型继承自QQuickPaintedItem,并使用Q_PROPERTY()声明其属性:

class PieSlice : public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(QColor color READ color WRITE setColor)
    QML_ELEMENT

public:
    PieSlice(QQuickItem *parent = nullptr);

    QColor color() const;
    void setColor(const QColor &color);

    void paint(QPainter *painter) override;

private:
    QColor m_color;
};

To use it in PieChart, we modify the color property declaration and associated method signatures:

为了在PieChart中使用它,我们修改了颜色属性color声明和相关的方法签名:

class PieChart : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(PieSlice* pieSlice READ pieSlice WRITE setPieSlice)
    ...
public:
    ...
    PieSlice *pieSlice() const;
    void setPieSlice(PieSlice *pieSlice);
    ...
};

There is one thing to be aware of when implementing setPieSlice(). The PieSlice is a visual item, so it must be set as a child of the PieChart using QQuickItem::setParentItem() so that the PieChart knows to paint this child item when its contents are drawn:

​在实现setPieSicle()时,有一件事需要注意。PieSicle是一个可视项,因此必须使用QQuickItem::setParentItem()将其设置为PieChart的子项,以便PieChart在绘制其内容时知道绘制该子项:

void PieChart::setPieSlice(PieSlice *pieSlice)
{
    m_pieSlice = pieSlice;
    pieSlice->setParentItem(this);
}

Like the PieChart type, the PieSlice type has to be exposted to QML using QML_ELEMENT. As with PieChart, we add the "Charts" type namespace, version 1.0 to the .pro file:

​与PieChart类型一样,PieSlice类型必须使用QML_ELEMENT暴露给QML。与PieChart一样,我们将“Charts”类型的名称空间1.0版添加到.pro文件:

class PieSlice : public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(QColor color READ color WRITE setColor)
    QML_ELEMENT

public:
    PieSlice(QQuickItem *parent = nullptr);

    QColor color() const;
    void setColor(const QColor &color);

    void paint(QPainter *painter) override;

private:
    QColor m_color;
};
    ...
QT += qml quick

CONFIG += qmltypes
QML_IMPORT_NAME = Charts
QML_IMPORT_MAJOR_VERSION = 1

HEADERS += piechart.h \
           pieslice.h
SOURCES += piechart.cpp \
           pieslice.cpp \
           main.cpp

RESOURCES += chapter4-customPropertyTypes.qrc

DESTPATH = $$[QT_INSTALL_EXAMPLES]/qml/tutorials/extending-qml/chapter4-customPropertyTypes
target.path = $$DESTPATH
INSTALLS += target

The source code from the following files are referred to in this chapter:

本章引用了以下文件中的源代码:

Chapter 5: Using List Property Types

第5章:使用列表属性类型

extending-qml/chapter5-listproperties

Right now, a PieChart can only have one PieSlice. Ideally a chart would have multiple slices, with different colors and sizes. To do this, we could have a slices property that accepts a list of PieSlice items:

现在,一张饼图只能有一个饼片。理想情况下,图表应该有多个不同颜色和大小的切片。要做到这一点,我们可以有一个slices属性,它接受PieSlice项的列表:

import Charts 1.0
import QtQuick 2.0

Item {
    width: 300; height: 200

    PieChart {
        anchors.centerIn: parent
        width: 100; height: 100

        slices: [
            PieSlice {
                anchors.fill: parent
                color: "red"
                fromAngle: 0; angleSpan: 110
            },
            PieSlice {
                anchors.fill: parent
                color: "black"
                fromAngle: 110; angleSpan: 50
            },
            PieSlice {
                anchors.fill: parent
                color: "blue"
                fromAngle: 160; angleSpan: 100
            }
        ]
    }
}

To do this, we replace the pieSlice property in PieChart with a slices property, declared as a QQmlListProperty type. The QQmlListProperty class enables the creation of list properties in QML extensions. We replace the pieSlice() function with a slices() function that returns a list of slices, and add an internal append_slice() function (discussed below). We also use a QList to store the internal list of slices as m_slices:

​为此,我们将PieChart中的pieSlice属性替换为声明为QQmlListProperty类型的slices属性。QQmlListProperty类支持在QML扩展中创建列表属性。我们用一个返回切片列表的slices()函数替换pieSlice()函数,并添加一个内部append_slice()函数(下面讨论)。我们还使用QList将内部切片列表存储为m_slices

class PieChart : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(QQmlListProperty<PieSlice> slices READ slices)
    ...
public:
    ...
    QQmlListProperty<PieSlice> slices();

private:
    static void append_slice(QQmlListProperty<PieSlice> *list, PieSlice *slice);

    QString m_name;
    QList<PieSlice *> m_slices;
};

Although the slices property does not have an associated WRITE function, it is still modifiable because of the way QQmlListProperty works. In the PieChart implementation, we implement PieChart::slices() to return a QQmlListProperty value and indicate that the internal PieChart::append_slice() function is to be called whenever a request is made from QML to add items to the list:

​尽管slices属性没有关联的写函数,但由于QQmlListProperty的工作方式,它仍然可以修改。在PieChart实现中,我们实现PieChart::slices()以返回QQmlListProperty值,并指示每当从QML发出向列表中添加项目的请求时,将调用内部PieChart::append_slice()函数:

QQmlListProperty<PieSlice> PieChart::slices()
{
    return QQmlListProperty<PieSlice>(this, nullptr, &PieChart::append_slice, nullptr,
                                      nullptr, nullptr, nullptr, nullptr);
}

void PieChart::append_slice(QQmlListProperty<PieSlice> *list, PieSlice *slice)
{
    PieChart *chart = qobject_cast<PieChart *>(list->object);
    if (chart) {
        slice->setParentItem(chart);
        chart->m_slices.append(slice);
    }
}

The append_slice() function simply sets the parent item as before, and adds the new item to the m_slices list. As you can see, the append function for a QQmlListProperty is called with two arguments: the list property, and the item that is to be appended.

​append_slice()函数只需像以前一样设置父项,并将新项添加到m_slice列表中。如您所见,qqmlistproperty的append函数由两个参数调用:list属性和要追加的项。

The PieSlice class has also been modified to include fromAngle and angleSpan properties and to draw the slice according to these values. This is a straightforward modification if you have read the previous pages in this tutorial, so the code is not shown here.

PieSlice类也被修改为包含fromAngle和angleSpan属性,并根据这些值绘制切片。如果您已经阅读了本教程的前几页,那么这是一个简单的修改,因此这里不显示代码。

The source code from the following files are referred to in this chapter:

本章引用了以下文件中的源代码:

Chapter 6: Writing an Extension Plugin

第6章:编写扩展插件

extending-qml/chapter6-plugins

Currently the PieChart and PieSlice types are used by app.qml, which is displayed using a QQuickView in a C++ application. An alternative way to use our QML extension is to create a plugin library to make it available to the QML engine as a new QML import module. This allows the PieChart and PieSlice types to be registered into a type namespace which can be imported by any QML application, instead of restricting these types to be only used by the one application.

​目前app.qml使用PieChart和PieSlice类型,在C++应用程序中使用QQuickView显示。使用我们的QML扩展的另一种方法是创建一个插件库,将其作为新的QML导入模块提供给QML引擎。这允许PieChart和PieSlice类型注册到一个类型命名空间中,该命名空间可以由任何QML应用程序导入,而不是将这些类型限制为仅由一个应用程序使用。

The steps for creating a plugin are described in Creating C++ Plugins for QML. To start with, we create a plugin class named ChartsPlugin. It subclasses QQmlEngineExtensionPlugin and uses the Q_PLUGIN_METADATA() macro to register the plugin with the Qt meta object system.

​为QML创建C++插件中描述了创建插件的步骤。首先,我们创建一个名为ChartsPlugin的插件类。它是QQmlEngineExtensionPlugin的子类,并使用Q_PLUGIN_METADATA()宏将插件注册到Qt元对象系统。

Here is the ChartsPlugin definition in chartsplugin.h:

以下是ChartsPlugin中的chartsplugin.h定义:

#include <QQmlEngineExtensionPlugin>

class ChartsPlugin : public QQmlEngineExtensionPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid)
};

Then, we write a .pro project file that defines the project as a plugin library.

然后,我们写一个 .pro项目文件。将项目定义为插件库。

TEMPLATE = lib
CONFIG += plugin qmltypes
QT += qml quick

QML_IMPORT_NAME = Charts
QML_IMPORT_MAJOR_VERSION = 1

TARGET = $$qtLibraryTarget(chartsplugin)

HEADERS += piechart.h \
           pieslice.h \
           chartsplugin.h

SOURCES += piechart.cpp \
           pieslice.cpp

DESTPATH=$$[QT_INSTALL_EXAMPLES]/qml/tutorials/extending-qml/chapter6-plugins/$$QML_IMPORT_NAME

target.path=$$DESTPATH
qmldir.files=$$PWD/qmldir
qmldir.path=$$DESTPATH
INSTALLS += target qmldir

CONFIG += install_ok  # Do not cargo-cult this!

OTHER_FILES += qmldir

# Copy the qmldir file to the same folder as the plugin binary
cpqmldir.files = qmldir
cpqmldir.path = .
COPIES += cpqmldir

When building this example on Windows or Linux, the Charts directory will be located at the same level as the application that uses our new import module. This way, the QML engine will find our module as the default search path for QML imports includes the directory of the application executable. On macOS, the plugin binary is copied to Contents/PlugIns in the the application bundle; this path is set in chapter6-plugins/app.pro:

在Windows或Linux上构建此示例时,Charts目录将与使用新导入模块的应用程序位于同一级别。这样,QML引擎将找到我们的模块,因为QML导入的默认搜索路径包括应用程序可执行文件的目录。在macOS上,插件二进制文件被复制到应用程序包中的内容/插件中;此路径在chapter6-plugins/app.pro中设置:

macos:!qtConfig(static) {
    charts.files = $$OUT_PWD/Charts
    charts.path = Contents/PlugIns
    QMAKE_BUNDLE_DATA += charts
}

To account for this, we also need to add this location as a QML import path in main.cpp:

​为了说明这一点,我们还需要在main.cpp中添加QML import path作为QML导入路径:

 QQuickView view;
#ifdef Q_OS_OSX
    view.engine()->addImportPath(app.applicationDirPath() + "/../PlugIns");
#endif
    ...

Defining custom import paths is useful also when there are multiple applications using the same QML imports.

当多个应用程序使用相同的QML导入时,定义自定义导入路径也很有用。

The .pro file also contains additional magic to ensure that the module definition qmldir file is always copied to the same location as the plugin binary.

​.pro文件还包含额外的魔力,以确保模块定义qmldir文件始终复制到插件二进制文件所在的位置。

The qmldir file declares the module name and the plugin that is made available by the module:

qmldir文件声明模块名称和模块提供的插件:

module Charts
plugin chartsplugin

Now we have a QML module that can be imported to any application, provided that the QML engine knows where to find it. The example contains an executable that loads app.qml, which uses the import Charts 1.0 statement. Alternatively, you can load the QML file using the qml tool, setting the import path to the current directory so that it finds the qmldir file:

​现在我们有了一个可以导入到任何应用程序的QML模块,只要QML引擎知道在哪里可以找到它。该示例包含一个加载app.qml的可执行文件,它使用import Charts 1.0声明。或者,可以使用QML工具加载QML文件,将导入路径设置为当前目录,以便找到qmldir文件:

qml -I . app.qml

The module "Charts" will be loaded by the QML engine, and the types provided by that module will be available for use in any QML document which imports it.

模块“图表”将由QML引擎加载,该模块提供的类型将可用于导入该模块的任何QML文档中。

The source code from the following files are referred to in this chapter:

本章引用了以下文件中的源代码:

Chapter 7: Summary

第7章:总结

In this tutorial, we've shown the basic steps for creating a QML extension:

在本教程中,我们展示了创建QML扩展的基本步骤:

  • Define new QML types by subclassing QObject and registering them with QML_ELEMENT or QML_NAMED_ELEMENT()
  • ​定义新的QML类型,方法是将QObject子类化,并将它们注册到QML_ELEMENT或QML_NAMED_ELEMENT()
  • Add callable methods using Q_INVOKABLE or Qt slots, and connect to Qt signals with an onSignal syntax
  • ​使用Q_INVOKABLE或Qt插槽添加可调用方法,并使用onSignal语法连接到Qt信号
  • Add property bindings by defining NOTIFY signals
  • ​通过定义通知信号添加属性绑定
  • Define custom property types if the built-in types are not sufficient
  • 如果内置类型不足,请定义自定义特性类型
  • Define list property types using QQmlListProperty
  • ​使用qqmlistproperty定义列表属性类型
  • Create a plugin library by defining a Qt plugin and writing a qmldir file
  • ​通过定义Qt插件并编写qmldir文件来创建插件库

The QML and C++ Integration overview documentation shows other useful features that can be added to QML extensions. For example, we could use default properties to allow slices to be added without using the slices property:

​QML和C++集成概述文档显示了可以添加到QML扩展中的其他有用功能。例如,我们可以使用默认属性来允许在不使用slices属性的情况下添加切片:

PieChart {
    PieSlice { ... }
    PieSlice { ... }
    PieSlice { ... }
}

Or randomly add and remove slices from time to time using property value sources:

​或者使用属性值源不时随机添加和删除切片:

PieChart {
    PieSliceRandomizer on slices {}
}

Example project @ code.qt.io

See also Simple QML and C++ Integration Example.

​另请参见简单的QML和C++集成示例。

© 2022 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值