簡體   English   中英

如何創建用於 QML 的通用對象模型?

[英]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 屬性。

(我知道有QQmlListPropertyQQmlListProperty不支持部分刷新。重建所有 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 typeproperty int type ,並且基於它您可以確定該對象的可用屬性。 我通常的方法是為委托設置一個Loader ,並將對象作為數據源傳遞給不同的 QML UI 實現,以可視化它實例化的對象類型。 通過這種方式,模型中既有不同的對象,又有不同的 QML 項作為視圖委托。

制作最終“萬事通”列表/模型對象的最后一步是為其實現QQmlListPropertyQ_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM