[英]How to create a generic object model for use in QML?
我想知道是否有任何宏或方法如何將 Qt 模型注冊為 QObject 的屬性。
例如,我有AnimalModel
( http://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel )。
我知道我可以將它傳遞給 QuickView 的根上下文
QuickView view;
view.rootContext()->setContextProperty("myModel", &model);
如果我通過 Qml 宏注冊了 QObject,我也可以傳遞這個對象來查看:
view.rootContext()->setContextProperty("obj", pDataObject);
但是如果我想要擁有任何數據模型的 QObject 呢?
例如:
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)
};
到目前為止,我發現的每個示例都展示了如何將QAbstractListModel
傳遞給根上下文。 但沒有如何將它用作 QObject 屬性。
(我知道有QQmlListProperty
但QQmlListProperty
不支持部分刷新。重建所有 Qml 對象總是必要的)
//Is this possible in any way?
//Q_PROPERTY(AnimalModel modelAnimals READ modelAnimals NOTIFY modelAnimalsChanged)
是的,你沒有嘗試過嗎? 當然,它不會是AnimalModel
而是AnimalModel *
,但只要模型繼承QAbstractListModel
,這就是你所需要的。 您甚至不需要NOTIFY
部分,因為無論如何都會自動反映模型內部的更改。 modelAnimalsChanged
僅在您用不同的模型替換整個模型時才有意義,並且自然而然地關閉 QML 關於在沒有通知信號的情況下使用屬性的警告。 當模型對象沒有改變時,更AnimalModel *
是只從插槽或Q_INVOKABLE
返回一個AnimalModel *
。
如果你想要一個真正靈活的模型,你可以創建一個存儲QObject *
,然后從 QML 你可以創建具有任意屬性的任意對象,並添加到模型中。 然后從模型中,您有一個返回對象的object
角色,您可以查詢和使用該對象來檢索它所擁有的屬性。 “經典”列表模型實現將定義一個具有靜態、固定模式的模型,而使用這種方法允許在模型中擁有具有不同屬性的“無定形”對象。
自然,這需要某種類型安全性,例如在這樣的模型中為每個對象都有一個property int type
的property int type
,並且基於它您可以確定該對象的可用屬性。 我通常的方法是為委托設置一個Loader
,並將對象作為數據源傳遞給不同的 QML UI 實現,以可視化它實例化的對象類型。 通過這種方式,模型中既有不同的對象,又有不同的 QML 項作為視圖委托。
制作最終“萬事通”列表/模型對象的最后一步是為其實現QQmlListProperty
和Q_CLASSINFO("DefaultProperty", "container")
,允許您動態地組合列表/模型,或使用 QML 的聲明句法。 另請注意,使用此解決方案,您可以向此類模型添加或刪除,甚至刪除聲明性實例化的對象。
此外,根據您的使用場景,您可能需要為模型使用qmlRegisterType()
或qmlRegisterUncreatableType()
。
好吧,乍一看,“任何數據的模型”似乎並不是指無模式模型,而是指不同的模式模型。 在這種情況下,您可以使用QAbstractListModel *
甚至QObject *
而不是返回AnimalModel *
- 它無論如何都可以在 QML 中工作,因為它通過元系統使用動態。 但無論如何,無模式模型更加強大和靈活,它們不需要定義 C++ 代碼,它可以單獨從 QML 工作。
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();
};
然后,在你qmlRegisterType<List>("Core", 1, 0, "List");
你幾乎可以以任何你想要的方式使用它 - 它會保存任何QObject
或派生的,自然包括 QMLs QtObject
它可以直接用作模型來驅動ListView
。 您可以使用插槽或聲明來動態填充它,如下所示:
List {
QtObject { ... }
QtObject { ... }
List {
QtObject { ... }
QtObject { ... }
}
}
它還將處理對象所有權,您可以輕松嵌套它,從本質上生成一個分區樹模型 - 請注意,您不能使用 QML 的ListModel
聲明性地執行此操作。 您可能想要添加一個parentChanged
信號並實現一個發出它的 setter,如果您想綁定一個變化的父級,這在我的情況下是沒有必要的。
至於如何在視圖中使用它,您可以使用objectName
屬性或int type
屬性或基本上任何方法來區分不同的對象類型,並為委托使用Loader
:
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*/})
}
更新:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.