从 C++ 设置 GridView 模型 属性 而不设置上下文

Set GridView model property from C++ without setting Context

我正在尝试通过调用
从 C++ 中设置 QML GridViewmodel 属性 QQmlProperty::write(gridview, "model", QVariant::fromValue(objlist));.

gridview 设置正确,我可以从 C++ 修改 属性,当我将它设置为具有 6 个条目的 QList 并从 QML 打印它们时,我得到
qml: model = item(0x30617b50), Item(0x30617b90), Item(0x30617bd0), Item(0x30617c10), Item(0x30617c50), Item(0x30617cd0),虽然没有显示模型。

Qt 文档建议调用

QQmlContext *ctxt = view->rootContext(); ctxt->setContextProperty("gridModel", QVariant::fromValue(objlist));

然后使用 model: gridModel 从 QML 设置 属性 但这并不真正适合我的需要。不过它工作正常,只要设置 属性 就会显示正确的数据。当我从 QML 打印变量时,输出是
qml: model = [object Object] 所以设置上下文 属性 和设置对象 属性 肯定是有区别的,但我不知道如何解决这个问题。

如果您说 QQmlProperty::write 正确设置了模型,那么您的问题是什么?无论如何,我建议另一种解决方案:

假设您 GridView 如下所示:

GridView {
    anchors.fill: parent
    objectName: "grid"
    delegate: Rectangle {
        id: rect
        width: 100;
        height: 100
        color: Qt.rgba(Math.random(),Math.random(),Math.random(),1)
        Text {
            anchors.centerIn: rect
            text: modelData
        }
    }
}

objectName 为必填项。

所以从 C++ 访问可以是:

QStringList list;
list.append("String1");
list.append("String2");
list.append("String3");

QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

QObject *obj = engine.rootObjects()[0]->findChild<QObject *>("grid");
if(obj) {
    obj->setProperty("model",QVariant(list));
}

与其尝试从 C++ 访问 QML 对象或属性,我建议在 QML 端使用绑定并提供来自 C++ 的 属性 值。

如果通过 setContextProperty 公开模型实例不太符合您的需求,例如如果模型在 QML 加载时间后实例化,那么我建议采用以下方法:

  1. 通过 setContextProperty(), i
  2. 公开派生的 QObject 实例 class
  3. class 为您的模型获得 Q_PROPERTY,包括 NOTIFY 信号
  4. 在 QML 中,您将 属性 绑定到 GridView 的模型
  5. 无论何时用 C++ 创建模型或需要创建模型的新实例时,都会发出上述 NOTIFY 信号

界面 class 看起来有点像这样

class MyInterface : public QObject
{
    Q_OBJECT
    Q_PROPERTY(MyModel* model READ model NOTIFY modelChanged)

public:
    MyModel *model() const { return m_model; }

    void setModel(MyModel *model) {
        m_model = model;
        emit modelChanged();
    }

private:
    MyModel *m_model = 0;
};

当然,setter 的变化可以在 MyInterface 内部,等等。 这使您可以在 C++ 端完全控制何时创建模型实例、何时更改它、何时删除它。 如果您将类型更改为 QAbstractItemModel*,或您的一些常见模型库 class,您甚至可以在运行时根据需要更改模型类型

我想出的解决方案与 posted 的答案略有不同,所以我认为写一个明确的答案是最好的。

关键是对 QAbstractItemModel 进行子类化(或更准确地说 ..ListModel,这样您就不必在 C++ 和 [=44= 中处理 rows/columns ] QML).

这样做时,您不仅可以简单地将模型设置为 属性 和

QQuickItem *mainform = view->rootObject();
QQuickItem *grid = (QQuickItem *)mainform->findChild<QObject*>("GridView object name");

ItemModel itemmodel;
itemmodel.setItems(objlist):

QQmlProperty::write(grid, "model", QVariant::fromValue(&itemmodel));

每当对模型进行更改时,它也会通知 QML,例如一个项目被删除。 (不过,您必须正确处理更改,请参阅示例中的 removeRows()

这是我的 ItemModel:

// itemmodel.h

#include <QAbstractListModel>
#include <item.h>

class ItemModel : public QAbstractListModel
{
    Q_OBJECT
public:
    explicit ItemModel(QObject *parent = 0);
    QHash<int, QByteArray> roleNames() const;

public slots:
    void setItems(QList<Item *> items);
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    int rowCount(const QModelIndex & parent = QModelIndex()) const;
    bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());

private:
    QList<Item *> items;
};

// itemmodel.cpp
#include "itemmodel.h"

ItemModel::ItemModel(QObject *parent) : QAbstractListModel(parent)
{

}

// Column Names have to match all the Q_PROPERTYs defined in Item
const char* COLUMN_NAMES[] = {
    "property1",
    "property2",
    "...",
    NULL
};
QHash<int, QByteArray> makeRoleNames()
{
    int idx = 0;
    QHash<int, QByteArray> roleNames;
    while(COLUMN_NAMES[idx])
        roleNames[Qt::UserRole + idx + 1] = COLUMN_NAMES[idx++];

    return roleNames;
}

QHash<int, QByteArray> ItemModel::roleNames() const
{
    static const QHash<int, QByteArray> roleNames = makeRoleNames();
    return roleNames;
}

void ItemModel::setItems(QList<Item *> items)
{
    this->items = items;
}

int ItemModel::rowCount(const QModelIndex & /* parent */) const
{
    return items.count();
}

bool ItemModel::removeRows(int row, int count, const QModelIndex &parent)
{
    Q_UNUSED(parent);
    beginRemoveRows(QModelIndex(), row, row + count - 1);
    while (count--) delete items.takeAt(row);
    // example for custom deletion:
    //              items.takeAt(row)->removeFromRoot();
    endRemoveRows();
    return true;
}

QVariant ItemModel::data(const QModelIndex &index, int role) const {
    if (!index.isValid())
        return QVariant();

    if (index.row() >= items.size() || index.row() < 0)
        return QVariant();

    if (role == Qt::DisplayRole) {
        return QVariant::fromValue(this->items.at(index.row()));
    }

    if (role > Qt::UserRole)
        return this->items.at(index.row())->property(makeRoleNames()[role]);
}

来源:

  • [1] 关于为 QML 创建 QAbstractItemModel 子类的 Qt 文档页面
  • [2] QAbstractItemModel 参考
  • [3] QAbstractItemModel 子类化指南
  • [4] 带有 QAbstractListModel 子类的论坛 post
  • [5] QAbstractItemModel 的工作(几乎data() 略有改动)我从中获取角色名称函数