I try to register my custom CPP Class in QML with Qt5.15.0 and the new macro QML_ELEMENT, but the Module is not found. The Qt Creater help files describes the steps for the QML_ELEMENT. I checked the Qt Manual too, but without happy end (https://doc.qt.io/qt-5/qtqml-cppintegration-definetypes.html).
//testclass.h
#ifndef TESTCLASS_H
#define TESTCLASS_H
#include <QObject>
#include <QtQml>
class testclass : public QObject
{
Q_OBJECT
QML_ELEMENT
public:
explicit testclass(QObject *parent = nullptr);
signals:
};
#endif // TESTCLASS_H
// testclass.cpp
#include "testclass.h"
testclass::testclass(QObject *parent) : QObject(parent)
{
}
The pro file looks like this:
QT += quick
CONFIG += c++11
CONFIG += qmltypes
QML_IMPORT_NAME = com.mycompany.test
QML_IMPORT_MAJOR_VERSION = 1
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Refer to the documentation for the
# deprecated API to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
testclass.cpp
RESOURCES += qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
HEADERS += \
testclass.h
The Metafile gets generated with this content:
[
{
"classes": [
{
"classInfos": [
{
"name": "QML.Element",
"value": "auto"
}
],
"className": "testclass",
"object": true,
"qualifiedClassName": "testclass",
"superClasses": [
{
"access": "public",
"name": "QObject"
}
]
}
],
"inputFile": "testclass.h",
"outputRevision": 67
}
]
And afterwards I try to use this class in my Main.qml, but Qt Creator shows QML Module not found.
// main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import com.mycompany.test 1.0 // QML Module not found
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
}
//Main.cpp
#include "testclass.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
Did I missed something or is it buggy?
** Edit **
I refactored the Classname from testclass to Testclass, but still I get the same error. Of Course I made Clean and rebuild. I'm using Windows 10, MinGw-64Bit and qmake.
//testclass.h
#ifndef TESTCLASS_H
#define TESTCLASS_H
#include <QObject>
#include <QtQml>
class Testclass : public QObject
{
Q_OBJECT
QML_ELEMENT
public:
explicit Testclass(QObject *parent = nullptr);
signals:
};
#endif // TESTCLASS_H
//testclass.cpp
#include "testclass.h"
Testclass::Testclass(QObject *parent) : QObject(parent)
{
}
// Metafile
[
{
"classes": [
{
"classInfos": [
{
"name": "QML.Element",
"value": "auto"
}
],
"className": "Testclass",
"object": true,
"qualifiedClassName": "Testclass",
"superClasses": [
{
"access": "public",
"name": "QObject"
}
]
}
],
"inputFile": "testclass.h",
"outputRevision": 67
}
]
//main.cpp
#include "testclass.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
//main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import com.mycompany.test 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
}
I got the same error with the example code trying to build it with MinGW. But the strange thing is that the same code works flawless after compiling for Android with Clang multi-Abi.
you could try using the older methode of doing this from QT 5.9 https://doc.qt.io/qt-5.9/qtqml-cppintegration-topic.html
you just need to add this in you main.cpp and include your Testclass header
qmlRegisterType<Testclass>("com.mycompany.test", 1, 0, "Testclass")
Although I don't get that error if I get the following warning:
Invalid QML element name "testclass"; type names must begin with an uppercase letter
And since when using QML_ELEMENT the name of the class is taking as the name of the item and the QML rules the names of the items must start with capital letters so it causes an error. The solution is to change the class name from testclass to Testclass, then recompile (it may be necessary to remove the build folder).
Related
I am new to Qt and learning few of the modules in Qt-Qml from youtube and Qt documentation. I want to create cpp plugin which is to be consumed by the qml application.
For this, I have created two projects. One is for creating a plugin library and another application is for consuming the plugin.
I am able to create the plugin library successfully. But, not able to consume it by the test application.
The test application is compiled successfully but throwing below error during run time.
QQmlApplicationEngine failed to load component
qrc:/main.qml:3:1: module "MyPlugin" is not installed
I have read the Qt documentation and googled a lot, but could not able to resolve it.
Qt : 5.15.7
Qt Creator: 5.0.3
OS: Ubuntu 18.04
Project 1: Creating Plugin library
CreatePlugin.pro
TEMPLATE = lib
TARGET = CreatePlugin
QT += qml quick
CONFIG += plugin c++11
DESTDIR = ../pluginFiles
TARGET = $$qtLibraryTarget($$TARGET)
uri = MyPlugin
# Input
SOURCES += \
createplugin_plugin.cpp \
myplugin.cpp
HEADERS += \
createplugin_plugin.h \
myplugin.h
DISTFILES = qmldir
!equals(_PRO_FILE_PWD_, $$OUT_PWD) {
copy_qmldir.target = $$OUT_PWD/qmldir
copy_qmldir.depends = $$_PRO_FILE_PWD_/qmldir
copy_qmldir.commands = $(COPY_FILE) "$$replace(copy_qmldir.depends, /, $$QMAKE_DIR_SEP)"
"$$replace(copy_qmldir.target, /, $$QMAKE_DIR_SEP)"
QMAKE_EXTRA_TARGETS += copy_qmldir
PRE_TARGETDEPS += $$copy_qmldir.target
}
qmldir.files = qmldir
unix {
installPath = $$[QT_INSTALL_QML]/$$replace(uri, \., /)
qmldir.path = $$installPath
target.path = $$installPath
INSTALLS += target qmldir
}
# Copy the qmldir file to the same folder as the plugin binary
cpqmldir.files = qmldir
cpqmldir.path = $$DESTDIR
COPIES += cpqmldir
createplugin_plugin.h
#ifndef CREATEPLUGIN_PLUGIN_H
#define CREATEPLUGIN_PLUGIN_H
#include <QQmlExtensionPlugin>
class CreatePluginPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
void registerTypes(const char *uri) override;
};
#endif // CREATEPLUGIN_PLUGIN_H
createplugin_plugin.cpp
#include "createplugin_plugin.h"
#include "myplugin.h"
#include <qqml.h>
void CreatePluginPlugin::registerTypes(const char *uri)
{
// #uri MyPlugin
qmlRegisterType<MyPlugin>(uri, 1, 0, "MyPlugin");
}
myplugin.h
#ifndef MYPLUGIN_H
#define MYPLUGIN_H
#include <QObject>
#include <QString>
class MyPlugin : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(MyPlugin)
public:
explicit MyPlugin(QObject *parent = nullptr);
~MyPlugin() override;
public slots:
QString getString();
};
#endif // MYPLUGIN_H
myplugin.cpp
#include "myplugin.h"
MyPlugin::MyPlugin(QObject *parent)
: QObject(parent)
{
// By default, QQuickItem does not draw anything. If you subclass
// QQuickItem to create a visual item, you will need to uncomment the
// following line and re-implement updatePaintNode()
// setFlag(ItemHasContents, true);
}
MyPlugin::~MyPlugin()
{
}
QString MyPlugin::getString()
{
return "This is CPP String";
}
qmldir
module MyPlugin
plugin CreatePlugin
Project 2: TestPlugin
TestPlugin.pro
QT += quick
CONFIG += c++11
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before
Qt 6.0.0
SOURCES += \
main.cpp
RESOURCES += qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import MyPlugin 1.0
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
MyPlugin {
id: mypluginId
}
Component.onCompleted: console.log(mypluginId.getString())
}
I have qtquick frontend and c++ backend. In Qml I have a checkbox with random initial value. I have a signal emitted onCheckedChanged which is never received. I believe, it is because the component is created before connect statements are made. When user interacts, I can catch those events but I miss the initial value. I cannot make the connections sooner because the QMl engine has to first create the components so I can have a reference to them to make the signal slot connection. So how to find out the initial value? Do I have to make a timer which will emit the value few seconds after startup? Is there a better way?
here is minimum example (when the initial value is true, the slot is never triggered)
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
ApplicationWindow {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
signal checkboxStateChanged(enabled: bool)
CheckBox{
checked: Math.random() > 0.5
text: "value"
onCheckedChanged: checkboxStateChanged(checked)
}
}
backend.h
#include <QObject>
#include <QDebug>
class Backend: public QObject{
Q_OBJECT
public:
Backend(){}
public slots:
void logChecked(bool checked){
qDebug()<<checked;
}
};
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QObject>
#include "backend.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
const auto root=engine.rootObjects();
QObject *window = root[0];
Backend b;
QObject::connect(window,SIGNAL(checkboxStateChanged(bool)),
&b,SLOT(logChecked(bool)));
return app.exec();
}
I am not sure about the approach you are trying but a cleaner method would be to bind the QML checked value to the c++ class. Then you can handle everything in your backend class which is a lot easier to debug and maintain. So using your example,
#include <QObject>
#include <QDebug>
class Backend: public QObject{
Q_OBJECT
// Add a Q_PROPERTY to bind in QML
Q_PROPERY(Qt::CheckState checked READ getChecked WRITE setChecked NOTIFY checkedChanged)
signals:
void checkedChanged();
public:
Backend() { m_checked = <random_value>;}
// implement the q_property methods
void setChecked(const Qt::CheckState value) {
if (m_checked != value) {
m_checked = value;
emit checkedChanged();
}
}
Qt::CheckState getChecked() const { return m_checked; }
public slots:
void logChecked(bool checked){
qDebug()<<checked;
}
private:
m_checked;
};
Then in QML:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQml 2.12 // Binding
import Backend 1.0
ApplicationWindow {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
signal checkboxStateChanged(enabled: bool)
CheckBox{
id: qmlCheckBox
checked: backend.checked // C++ to QML binding
text: "value" // you can bind this too
}
// QML to C++ binding
Binding {
target: backend
property: "checked"
value: qmlCheckBox.checked
}
}
In main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QObject>
#include "backend.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
// Register backend class for qml
qmlRegisterType<Backend>("Backend", 1, 0, "Backend");
engine.rootContext()->setContextProperty("backend", new Backend);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
How do you call for example QFile::exists(path) inside a QML file in Qt 5.5?
MyFile.qml
import QtQuick 2.5
// These are some various things I've tried, with the error message they
// give related to the row where I call QFile::exists()
#include <QFile> // Expected token `{'
import QtQml 2.5 // Expected token `;'
import io.qt // Expected token `;'
import io.qt.QFile // Expected token `;'
Item {
id: root
property string imageSource
Image {
id: test
source: fileOrFallback(root.imageSource)
}
function fileOrFallback (source) {
return QFile::exists(source)
? preprocessor.getFilePath(source)
: theme.example + 'placeholder.png'
}
}
I've seen some examples on how to import your custom Qt functions, but how do you call built-in Qt functions in QML?
You cannot directly import a C ++ function, in these cases the approach is to create a QObject that exposes the method through a Q_INVOKABLE:
backend.h
#ifndef BACKEND_H
#define BACKEND_H
#include <QObject>
class Backend : public QObject
{
Q_OBJECT
public:
using QObject::QObject;
Q_INVOKABLE bool exists(const QString &fileName);
};
#endif // BACKEND_H
backend.cpp
#include "backend.h"
#include <QFile>
bool Backend::exists(const QString &fileName){
return QFile::exists(fileName);
}
main.cpp
#include "backend.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
Backend backend;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("backend", &backend);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
*.qml
// ...
function fileOrFallback (source) {
return backend.exists(source)
? preprocessor.getFilePath(source)
: theme.example + 'placeholder.png'
}
// ...
I have a small issue while dynamically changing the language of my application on the fly.
The language can be selected from a Menu in a MenuBar, whenever I switch to another language the changes are immediately visible in the UI, except on the MenuBar. If I do hover the MenuBar or resize the window a bit the MenuBar gets updated.
Here's the problem in action:
I've been trying to find a refresh function on QML types but couldn't find any.
Question
How to ensure a MenuBar gets refreshed after changing language?
Edit
Here's the full source of my application:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "Translator.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
Translator translator(engine);
engine.rootContext()->setContextProperty("translator", &translator);
return app.exec();
}
Translator.h
#ifndef TRANSLATOR_H
#define TRANSLATOR_H
#include <QObject>
#include <QTranslator>
#include <QGuiApplication>
#include <QQmlEngine>
class Translator : public QObject
{
Q_OBJECT
public:
explicit Translator(QQmlEngine &engine, QObject *parent = nullptr);
signals:
void languageChanged();
public slots:
void setLanguage(const QString &language);
private:
QQmlEngine &m_engine;
QTranslator m_trans_fr;
};
#endif // TRANSLATOR_H
Translator.cpp
#include "Translator.h"
Translator::Translator(QQmlEngine &engine, QObject *parent) : QObject(parent),
m_engine(engine)
{
}
void Translator::setLanguage(const QString &language)
{
if (language == QString("fr"))
{
m_trans_fr.load("WipeoutViewer_fr.qm");
qApp->installTranslator(&m_trans_fr);
}
else if (language == QString("en"))
{
qApp->removeTranslator(&m_trans_fr);
}
m_engine.retranslate();
emit languageChanged();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 1.6
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Wipeout viewer")
menuBar: MenuBar {
Menu {
title: qsTr("&File")
MenuItem {
text: qsTr("&Quit")
onTriggered: Qt.quit()
}
}
Menu {
title: qsTr("&Language")
MenuItem {
text: qsTr("&English")
onTriggered: translator.setLanguage("en")
}
MenuItem {
text: qsTr("&French")
onTriggered: translator.setLanguage("fr")
}
}
Menu {
title: qsTr("&Help")
MenuItem {
text: qsTr("&About")
}
}
}
}
I've taken all care to disable the ANGLE layer in my Qt app, but apparently it's not happening. When I run the app in the CodeXL debugger, the event log contains lines like:
DLL Loaded: C:\Windows\SysWOW64\d3d11.dll
So it's loading Direct3D, which in Qt only happens via ANGLE I think. Also hitting the "Break" button in CodeXL does nothing, which to me means that no real OpenGL calls are happening, they're getting translated to D3D only.
The event log also says this:
Debug String: Failed to load opengl32.dll (The specified module could not be found.)
Why might that happen, how can I fix it?
The reason I want to disable ANGLE is because otherwise I can't debug with CodeXL (it doesn't support D3D debugging).
My system:
Windows 10
First GPU: Intel HD Graphics 5500
Second GPU: AMD Radeon R5 M330 (I think this is the one my app uses)
My code:
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickFramebufferObject>
#include <QOpenGLShaderProgram>
#include <QOpenGLFramebufferObject>
#include <QQuickWindow>
class MyItem : public QQuickFramebufferObject {
Q_OBJECT
public:
Renderer* createRenderer() const;
};
class MyItemRenderer : public QQuickFramebufferObject::Renderer {
public:
void render() {
update();
}
QOpenGLFramebufferObject* createFramebufferObject(const QSize &size) {
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
return new QOpenGLFramebufferObject(size, format);
}
};
QQuickFramebufferObject::Renderer* MyItem::createRenderer() const {
return new MyItemRenderer();
}
int main(int argc, char **argv) {
qputenv("QT_OPENGL_BUGLIST", "Z:/disable_angle.txt");
QGuiApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
QGuiApplication app(argc, argv);
qmlRegisterType<MyItem>("MyItem", 1, 0, "MyItem");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.0
import MyItem 1.0
import QtQuick.Window 2.2
Window {
visible: true
width: 400
height: 400
MyItem {
anchors.fill: parent
}
}
Z:/disable_angle.txt:
{
"entries": [
{
"id": 1,
"description": "Disable angle",
"os": {
"type": "win"
},
"features": [
"disable_angle"
]
}
]
}
Set QT_LOGGING_RULES environment variable to 'qt.qpa.gl=true'. Thus, you will see some additional debug output that will help to understand what exactly Qt chooses (opengl/angle/software).
Try changing Z:/disable_angle.txt to Z:\disable_angle.txt
Note the backslash :) It's Windows, so this may be the culprit.
Apart from disable_angle (your config seems correct to me), try also setting disable_d3d11 and disable_d3d9.
"disable_angle", "disable_d3d11", "disable_d3d9"
If you want to completely disable Angle, you should set the application attribute Qt::AA_UseDesktopOpenGL. If this is not enough, linking against OpenGL32.lib might help.