qmlRegisterType still usable?
-
Some time ago I started using Qt6 but had to stop and am now picking it pack up with 6.6.1. I have a QML project that has a C++ class that I want to invoke from QML. For testing purposes it just does a qDebug() output and is declared with the Q_INVOKABLE macro. The class exists in a subdirectory of the project so I have
Project - CMakeLists.txt, Main.qml, main.cpp
utilities - CMakeLists.txt, utilities.h, utilities.cppThe project CMakelists.txt has add_subdirectory(utilities) and this to link the result.
target_link_libraries(appAirmailBeacons
PRIVATE utilities_moduleplugin
)In past projects I used qmlRegisterType<T>("name", major, minor, Somename) in main.cpp and this worked. However, reading through Qt Blogs (https://www.qt.io/blog/qml-modules-in-qt-6.2, https://www.qt.io/blog/whats-new-for-qml-modules-in-6.5 and others) and looking at the Qt 6.6 docs I am confused totally!
From what I can get from the Blogs I simply have to define a static library in my subdirectory CMakeLists.txt, use the qt6_add_qml_module(name URI utilities Version 1.0 SOURCES utilities.h utilities.cpp) and then link this library in the main project CMakeLists.txt.
I then should be able to use the Q_IMPORT_QML_PLUGIN(utilitiesPlugin,), import utilities in the QML file and it should work and can can use call the function as utilitities.test(). This doesn't work. I get a test is not defined error.
From past experience I know engine.rootContext()->setContextProperty(blah) can be used as well as qmlRegisterType but based on my reading those seem to be discouraged. The Qt 6.6 examples keep referring to QQuickview and view.set but I'm using QQmlApplicationEngine which was set when I created the project and I understand QQmlApplication Engine replaces QQuickview.
So for Qt6 what is the official method way to have items from C++. I really would like to use the "correct Qt6" way but from my research and reading what I thought I should do does not work. The application builds correctly but tells me when I call utilities.test() that test is undefined.
-
If I simply want to call a C++ function that is QINVOKABLE from QML what do I need. Do I use the Q_IMPORT_QML_PLUGIN and if so how to make it work, and/or engine.rootContext(), and/or qmlRegisterType? From what I can find I should register it.
-
If I want to create an instance of it for use in QML as in mystuff {blah} in a QML file I should use the rootContext from what I can find out.
However, the Blogs seem to indicate I do not need this.
Thank you. Any insight is appreciated.
Code:
Project CMakeLists.txt#============================================================================= # Project setup cmake_minimum_required(VERSION 3.16) project(AirmailBeacons VERSION 0.1 LANGUAGES CXX) set(CMAKE_CXX_STANDARD_REQUIRED ON) #============================================================================= # Various constants. # Set application name for use in building. set(appName "AirmailBeacons") #============================================================================= find_package(Qt6 REQUIRED COMPONENTS Core Qml Quick ) qt_standard_project_setup(REQUIRES 6.5) #============================================================================= # The executable qt_add_executable(appAirmailBeacons main.cpp ) #============================================================================== # Our subdirectories add_subdirectory ( utilities ) #============================================================================= #QML modules qt_add_qml_module(appAirmailBeacons URI AirmailBeacons VERSION 1.0 QML_FILES Main.qml ) #============================================================================= # Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. # If you are developing for iOS or macOS you should consider setting an # explicit, fixed bundle identifier manually though. set_target_properties(appAirmailBeacons PROPERTIES # MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appAirmailBeacons MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} MACOSX_BUNDLE TRUE WIN32_EXECUTABLE TRUE ) #============================================================================== # Libraries target_link_libraries(appAirmailBeacons PRIVATE Qt6::Core PRIVATE Qt6::Quick PRIVATE Qt6::Qml ) #============================================================================== # Our libraries target_link_libraries(appAirmailBeacons PRIVATE utilities_moduleplugin ) #============================================================================= include(GNUInstallDirs) install(TARGETS appAirmailBeacons BUNDLE DESTINATION . LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) #============================================================================= # Set target properties set_target_properties(appAirmailBeacons PROPERTIES OUTPUT_NAME ${appName}) #============================================================================= # Copy the file after it's built.
utilities/CMakeList.txt
# CMakeLists.txt to build import backend. qt_add_library(utilities_module STATIC) qt6_add_qml_module(utilities_module URI utilities VERSION 1.0 SOURCES utilities.h utilities.cpp )
main.cpp
// Qt includes #include <QGuiApplication> #include <QQmlApplicationEngine> #include <QtQml/QQmlExtensionPlugin> // Our includes #include "utilities/utilities.h" // Our Imports Q_IMPORT_QML_PLUGIN(utilitiesPlugin) int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; QObject::connect( &engine, &QQmlApplicationEngine::objectCreationFailed, &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.loadFromModule("AirmailBeacons", "Main"); return app.exec(); }
Main.qml
// QML imports import QtQuick import QtQuick.Controls import QtQml // Our imports import utilities 1.0 ApplicationWindow { width: 640 height: 480 visible: true title: qsTr("Airmail Beacons") // Menu Bar menuBar: MenuBar { Menu { id: fileMenu title: qsTr("&File") MenuItem { text: "&Open" onTriggered: { console.log("Open") //utilities:test() } } MenuSeparator {} MenuItem { text: qsTr("&Quit") onTriggered: { Qt.exit(0) } } } // End File Menu } // End Menubar } // End ApplicationWindow
utilities.h
#ifndef UTILITIES_H #define UTILITIES_H // Qt includes #include <QObject> #include <QQmlEngine> #include <QString> #include <QtQml/qqmlregistration.h> #include <QUrl> #include <QDir> //Our includes #include <QDebug> class Utilities : public QObject { Q_OBJECT QML_ELEMENT public: explicit Utilities(QObject *parent = nullptr); //========================================================================= // Functions QString urltonativepath(const QUrl& urlpath); //----------------------------------------------------------------------------- // Test function Q_INVOKABLE void test(); //========================================================================= // Signals signals: }; // End class Utilities. #endif // UTILITIES_H
utilities.cpp
#include "utilities.h" Utilities::Utilities(QObject *parent) : QObject{parent} { } //============================================================================= // Functions // QString urltonativepath(const Qurl &urlpath) - converts url to native // file path. // // Parameters: // urlpath - the url to convert // // Returns: // native file path as a Qstring. QString Utilities::urltonativepath(const QUrl &urlpath) { return QDir::toNativeSeparators(urlpath.toLocalFile()); } //----------------------------------------------------------------------------- // Test void Utilities::test() { qInfo() << "Called test"; } // End urltonativepath
-
-
Some time ago I started using Qt6 but had to stop and am now picking it pack up with 6.6.1. I have a QML project that has a C++ class that I want to invoke from QML. For testing purposes it just does a qDebug() output and is declared with the Q_INVOKABLE macro. The class exists in a subdirectory of the project so I have
Project - CMakeLists.txt, Main.qml, main.cpp
utilities - CMakeLists.txt, utilities.h, utilities.cppThe project CMakelists.txt has add_subdirectory(utilities) and this to link the result.
target_link_libraries(appAirmailBeacons
PRIVATE utilities_moduleplugin
)In past projects I used qmlRegisterType<T>("name", major, minor, Somename) in main.cpp and this worked. However, reading through Qt Blogs (https://www.qt.io/blog/qml-modules-in-qt-6.2, https://www.qt.io/blog/whats-new-for-qml-modules-in-6.5 and others) and looking at the Qt 6.6 docs I am confused totally!
From what I can get from the Blogs I simply have to define a static library in my subdirectory CMakeLists.txt, use the qt6_add_qml_module(name URI utilities Version 1.0 SOURCES utilities.h utilities.cpp) and then link this library in the main project CMakeLists.txt.
I then should be able to use the Q_IMPORT_QML_PLUGIN(utilitiesPlugin,), import utilities in the QML file and it should work and can can use call the function as utilitities.test(). This doesn't work. I get a test is not defined error.
From past experience I know engine.rootContext()->setContextProperty(blah) can be used as well as qmlRegisterType but based on my reading those seem to be discouraged. The Qt 6.6 examples keep referring to QQuickview and view.set but I'm using QQmlApplicationEngine which was set when I created the project and I understand QQmlApplication Engine replaces QQuickview.
So for Qt6 what is the official method way to have items from C++. I really would like to use the "correct Qt6" way but from my research and reading what I thought I should do does not work. The application builds correctly but tells me when I call utilities.test() that test is undefined.
-
If I simply want to call a C++ function that is QINVOKABLE from QML what do I need. Do I use the Q_IMPORT_QML_PLUGIN and if so how to make it work, and/or engine.rootContext(), and/or qmlRegisterType? From what I can find I should register it.
-
If I want to create an instance of it for use in QML as in mystuff {blah} in a QML file I should use the rootContext from what I can find out.
However, the Blogs seem to indicate I do not need this.
Thank you. Any insight is appreciated.
Code:
Project CMakeLists.txt#============================================================================= # Project setup cmake_minimum_required(VERSION 3.16) project(AirmailBeacons VERSION 0.1 LANGUAGES CXX) set(CMAKE_CXX_STANDARD_REQUIRED ON) #============================================================================= # Various constants. # Set application name for use in building. set(appName "AirmailBeacons") #============================================================================= find_package(Qt6 REQUIRED COMPONENTS Core Qml Quick ) qt_standard_project_setup(REQUIRES 6.5) #============================================================================= # The executable qt_add_executable(appAirmailBeacons main.cpp ) #============================================================================== # Our subdirectories add_subdirectory ( utilities ) #============================================================================= #QML modules qt_add_qml_module(appAirmailBeacons URI AirmailBeacons VERSION 1.0 QML_FILES Main.qml ) #============================================================================= # Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. # If you are developing for iOS or macOS you should consider setting an # explicit, fixed bundle identifier manually though. set_target_properties(appAirmailBeacons PROPERTIES # MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appAirmailBeacons MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} MACOSX_BUNDLE TRUE WIN32_EXECUTABLE TRUE ) #============================================================================== # Libraries target_link_libraries(appAirmailBeacons PRIVATE Qt6::Core PRIVATE Qt6::Quick PRIVATE Qt6::Qml ) #============================================================================== # Our libraries target_link_libraries(appAirmailBeacons PRIVATE utilities_moduleplugin ) #============================================================================= include(GNUInstallDirs) install(TARGETS appAirmailBeacons BUNDLE DESTINATION . LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) #============================================================================= # Set target properties set_target_properties(appAirmailBeacons PROPERTIES OUTPUT_NAME ${appName}) #============================================================================= # Copy the file after it's built.
utilities/CMakeList.txt
# CMakeLists.txt to build import backend. qt_add_library(utilities_module STATIC) qt6_add_qml_module(utilities_module URI utilities VERSION 1.0 SOURCES utilities.h utilities.cpp )
main.cpp
// Qt includes #include <QGuiApplication> #include <QQmlApplicationEngine> #include <QtQml/QQmlExtensionPlugin> // Our includes #include "utilities/utilities.h" // Our Imports Q_IMPORT_QML_PLUGIN(utilitiesPlugin) int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; QObject::connect( &engine, &QQmlApplicationEngine::objectCreationFailed, &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.loadFromModule("AirmailBeacons", "Main"); return app.exec(); }
Main.qml
// QML imports import QtQuick import QtQuick.Controls import QtQml // Our imports import utilities 1.0 ApplicationWindow { width: 640 height: 480 visible: true title: qsTr("Airmail Beacons") // Menu Bar menuBar: MenuBar { Menu { id: fileMenu title: qsTr("&File") MenuItem { text: "&Open" onTriggered: { console.log("Open") //utilities:test() } } MenuSeparator {} MenuItem { text: qsTr("&Quit") onTriggered: { Qt.exit(0) } } } // End File Menu } // End Menubar } // End ApplicationWindow
utilities.h
#ifndef UTILITIES_H #define UTILITIES_H // Qt includes #include <QObject> #include <QQmlEngine> #include <QString> #include <QtQml/qqmlregistration.h> #include <QUrl> #include <QDir> //Our includes #include <QDebug> class Utilities : public QObject { Q_OBJECT QML_ELEMENT public: explicit Utilities(QObject *parent = nullptr); //========================================================================= // Functions QString urltonativepath(const QUrl& urlpath); //----------------------------------------------------------------------------- // Test function Q_INVOKABLE void test(); //========================================================================= // Signals signals: }; // End class Utilities. #endif // UTILITIES_H
utilities.cpp
#include "utilities.h" Utilities::Utilities(QObject *parent) : QObject{parent} { } //============================================================================= // Functions // QString urltonativepath(const Qurl &urlpath) - converts url to native // file path. // // Parameters: // urlpath - the url to convert // // Returns: // native file path as a Qstring. QString Utilities::urltonativepath(const QUrl &urlpath) { return QDir::toNativeSeparators(urlpath.toLocalFile()); } //----------------------------------------------------------------------------- // Test void Utilities::test() { qInfo() << "Called test"; } // End urltonativepath
I found this topic extremely helpful and got a test case to work. However, I'm still working on getting the case where I have C++ classes in subdirectories working.
https://forum.qt.io/topic/145977/registering-an-instantiable-object-type-official-example/5
-
-
This!!!!
Last year I created a larger project using python and qml.
I got confident and built tools with GUIs in widgets and C++. My actual learning project cannot be created from Qt Creator but from QT Design Studio.And since 6 days iI tried to get into how to connect my backend classes with my GUI (I want to load data from files, do data processing, handle user input, reprocess, create and display an svg from data).
I said to myself: " don't waste others people time, read the reference". But the reference is somehow "to easy".
Since I'm not a cmake-Jedi-master i mostly get stuck there. the blogs always look so different it almost feels like doing a microchip project: reference against unexperienced developer.back to topic:
I created an object of my backend on heap. Then i tried to map the object to a QVariantMap and call setInitialProperties from QQmlApplicationEngine. But somehow i cannot do that because I'm failing to map the object (no matching constructor of the map).I hope someone can deliver a general solution.
-
This!!!!
Last year I created a larger project using python and qml.
I got confident and built tools with GUIs in widgets and C++. My actual learning project cannot be created from Qt Creator but from QT Design Studio.And since 6 days iI tried to get into how to connect my backend classes with my GUI (I want to load data from files, do data processing, handle user input, reprocess, create and display an svg from data).
I said to myself: " don't waste others people time, read the reference". But the reference is somehow "to easy".
Since I'm not a cmake-Jedi-master i mostly get stuck there. the blogs always look so different it almost feels like doing a microchip project: reference against unexperienced developer.back to topic:
I created an object of my backend on heap. Then i tried to map the object to a QVariantMap and call setInitialProperties from QQmlApplicationEngine. But somehow i cannot do that because I'm failing to map the object (no matching constructor of the map).I hope someone can deliver a general solution.
-
So what is the answer? I totally lost the plot with the QT academy tutorial, others do the same. I want to do one thing, why tell me about other stuff?
How do you connect C++ and QML? I have come to the conclusion that the QT company want to ditch C++ going forward as this one essential topic is never discussed in any meaningful way. Any information on QML says that it will not talk about C++, well Stroustrop will not exactly be telling you how to do it either as this is a QML thing not a C++ one. QT decided to go down the javascript/custom new language path, well now tell us how to use it in the context of reality. Reality is that we write our backends in C++!!
-
I do not know if QT company wants to ditch C++. I guess it may be unlikely.
QML simply follows the trend just like
Swift in iOS
Flutter in Google
It allows developers to define UI elements and their properties in a descriptive manner, rather than through imperative code. It may be relatively easier to make UI design.Ideally, it is better to set-up one standard which can be applied for any programming language. Coders do not need to spend time to learn all of them, Swift, Flutter and QML, ....
-
So what is the answer? I totally lost the plot with the QT academy tutorial, others do the same. I want to do one thing, why tell me about other stuff?
How do you connect C++ and QML? I have come to the conclusion that the QT company want to ditch C++ going forward as this one essential topic is never discussed in any meaningful way. Any information on QML says that it will not talk about C++, well Stroustrop will not exactly be telling you how to do it either as this is a QML thing not a C++ one. QT decided to go down the javascript/custom new language path, well now tell us how to use it in the context of reality. Reality is that we write our backends in C++!!
@Groundbounce said in qmlRegisterType still usable?:
How do you connect C++ and QML?
Like explained in the documentation: https://doc.qt.io/qt-6/qtqml-cppintegration-overview.html
QtCompany will for sure not ditch C++ as QML is only for UI and some logic in the UI. Everything else is still C++.