【问题标题】:How to create a generic object model for use in QML?如何创建用于 QML 的通用对象模型?
【发布时间】:2016-05-11 16:44:04
【问题描述】:

我想知道是否有任何宏或方法如何将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对象)

【问题讨论】:

    标签: c++ qt generics model qtquick2


    【解决方案1】:
    //Is this possible in any way?
    //Q_PROPERTY(AnimalModel modelAnimals READ modelAnimals NOTIFY modelAnimalsChanged)
    

    是的,你没试过吗?当然,它不会是AnimalModel,而是AnimalModel *,但只要模型继承QAbstractListModel,这就是你所需要的。您甚至不需要 NOTIFY 部分,因为无论如何都会自动反映模型内部的更改。 modelAnimalsChanged 仅在您将整个模型替换为不同的模型时才有意义,并且自然而然地关闭 QML 关于在没有通知信号的情况下使用属性的警告。当模型对象不改变时,执行后者的一种更简洁的方法是仅从插槽返回 AnimalModel *Q_INVOKABLE

    如果您想要一个真正灵活的模型,您可以创建一个存储QObject * 的模型,然后您可以从 QML 创建具有任意属性的任意对象,并将其添加到模型中。然后从模型中,您有一个返回对象的object 角色,您可以查询并使用该对象来检索它所拥有的属性。虽然“经典”列表模型实现将定义具有静态、固定模式的模型,但使用这种方法允许在模型中拥有具有不同属性的“无定形”对象。

    当然,这需要某种类型的安全性,例如,对于此类模型中的每个对象都有一个property 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&lt;List&gt;("Core", 1, 0, "List"); 之后,你几乎可以以任何你想要的方式使用它 - 它会保存任何 QObject 或派生的,自然包括 QML QtObject 它可以直接用作驱动 @ 的模型987654345@。您可以使用插槽或声明式动态填充它,如下所示:

    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*/})
    }
    

    更新:

    【讨论】:

    • 感谢您提供非常复杂的答案以及许多有趣的想法。你是对的,根本问题是无效的Q_PROPERTY 类型。使用AnimalModel* 效果很好。关于QObject* 的模型,这是我已经拥有的东西(stackoverflow.com/questions/35065627/...)。但是您能否发布示例或更具体地了解Q_CLASSINFOqmlRegisterType 用例?看起来很有趣,但我不确定在这种情况下如何使用它。
    • @LudekVodicka - 我添加了一些代码,还冒昧地重新命名了您的问题,以便寻找类似解决方案的人更容易访问代码。
    • 这绝对是杰作。我上个月半在学习 Qml,没想到它可以以如此好的方式结合起来。谢谢你的解释和例子。唯一出现在我脑海中的问题。为什么 QHashMap 作为静态 ptr 而不是仅静态?可以将其定义为static QHash&lt;int, QByteArray&gt; roles = { { ObjectRole, "object" } };,但它需要 c++11。是这个原因吗?
    • 是的,它是为了向后兼容。是的,这种方法非常灵活和强大,您可以只使用它作为您的 C++ 代码创建非常复杂的项目,并在 QML 中执行其他所有操作 - 它是动态的,可以使用动态生成的代码并且开发速度要快得多我真的很想知道为什么一开始就没有将如此简单但如此强大的东西并入 QML 中。
    • 完美。这正是我尝试自己开发的原因。这是我期望 Qml 从一开始就有的东西。目前的 Qt/Qml 状态似乎只为简单的 UI 开发而准备,但没有更复杂的。谢谢大家的回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-10
    • 2021-09-21
    • 2010-10-31
    • 1970-01-01
    • 2013-09-29
    • 2011-08-02
    • 2021-08-10
    相关资源
    最近更新 更多