Qt5.6 QML, why are dynamic models destroyed after garbage collection? - qt

I have a variable number of components, so i'm trying to give each one its own model. In this example, i just create one, but the idea is the same.
GC() is a bit random, so in the example, i force the gc() after a click to flush out the problem. What happens is that the model is destroyed and becomes null. after that the click method cannot use it.
main.qml:
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.2
import com.example.qml 1.0
ApplicationWindow
{
visible: true
width: 640
height: 480
// builder of dynamic models
ModelFactory { id: maker }
Column
{
anchors.fill: parent
Repeater
{
// create dynamic model
model: maker.makeModel();
delegate: Label
{
id: label
text: model.name
MouseArea
{
anchors.fill: parent
onClicked:
{
// works once until gc()
console.log("clicked on " + model.name)
// wont work anymore. model is destroyed
gc();
}
}
}
}
}
}
c++/mymodel.h:
#include <QAbstractListModel>
#include <QQmlApplicationEngine>
#include <QObject>
#include <QString>
#include <QDebug>
class BoxModel : public QAbstractListModel
{
Q_OBJECT
public:
~BoxModel()
{
// see that it does get destroyed
qDebug() << "~BoxModel()";
}
int rowCount(const QModelIndex& parent = QModelIndex()) const override
{
return 5;
}
QVariant data(const QModelIndex &index, int role) const override
{
int ix = index.row();
if (ix < 1) return "Larry";
if (ix < 2) return "Barry";
if (ix < 3) return "Gary";
if (ix < 4) return "Harry";
return "Sally";
}
QHash<int, QByteArray> roleNames() const override
{
QHash<int, QByteArray> roles;
roles[Qt::UserRole+1] = "name";
return roles;
}
};
class ModelFactory: public QObject
{
Q_OBJECT
public:
Q_INVOKABLE BoxModel* makeModel()
{
return new BoxModel();
}
};
main.cpp just registers the types:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <qqmlcontext.h>
#include <qqml.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
#include "mymodel.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<BoxModel>("com.example.qml", 1, 0, "BoxModel");
qmlRegisterType<ModelFactory>("com.example.qml", 1, 0, "ModelFactory");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
what you see:
Click on any of the names. it will work once and after that they will be undefined because model becomes null.
eg
qml: clicked on Sally
~BoxModel()
qml: clicked on undefined
My question is why is this, when i still have a reference to it?
In the example, onClicked could be changed to label.text rather than model.name to fix, but the real problem is that, in general, the model is accessed by the object at any time, for any data. For example, when the box needs to redraw. randomly the data is gone, depending on GC.
I've tried making c++ manage the life of the dynamic model. this could work if i know when exactly QML has finished with it.
thanks for info and ideas.
running on windows 8.1/qt5.6mingw
EDIT1: files as a gist,
https://gist.github.com/anonymous/86118b67ec804e6149423c14792f312d

As Kuba said, this does indeed seem like a bug. However, you can take another approach and take ownership of the models yourself via QQmlEngine::setObjectOwnership(). Specifically, changing
Q_INVOKABLE BoxModel* makeModel()
{
return new BoxModel();
}
to
Q_INVOKABLE BoxModel* makeModel()
{
BoxModel *model = new BoxModel(this);
QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership);
return model;
}
will fix this (remember to parent the returned model to BoxModel so it gets deleted appropriately). The reason for the behaviour is explained here:
Generally an application doesn't need to set an object's ownership explicitly. QML uses a heuristic to set the default ownership. By default, an object that is created by QML has JavaScriptOwnership. The exception to this are the root objects created by calling QQmlComponent::create() or QQmlComponent::beginCreate(), which have CppOwnership by default. The ownership of these root-level objects is considered to have been transferred to the C++ caller.
Objects not-created by QML have CppOwnership by default. The exception to this are objects returned from C++ method calls; their ownership will be set to JavaScriptOwnership. This applies only to explicit invocations of Q_INVOKABLE methods or slots, but not to property getter invocations.

I just had the same problem with a ComboBox.
As a workaround, you may create your own property to keep a strong reference to it:
Repeater {
property QtObject myModel: maker.makeModel();
model: myModel
// …
}

I know this is an old question, but I've just faced similar issue, and found your question in process of writing mine. See QObject gets destroyed after being put into QML variable for full story, and I'll cite it here.
What I've figured out that if I set the parent of that QObject before I pass it into QML, then it doesn't get deleted. So, I've concluded that passing unparented QObject into QML scope makes that scope become a parent of QObject and call its destructor after scope ends.

Related

SetProperty for loaded QML component without using SetContextProperty

I have a C++ plugin system, where a QQmlComponent is created and a qml file is loaded when the user requests a new plugin instance.
Currently I am using setContextProperty() to tell QML about a QObject that is needed for proper initialization.
mEngine->rootContext()->setContextProperty("controller", QVariant::fromValue(mController));
mComponent = new QQmlComponent(mEngine);
mComponent->loadUrl(QUrl{ "qrc:///MyPlugin.qml" });
The problem is, when instantiating a second plugin, both will use the controller of the second one because "controller" is global in QML.
Repeater {
model: controller.numEntries
Is there a way to set a context property locally (only for the current instance)?
I found solutions using setProperty() or QQmlIncubator and setInitialState(), but they all seem to require an object that was already created from my component. But in my plugin I only define the component, which is loaded in the main application through a Loader item. So, when trying these approaches, I always ended up in setting the value in a copy of the item, but not the one being created in my backend.
How can I get access to a property of the component that is created in QML?
mComponent->findChild<QQuickItem*>("controller");
does not give me any results, even if I defined the property in MyPlugin.qml.
Maybe you can create a QObject based class and have a slot and instead of using property you can call slot to slot create a new property in c++ and return it to QML
ControllerCreator.h
#ifndef CONTROLLERCREATOR_H
#define CONTROLLERCREATOR_H
#include <QObject>
class ControllerCreator : public QObject {
Q_OBJECT
public:
explicit ControllerCreator(QObject *parent = nullptr);
signals:
public slots:
int propertyCreator();
private:
int m_example;
};
#endif // CONTROLLERCREATOR_H
ControllerCreator.cpp
#include "ControllerCreator.h"
ControllerCreator::ControllerCreator(QObject *parent)
: QObject(parent), m_example(0)
{
}
int ControllerCreator::propertyCreator()
{
m_example++;
return m_example;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "ControllerCreator.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
ControllerCreator controllerCreator;
engine.rootContext()->setContextProperty("creator", &controllerCreator);
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Column{
anchors.fill: parent
Text{
text: creator.propertyCreator()
color: "blue"
}
Text{
text: creator.propertyCreator()
color: "red"
}
Text{
text: creator.propertyCreator()
color: "green"
}
}
}

QOpenGLTexture::allocateStorage causes GL_INVALID_VALUE error

I've distilled this to trivial Qt code in which the only interesting thing is several lines in synchronize() handling a texture. I get a GL_INVALID_VALUE from the allocateStorage line. Anyone knows why? I suspect it's probably due to the parameters I pass to this function.
My code:
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickFramebufferObject>
#include <QOpenGLFramebufferObject>
#include <QOpenGLFunctions>
#include <QOpenGLTexture>
class MyItem : public QQuickFramebufferObject {
Q_OBJECT
public:
Renderer* createRenderer() const;
};
class MyItemRenderer : public QQuickFramebufferObject::Renderer, protected QOpenGLFunctions {
public:
MyItemRenderer() {
initializeOpenGLFunctions();
}
void render() {
}
QOpenGLFramebufferObject* createFramebufferObject(const QSize &size) {
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
return new QOpenGLFramebufferObject(size, format);
}
protected:
void synchronize(QQuickFramebufferObject* qqfbo) {
Q_UNUSED(qqfbo)
QOpenGLTexture tex(QOpenGLTexture::Target2D);
tex.setSize(100, 100);
tex.allocateStorage(QOpenGLTexture::BGRA, QOpenGLTexture::Int8);
qDebug() << "starting loop";
GLenum err;
while ((err = glGetError()) != GL_NO_ERROR) {
qDebug("\tgl error: 0x%x", err, 0, 16);
}
}
};
QQuickFramebufferObject::Renderer* MyItem::createRenderer() const {
return new MyItemRenderer();
}
int main(int argc, char **argv) {
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
}
}
After looking at the source of QOpenGLTexture::createMutableTexture and QOpenGLTexture::allocateStorage() (no-argument version) I saw that two-argument allocateStorage overload is really, really misleading. The docs make it seem like its 2 args will be used as the internal format of the texture (3rd arg of the glTexImage2D call in the implementation), when in fact they're used only as the "source-data-to-transfer-from-system-RAM" format/type (7th and 8th args of glTexImage2D), which, in the case of passing a null pointer to glTexImage2D, don't matter at all except in fringe OpenGL ES 2 cases.
The way to actually request a certain internal format for the texture is to call tex.setFormat before calling tex.allocateStorage. So I replaced my allocateStorage line with this:
tex.setFormat(QOpenGLTexture::RGBA8_UNorm);
tex.allocateStorage();
And that fixed the error.
You have...
QOpenGLTexture tex(QOpenGLTexture::Target2D);
According to the docs that doesn't actually create the underlying texture object. I think you then need...
tex.create();
before setting the size and allocating storage. So...
QOpenGLTexture tex(QOpenGLTexture::Target2D);
if (!tex.create()) {
/*
* Take appropriate action.
*/
}
tex.setSize(100, 100);
tex.allocateStorage(QOpenGLTexture::BGRA, QOpenGLTexture::Int8);
Edit 1:
Actually, looking at the code it appears that tex.allocateStorage should allocate a texture id if necessary. However, if that fails for some reason (no current context for example) then that could be causing the problem you're seeing.
During "allocatestorage", You are conveying opeGL driver about the texture storage format.
For the formats "GL_BGRA or GL_RGBA", OpenGL recommends "GL_UNSIGNED_BYTE",
which is in Qt ----- "QOpenGLTexture::BGRA" (for GL_BGRA) and "QOpenGLTexture::UInt8" (for GL_UNSIGNED_BYTE).
https://www.khronos.org/opengl/wiki/Textures_-_more
So your allocate storage function based on (openGL recommendation) should be
tex.allocateStorage(QOpenGLTexture::BGRA, QOpenGLTexture::UInt8);

Qt5 QML, why does model inside a model cause missing model variable? [duplicate]

I have a variable number of components, so i'm trying to give each one its own model. In this example, i just create one, but the idea is the same.
GC() is a bit random, so in the example, i force the gc() after a click to flush out the problem. What happens is that the model is destroyed and becomes null. after that the click method cannot use it.
main.qml:
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.2
import com.example.qml 1.0
ApplicationWindow
{
visible: true
width: 640
height: 480
// builder of dynamic models
ModelFactory { id: maker }
Column
{
anchors.fill: parent
Repeater
{
// create dynamic model
model: maker.makeModel();
delegate: Label
{
id: label
text: model.name
MouseArea
{
anchors.fill: parent
onClicked:
{
// works once until gc()
console.log("clicked on " + model.name)
// wont work anymore. model is destroyed
gc();
}
}
}
}
}
}
c++/mymodel.h:
#include <QAbstractListModel>
#include <QQmlApplicationEngine>
#include <QObject>
#include <QString>
#include <QDebug>
class BoxModel : public QAbstractListModel
{
Q_OBJECT
public:
~BoxModel()
{
// see that it does get destroyed
qDebug() << "~BoxModel()";
}
int rowCount(const QModelIndex& parent = QModelIndex()) const override
{
return 5;
}
QVariant data(const QModelIndex &index, int role) const override
{
int ix = index.row();
if (ix < 1) return "Larry";
if (ix < 2) return "Barry";
if (ix < 3) return "Gary";
if (ix < 4) return "Harry";
return "Sally";
}
QHash<int, QByteArray> roleNames() const override
{
QHash<int, QByteArray> roles;
roles[Qt::UserRole+1] = "name";
return roles;
}
};
class ModelFactory: public QObject
{
Q_OBJECT
public:
Q_INVOKABLE BoxModel* makeModel()
{
return new BoxModel();
}
};
main.cpp just registers the types:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <qqmlcontext.h>
#include <qqml.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
#include "mymodel.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<BoxModel>("com.example.qml", 1, 0, "BoxModel");
qmlRegisterType<ModelFactory>("com.example.qml", 1, 0, "ModelFactory");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
what you see:
Click on any of the names. it will work once and after that they will be undefined because model becomes null.
eg
qml: clicked on Sally
~BoxModel()
qml: clicked on undefined
My question is why is this, when i still have a reference to it?
In the example, onClicked could be changed to label.text rather than model.name to fix, but the real problem is that, in general, the model is accessed by the object at any time, for any data. For example, when the box needs to redraw. randomly the data is gone, depending on GC.
I've tried making c++ manage the life of the dynamic model. this could work if i know when exactly QML has finished with it.
thanks for info and ideas.
running on windows 8.1/qt5.6mingw
EDIT1: files as a gist,
https://gist.github.com/anonymous/86118b67ec804e6149423c14792f312d
As Kuba said, this does indeed seem like a bug. However, you can take another approach and take ownership of the models yourself via QQmlEngine::setObjectOwnership(). Specifically, changing
Q_INVOKABLE BoxModel* makeModel()
{
return new BoxModel();
}
to
Q_INVOKABLE BoxModel* makeModel()
{
BoxModel *model = new BoxModel(this);
QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership);
return model;
}
will fix this (remember to parent the returned model to BoxModel so it gets deleted appropriately). The reason for the behaviour is explained here:
Generally an application doesn't need to set an object's ownership explicitly. QML uses a heuristic to set the default ownership. By default, an object that is created by QML has JavaScriptOwnership. The exception to this are the root objects created by calling QQmlComponent::create() or QQmlComponent::beginCreate(), which have CppOwnership by default. The ownership of these root-level objects is considered to have been transferred to the C++ caller.
Objects not-created by QML have CppOwnership by default. The exception to this are objects returned from C++ method calls; their ownership will be set to JavaScriptOwnership. This applies only to explicit invocations of Q_INVOKABLE methods or slots, but not to property getter invocations.
I just had the same problem with a ComboBox.
As a workaround, you may create your own property to keep a strong reference to it:
Repeater {
property QtObject myModel: maker.makeModel();
model: myModel
// …
}
I know this is an old question, but I've just faced similar issue, and found your question in process of writing mine. See QObject gets destroyed after being put into QML variable for full story, and I'll cite it here.
What I've figured out that if I set the parent of that QObject before I pass it into QML, then it doesn't get deleted. So, I've concluded that passing unparented QObject into QML scope makes that scope become a parent of QObject and call its destructor after scope ends.

How to display QML view( with scroll) to load and display multiline Text around 10 million lines from file [duplicate]

I have a Qt application and I'd like to show some log. I use a TextArea. However, if the log is large or the events come too fast the GUI can't draw Textarea fast enough.
I have analyzed this problem with Qt Creator (QML Profiler) and if the log is large it takes 300 ms to draw the GUI. I use this software on a Raspberry Pi2.
Any ideas how to solve this? Should I use other QML controls? Thanks.
QML code:
TextArea {
text: appHandler.rawCommunication
readOnly: true
}
C++ code:
Q_PROPERTY(QString rawCommunication READ rawCommunication WRITE setrawCommunication NOTIFY rawCommunicationChanged)
void setrawCommunication(QString val)
{
val.append("\n");
val.append(m_rawCommunication);
m_rawCommunication = val;
emit rawCommunicationChanged(m_rawCommunication);
}
Use a view, like ListView. They instantiate their delegates as needed, based on which data the view says it needs to show depending on the position the user is at in the list. This means that they perform much better for visualising large amounts of data than items like TextArea, which in your case is going to keep a massive, ever-growing string in memory.
Your delegate could then be a TextArea, so you'd have one editable block of text per log line. However, if you don't need styling, I'd recommend going with something a bit lighter, like TextEdit. Taking it one step further: if you don't need editable text, use plain old Text. Switching to these might not make much of a difference, but if you're still seeing slowness (and have lots of delegates visible at a time), it's worth a try.
I tried the ListView suggestion but it has several drawbacks:
No easy way to keep the view positioned at the bottom when new output is added
No selection across lines/delegates
So I ended up using a cached TextArea, updating once every second:
TextArea {
id: outputArea_text
wrapMode: TextArea.Wrap
readOnly: true
font.family: "Ubuntu Mono, times"
function appendText(text){
logCache += text + "\n";
update_timer.start();
}
property string logCache: ""
Timer {
id: update_timer
// Update every second
interval: 1000
running: false
repeat: false
onTriggered: {
outputArea_text.append(outputArea_text.logCache);
outputArea_text.logCache = "";
}
}
Component.onCompleted: {
my_signal.connect(outputArea_text.appendText)
}
}
try this approach:
create a c++ logger class
append all logs to this class
and print them using some action, example a button click
this will solve your performance issue
Example of code:
Logger.h
#ifndef LOGGER_H
#define LOGGER_H
#include <QQmlContext>
#include <QObject>
#include <QStringList>
#include <QQmlEngine>
#include <QString>
#include <QtCore>
#include <QDebug>
class Logger : public QObject
{
Q_OBJECT
public:
explicit Logger(QObject *parent = 0);
~Logger();
Q_INVOKABLE QStringList *getLogStream();
Q_INVOKABLE void printLogStream();
Q_INVOKABLE void appendLog(QString log);
Q_INVOKABLE void log(QString log="");
Q_INVOKABLE void log(QString fileName, QString log);
signals:
public slots:
private:
QStringList* stringStream_;
};
#endif // LOGGER_H
Logger.cpp
#include "logger.h"
Logger::Logger(QObject *parent) :
QObject(parent),
stringStream_(new QStringList)
{
}
~Logger(){
if(stringStream_ != NULL)
{
delete stringStream_;
stringStream_ = NULL;
}
}
QStringList* Logger::getLogStream(){
return stringStream_;
}
void Logger::printLogStream()
{
QStringListIterator itr(*stringStream_);
while (itr.hasNext())
qDebug()<< itr.next()<<"\n";
}
void Logger::appendLog(QString log){
stringStream_->push_back(log) ;
}
void Logger::log(QString fileName,QString log)
{
#ifdef ENABLElogs
fileName.push_front(" [");
if(!fileName.contains(".qml"))
{
fileName.append(".qml]:");
}
qDebug()<<fileName<<log;
#else
Q_UNUSED(log);
Q_UNUSED(fileName);
#endif
}
void Logger::log(QString log)
{
#ifdef ENABLElogs
qDebug()<<log;
#else
Q_UNUSED(log);
#endif
}
main.cpp
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include "logger.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer *viewer = new QtQuick2ApplicationViewer;
Logger* stream = new Logger;
viewer->rootContext()->setContextProperty("Stream",stream);
viewer->setMainQmlFile(QStringLiteral("qml/project/main.qml"));
viewer->showExpanded();
return app.exec();
}
main.qml
import QtQuick 2.0
import QtQuick.Controls 1.1
Rectangle {
width: 800
height: 480
Text {
text: qsTr("Hello World")
anchors.centerIn: parent
Component.onCompleted: Stream.appendLog("Text object is completed")
}
Column{
x:300
Button{
text:"append"
onClicked: {
Stream.appendLog("MouseArea object clicked")
}
Component.onCompleted: Stream.appendLog("Button object is completed")
}
Button{
text:"logger"
onClicked: {
Stream.printLogStream()
}
Component.onCompleted: Stream.appendLog("Button logger object is completed")
}
}
TextArea{
text:"blablabla"
Component.onCompleted: Stream.appendLog("TextArea object is completed")
}
Component.onCompleted: Stream.appendLog("the main object is completed")
}
project.pro
#add this line
# comment it, run qmake and recompile to disable logs
DEFINES += ENABLElogs
using this approch you can stop all logs with a single line change when you want to release your soft
However, I have included complete code, using "QAbstractListModel" for a Logging heavy data to QML
listmodel.h
#ifndef LISTMODEL_H
#define LISTMODEL_H
#include <QAbstractListModel>
class ListModel: public QAbstractListModel
{
Q_OBJECT
public:
ListModel();
// Q_PROPERTY(QStringList logs READ name WRITE nameChanged)
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
Q_INVOKABLE QVariant activate(int i);
private:
QStringList m_list;
};
#endif // LISTMODEL_H
listmodel.cpp
#include "listmodel.h"
#include <QFile>
#include <QHash>
ListModel::ListModel()
{
QFile file("/home/ashif/LogFile");
if(!file.open(QIODevice::ReadOnly))
{
qDebug( "Log file open failed" );
}
bool isContinue = true;
do
{
if(file.atEnd())
{
isContinue = false;
}
m_list.append(file.readLine());
}
while( isContinue);
}
int ListModel::rowCount(const QModelIndex & parent ) const
{
return m_list.count();
}
QVariant ListModel::data(const QModelIndex & index, int role ) const
{
if(!index.isValid()) {
return QVariant("temp");
}
return m_list.value(index.row());
}
QVariant ListModel::activate(int i)
{
return m_list[i];
}
main.qml
import QtQuick 2.3
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
Window {
visible: true
ListView
{
width: 200; height: 250
anchors.centerIn: parent
model:mylistModel
delegate: Text
{
text:mylistModel.activate(index)
}
}
}
main.cpp
#include <QGuiApplication>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include "logger.h"
#include "listmodel.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
Logger myLogger;
ListModel listModel;
engine.rootContext()->setContextProperty("mylistModel", &listModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}

QVector inside QML/Javascript

How Can I Use QVector inside QML/Javascript?
Example:
C++:
Custom class I use in QML. The class is including function that return QVector of registered ElementType
class CustomType : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE QVector<ElementType*> getElements();
//.....
}
//.........
qmlRegisterType<CustomType>("CustomComponents", 1, 0, "CustomType");
qmlRegisterType<ElementType>("CustomComponents", 1, 0, "ElementType");
qRegisterMetaType<ElementType*>("ElementType*");
QML:
QML code receives instance of CustomType class (custom) and try to get QVector<ElementType*> of elements and reads its properties. But QML can't recognize QVector type.
//.......
function(){
var elements = custom.getElements()
elements[0].property //?
}
QML-accessible list properties need to be constructed with QDeclarativeListProperty. This applies to everything that's list-shaped, also vectors. This part of the Qt documentation has the details.
I am answering this years late, But, here goes. This is a simple problem to resolve. Just use a Qt QVariantList type in your C++ code instead of QVector. Qt's QVariantList is essentially a QVector containing QVariant elements. And QVariants can be damn near anything once you know what you are doing. Also, QML automatically converts a C++ QVariant to its QML equivalent. Hence, if you do this in main.cpp:
#include <QCoreApplication>
#include <QGuiApplication>
#include <QQmlContext>
#include <QQuickView>
int main(int argc, char *argv[])
{
// Prepare to enter the QML GUI world
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
// Create a QVariantList to be used instead of a QVector
QVariantList dataList;
dataList << "Item 1";
dataList << 2;
dataList << 3.3;
// Create QML UI
QQuickView view;
// make your QVariantList visible to QML
QQmlContext *ctxt = view.rootContext();
ctxt->setContextProperty("myModel",
QVariant::fromValue(dataList));
// show QML UI
view.setSource(QUrl("qrc:/main.qml"));
view.show();
return 1;
} // main()
You can then do this in your main.qml:
import QtQuick 2.12
import QtQuick.Window 2.12
Item
{
// Print contents of C++ QVariantList to console
function printMeBruh()
{
console.log("modelData contains:")
console.log("-------------------")
for (var i = 0; i < 3; i++)
{
console.log(myModel[i])
}
}
Component.onCompleted: printMeBruh()
}
In your example you just need to return QList of QObject* from a Q_INVOKABLE function. Note, that all objects returned that way will have JavaScript ownership and will be garbage collected when your JavaScript function returns. To prevent this behavior use setObjectOwnership before pushing objects into your QList collection.
For more information about using collections in QML see QML Data Models

Resources