Как реализовать QML ListModel как метод get для производной модели QAbstractListModel

Я хочу использовать производную модель QAbstractListModel в QML. Привязка модели к представлениям уже отлично работает.

следующее, что я хочу достичь, - это возможность доступа к определенным элементам и их роли, как это возможно с помощью QML ListModel

grid.model.get(index).DisplayRole

но я понятия не имею, как реализовать этот метод get в моей производной модели QAbstractListModel.

какие-то намеки?

5 ответов


вы можете добавить функцию Q_INVOKABLE в производный класс QAbstractItemModel следующим образом:

...

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 на нем


если вы хотите использовать классический подход для ролей в моделях списков, вам не нужно делать ничего особенного на стороне c++, у вас есть модель, как всегда, и она должна реализовать метод данных:

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

для доступа к различным ролям из QML model вложенное свойство можно использовать в делегате ListView:

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 doc (пока), но чтобы узнать, какие свойства вы можете получить доступ к форме QML, просто начните приложение в режиме отладки и поставить точку останова в делегате или распечатать все свойства на консоли. Кстати,model свойство внутри делегата имеет тип QQmlDMAbstractItemModelData, поэтому в фоновом режиме происходит какая-то "магия Qt", выглядит как некоторая обертка вокруг данных модели списка, но я все равно не смог найти ничего официального в документации Qt об этом (я понял это сам с отладчиком QML и прочее).

Если вам нужно получить доступ к модели данных от вне делегата я не думаю, что для этого есть какие-либо функциональные возможности, поэтому вы должны сделать это сами.

Я сделал пример для пользовательского класса QAbstractListModel, который предоставляет count собственность и get - функция, аналогичная QML ListModel по умолчанию:

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
    }
}

мой подход заключается в том, чтобы предоставить свойства объектов непосредственно QML. Вот ее реализация -https://stackoverflow.com/a/14424517/1059494


альтернативным подходом для этого было бы прямое использование встроенных функций QAbstractItemModel, например, via

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

это технически работает, но требует, чтобы пользователь знал числовые коды для ролей, а не обозначающие строки. Основная проблема здесь в том, что в лучшем случае есть только встроенная функциональность для определения roleNames(). Для правильного отображения строк имен в соответствующие числовые значения необходимо либо реализовать инвертирующую функцию и разоблачить его с помощью Q_INVOKABLE и QHash в результате roleNames() в QML вручную.


это заняло у меня очень много времени, чтобы найти, так как есть много неправильных решений в Stackoverflow.

Я разместил ответ здесь:

как получить доступ к текущему элементу ListView из qml

Это работает для всех моделей, производных от QAbstractItemModel или встроенных непосредственно в QML, и даже позволяет получить доступ к древовидным моделям.