Enum in Qt property - c++

I have a code, which works with Qt 5.5 and doesn't with Qt 5.2. Problem is with this enum:
#include <QtCore/QMetaType>
enum Area
{
Area_A,
Area_B,
Area_C
};
Q_DECLARE_METATYPE(Area)
Then I have an object, which exposes this area property:
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(Area area READ area WRITE setArea NOTIFY areaChanged)
public:
explicit MyClass(QObject *parent = 0)
: QObject(parent), m_area(Area_A){}
Area area() const { return m_area; }
void setArea(Area area) {
m_area = area;
emit areaChanged(area);
}
signals:
void areaChanged(Area area);
private:
Area m_area;
};
And main.cpp:
#include <QtGui/QGuiApplication>
#include <QtQml/QQmlApplicationEngine>
#include <QtQml/QQmlContext>
#include <QtQml/QtQml>
#include "MyClass.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<MyClass>("GLib", 1, 0, "MyClass");
MyClass controller;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("controller", &controller);
engine.load("./main.qml");
controller.setArea(Area_B);
return app.exec();
}
It compiles, everything is OK. But when I tried to use area property in qml:
import QtQuick 2.0
import QtQuick.Window 2.0
import GLib 1.0
Window {
visible: true
id: root
property int area: controller.area
Text {
id: name
text: "Test"
x: area * 30
y: area * 30
}
}
I have run-time errors, if Qt 5.2 is used (Linux, x64):
QMetaProperty::read: Unable to handle unregistered datatype 'Area' for
property 'MyClass::area'
file:///home/yech844/devel/test_qml/main.qml:10:24: Unable to assign
[undefined] to int QMetaProperty::read: Unable to handle unregistered
datatype 'Area' for property 'MyClass::area'
file:///home/yech844/devel/test_qml/main.qml:10:24: Unable to assign
[undefined] to int
Is it a bug in Qt? Why I can't use Enum, which is declared out of Class scope?

Qt 5.5 introduced Q_ENUM macro which removed the need to use Q_DECLARE_METATYPE. Read more about it here: https://woboq.com/blog/q_enum.html

I don't know why the code works in Qt 5.5, but I know why it doesn't in Qt 5.2.
Q_DECLARE_METATYPE(...) only makes the type available in static (compiled) context. For example, if you want to use the type in QVariant::fromValue(...). Here, the type you pass to the function can be processed during compile time, and for this, Q_DECLARE_METATYPE is enough.
However, if you want to use a type in a pure runtime context, for example in a QML document, the Qt runtime doesn't know the type declared with Q_DECLARE_METATYPE. For this, a function call (evaluated during runtime) needs to be made, and qRegisterMetatype is the tool for this:
qRegisterMetaType<Area>("Area");
My guess for Qt 5.5 not needing that line is that qmlRegisterType might detect the use of the meta type in the property and automatically calls the above function for you.

Related

Can't invoke Qt c++ method in QML ListView

I have a list of QObjects acting as a qml model for ListView. I can change their properties alright, but can't invoke any slots or Q_INVOKABLE methods. This is the minimal example for my issue (sad that it's still quite large).
Define a really simple class with a property and an invokable method
// DummyObject.h
class DummyElem : public QObject
{
Q_OBJECT
Q_PROPERTY(QString dummy READ getDummy CONSTANT)
public:
explicit DummyElem(QObject *parent = nullptr);
QString getDummy();
Q_INVOKABLE void notifyStuff();
};
Implement trivial methods of this simple class
// DummyObject.cpp
#include "DummyElem.h"
#include <QDebug>
DummyElem::DummyElem(QObject *parent) : QObject(parent) {}
QString DummyElem::getDummy() {return "lorem";}
void DummyElem::notifyStuff() {qDebug() << "ipsum";}
Start a qml app with the list as a root property. Exactly copy-pasted from a tutorial, the one they called q_incokable methods in.
// main.cpp
#include "DummyElem.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QList<QObject*> dataList;
dataList.append(new DummyElem);
dataList.append(new DummyElem);
QQmlApplicationEngine engine;
QQmlContext* context = engine.rootContext();
context->setContextProperty("dataModel", QVariant::fromValue(dataList));
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
Describe a qml layout with a ListView and a delegate that will invoke a c++ method when clicked.
// main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
Window {
visible: true
ListView {
anchors.fill: parent
model: dataModel
delegate: Component {
Text {
text: model.dummy
MouseArea {
anchors.fill: parent
onClicked: {model.notifyStuff()}
}
}
}
}
}
The issue is hard to debug as a c++ class model can't be json-strigified, nor can i get its javascript entries(). The error i get is "undefined is not a function" which is cool too.
I tried registering the Qt type in QML, but that didn't do anything either.
I'm using Qt libraries version 5.9.4, but the "minimal qt version required" box in QtCreator is set to "Qt 5.6".
You need to use modelData. I'm not entirely sure why, most probably because of the QVariantList. You can read a bit more on this page.
Window {
visible: true
ListView {
anchors.fill: parent
model: dataModel
delegate: Component {
Text {
text: modelData.dummy
MouseArea {
anchors.fill: parent
onClicked: modelData.notifyStuff();
}
}
}
}
}
Fun fact: this is the error I get on Qt 5.11.3:
TypeError: Property 'notifyStuff' of object QQmlDMObjectData(0x5585fe567650) is not a function
At least a bit more telling than undefined, but still not fully descriptive I'd say.

Qt - Connecting C++ and QML - ReferenceError: xxx is not defined

I am new to Qt and am currently trying to connect a c++ part of my program with the qml part. The goal of th connection is to pass the currently selected item of a TreeView to qml (possibly via on_treeView_doubleClicked). Since that didnt work I tried all the suggested connections on here for a very basic programm, but I am always receiving the following error:
file::/main.qml:10: ReferenceError: test is not defined
This is the piece of my code regarding the connection:
test.h:
#ifndef TEST_H
#define TEST_H
#include <QObject>
#include <QDebug>
class test : public QObject
{
Q_OBJECT
public:
explicit test(QObject *parent = nullptr);
Q_INVOKABLE void testFunc();
signals:
public slots:
};
#endif // TEST_H
test.cpp:
#include "test.h"
test::test(QObject *parent) : QObject(parent)
{
}
void test::testFunc()
{
qDebug() << "Hello from C++!";
}
mainwindow.cpp:
test testObj;
QQmlApplicationEngine engine;
ui->quickWidget->setSource(QUrl::fromLocalFile(":/main.qml"));
engine.rootContext()->setContextProperty("test", &testObj);
main.qml:
import QtQuick 2.0
import QtQuick.Window 2.3
Item {
Timer{
id: timer
interval: 1000; running: true; repeat:true
onTriggered:{
console.log("ex")
test.testFunc();
}
}
}
I would be really thankful for any help (not only for the simple programm, but possibly for passing the currently selected item of my treeView). I know there a a couple suggestions in other threads, but they dont seem to work for me, so please dont mark this as duplicate.
Thanks in advance,
Lucas
You have 3 main errors:
If you are going to use a .qml from a qresource you should not use QUrl::fromLocalFile() since the qresource is not a local file but virtual.
testObj is a local variable so it will be eliminated as soon as the function where it was created is finished, one solution is to make testObj a member of the class.
And for the last one, the main error, QQuickWidget already has a QQmlEngine, you do not have to create another one.
*.h
Ui::MainWindow *ui;
test testObj;
*.cpp
ui->quickWidget->setSource(QUrl("qrc:/main.qml"));
ui->quickWidget->engine()->rootContext()->setContextProperty("test", &testObj);
My full test can be found at the following link

Q_ENUMS are "undefined" in QML?

Enums are not working out for me.
I have registered them with Q_ENUMS()
I did not forget the Q_OBJECT macro
the type is registered using qmlRegisterType()
the module is imported in QML
In short, everything is "by-the-book" but for some reason I continue getting undefined for each and every enum in QML. Am I missing something?
class UI : public QQuickItem {
Q_OBJECT
Q_ENUMS(ObjectType)
public:
enum ObjectType {
_Root = 0,
_Block
};
...
};
...
qmlRegisterType<UI>("Nodes", 1, 0, "UI");
...
import Nodes 1.0
...
console.log(UI._Root) // undefined
EDIT: Also note that the registered enums are indeed available to use for the metasystem, for some reason they do not work in QML.
UPDATE: I just found this bug: https://bugreports.qt.io/browse/QTBUG-33248
But unlike that bug my root component is a bare UI not a custom element with UI as its root.
Turns out that it is actually possible to use enum values form QML in console.log(), the following code is actually working.
class A : public QObject {
Q_OBJECT
Q_ENUMS(EA)
public:
enum EA {
EA_NULL = 0,
EA_ONE
};
};
class B : public A {
Q_OBJECT
Q_ENUMS(EB)
public:
enum EB {
EA_TWO = 2,
EA_THREE
};
};
#include "main.moc"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<A>("test", 1, 0, "A");
qmlRegisterType<B>("test", 1, 0, "B");
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/enums/main.qml"));
viewer.showExpanded();
return app.exec();
}
and...
Component.onCompleted: {
console.log(A.EA_NULL)
console.log(A.EA_ONE)
console.log(B.EA_NULL)
console.log(B.EA_ONE)
console.log(B.EA_TWO)
console.log(B.EA_THREE)
}
Output is:
0
1
0
1
2
3
So I guess there is another problem besides "you are not using it correctly"... It might have to do with the bug I mentioned above, and the fact that when I instantiate the UI element, I actually instantiate a QML component which is a tree of objects with the UI as the root. While this doesn't prove to be any problem for working with pointers from C++ with the full QML objects, it does seem to mess enums for some reason.
Your problem is not the exposure of the enum, but the fact that you have a leading underscore. Once you remove that, it will work.
You need to start the enum value with uppercase letter. Some rule is necessary to distinguish enums from attached properties from enums. Leading uppercase will refer to enums, and the rest for attached properties (or undefined if not set).
Admittedly, there is also a warning in Qt itself because if you try to assign that enum value to an int or var property, you are currently not getting a warning, and having discussed that issue a little bit with the current maintainer, it seems to be a bug which will be fixed later on.
See the working code below with the correspondigly proposed solution:
main.cpp
#include <QQuickView>
#include <QQuickItem>
#include <QGuiApplication>
#include <QUrl>
class UI : public QQuickItem {
Q_OBJECT
Q_ENUMS(ObjectType)
public:
enum ObjectType {
Root = 0,
_Block
};
};
#include "main.moc"
int main(int argc, char **argv)
{
QGuiApplication guiApplication(argc, argv);
qmlRegisterType<UI>("Nodes", 1, 0, "UI");
QQuickView *view = new QQuickView;
view->setSource(QUrl::fromLocalFile("main.qml"));
view->show();
return guiApplication.exec();
}
main.qml
import Nodes 1.0
import QtQuick 2.0
Rectangle {
id: button
width: 500; height: 500
MouseArea {
anchors.fill: parent
onClicked: console.log(UI.Root)
}
}
main.pro
TEMPLATE = app
TARGET = main
QT += quick
SOURCES += main.cpp
Build and Run
qmake && make && ./main
Output
0
I had the exact same problem, and thanks to these solutions I figured out my problem. However, in case anyone else is dealing with the same mixup, here it is a little clearer.
I was using a class from C++ in QML just like here, so I had something like this in main.cpp
qmlRegisterType<EnumClass>("Enums", 1, 0, "EnumClass");
and in the .qml
import Enums 1.0
EnumClass {
id: ec
}
and tried and tried to access ec.SomeEnum but kept getting undefined even though, the QtCreator autocomplete said ec.SomeEnum should exist.
This simply does not work, and to get this to work I had to use
EnumClass.SomeEnum
instead (just like they do here).

Can't call slot or Q_INVOKABLE from QML in subclass of QQmlPropertyMap

I'm trying to test drive the QQmlPropertyMap class. It seems like it might work well for what I want, if I can subclass it. The documentation here even gives some rudimentary instructions on what to do for subclassing it. Said documentation also indicates that this class derives from QObject.
For what it's worth, I'm using QtCreator 2.6.1 on Qt 5.0.0 with QtQuick 2.0.
My main.qml:
import QtQuick 2.0
Rectangle {
width: 360
height: 360
Text {
text: owner.field
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
owner.testFunc();
}
}
}
My main.cpp:
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include "TestMap.h"
#include <QQmlContext>
int main(int argc, char *argv[])
{
TestMap* map = new TestMap();
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
QQmlContext* ctxt = viewer.rootContext();
ctxt->setContextProperty("owner", map);
viewer.setMainQmlFile(QStringLiteral("qml/TestMap/main.qml"));
viewer.showExpanded();
return app.exec();
}
My TestMap.h
#ifndef TESTMAP_H
#define TESTMAP_H
#include <QObject>
#include <QQmlPropertyMap>
#include <QDebug>
class TestMap: public QQmlPropertyMap // QObject
{
Q_OBJECT
public:
TestMap(QObject* parent = 0): QQmlPropertyMap(this, parent) // QObject(parent)
{
insert("field", "value"); // Comment this out
}
TestMap(const TestMap& value) { }
virtual ~TestMap() {}
public slots:
void testFunc()
{
qDebug() << "Success!";
}
};
Q_DECLARE_METATYPE(TestMap)
#endif
When I run, I get a window saying "value", as I'd expect. But when I click on the window, I get a console output saying
TypeError: Property 'testFunc' of object TestMap(0xaaa0b8) is not a function
I've looked for similar problems, but all the search results are about people that forgot to include the Q_OBJECT macro. It must be something I'm doing wrong in the code, because if I make all the changes indicated in the comments of the TestMap file (and leave the main.cpp and main.qml exactly as is), I get the qDebug message I expect.
I'm not sure whether I'm supposed to Q_DECLARE_METATYPE or not (I think the 2-arg protected constructor is supposed to do it for me), but it doesn't work either way.
For the record, the only things I have to change to get it to work are:
1) Derive from QObject instead of QQmlPropertyMap.
2) Change the constructor to:
TestMap(QObject* parent = 0): QObject(parent) {}
And that's it. Since it works when I don't change anything about the main.cpp, main.qml, or the slot itself, I have to conclude it's nothing wrong with those. Can anyone tell me what I'm doing wrong?
This is now fixed in Qt 5.1.0 and onwards. See the following codereview url for details:
https://codereview.qt-project.org/#change,57418

Calling C++ method from QML (Qt Quick application)

So, I've done some searching and none of the similar questions I've read have had advice that worked.
I'm using Qt Creator (and I'm not too familiar with Qt) so I'm not sure what voodoo it's done in the background. However, I'm using a standard Qt Quick Application project.
Essentially, I want to call a C++ function from QML that returns a string that replaces some text in the layout, periodically.
Here is main.cpp:
#include <QtGui/QApplication>
#include "qmlapplicationviewer.h"
#include <QDeclarativeContext>
class testClass : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE QString gimmeText() {
return QString("new text");
}
};
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QScopedPointer<QApplication> app(createApplication(argc, argv));
QmlApplicationViewer viewer;
viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
viewer.setMainQmlFile(QLatin1String("qml/Picenter2/main.qml"));
testClass t;
viewer.rootContext()->setContextProperty("testOb", &t);
viewer.showFullScreen();
return app->exec();
}
And here is a snippet of the layout (as most of it is obviously irrelevant):
Text {
id: text1
x: 105
y: 156
color: "#ffffff"
text: qsTr("text")
font.pixelSize: 12
Timer {
interval: 1000; running: true; repeat: false
onTriggered: text1.text = testOb.gimmeText();
}
The errors given are:
invalid use of incomplete type 'struct QDeclarativeContext' main.cpp (28)
forward declaration of 'struct QDeclarativeContext' qdeclarativeview.h (60)
EDIT: with QDeclarativeContext included, the above disappear, giving these errors:
(.text.startup+0x3e):-1: error: undefined reference to `vtable for testClass'
(.text.startup+0xcf):-1: error: undefined reference to `vtable for testClass'
(.text.startup+0x12b):-1: error: undefined reference to `vtable for testClass'
:-1: error: collect2: ld returned 1 exit status
I haven't done much C++ programming, so I'm not entirely familiar with what that means. Following advice for essentially identical problems has only given me vtable errors or more incomprehensible things.
What really confuses me is that, looking at the header file, the QmlApplicationViewer is derived from QDeclarativeView, which is exactly what the Qt documentation uses here to do almost exactly what I want. Thanks for any suggestions anyone has.
You have to register your class for using it with QML. You can do this in the main function. You have to import it in the QML code too. Your code should look like this :
main.cpp :
#include <QtGui/QApplication>
#include "qmlapplicationviewer.h"
#include <QDeclarativeContext>
#include <QtDeclarative> // Required for registration
class testClass : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE QString gimmeText() {
return QString("new text");
}
};
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QScopedPointer<QApplication> app(createApplication(argc, argv));
qmlRegisterType<testClass>("MyCustomQMLObjects", 2, 35, "testClassNameInQML");
QmlApplicationViewer viewer;
viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
viewer.setMainQmlFile(QLatin1String("qml/Picenter2/main.qml"));
testClass t;
viewer.rootContext()->setContextProperty("testOb", &t);
viewer.showFullScreen();
return app->exec();
}
QML code :
// ...
import MyCustomQMLObjects 2.35
// ...
property testClassNameInQML testOb
// ...
Text {
id: text1
x: 105
y: 156
color: "#ffffff"
text: qsTr("text")
font.pixelSize: 12
Timer {
interval: 1000; running: true; repeat: false
onTriggered: text1.text = testOb.gimmeText();
}
// ...
I have no experience with qt and I cant see what in the code triggers the error. However when such errors occur, it is because a class (struct QDeclarativeContext) has been forward declared, but is used as if the entire definition is known (access member, declare variable of this type, etc). To fix this you need to include the header which has this type's definition.