【问题标题】:How to implement QML ListModel like get method for an QAbstractListModel derived model如何为 QAbstractListModel 派生模型实现 QML ListModel 之类的 get 方法
【发布时间】:2014-05-07 19:57:35
【问题描述】:

我想在 QML 中使用 QAbstractListModel 派生模型。将模型绑定到视图已经很有效了。

接下来我想要实现的是访问特定项目及其角色的能力,就像使用 QML ListModel 一样

grid.model.get(index).DisplayRole

但我不知道如何在我的 QAbstractListModel 派生模型中实现这个 get 方法。

有什么提示吗?

【问题讨论】:

    标签: qt qml qt-quick qtquick2 qabstractitemmodel


    【解决方案1】:

    您可以像这样向 QAbstractItemModel 派生类添加 Q_INVOKABLE 函数:

    ...
    
    Q_INVOKABLE QVariantMap get(int row);
    
    ...
    
    QVariantMap get(int row) {
        QHash<int,QByteArray> names = roleNames();
        QHashIterator<int, QByteArray> i(names);
        QVariantMap res;
        while (i.hasNext()) {
            i.next();
            QModelIndex idx = index(row, 0);
            QVariant data = idx.data(i.key());
            res[i.value()] = data;
            //cout << i.key() << ": " << i.value() << endl;
        }
        return res;
    }
    

    这将返回类似{ "bookTitle" : QVariant("Bible"), "year" : QVariant(-2000) } 的内容,因此您可以在其上使用 .bookTitle

    【讨论】:

    • 您可以将QModelIndex idx = index(row, 0); 移出循环以提高性能。
    【解决方案2】:

    如果您想在列表模型中使用经典方法来处理角色,您不必在 c++ 端做任何特殊的事情,您的模型就像往常一样,它应该实现数据方法:

    QVariant QAbstractItemModel::data(const QModelIndex & index, int role = Qt::DisplayRole) const
    

    要从 QML 访问不同的角色,可以在 ListView 委托中使用 model 附加属性:

    model.display // model.data(index, Qt::DisplayRole) in c++
    model.decoration // Qt::DecorationRole
    model.edit // Qt::EditRole
    model.toolTip // Qt::ToolTipRole
    // ... same for the other roles
    

    我认为 Qt 文档中还没有记录,但要找出可以从 QML 访问的属性,只需在调试模式下启动应用程序并在委托中放置断点或打印所有属性到控制台。顺便说一句,委托中的model 属性是 QQmlDMAbstractItemModelData 类型,所以在后台发生了一些“Qt 魔法”,看起来像是列表模型数据的一些包装,但我仍然在 Qt 文档中找不到任何官方的关于它的信息(我自己用 QML 调试器和其他东西解决了这个问题)。

    如果您需要从委托外部访问模型数据,我认为没有任何内置功能,因此您必须自己做。

    我为自定义 QAbstractListModel 类做了一个示例,它公开了类似于默认 QML ListModel 的 count 属性和 get-function:

    mylistmodel.h

    class MyListModel : public QAbstractListModel
    {
        Q_OBJECT
        Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
    
    public:
        explicit MyListModel(QObject *parent = 0);
    
        int rowCount(const QModelIndex & = QModelIndex()) const override { return m_data.count(); }
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    
        Q_INVOKABLE int get(int index) const { return m_data.at(index); }
    
    signals:
        void countChanged(int c);
    
    private:
        QList<int> m_data;
    };
    

    mylistmodel.cpp

    MyListModel::MyListModel(QObject *parent) :
        QAbstractListModel(parent)
    {
        m_data << 1 << 2 << 3 << 4 << 5; // test data
        emit countChanged(rowCount());
    }
    
    QVariant MyListModel::data(const QModelIndex &index, int role) const
    {
        if (!index.isValid() || index.row() < 0 || index.row() >= rowCount())
            return QVariant();
    
        int val = m_data.at(index.row());
    
        switch (role) {
        case Qt::DisplayRole:
            return QString("data = %1").arg(val);
            break;
        case Qt::DecorationRole:
            return QColor(val & 0x1 ? Qt::red : Qt::green);
            break;
        case Qt::EditRole:
            return QString::number(val);
            break;
        default:
            return QVariant();
        }
    }
    

    由于向 QML 公开属性和函数非常容易,我想这是一个很好的方法来了解它。

    为了完整起见,这里有一个使用我的自定义模型的示例 ListView:

    ListView {
        anchors.fill: parent
        model: MyListModel { id: myModel }
        delegate: Text {
            text: model.display
        }
    
        Component.onCompleted: {
            console.log(myModel.count) // 5
            console.log(myModel.get(0)) // 1
        }
    }
    

    【讨论】:

    • 我想在 JS 函数中访问特定索引和特定角色的项目。例如myString = grid.model.get(3).DisplayRole。我看不出您的回答如何帮助我实现这一目标。
    • 如果您在代表内部,我的回答会更笼统。从外部访问特定索引是相当罕见的,但它的工作方式应该与您尝试myString = grid.model.get(3).display(不是 DisplayRole)的方式相同,如果这不起作用,您可以遍历模型数据的所有属性以查看哪些属性是可用的,因为我猜它们没有记录(如果没有示例应用程序,我无法做到这一点)。
    • myString = grid.model.get(3).display 怎么能正常工作? QAbstractListModel 没有称为 get 的方法。所以我必须自己实现它。这就是问题所在。我该如何实现这个方法?
    • 我认为 QML get 函数会调用 QAbstractListModel 的虚拟“数据”方法(这是获取数据的默认方式)。我可能需要设置一个示例应用程序并自己测试它,然后明天再来这里。我确实在另一个项目中使用了 QAbstractListModel,这很容易。
    • +1000 -- 谢谢。 model 真的需要记录在案!它仍然不是(!?)。
    【解决方案3】:

    这花了我很长时间才找到,因为 Stackoverflow 上有很多不正确的解决方案。

    我已在此处发布回复:

    How to access ListView's current item from qml

    这适用于所有模型,无论是派生自 QAbstractItemModel 还是直接在 QML 中构建,甚至允许访问树形模型。

    【讨论】:

      【解决方案4】:

      另一种方法是直接使用QAbstractItemModel 的内置函数,例如通过

      grid.model.data(grid.model.index(index, 0), 0 /*== Qt::DisplayRole*/)
      

      这在技术上可行,但要求用户知道角色的数字代码,而不是指定字符串。这里的主要问题是充其量只有内置功能来确定roleNames()。为了将名称字符串正确映射到相应的数值,需要实现一个反转函数并使用Q_INVOKABLE 公开它,或者手动处理QML 中roleNames() 产生的QHash

      【讨论】:

        【解决方案5】:

        我的方法是将对象的属性直接暴露给 QML。这是它的实现 -https://stackoverflow.com/a/14424517/1059494

        【讨论】:

        • 我真的不明白对象的属性是如何暴露的。我不需要在某处看到 Q_PROPERTIES 宏吗?
        • 我暴露了整个对象。然后 JS 可以查找使用 Q_PROPERTY 宏声明的特定属性。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-11-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多