为 QML ListView 实现 QAbstractListModel 子类?

Posted

技术标签:

【中文标题】为 QML ListView 实现 QAbstractListModel 子类?【英文标题】:Implementing a QAbstractListModel subclass for QML ListView? 【发布时间】:2020-12-23 03:53:20 【问题描述】:

如何实现QAbstractListModeldata 函数,使其返回具有从QML ListView 的委托可见的属性的东西?

对于项目类型,我尝试实现具有Q_PROPERTYs 的QObject 子类,但QAbstractListModel::data 返回QVariant,并且QObject* 无法转换为QVariant

如何创建一个 QVariant,它的命名属性对 QML 可见?

class MyListItem : public QObject 
  Q_OBJECT
  Q_PROPERTY(type name READ name WRITE set_name NOTIFY nameChanged)
  /*...*/
public:
  MyListItem(QObject* parent, const QString& name) : QObject(parent) 
    set_name(name);
  
;

class MyList : public QAbstractListModel

public:
  MyList(QObject* parent);

  Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const override;
  Q_INVOKABLE QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

  QVector<MyListItem*> items;
;

Q_INVOKABLE int MyList::rowCount(const QModelIndex &) const 
  return items.size();


Q_INVOKABLE QVariant MyList::data(const QModelIndex &index, int) const 
  MyListItem* item = items[index.row()];
  return item; // <--- ERROR

获取:

In member function ‘virtual QVariant MyList::data(const QModelIndex&, int) const’:
error: use of deleted function ‘QVariant::QVariant(void*)’
  18 |   return item;
     |          ^~~~

【问题讨论】:

请提供一个minimal reproducible example,如果它可以包含指向QObject的指针,请提供一个QVariant,所以我认为问题是另一个 @eyllanesc:查看更新。无法将 QObject* 转换为 QVariant 【参考方案1】:

如果你想将一个 QObject 封装在一个 QVariant 中,那么你必须使用QVariant::fromValue:

return QVariant::fromValue(item);

MWE:

ma​​in.cpp

#include <QAbstractListModel>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

class MyListItem : public QObject 
    Q_OBJECT
    Q_PROPERTY(QString name READ name WRITE set_name NOTIFY nameChanged)
public:
    MyListItem(const QString& name, QObject* parent=nullptr) : QObject(parent), m_name(name) 
    
    QString name() const
        return m_name;
    
public slots:
    void set_name(QString name)
        if (m_name == name)
            return;
        m_name = name;
        emit nameChanged(m_name);
    
signals:
    void nameChanged(QString name);
private:
    QString m_name;
;

class MyList : public QAbstractListModel

public:
    enum MyListRoles 
        ItemRole = Qt::UserRole + 1
    ;
    MyList(QObject* parent=nullptr): QAbstractListModel(parent)
    ~MyList()
        qDeleteAll(items);
        items.clear();
    
    void append(MyListItem *item)
        beginInsertRows(QModelIndex(), rowCount(), rowCount());
        items.append(item);
        endInsertRows();
    
    int rowCount(const QModelIndex &parent=QModelIndex()) const override
        if(parent.isValid())
            return 0;
        return items.count();
    
    QVariant data(const QModelIndex &index, int role) const override
        if(!index.isValid())
            return ;
        if(index.row() <0 || index.row() >= rowCount())
            return ;
        if(role == ItemRole)
            return QVariant::fromValue(items[index.row()]);
        return ;
    
    QHash<int, QByteArray> roleNames() const override
        return ItemRole, "item";
    
private:
    QVector<MyListItem*> items;
;
#include "main.moc"
int main(int argc, char *argv[])

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);
    MyList model;
    model.append(new MyListItem("foo1"));
    model.append(new MyListItem("foo2"));
    model.append(new MyListItem("foo3"));
    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("mylist", &model);
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) 
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    , Qt::QueuedConnection);
    engine.load(url);
    return app.exec();

ma​​in.qml

import QtQuick 2.12
import QtQuick.Window 2.12

Window 
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    ListView
        anchors.fill: parent
        model: mylist
        delegate: Text 
            text: model.item.name
        
    

【讨论】:

谢谢,这很有帮助。此外,我缺少的部分是角色的概念。在实现roleNames 并观察roleQAbstractListModel::data 参数之后,我现在可以正常工作了。 (我认为 ListView 委托中的引用是 QObject 的属性——事实并非如此——它们使用的是角色机制)

以上是关于为 QML ListView 实现 QAbstractListModel 子类?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 QML 中进行基于状态的模型更改

Qml自定义组件 - ListView下拉刷新 - PullToRefreshHandler

qml----Model/View入门ListView动画效果

QML ListView 填充不透明动画

如何在QML中设计一个expandable ListView

qml----Model/View入门ListView分组显示