Return type constant properties
-
Hello,
I have the following class, that I am exposing to QML.class RecipeBackEnd : public QObject { Q_OBJECT Q_PROPERTY(QStringListModel recipes READ recipes CONSTANT) QML_ELEMENT public: explicit RecipeBackEnd(QObject *parent = nullptr); const QStringListModel *recipes() const { return &m_recipeList; } private: QStringListModel m_recipeList; };
The class itself is registered correctly, but I cannot access it's property.
I get the following QML error:
QMetaProperty::read: Unable to handle unregistered datatype 'QStringListModel' for property 'RecipeBackEnd::recipes'I also tried setting the property type to a const QStringListModel *:
Q_PROPERTY(const QStringListModel * recipes READ recipes CONSTANT)
but it results in the same error for a const QStringListModel *.
I want it to return a QStringListModel that I can show in a QML ListView. I am not allowed (and I don't want to) make a copy of the QStringListModel. The model itself never changes runtime, so I made it a CONSTANT property with only a READ method.
Can anyone tell me what I am doing wrong?
Thanks!
-
I've experimented some more, but still haven't got it working.
I tried making the recipes method invokable by prefixing it with Q_INVOKABLE. But then when calling this method from QML, I get the error that const QStringListModel * is not recognized.
Then I tried setting the property explicitly from within C++, using:
QObject::setProperty("recipes", QVariant::fromValue(m_recipeList)
but still getting the property as undefined.
I assume my problem was that I was instantiating the QStringListModel from c++ and then try to return it to QML using a property. I had the feeling that this is the wrong approach.
So, I tried creating a class RecipeList derived from QStringListModel and register that class as QML type:
class RecipeList : public QStringListModel { Q_OBJECT QML_ELEMENT public: explicit RecipeList(QObject *parent = nullptr); }; //cpp implementation RecipeList::RecipeList(QObject *parent) : QStringListModel(parent) { setStringList(QStringList() << "item1" << "item2"); }
Then created the ListView in QML and assigned my registered class as model, like this:
ListView { anchors.fill: parent model: RecipeList {} delegate: Rectangle { height: 50 width: 100 Text { text: modelData } } }
I see no QML errors anymore, but unfortunately the list still remains empty.
-
Hello,
I have the following class, that I am exposing to QML.class RecipeBackEnd : public QObject { Q_OBJECT Q_PROPERTY(QStringListModel recipes READ recipes CONSTANT) QML_ELEMENT public: explicit RecipeBackEnd(QObject *parent = nullptr); const QStringListModel *recipes() const { return &m_recipeList; } private: QStringListModel m_recipeList; };
The class itself is registered correctly, but I cannot access it's property.
I get the following QML error:
QMetaProperty::read: Unable to handle unregistered datatype 'QStringListModel' for property 'RecipeBackEnd::recipes'I also tried setting the property type to a const QStringListModel *:
Q_PROPERTY(const QStringListModel * recipes READ recipes CONSTANT)
but it results in the same error for a const QStringListModel *.
I want it to return a QStringListModel that I can show in a QML ListView. I am not allowed (and I don't want to) make a copy of the QStringListModel. The model itself never changes runtime, so I made it a CONSTANT property with only a READ method.
Can anyone tell me what I am doing wrong?
Thanks!
-
Thanks, but this results in a compiler error of the generated moc file:
moc_recipebackend.cpp:89: error: invalid conversion from ‘const QStringListModel*’ to ‘QStringListModel*’ [-fpermissive] moc_recipebackend.cpp: In static member function ‘static void RecipeBackEnd::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)’: moc_recipebackend.cpp:89:73: error: invalid conversion from ‘const QStringListModel*’ to ‘QStringListModel*’ [-fpermissive] case 0: *reinterpret_cast< QStringListModel**>(_v) = _t->recipes(); break; ~~~~~~~~~~~^~
So, it seems that the type of the PROPERTY has to match the return type of the READ member.
I, also tried your second suggestion:
Q_PROPERTY(const QAbstractListModel * recipes READ recipes CONSTANT)
But, this still results in a runtime QML error:
QMetaProperty::read: Unable to handle unregistered datatype 'const QAbstractListModel' for property 'RecipeBackEnd::recipes'*
-
I've experimented some more, but still haven't got it working.
I tried making the recipes method invokable by prefixing it with Q_INVOKABLE. But then when calling this method from QML, I get the error that const QStringListModel * is not recognized.
Then I tried setting the property explicitly from within C++, using:
QObject::setProperty("recipes", QVariant::fromValue(m_recipeList)
but still getting the property as undefined.
I assume my problem was that I was instantiating the QStringListModel from c++ and then try to return it to QML using a property. I had the feeling that this is the wrong approach.
So, I tried creating a class RecipeList derived from QStringListModel and register that class as QML type:
class RecipeList : public QStringListModel { Q_OBJECT QML_ELEMENT public: explicit RecipeList(QObject *parent = nullptr); }; //cpp implementation RecipeList::RecipeList(QObject *parent) : QStringListModel(parent) { setStringList(QStringList() << "item1" << "item2"); }
Then created the ListView in QML and assigned my registered class as model, like this:
ListView { anchors.fill: parent model: RecipeList {} delegate: Rectangle { height: 50 width: 100 Text { text: modelData } } }
I see no QML errors anymore, but unfortunately the list still remains empty.
-
I've experimented some more, but still haven't got it working.
I tried making the recipes method invokable by prefixing it with Q_INVOKABLE. But then when calling this method from QML, I get the error that const QStringListModel * is not recognized.
Then I tried setting the property explicitly from within C++, using:
QObject::setProperty("recipes", QVariant::fromValue(m_recipeList)
but still getting the property as undefined.
I assume my problem was that I was instantiating the QStringListModel from c++ and then try to return it to QML using a property. I had the feeling that this is the wrong approach.
So, I tried creating a class RecipeList derived from QStringListModel and register that class as QML type:
class RecipeList : public QStringListModel { Q_OBJECT QML_ELEMENT public: explicit RecipeList(QObject *parent = nullptr); }; //cpp implementation RecipeList::RecipeList(QObject *parent) : QStringListModel(parent) { setStringList(QStringList() << "item1" << "item2"); }
Then created the ListView in QML and assigned my registered class as model, like this:
ListView { anchors.fill: parent model: RecipeList {} delegate: Rectangle { height: 50 width: 100 Text { text: modelData } } }
I see no QML errors anymore, but unfortunately the list still remains empty.
@michjans The role of a QStringListModel is "display" so you should not use modelData.
On the other hand, I have used the option indicated in my previous comment and it works correctly for me (I recommend deleting the build folder)
*.pro
QT += quick CONFIG += c++11 SOURCES += \ main.cpp \ recipebackend.cpp HEADERS += recipebackend.h RESOURCES += qml.qrc CONFIG += qmltypes QML_IMPORT_NAME = Recipe QML_IMPORT_MAJOR_VERSION = 1
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(); }
recipebackend.h
#ifndef RECIPEBACKEND_H #define RECIPEBACKEND_H #include <QObject> #include <qqml.h> #include <QStringListModel> class RecipeBackEnd : public QObject { Q_OBJECT Q_PROPERTY(QStringListModel* recipes READ recipes CONSTANT) QML_ELEMENT public: explicit RecipeBackEnd(QObject *parent = nullptr); QStringListModel *recipes(); private: QStringListModel m_recipeList; }; #endif // RECIPEBACKEND_H
recipebackend.cpp
#include "recipebackend.h" RecipeBackEnd::RecipeBackEnd(QObject *parent) : QObject(parent) { m_recipeList.setStringList(QStringList() << "item1" << "item2"); } QStringListModel *RecipeBackEnd::recipes() { return &m_recipeList; }
main.qml
import QtQuick 2.12 import QtQuick.Window 2.12 import Recipe 1.0 Window { width: 640 height: 480 visible: true title: qsTr("Hello World") RecipeBackEnd{ id: backend } ListView { anchors.fill: parent model: backend.recipes delegate: Rectangle { height: 50 width: 100 Text { text: model.display } } } }
qml.qrc
<RCC> <qresource prefix="/"> <file>main.qml</file> </qresource> </RCC>
Output:
-
@michjans The role of a QStringListModel is "display" so you should not use modelData.
On the other hand, I have used the option indicated in my previous comment and it works correctly for me (I recommend deleting the build folder)
*.pro
QT += quick CONFIG += c++11 SOURCES += \ main.cpp \ recipebackend.cpp HEADERS += recipebackend.h RESOURCES += qml.qrc CONFIG += qmltypes QML_IMPORT_NAME = Recipe QML_IMPORT_MAJOR_VERSION = 1
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(); }
recipebackend.h
#ifndef RECIPEBACKEND_H #define RECIPEBACKEND_H #include <QObject> #include <qqml.h> #include <QStringListModel> class RecipeBackEnd : public QObject { Q_OBJECT Q_PROPERTY(QStringListModel* recipes READ recipes CONSTANT) QML_ELEMENT public: explicit RecipeBackEnd(QObject *parent = nullptr); QStringListModel *recipes(); private: QStringListModel m_recipeList; }; #endif // RECIPEBACKEND_H
recipebackend.cpp
#include "recipebackend.h" RecipeBackEnd::RecipeBackEnd(QObject *parent) : QObject(parent) { m_recipeList.setStringList(QStringList() << "item1" << "item2"); } QStringListModel *RecipeBackEnd::recipes() { return &m_recipeList; }
main.qml
import QtQuick 2.12 import QtQuick.Window 2.12 import Recipe 1.0 Window { width: 640 height: 480 visible: true title: qsTr("Hello World") RecipeBackEnd{ id: backend } ListView { anchors.fill: parent model: backend.recipes delegate: Rectangle { height: 50 width: 100 Text { text: model.display } } } }
qml.qrc
<RCC> <qresource prefix="/"> <file>main.qml</file> </qresource> </RCC>
Output:
@eyllanesc I took over your example into my project, deleted my build folder and rebuild my project again. After this it works correctly!
I'm not exactly sure where it went wrong.
Thanks for your help!