【问题标题】:Exposing QAbstractListModel of QObjects for QML's ListView. Good practice?为 QML 的 ListView 公开 QObjects 的 QAbstractListModel。好习惯?
【发布时间】:2016-03-04 13:48:56
【问题描述】:

所以我有这样的想法,即使用 QObjects 的 Q_PROPERTYs 而不是 QAbstractListModel 的角色名称来将可通知的属性公开给 QML。

我的问题是,这是否是一个好习惯,因为使用继承自 QAbstractItemModel 的类感觉有点不自然。

让我更详细地解释一下。
因此,为 QML 创建 C++ 模型的推荐方法是:

  1. 继承 QAbstractListModel(或其他)以创建您的自定义模型。
  2. 过载rowCount()
  3. 定义您自己的数据角色。

例如:

enum DataRoles {
    Name = Qt::UserRole + 1,
    Description,
    CustomData
};

...


QHash<int, QByteArray> TestList::roleNames() const
{
    QHash<int, QByteArray> res;
    res[static_cast<int>(DataRoles::Name)] = "name";
    res[static_cast<int>(DataRoles::Description)] = "description";
    res[static_cast<int>(DataRoles::CustomData)] = "customData";

    return res;
}
  1. 在重载的data()成员函数中返回对应的数据。

例如:

QVariant TestList::data(const QModelIndex & index, int role) const
{
    QVariant result = QVariant();

    if ( index.isValid() == true ) {
        EntityPtr entityPtr = entities_.at(index.row());

        switch (role) {
        case DataRoles::Name:
            result = entityPtr->name();
            break;
        case DataRoles::Description:
            result = entityPtr->description();
            break;
        case DataRoles::CustomData:
            result = entityPtr->customData();
            break;
        }
    }

    return result;
}

然后,在 QML 的上下文中注册您的模型实例后,您可以按名称访问 ListView 委托中的实体字段,在角色名称()中定义,例如:

ListView {
    model: yourModelInstance
    ...
    delegate: Item {
        ...
        Text {
            text: name // access DataRoles::Name
        }
        ...
    }
}

IMO,这个实现很好,但是当涉及到从 C++ 端更改属性时,您应该在每次实体属性更改时调用QAbstractListView 的 dataChaged() 信号

我希望能够自动通知视图有关更改,例如当我打电话给entity-&gt;setName()

我的想法是只注册一个数据角色,例如"object" 将返回整个 QObject,它将具有Q_PROPERTYs,然后从 qml 访问它,例如:

Text {
    text: object.name //access `name` Q_PROPERTY of the object
}

这样,如果设置器会发出正确的信号,注册在 Q_PROPERTY 声明中,QML 端将自动通知设置新值的更改。

所以问题是,如果这是实现我的目标的好方法,因为,正如我上面所说,感觉有点不自然,而 Qt 库很好地表明你做错了什么做错事很难(不自然)。

编辑

按照 cmets 中的要求,我制作了一个小型 Qt 示例应用程序来展示我想要实现的目标。

git repo - https://github.com/shtemberko/SO_question_temp

【问题讨论】:

  • 您可以轻松地创建一个模型,该模型包装 QObjectList 并将对象的某个或多个属性公开为单个单元格。不过,您不会传递任何 QObjects - QVariant 从来没有打算与他们合作。它是一个值类,QObject 不是一个值类。
  • QObjectList 只是QList&lt;QObject*&gt; 的类型定义,不是吗?在通知道具被更改时,我会面临同样的问题。整个目标是让开发人员不必在每次想要更改某些属性或以某种方式从对象设置器函数通知模型时调用dataChanged 信号。
  • 我同意你上面的 cmets。然而,如果不直接使用 QObject 的属性,我只看到一种可能的实现 - 在将实体添加到模型时,将每个实体的每个 xxxChanged() 信号连接到模型的 dataChaged() 信号。这听起来不像是维护 + imo 有趣的事情,会导致更强的耦合,这很少是好的。另一个选项是每次用户想要更改数据时使用 QAbstractListView 的 setData()。这将导致从 QML 更改数据的可能性 + 大量其他封装问题。
  • 信号和插槽完全由程序控制。您可以枚举它们并自动连接它们。只要您有一个发送这些信号的对象列表,以及存储在某处的对象到行/列或信号到行/列的分配,就可以在不编写任何每个信号代码的情况下完成此操作。事实上,无论如何你都不应该为它编写任何信号代码。请更详细地描述您的架构,也许提供一个代码示例来说明您要实现的目标。无论如何,您无法真正将任何东西连接到dataChanged:发件人不会提供参数。
  • 再次重申:到目前为止,您所描述的所有内容都可以通过一段通用代码来实现,该代码枚举必要的信号或属性并自动建立连接。在这一点上,问题中没有给出足够的实现来提出解决方案。什么是“实体”?帮助我们帮助您。

标签: c++ qt model-view-controller qml observer-pattern


【解决方案1】:

我认为您需要在 Entity 中为每个属性设置一个 propertyChanged 信号。然后将 propertyChanged 链接到 dataChanged 信号。当您 setName 或 setPosition 时,发出 propertyChanged 信号。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-03
    • 2019-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多