Ich würde gerne wissen, ob es ein Makro oder eine Möglichkeit gibt, das Qt-Modell als Eigenschaft von QObject zu registrieren.
Ich habe zum Beispiel AnimalModel
(http://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel).
Ich weiß, dass ich es an den Stammkontext von QuickView übergeben kann
QuickView view;
view.rootContext()->setContextProperty("myModel", &model);
Falls ich QObject über Qml-Makros registriert habe, kann ich dieses Objekt auch zur Ansicht übergeben:
view.rootContext()->setContextProperty("obj", pDataObject);
Aber was ist, wenn ich QObject haben möchte, das ein Modell aller Daten enthält?
Zum Beispiel:
class DataObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged)
...
AnimalModel m_modelAnimals;
//Is this possible in any way?
//Q_PROPERTY(AnimalModel modelAnimals READ modelAnimals NOTIFY modelAnimalsChanged)
};
Jedes Beispiel, das ich bis jetzt gefunden habe, zeigt, wie man besteht QAbstractListModel
zum Wurzelkontext. Aber keine, wie man es als QObject-Eigenschaft verwendet.
(Ich weiß, es gibt QQmlListProperty
aber QQmlListProperty
unterstützt keine teilweise Aktualisierung. Es ist immer notwendig, alle Qml-Objekte neu zu erstellen)
//Is this possible in any way?
//Q_PROPERTY(AnimalModel modelAnimals READ modelAnimals NOTIFY modelAnimalsChanged)
Ja, hast du es nicht versucht? Natürlich wird es keine sein AnimalModel
aber ein AnimalModel *
aber solange das Modell erbt QAbstractListModel
, das ist alles, was Sie brauchen. Du brauchst die nicht einmal NOTIFY
Teil, da Änderungen innerhalb des Modells ohnehin automatisch widergespiegelt werden. modelAnimalsChanged
macht nur Sinn, wenn Sie das gesamte Modell durch ein anderes Modell ersetzen, und natürlich, um die Warnungen von QML zur Verwendung einer Eigenschaft ohne ein Notify-Signal zum Schweigen zu bringen. Eine sauberere Möglichkeit, Letzteres zu tun, wenn sich das Modellobjekt nicht ändert, besteht darin, einfach a zurückzugeben AnimalModel *
aus einem Slot oder a Q_INVOKABLE
.
Wenn Sie ein wirklich flexibles Modell wünschen, können Sie eines herstellen, das sich speichern lässt QObject *
, dann können Sie aus QML beliebige Objekte mit beliebigen Eigenschaften erstellen und dem Modell hinzufügen. Dann haben Sie aus dem Modell eine einzige object
Rolle, die das Objekt zurückgibt, und Sie können das Objekt abfragen und verwenden, um die darin enthaltenen Eigenschaften abzurufen. Während eine “klassische” Listenmodellimplementierung ein Modell mit einem statischen, festen Schema definiert, ermöglicht die Verwendung dieses Ansatzes, “amorphe” Objekte mit unterschiedlichen Eigenschaften im Modell zu haben.
Natürlich erfordert dies eine gewisse Typsicherheit, zum Beispiel eine property int type
für jedes Objekt in einem solchen Modell, und basierend darauf können Sie die verfügbaren Eigenschaften für das Objekt bestimmen. Mein üblicher Ansatz ist, a zu haben Loader
für einen Delegaten, und lassen Sie ihn das Objekt als Datenquelle an verschiedene QML-UI-Implementierungen übergeben, die diesen Objekttyp visualisieren, den er instanziiert. Auf diese Weise haben Sie sowohl unterschiedliche Objekte im Modell als auch unterschiedliche QML-Elemente als Ansichtsdelegierte.
Der letzte Schritt zur Erstellung des ultimativen „Alleskönner“-Listen-/Modellobjekts ist die Implementierung QQmlListProperty
und Q_CLASSINFO("DefaultProperty", "container")
dafür, sodass Sie die Liste/das Modell dynamisch zusammenstellen oder die deklarative Syntax von QML verwenden können. Beachten Sie auch, dass Sie mit dieser Lösung einem solchen Modell etwas hinzufügen oder daraus entfernen und sogar deklarativ instanziierte Objekte entfernen können.
Abhängig von Ihrem Nutzungsszenario müssen Sie möglicherweise auch beides qmlRegisterType()
oder qmlRegisterUncreatableType()
für das Modell.
OK, auf den zweiten Blick sieht es so aus, als hätten Sie mit “Modell beliebiger Daten” nicht schemalose Modelle gemeint, sondern einfach unterschiedliche Schemamodelle. In diesem Fall, anstatt eine zurückzugeben AnimalModel *
können Sie eine verwenden QAbstractListModel *
oder sogar ein QObject *
– es funktioniert sowieso in QML, da es Dynamik durch das Metasystem verwendet. Aber auf jeden Fall sind schemalose Modelle viel leistungsfähiger und flexibler, und sie brauchen keinen C++-Code, um definiert zu werden, es kann alles allein mit QML funktionieren.
class List : public QAbstractListModel {
Q_OBJECT
QList<QObject *> _data;
Q_PROPERTY(int size READ size NOTIFY sizeChanged)
Q_PROPERTY(QQmlListProperty<QObject> content READ content)
Q_PROPERTY(QObject * parent READ parent WRITE setParent)
Q_CLASSINFO("DefaultProperty", "content")
public:
List(QObject *parent = 0) : QAbstractListModel(parent) { }
int rowCount(const QModelIndex &p) const { Q_UNUSED(p) return _data.size(); }
QVariant data(const QModelIndex &index, int role) const {
Q_UNUSED(role)
return QVariant::fromValue(_data[index.row()]);
}
QHash<int, QByteArray> roleNames() const {
static QHash<int, QByteArray> roles = { { Qt::UserRole + 1, "object" } };
return roles;
}
int size() const { return _data.size(); }
QQmlListProperty<QObject> content() { return QQmlListProperty<QObject>(this, _data); }
public slots:
void add(QObject * o) { insert(o, _data.size()); }
void insert(QObject * o, int i) {
if (i < 0 || i > _data.size()) i = _data.size();
beginInsertRows(QModelIndex(), i, i);
_data.insert(i, o);
o->setParent(this);
sizeChanged();
endInsertRows();
}
QObject * take(int i) {
if ((i > -1) && (i < _data.size())) {
beginRemoveRows(QModelIndex(), i, i);
QObject * o = _data.takeAt(i);
o->setParent(0);
sizeChanged();
endRemoveRows();
return o;
} else qDebug() << "ERROR: take() failed - object out of bounds!";
return 0;
}
QObject * get(int i) {
if ((i > -1) && (i < _data.size())) return _data[i];
else qDebug() << "ERROR: get() failed - object out of bounds!";
return 0;
}
void internalChange(QObject * o) { // added to force sort/filter reevaluation
int i = _data.indexOf(o);
if (i == -1) {
qDebug() << "internal change failed, obj not found";
return;
} else {
dataChanged(index(i), index(i));
}
}
signals:
void sizeChanged();
};
Dann nach dir qmlRegisterType<List>("Core", 1, 0, "List");
Sie können es so ziemlich beliebig verwenden – es hält alles QObject
oder abgeleitet, natürlich einschließlich QMLs QtObject
Es kann direkt als Modell verwendet werden, um einen zu fahren ListView
. Sie können es dynamisch mit den Slots oder deklarativ wie folgt füllen:
List {
QtObject { ... }
QtObject { ... }
List {
QtObject { ... }
QtObject { ... }
}
}
Es wird auch den Besitz von Objekten handhaben, und Sie können es leicht verschachteln und im Wesentlichen ein unterteiltes Baummodell erstellen – beachten Sie, dass Sie dies nicht deklarativ mit QMLs tun können ListModel
. Vielleicht möchten Sie eine hinzufügen parentChanged
signalisieren und einen Setter implementieren, der es aussendet, wenn man gegen einen wechselnden Elternteil binden will, das war in meinem Fall nicht nötig.
Um es mit einer Ansicht zu verwenden, können Sie entweder die objectName
Eigentum oder ein int type
Eigenschaft oder im Grunde jedes Mittel, um zwischen verschiedenen Objekttypen zu unterscheiden, und verwenden Sie a Loader
für den Delegierten:
Loader {
// using component in order to capture context props and present to the variable delegate
sourceComponent: Qt.createComponent(obj.objectName + ".qml")
// if that is not needed simply use
// source: obj.objectName + ".qml"
// or setSource to pass specific properties to delegate properties
// Component.onCompleted: setSource(obj.objectName + ".qml", {/*prop list*/})
}
Aktualisieren:
Hier ist auch das Wesentliche der Implementierung für einen einfachen und ebenso dynamischen und generischen Sortier- und Filter-Proxy, der mit diesem Modell für eine verbesserte Benutzerfreundlichkeit einhergeht.