Schreiben von QML-Erweiterungen mit C++
Das Qt Qml Modul bietet eine Reihe von APIs zur Erweiterung von QML durch C++-Erweiterungen. Sie können Erweiterungen schreiben, um Ihre eigenen QML-Typen hinzuzufügen, bestehende Qt-Typen zu erweitern oder C/C++-Funktionen aufzurufen, die vom normalen QML-Code aus nicht zugänglich sind.
Dieses Tutorial zeigt, wie man eine QML-Erweiterung mit C++ schreibt, die zentrale QML-Funktionen wie Eigenschaften, Signale und Bindungen enthält. Es wird auch gezeigt, wie Erweiterungen durch Plugins bereitgestellt werden können.
Viele der in diesem Tutorial behandelten Themen sind in Übersicht - QML- und C++-Integration und den entsprechenden Unterthemen der Dokumentation ausführlicher dokumentiert. Insbesondere die Unterthemen Exposing Attributes of C++ Classes to QML und Defining QML Types from C++ könnten für Sie von Interesse sein.
Öffnen der Tutorial-Quellen
Der Code in diesem Tutorial ist als Teil der Qt-Quellen verfügbar. Wenn Sie Qt mit dem Qt Online Installer installiert haben, finden Sie die Quellen im Qt-Installationsverzeichnis unter Examples/Qt-6.8.2/qml/tutorials/extending-qml/.
Projekt von Grund auf neu erstellen
Alternativ können Sie dem Tutorial auch folgen, indem Sie die Quellen von Grund auf neu erstellen: Erstellen Sie für jedes Kapitel ein neues Projekt unter Verwendung der Qt Quick Anwendungsvorlage in Qt Creator, wie in Qt Creator beschrieben: Erstellen von Qt Quick Projekten. Dann folgen Sie den Anweisungen, indem Sie den generierten Skelettcode anpassen und erweitern.
Kapitel 1: Erstellen eines neuen Typs
extending-qml/chapter1-basics
Eine häufige Aufgabe bei der Erweiterung von QML ist es, einen neuen QML-Typ zu erstellen, der eine benutzerdefinierte Funktionalität unterstützt, die über die der eingebauten Qt Quick types. Dies kann beispielsweise geschehen, um bestimmte Datenmodelle zu implementieren, Typen mit benutzerdefinierten Mal- und Zeichenfunktionen zu versehen oder auf Systemfunktionen wie die Netzwerkprogrammierung zuzugreifen, die über die eingebauten QML-Funktionen nicht zugänglich sind.
In diesem Tutorial wird gezeigt, wie man die C++-Klassen im Modul Qt Quick verwendet, um QML zu erweitern. Das Endergebnis wird eine einfache Tortendiagramm-Anzeige sein, die durch mehrere benutzerdefinierte QML-Typen implementiert wird, die durch QML-Funktionen wie Bindungen und Signale miteinander verbunden sind und der QML Runtime über ein Plugin zur Verfügung gestellt werden.
Zunächst erstellen wir einen neuen QML-Typ namens "PieChart", der zwei Eigenschaften hat: einen Namen und eine Farbe. Wir werden ihn in einem importierbaren Typ-Namensraum namens "Charts" mit einer Version von 1.0 zur Verfügung stellen.
Wir möchten, dass dieser PieChart
Typ von QML aus wie folgt verwendet werden kann:
import Charts PieChart { width: 100; height: 100 name: "A simple pie chart" color: "red" }
Dazu benötigen wir eine C++-Klasse, die diesen Typ PieChart
und seine Eigenschaften kapselt. Da QML das Metaobjektsystem von Qt ausgiebig nutzt, muss diese neue Klasse:
- Vererben von QObject
- ihre Eigenschaften mit dem Makro Q_PROPERTY deklarieren
Klassendeklaration
Hier ist unsere Klasse PieChart
, definiert in piechart.h
:
#include <QtQuick/QQuickPaintedItem> #include <QColor> class PieChart : public QQuickPaintedItem { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName FINAL) Q_PROPERTY(QColor color READ color WRITE setColor FINAL) 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; };
Die Klasse erbt von QQuickPaintedItem, weil wir QQuickPaintedItem::paint() überschreiben wollen, um Zeichenoperationen mit der QPainter API durchzuführen. Wenn die Klasse nur einen Datentyp repräsentieren würde und nicht ein Element wäre, das tatsächlich angezeigt werden müsste, könnte sie einfach von QObject erben. Oder, wenn wir die Funktionalität einer bestehenden QObject-basierten Klasse erweitern wollen, könnte sie stattdessen von dieser Klasse erben. Wenn wir ein visuelles Element erstellen wollen, das keine Zeichenoperationen mit der API von QPainter durchführen muss, können wir auch einfach eine Unterklasse von QQuickItem erstellen.
Die Klasse PieChart
definiert die beiden Eigenschaften name
und color
mit dem Makro Q_PROPERTY und setzt QQuickPaintedItem::paint() außer Kraft. Die Klasse PieChart
wird mit dem Makro QML_ELEMENT registriert, damit sie von QML aus verwendet werden kann. Wenn Sie die Klasse nicht registrieren, ist App.qml
nicht in der Lage, eine PieChart
zu erstellen.
qmake-Einrichtung
Damit die Registrierung wirksam wird, wird die Option qmltypes
zu CONFIG
in der Projektdatei hinzugefügt, und es werden QML_IMPORT_NAME
und QML_IMPORT_MAJOR_VERSION
angegeben:
CONFIG += qmltypes QML_IMPORT_NAME = Charts QML_IMPORT_MAJOR_VERSION = 1
CMake-Setup
Damit die Registrierung auch bei der Verwendung von CMake wirksam wird, verwenden Sie den Befehl qt_add_qml_module:
qt_add_qml_module(chapter1-basics URI Charts QML_FILES App.qml DEPENDENCIES QtQuick )
Klassenimplementierung
Die Klassenimplementierung in piechart.cpp
setzt einfach die Werte m_name
und m_color
und gibt sie entsprechend zurück, und implementiert paint()
, um ein einfaches Tortendiagramm zu zeichnen:
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); }
QML-Verwendung
Nachdem wir nun den Typ PieChart
definiert haben, werden wir ihn in QML verwenden. Die Datei App.qml
erstellt ein Element PieChart
und zeigt die Details des Kreisdiagramms mit einem standardmäßigen QML-Element Text an:
import Charts import QtQuick 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 } }
Beachten Sie, dass die Farbe zwar als String in QML angegeben wird, aber automatisch in ein QColor Objekt für die PieChart color
Eigenschaft konvertiert wird. Automatische Konvertierungen sind für verschiedene andere Wertetypen vorgesehen. Zum Beispiel kann ein String wie "640x480" automatisch in einen QSize Wert konvertiert werden.
Wir werden auch eine C++-Anwendung erstellen, die ein QQuickView verwendet, um App.qml
auszuführen und anzuzeigen.
Hier ist die Anwendung 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.loadFromModule("Charts", "App"); view.show(); return QGuiApplication::exec(); }
Projekt erstellen
Um das Projekt zu erstellen, binden wir die Dateien ein, linken gegen die Bibliotheken und definieren einen Typ-Namensraum namens "Charts" mit Version 1.0 für alle Typen, die QML ausgesetzt sind.
Verwendung von qmake:
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
Verwendung von CMake:
# Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause cmake_minimum_required(VERSION 3.16) project(chapter1-basics LANGUAGES CXX) find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick) qt_standard_project_setup(REQUIRES 6.8) qt_add_executable(chapter1-basics main.cpp piechart.cpp piechart.h ) set_target_properties(chapter1-basics PROPERTIES WIN32_EXECUTABLE TRUE MACOSX_BUNDLE TRUE ) target_link_libraries(chapter1-basics PUBLIC Qt6::Core Qt6::Gui Qt6::Qml Qt6::Quick ) qt_add_qml_module(chapter1-basics URI Charts QML_FILES App.qml DEPENDENCIES QtQuick ) install(TARGETS chapter1-basics BUNDLE DESTINATION . RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) qt_generate_deploy_qml_app_script( TARGET chapter1-basics OUTPUT_SCRIPT deploy_script MACOS_BUNDLE_POST_BUILD NO_UNSUPPORTED_PLATFORM_ERROR DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM ) install(SCRIPT ${deploy_script})
Jetzt können wir die Anwendung erstellen und ausführen:
Hinweis: Möglicherweise wird eine Warnung angezeigt Expression ... depends on non-NOTIFYable properties: PieChart::name. Dies geschieht, weil wir eine Bindung an die beschreibbare Eigenschaft name
hinzufügen, aber noch kein Benachrichtigungssignal für diese definiert haben. Die QML-Engine kann daher die Bindung nicht aktualisieren, wenn sich der Wert name
ändert. Dies wird in den folgenden Kapiteln behandelt.
Kapitel 2: Verbindung zu C++ Methoden und Signalen
extending-qml/chapter2-methods
Angenommen, PieChart
soll eine "clearChart()"-Methode haben, die das Diagramm löscht und dann ein "chartCleared"-Signal ausgibt. Unser App.qml
könnte dann clearChart()
aufrufen und chartCleared()
Signale wie dieses empfangen:
import Charts import QtQuick 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" } }
Dazu fügen wir unserer C++ Klasse eine clearChart()
Methode und ein chartCleared()
Signal hinzu:
class PieChart : public QQuickPaintedItem { ... public: ... Q_INVOKABLE void clearChart(); signals: void chartCleared(); ... };
Die Verwendung von Q_INVOKABLE macht die Methode clearChart()
für das Qt-Meta-Object-System und damit auch für QML verfügbar.
Hinweis: Sie können die Methode auch als Qt-Slot deklarieren, anstatt Q_INVOKABLE zu verwenden, da öffentliche und geschützte Slots auch von QML aus aufgerufen werden können (private Slots können Sie nicht aufrufen).
Die Methode clearChart()
ändert die Farbe in Qt::transparent, färbt das Diagramm neu und gibt dann das Signal chartCleared()
aus:
Wenn wir nun die Anwendung ausführen und auf das Fenster klicken, wird das Tortendiagramm ausgeblendet, und die Anwendung wird ausgegeben:
qml: The chart has been cleared
Kapitel 3: Hinzufügen von Eigenschaftsbindungen
extending-qml/chapter3-bindings
Die Eigenschaftsbindung ist eine leistungsstarke Funktion von QML, mit der Werte verschiedener Typen automatisch synchronisiert werden können. Sie verwendet Signale, um die Werte anderer Typen zu benachrichtigen und zu aktualisieren, wenn Eigenschaftswerte geändert werden.
Lassen Sie uns die Eigenschaftsbindung für die Eigenschaft color
aktivieren. Das heißt, wenn wir Code wie diesen haben:
import Charts import QtQuick 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" } }
Die Anweisung "color: chartA.color" bindet den Wert color
von chartB
an den Wert color
von chartA
. Immer wenn sich der Wert color
von chartA
ändert, wird der Wert color
von chartB
auf denselben Wert aktualisiert. Wenn das Fenster angeklickt wird, ändert der onClicked
-Handler in MouseArea die Farbe von chartA
, wodurch beide Diagramme die Farbe Blau erhalten.
Es ist einfach, die Eigenschaftsbindung für die Eigenschaft color
zu aktivieren. Wir fügen ein NOTIFY-Merkmal in die Q_PROPERTY()-Deklaration ein, um anzugeben, dass ein "colorChanged"-Signal ausgegeben wird, sobald sich der Wert ändert.
class PieChart : public QQuickPaintedItem { ... Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL) public: ... signals: void colorChanged(); ... };
Anschließend wird dieses Signal in setColor()
ausgegeben:
void PieChart::setColor(const QColor &color) { if (color != m_color) { m_color = color; update(); // repaint with the new color emit colorChanged(); } }
Es ist wichtig, dass setColor()
überprüft, ob sich der Farbwert tatsächlich geändert hat, bevor es colorChanged()
sendet. Dadurch wird sichergestellt, dass das Signal nicht unnötig ausgegeben wird und Schleifen vermieden werden, wenn andere Typen auf die Wertänderung reagieren.
Die Verwendung von Bindungen ist ein wesentlicher Bestandteil von QML. Sie sollten immer NOTIFY-Signale für Eigenschaften hinzufügen, wenn diese implementiert werden können, so dass Ihre Eigenschaften in Bindungen verwendet werden können. Eigenschaften, die nicht gebunden werden können, lassen sich nicht automatisch aktualisieren und können in QML nicht so flexibel verwendet werden. Da Bindungen bei der Verwendung von QML so häufig aufgerufen werden und man sich auf sie verlässt, können Benutzer Ihrer benutzerdefinierten QML-Typen ein unerwartetes Verhalten feststellen, wenn Bindungen nicht implementiert sind.
Kapitel 4: Verwendung von benutzerdefinierten Eigenschaftstypen
extending-qml/chapter4-customPropertyTypes
Der Typ PieChart
hat derzeit eine Eigenschaft vom Typ String und eine Eigenschaft vom Typ Color. Er könnte viele andere Arten von Eigenschaften haben. Zum Beispiel könnte er eine Eigenschaft vom Typ int haben, um einen Bezeichner für jedes Diagramm zu speichern:
// 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 }
Abgesehen von int
könnten wir verschiedene andere Eigenschaftstypen verwenden. Viele der Qt-Datentypen wie QColor, QSize und QRect werden automatisch von QML unterstützt. (Eine vollständige Liste finden Sie in der Dokumentation Datentypkonvertierung zwischen QML und C++ ).
Wenn wir eine Eigenschaft erstellen wollen, deren Typ von QML nicht standardmäßig unterstützt wird, müssen wir den Typ bei der QML-Engine registrieren.
Ersetzen wir zum Beispiel die Verwendung von property
durch einen Typ namens "PieSlice", der eine color
Eigenschaft hat. Anstatt eine Farbe zuzuweisen, weisen wir einen PieSlice
Wert zu, der selbst eine color
enthält:
import Charts import QtQuick 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) }
Wie PieChart
erbt auch dieser neue Typ PieSlice
von QQuickPaintedItem und deklariert seine Eigenschaften mit Q_PROPERTY():
class PieSlice : public QQuickPaintedItem { Q_OBJECT Q_PROPERTY(QColor color READ color WRITE setColor FINAL) QML_ELEMENT public: PieSlice(QQuickItem *parent = nullptr); QColor color() const; void setColor(const QColor &color); void paint(QPainter *painter) override; private: QColor m_color; };
Um sie in PieChart
zu verwenden, ändern wir die Eigenschaftsdeklaration color
und die zugehörigen Methodensignaturen:
class PieChart : public QQuickItem { Q_OBJECT Q_PROPERTY(PieSlice* pieSlice READ pieSlice WRITE setPieSlice FINAL) ... public: ... PieSlice *pieSlice() const; void setPieSlice(PieSlice *pieSlice); ... };
Bei der Implementierung von setPieSlice()
muss eine Sache beachtet werden. PieSlice
ist ein visuelles Element, daher muss es mit QQuickItem::setParentItem() als untergeordnetes Element von PieChart
festgelegt werden, damit PieChart
weiß, dass dieses untergeordnete Element gezeichnet werden soll, wenn sein Inhalt gezeichnet wird:
void PieChart::setPieSlice(PieSlice *pieSlice) { m_pieSlice = pieSlice; pieSlice->setParentItem(this); }
Wie der Typ PieChart
muss auch der Typ PieSlice
mit QML_ELEMENT in QML exponiert werden.
class PieSlice : public QQuickPaintedItem { Q_OBJECT Q_PROPERTY(QColor color READ color WRITE setColor FINAL) QML_ELEMENT public: PieSlice(QQuickItem *parent = nullptr); QColor color() const; void setColor(const QColor &color); void paint(QPainter *painter) override; private: QColor m_color; }; ...
Wie bei PieChart
fügen wir den Typ-Namensraum "Charts", Version 1.0, zu unserer Build-Datei hinzu:
Mit qmake:
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
CMake verwenden:
... qt_add_executable(chapter4-customPropertyTypes main.cpp piechart.cpp piechart.h pieslice.cpp pieslice.h ) qt_add_qml_module(chapter4-customPropertyTypes URI Charts QML_FILES App.qml DEPENDENCIES QtQuick ) ...
Kapitel 5: Listeneigenschaftstypen verwenden
extending-qml/chapter5-listproperties
Momentan kann ein PieChart
nur eine PieSlice
haben. Idealerweise hätte ein Diagramm mehrere Slices, mit unterschiedlichen Farben und Größen. Um dies zu erreichen, könnten wir eine slices
Eigenschaft haben, die eine Liste von PieSlice
Elementen akzeptiert:
import Charts import QtQuick 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 } ] } }
Dazu ersetzen wir die Eigenschaft pieSlice
in PieChart
durch eine Eigenschaft slices
, die als Typ QQmlListProperty deklariert ist. Die Klasse QQmlListProperty ermöglicht die Erstellung von Listeneigenschaften in QML-Erweiterungen. Wir ersetzen die Funktion pieSlice()
durch eine Funktion slices()
, die eine Liste von Slices zurückgibt, und fügen eine interne Funktion append_slice()
hinzu (siehe unten). Wir verwenden auch QList, um die interne Liste der Slices als m_slices
zu speichern:
class PieChart : public QQuickItem { Q_OBJECT Q_PROPERTY(QQmlListProperty<PieSlice> slices READ slices FINAL) ... public: ... QQmlListProperty<PieSlice> slices(); private: static void append_slice(QQmlListProperty<PieSlice> *list, PieSlice *slice); QString m_name; QList<PieSlice *> m_slices; };
Obwohl die Eigenschaft slices
keine zugehörige Funktion WRITE
hat, ist sie aufgrund der Funktionsweise von QQmlListProperty dennoch änderbar. In der PieChart
-Implementierung implementieren wir PieChart::slices()
, um einen QQmlListProperty -Wert zurückzugeben und anzugeben, dass die interne PieChart::append_slice()
-Funktion immer dann aufgerufen werden soll, wenn eine Anfrage von QML zum Hinzufügen von Elementen zu der Liste gestellt wird:
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); } }
Die Funktion append_slice()
setzt einfach das übergeordnete Element wie zuvor und fügt das neue Element der Liste m_slices
hinzu. Wie Sie sehen, wird die Append-Funktion für QQmlListProperty mit zwei Argumenten aufgerufen: der Listeneigenschaft und dem Element, das angefügt werden soll.
Die Klasse PieSlice
wurde außerdem so geändert, dass sie die Eigenschaften fromAngle
und angleSpan
enthält und das Slice entsprechend diesen Werten zeichnet. Dies ist eine einfache Änderung, wenn Sie die vorherigen Seiten dieses Tutorials gelesen haben.
Kapitel 6: Ein Erweiterungs-Plugin schreiben
extending-qml/chapter6-plugins
Derzeit werden die Typen PieChart
und PieSlice
von App.qml
verwendet, das mit Hilfe von QQuickView in einer C++-Anwendung angezeigt wird. Eine alternative Möglichkeit, unsere QML-Erweiterung zu verwenden, besteht darin, eine Plugin-Bibliothek zu erstellen, um sie der QML-Engine als neues QML-Importmodul zur Verfügung zu stellen. Auf diese Weise können die Typen PieChart
und PieSlice
in einem Typennamensraum registriert werden, der von jeder QML-Anwendung importiert werden kann, anstatt diese Typen auf die Verwendung durch eine einzige Anwendung zu beschränken.
Die Schritte zur Erstellung eines Plugins sind in Erstellen von C++-Plugins für QML beschrieben. Zunächst erstellen wir eine Plugin-Klasse namens ChartsPlugin
. Sie ist eine Unterklasse von QQmlEngineExtensionPlugin und verwendet das Makro Q_PLUGIN_METADATA(), um das Plugin beim Qt-Metaobjektsystem zu registrieren.
Hier ist die ChartsPlugin
Definition in chartsplugin.h
:
#include <QQmlEngineExtensionPlugin> class ChartsPlugin : public QQmlEngineExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid) };
Dann konfigurieren wir die Build-Datei, um das Projekt als Plugin-Bibliothek zu definieren.
Verwendung von qmake:
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
CMake verwenden:
# Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause qt6_policy(SET QTP0001 NEW) qt6_add_qml_module(chartsplugin URI "Charts" PLUGIN_TARGET chartsplugin DEPENDENCIES QtQuick ) target_sources(chartsplugin PRIVATE piechart.cpp piechart.h pieslice.cpp pieslice.h ) target_link_libraries(chartsplugin PRIVATE Qt6::Core Qt6::Gui Qt6::Qml Qt6::Quick ) install(TARGETS chartsplugin RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/Charts" LIBRARY DESTINATION "${CMAKE_INSTALL_BINDIR}/Charts" ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir DESTINATION "${CMAKE_INSTALL_BINDIR}/Charts" )
Bei der Erstellung dieses Beispiels unter Windows oder Linux wird das Verzeichnis Charts
auf der gleichen Ebene wie die Anwendung liegen, die unser neues Importmodul verwendet. Auf diese Weise wird die QML-Engine unser Modul finden, da der Standardsuchpfad für QML-Importe das Verzeichnis der ausführbaren Anwendung enthält. Unter macOS wird die Plugin-Binärdatei nach Contents/PlugIns
in das Anwendungsbündel kopiert. Mit qmake wird dieser Pfad in chapter6-plugins/app.pro
gesetzt:
macos:!qtConfig(static) { charts.files = $$OUT_PWD/Charts charts.path = Contents/PlugIns QMAKE_BUNDLE_DATA += charts }
Um dies zu berücksichtigen, müssen wir diesen Ort auch als QML-Importpfad in main.cpp
hinzufügen:
QQuickView view; #ifdef Q_OS_MACOS view.engine()->addImportPath(app.applicationDirPath() + "/../PlugIns"); #endif ...
Die Definition von benutzerdefinierten Importpfaden ist auch dann nützlich, wenn mehrere Anwendungen dieselben QML-Importe verwenden.
Die Datei .pro
enthält außerdem einen zusätzlichen Trick, um sicherzustellen, dass die Moduldefinitionsdatei qmldir immer an denselben Ort wie die Plugin-Binärdatei kopiert wird.
Die Datei qmldir
deklariert den Modulnamen und das Plugin, das durch das Modul verfügbar gemacht wird:
module Charts optional plugin chartsplugin typeinfo plugins.qmltypes depends QtQuick prefer :/qt/qml/Charts/
Jetzt haben wir ein QML-Modul, das in jede Anwendung importiert werden kann, vorausgesetzt, die QML-Engine weiß, wo es zu finden ist. Das Beispiel enthält eine ausführbare Datei, die App.qml
lädt, die die Anweisung import Charts 1.0
verwendet. Alternativ können Sie die QML-Datei auch mit dem qml-Tool laden, indem Sie den Importpfad auf das aktuelle Verzeichnis setzen, damit die Datei qmldir
gefunden wird:
qml -I . App.qml
Das Modul "Charts" wird von der QML-Engine geladen, und die von diesem Modul bereitgestellten Typen stehen in jedem QML-Dokument, das es importiert, zur Verfügung.
Kapitel 7: Zusammenfassung
In diesem Lernprogramm haben wir die grundlegenden Schritte zur Erstellung einer QML-Erweiterung gezeigt:
- Definieren Sie neue QML-Typen, indem Sie QObject subclassing und sie mit QML_ELEMENT oder QML_NAMED_ELEMENT registrieren ()
- Hinzufügen von aufrufbaren Methoden mit Q_INVOKABLE oder Qt-Slots und Verbindung zu Qt-Signalen mit einer
onSignal
Syntax - Hinzufügen von Eigenschaftsbindungen durch Definition von NOTIFY-Signalen
- Definieren Sie eigene Eigenschaftstypen, wenn die eingebauten Typen nicht ausreichen
- Definieren Sie Listeneigentumstypen mit QQmlListProperty
- Erstellen Sie eine Plugin-Bibliothek, indem Sie ein Qt-Plugin definieren und eine qmldir-Datei schreiben.
Die Übersichtsdokumentation zur QML- und C++-Integration zeigt weitere nützliche Funktionen, die zu QML-Erweiterungen hinzugefügt werden können. Zum Beispiel könnten wir Standardeigenschaften verwenden, um das Hinzufügen von Slices zu ermöglichen, ohne die Eigenschaft slices
zu verwenden:
PieChart { PieSlice { ... } PieSlice { ... } PieSlice { ... } }
Oder wir können Slices von Zeit zu Zeit zufällig hinzufügen und entfernen, indem wir Quellen für Eigenschaftswerte verwenden:
PieChart { PieSliceRandomizer on slices {} }
Hinweis: Um mehr über QML-Erweiterungen und -Funktionen zu erfahren, folgen Sie dem Tutorial Writing advanced QML Extensions with C++.
© 2025 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.