Как реализовать QML ListModel, например метод get для модели, основанной на QAbstractListModel
Я хочу использовать модель QAbstractListModel, полученную в QML. Привязка модели к представлениям уже отлично работает.
Следующее, чего я хочу достичь, это возможность доступа к определенным элементам и их роли, как это возможно с помощью QML ListModel
grid.model.get(index).DisplayRole
Но я не знаю, как реализовать этот метод get в моей производной модели QAbstractListModel.
Любые намеки?
Ответы
Ответ 1
Вы можете добавить функцию 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
Ответ 2
если вы хотите использовать классический подход для ролей в моделях списков, вам не нужно делать что-либо особенное в стороне 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 запускает приложение в режиме отладки и помещает точку останова в делегат или печатает все свойства консоли. Btw свойство model
внутри делегата имеет тип QQmlDMAbstractItemModelData, поэтому есть некоторая "магия Qt", происходящая в фоновом режиме, похожая на некоторую оболочку вокруг данных модели списка, но я не мог найти ничего официального в документации Qt об этом (I понял, что я сам с отладчиком QML и т.д.).
Если вам нужно получить доступ к данным модели из-за пределов делегата, я не думаю, что для этого есть какая-либо функция сборки, поэтому вы должны сделать это самостоятельно.
Я сделал пример для пользовательского класса QAbstractListModel, который предоставляет свойство count
и get
-function аналогично стандарту 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
}
}
Ответ 3
Мой подход заключается в том, чтобы напрямую открывать свойства объектов в QML. Вот его реализация - fooobar.com/questions/1458842/...
Ответ 4
Альтернативным подходом для этого было бы прямое использование встроенных функций QAbstractItemModel
, например, посредством
grid.model.data(grid.model.index(index, 0), 0 /*== Qt::DisplayRole*/)
Это технически работает, но требует, чтобы пользователь знал числовые коды для ролей, а не обозначающие строки. Основная проблема здесь в том, что в лучшем случае есть только встроенные функции для определения roleNames()
. Для правильного сопоставления строк имен с соответствующими числовыми значениями необходимо будет либо реализовать функцию инвертирования, либо разоблачить ее с помощью Q_INVOKABLE
или обработать QHash
полученный из roleNames()
в QML вручную.
Ответ 5
Это заняло у меня очень много времени, так как на Stackoverflow существует много неправильных решений.
Я отправил ответ здесь:
Как получить доступ к текущему элементу ListView из qml
Это работает для всех моделей, будь то из QAbstractItemModel или построено непосредственно в QML, и даже позволяет получить доступ к древовидным моделям.