问题
I have a class Bar which inherits from QObject. I want to have a QList of Bar pointers in class Foo and expose it in qml.
foo.h
#ifndef FOO_H
#define FOO_H
#include <QQuickItem>
class Bar : public QObject
{
Q_OBJECT
Q_PROPERTY(int x READ x)
public:
explicit Bar(QObject *parent = nullptr)
: QObject(parent), mX(123) {}
virtual ~Bar() {}
int x() const { return mX; }
private:
int mX;
};
class Foo : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QList<QObject *> t1 READ t1)
Q_PROPERTY(QList<Bar *> t2 READ t2)
public:
explicit Foo(QQuickItem *parent = nullptr)
: QQuickItem(parent) {
mT1.append(new Bar(this));
mT1.append(new Bar(this));
mT2.append(new Bar(this));
mT2.append(new Bar(this));
}
virtual ~Foo() {}
QList<QObject *> t1() const { return mT1; }
QList<Bar *> t2() const { return mT2; }
private:
QList<QObject *> mT1;
QList<Bar *> mT2;
};
#endif // FOO_H
qmlRegisterType("Custom.Foo", 1, 0, "Foo");
main.qml
import QtQuick 2.6
import QtQuick.Window 2.2
import Custom.Foo 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MouseArea {
anchors.fill: parent
onClicked: {
console.log(foo.t1)
console.log(foo.t1.x)
console.log(foo.t2)
console.log(foo.t2.x)
}
}
Foo {
id: foo
}
}
QList<QObject *>
works I can access it's member x
and console.log(foo.t1)
outputs qml: [Bar(0x10e4be0),Bar(0x10bd6d0)]
but QList<Bar *>
doesn't.
When I try to access member x
I get qml: undefined and console.log(foo.t2)
outputs qml: QVariant(QList<Bar*>)
.
Why QList<Bar *>
is exposed in QVariant? Is there a way to expose it in qml same way QList<QObject *>
?
回答1:
First off read the following article carefully:
- Data Type Conversion Between QML and C++
In order to create JavaScript arrays or objects from C++ data structures, you should expose those of C++ in QVariantList
(which gets converted to JavaScript array) or QVariantMap
(which gets converted to JavaScript object).
Also section "Sequence Type to JavaScript Array" says:
Certain C++ sequence types are supported transparently in QML as JavaScript Array types.
In particular, QML currently supports:
QList<int> QList<qreal> QList<bool> QList<QString> and QStringList QList<QUrl> QVector<int> QVector<qreal> QVector<bool>
In your case, note that:
Other sequence types are not supported transparently, and instead an instance of any other sequence type will be passed between QML and C++ as an opaque
QVariantList
.
Therefore, you should prepare your list in a QVariantList
. Also note that since both QVariantMap
and QVariantList
are of type QVariant
, so you can insert a map into a list or vice versa, to create a complex array of objects or a complex object having a list inside.
回答2:
QML Engine recognizes QList<QObject*>
as a special case. You can't expose a property of type QList<Bar*>
, the engine doesn't understand it, and there is no way to register it as a new list type.
Another option which hadn't been mentioned is casting the list... as sinful as it may be considered by some.
If you must have your data stored in QList<Bar*>
then you can return it as a QList<QObject*>
as below...
QList<QObject *> t2() const {
Q_ASSERT(sizeof(QObject*) == sizeof(Bar*)); // check pointers are the same size
Q_ASSERT(sizeof(QList<QObject*>) == sizeof(QList<Bar*>)); // check lists are the same size
Q_ASSERT(qobject_cast<Bar*>((QObject*)mT2.at(0))); // check Bar is derived from QObject
return *reinterpret_cast<const QList<QObject *>*>(&mT2); // now cast the list
}
It's fast, easy, and all the object properties and Q_INVOKABLE methods of the Bar class are available in QML.
I've added a few checks in just in case, but if you can be certain your class is derived from QObject then you might choose not to bother.
QList<QObject*>
support is documented here: http://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qobjectlist-based-model. It is no coincidence that it is a special case... it has been chosen to be a special ;) Each QObject*
is copied from the QList
and used to create a new javascript array inside the QML Engine.
Encompassing things inside a variant
is fine, so long as the engine knows the type which is inside the variant
. You can register Bar
type so that the QML engine could understand:
QVariant(Bar*)
and evenQList<QVariant(Bar*)>
.
However you can never get the engine to understand QList<Bar*>
.
(note: if Bar
derives from QObject
, then there is no need to register it to QML, support will be implicit)
Other options...
QQmlListProperty
and QList<QVariant>
will work, but have been covered in other answers.
QAbstractListModel
is another option, but there are already enough resources elsewhere to see how that is done.
full code:
foo.h
#ifndef FOO_H
#define FOO_H
#include <QQuickItem>
class Bar : public QObject
{
Q_OBJECT
Q_PROPERTY(int x READ x)
public:
explicit Bar(QObject *parent = nullptr)
: QObject(parent), mX(123) {}
virtual ~Bar() {}
int x() const { return mX; }
private:
int mX;
};
class Foo : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QList<QObject *> t2 READ t2)
public:
explicit Foo(QQuickItem *parent = nullptr)
: QQuickItem(parent) {
mT2.append(new Bar(this));
mT2.append(new Bar(this));
}
virtual ~Foo() {}
QList<QObject *> t2() const {
Q_ASSERT(sizeof(QObject*) == sizeof(Bar*)); // check pointers are the same size
Q_ASSERT(sizeof(QList<QObject*>) == sizeof(QList<Bar*>)); // check lists are the same size
Q_ASSERT(qobject_cast<Bar*>((QObject*)mT2.at(0))); // check Bar is derived from QObject
return *reinterpret_cast<const QList<QObject *>*>(&mT2); // now cast the list
}
private:
QList<Bar *> mT2;
};
#endif // FOO_H
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "foo.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<Foo>("Custom.Foo", 1, 0, "Foo");
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.6
import QtQuick.Window 2.2
import Custom.Foo 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MouseArea {
anchors.fill: parent
onClicked: {
console.log(foo.t2)
console.log(foo.t2[0].x)
console.log(foo.t2[1].x)
}
}
Foo {
id: foo
}
}
回答3:
To expose a list of QObject
to Qml, Qt recommends to use QQmlListProperty
.
To create this object you need to override some of its access function, like size()
, at()
, etc.
You should have a look to
http://doc.qt.io/qt-5/qqmllistproperty.html
http://doc.qt.io/qt-5/qtqml-referenceexamples-properties-example.html
来源:https://stackoverflow.com/questions/46681920/qlistt-to-qml-array