Q_ENUMS are "undefined" in QML? - c++

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).

Related

Enum in Qt property

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.

seeing undefined reference to `vtable for CollidingMice'

I'm modifying a Qt example 'collidingmice' which comes with the Qt code.
In the original source, the QApplication contains QView and QScene, but I made a class CollidingMice containing the QView and QScene to kill the view and scene using keyboard input. I want to send the keyboard input to CollidingMice class.
I read 4 or 5 questions in stack overflow about 'undefined reference to vtable for..' but could not find the case that fits me. I checked that
1. there is no virtual function in the parent classes that is not defined.
2. I tried adding definition of destructor ~CollidingMices() {}
3. and I'm 99% sure there is no undefined member function in the CollidingMice code below.
#include "mouse.h"
#include <QtGui>
#include <math.h>
static const int MouseCount = 7;
class CollidingMice : public QMainWindow
{
Q_OBJECT
private:
QGraphicsView *view;
QGraphicsScene scene;
QTimer *timer;
public:
CollidingMice(QWidget *parent = 0): QMainWindow(parent) {
scene.setSceneRect(-300, -300, 600, 600);
scene.setItemIndexMethod(QGraphicsScene::NoIndex);
for (int i = 0; i < MouseCount; ++i) {
Mouse *mouse = new Mouse;
mouse->setPos(::sin((i * 6.28) / MouseCount) * 200,
::cos((i * 6.28) / MouseCount) * 200);
scene.addItem(mouse);
}
view = new QGraphicsView(this);
view->setRenderHint(QPainter::Antialiasing);
view->setBackgroundBrush(QPixmap(":/images/cheese.jpg"));
view->setCacheMode(QGraphicsView::CacheBackground);
view->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
view->setDragMode(QGraphicsView::ScrollHandDrag);
view->setWindowTitle(QT_TRANSLATE_NOOP(QGraphicsView, "Colliding Mice"));
#if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5) || defined(Q_WS_SIMULATOR)
view->showMaximized();
#else
view->resize(600, 450);
view->move(30,30);
view->show();
#endif
timer = new QTimer;
QObject::connect(timer, SIGNAL(timeout()), &scene, SLOT(advance()));
timer->start(1000 / 33);
}
private:
void keyPressEvent(QKeyEvent *event);
};
void CollidingMice::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_q) {
close();
}
}
int collidingmice_main(int argc, char **argv)
{
QApplication app(argc, argv);
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
CollidingMice w;
return app.exec();
}
ADD and EDIT : After deleting the QOBJECT above as svlasov told me to, and after fixing the constructor as below (see the setScene..my colleage suggested me.)
view = new QGraphicsView(this);
view->resize(600,500);
view->setScene(&scene);
view->setRenderHint(QPainter::Antialiasing);
I could compile it and execute it.
If you use Q_OBJECT in class definition you have to extract the class into separate header file.
If you don't declare signals and slots in CollidingMice class, just remove Q_OBJECT and it will compile.
UPDATE
As #KubaOber commented, you can simply include to the end of your file.cpp file:
#include "file.moc"
and qmake will do all the job.
It seems your class is declared in .cpp rather than .h
Qt MOC doesn't like it. You may add #include "mysrcfile_moc.cpp" at the end of the file or move the class declaration to mysrcfile.h Don't forget to clean & rebuild after that.
There are 3 issues with your code...
Qt parses the class header and constructs underlying functions related to QObject hierarchy, including symbols for export. This is a rudimentary parser and needs an explicit header file - both for ease of parsing and for symbol export. Create a header - trust me, it's 5 seconds of work to create a file, cut-paste the class declaration and include it back... and saves a lot of time troubleshooting Qt compile issues.
the scene has a scene rectangle, but the view is a regular QWidget - which means the Window should use a layout class and include it in like other QWidgets. Failing this, the view will be sized to something like QSize(1,1) and located at pos(0, 0) by default. This is why you can't see it.
For the QGraphicsScene, you're looking for the slot update() and not advance()

Qt and dead keys in a custom widget

Issue
I'm unable to make dead keys work in my Qt program, while on the same system Qt applications (konsole or kmail for instance) are correctly processing them.
How to reproduce
testcase.pro
TEMPLATE = app
TARGET = testcase
INCLUDEPATH += .
QT += core widgets gui
HEADERS += testcase.hpp
SOURCES += testcase.cpp
testcase.hpp
#include <QWidget>
class TestWindow: public QWidget
{
Q_OBJECT
public:
TestWindow(QWidget* parent=0, Qt::WindowFlags flags=0);
void keyPressEvent(QKeyEvent* event);
};
testcase.cpp
#include <QApplication>
#include <QDebug>
#include <QWidget>
#include <QKeyEvent>
#include "testcase.hpp"
TestWindow::TestWindow(QWidget* parent, Qt::WindowFlags flags)
: QWidget(parent, flags)
{
setAttribute(Qt::WA_KeyCompression);
}
void TestWindow::keyPressEvent(QKeyEvent* event)
{
qDebug() << event;
}
int main(int argc, char** argv)
{
QApplication app(argc, argv);
TestWindow mainWin;
mainWin.show();
return app.exec();
}
Compile the above program (qmake; make), launch it. Dead keys give for instance:
QKeyEvent(KeyPress, 1001252, 0, ""^"", false, 1)
QKeyEvent(KeyPress, 45, 0, ""e"", false, 1)
I was expecting
QKeyEvent(KeyPress, 234, 0, ""ê"", false, 1)
This would also be acceptable:
QKeyEvent(KeyPress, 1001252, 0, ""^"", false, 1)
QKeyEvent(KeyPress, 234, 0, ""ê"", false, 1)
What I've tried
I'm using a Ubuntu 14.10 system with locale fr_FR.UTF-8
I've tried
with Qt 5.3.0 and Qt 4.8.6 as provided on the system.
unseting XMODIFIERS (the default value is #im=ibus is being reported an issue by some)
changing the locale (again, google find reports were the part after the dot is an issue, I've tried the 4 variants UTF-8, utf-8, UTF8 and utf8)
with and without the setAttribute(Qt::WA_KeyCompression); in the constructor.
None changed my observable behavior.
Searching the web show mainly (only?) system related issues. As written above, I've tried the proposed solution, that doesn't solve my problem and the fact that other Qt applications I've tried are able to process the dead key makes me think I miss something in my code, especially that with a slightly more complex example, I'm able to use dead keys with Qt provided widgets (for instance a QLineEdit).
This is a partial answer, but as nobody answered that could be useful for someone later.
Dead keys are handled by input methods which are also handling the compose key and the way Chinese characters and others may be entered.
The widget must signify that it handles input method:
setAttribute(Qt::WA_InputMethodEnabled, true);
Then it has to overwrite two virtual members:
void inputMethodEvent(QInputMethodEvent*);
QVariant inputMethodQuery(Qt::InputMethodQuery) const;
To handle the dead keys and compose, it seems to be enough to have
void TestWindow::inputMethodEvent(QInputMethodEvent* event)
{
if (!event->commitString().isEmpty()) {
QKeyEvent keyEvent(QEvent::KeyPress, 0, Qt::NoModifier,
event->commitString());
keyPressEvent(&keyEvent);
}
event->accept();
}
QVariant TestWindow::inputMethodQuery(Qt::InputMethodQuery) const
{
return QVariant();
}
but that's were my answer is incomplete:
I'm not sure it is really enough even for that
I'm sure it is not enough for more complex writing system and I lack the prerequisite to understand the documentation I've found.

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.