Qt обращаясь к данным модели вне ItemDelegate

У меня есть некоторый модельный класс, который наследует QAbstractListModel:

VehiclesModel.h:

class VehiclesModel : public QAbstractListModel {
Q_OBJECT

public:
enum Roles {
ImagePathRole = Qt::UserRole + 1,   // QString
NameRole                            // QString
};

virtual int rowCount(const QModelIndex & parent = QModelIndex()) const override { ... }
virtual QVariant data(const QModelIndex & index, int role) const override { ... }
virtual QHash<int, QByteArray> roleNames() const override {
QHash<int, QByteArray> roles = QAbstractListModel::roleNames();

roles[ImagePathRole] = "imagePath";
roles[NameRole] = "name";

return roles;
}
};

main.cpp:

#include "VehiclesModel.h"
int main(int argc, char * argv[]) {
QGuiApplication app(argc, argv);
VehiclesModel vehiclesModel;
QQmlApplicationEngine engine;

engine.rootContext()->setContextProperty("vehiclesModel", &vehiclesModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}

А также ComboBox который отображает эту модель:
main.qml:

ComboBox {
id: control
model: vehiclesModel
delegate: ItemDelegate {
contentItem: RowLayout {
Image {
source: imagePath
}
Label {
text: name
}
}
highlighted: control.highlightedIndex == index
}
contentItem: RowLayout {
Image {
source: ??imagePath??
}
Label {
text: ??name??
}
}
}

Я хочу настроить ComboBox показать изображение и название автомобиля. Я могу получить доступ к данным модели из ItemDelegate но как получить доступ к данным модели за пределами ItemDelegate? Например, я хочу получить доступ к текущим данным индекса (ImagePathRole а также NameRole) для отображения изображения и имени автомобиля в contentItem,

Можно ли это сделать без звонка QAbstractListModel методы напрямую (т.е. index() а также data() методы) и их изготовление Q_INVOKABLE?

1

Решение

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

В настоящее время вы можете сделать это самостоятельно (как вы обсуждаете), за счет безопасности типов и т. Д., Или (как я обычно говорил ранее) вы можете создать подкласс QObject для представлять отдельный элемент в модели (ItemDataThing или как вы его называете); предоставить ему исходную модель & индекс, свойства и пусть он представляет один экземпляр данных из модели.

Что-то вроде:

class ImageDataThing : public QObject
{
Q_OBJECT
Q_PROPERTY(QString imagePath READ imagePath NOTIFY imagePathChanged)
Q_PROPERTY(QAbstractItemModel* model READ model WRITE setModel NOTIFY modelChanged)
Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged)

public:
QString imagePath() const;
QAbstractItemModel *model() const;
void setModel(const QAbstractItemModel *newModel);
int index() const;
void setIndex(int newIndex);
signals:
void imagePathChanged(const QString &imagePath);
void modelChanged(QAbstractItemModel *model);
void indexChanged(int indexChanged);
};

… и в вашей реализации всякий раз, когда модель установлена, подключите сигналы изменения (например, rowInserted, rowRemoved, …), чтобы изменить сохраненный индекс (если имеется), чтобы он отображался в правильном месте в модели.

В средствах получения данных модели (здесь, например, imagePath), получите доступ к экземпляру модели (используя индекс), чтобы получить данные и вернуть их.

Это имеет очевидный недостаток, заключающийся в том, что он состоит из множества шаблонов, но, с другой стороны, это простой в написании код, если вы знакомы с моделями, безопасны по типу, и его можно довольно легко автоматически сгенерировать.

2

Другие решения

Вы можете создать свою собственную функцию для получения данных из модели, например ту, которую я сейчас использую,
VehiclesModel.h:

public slots:
int size() const;   // to access from QML javascript
QVariant getData(int index, int role);  // to access from QML javascript

VehiclesModel.cpp:

int VehiclesModel::size() const {
return m_list.size();
}

QVariant VehiclesModel::getData(int index, int role) {
if (index < 0 || index >= m_list.count())
return QVariant();
switch (role) {
case ImagePathRole:
return ...
break;
default:
break;
}
}
1

Я настоятельно рекомендую посмотреть на Qt QML Tricks Библиотека, созданная Томасом Бутру:

http://gitlab.unique-conception.org/qt-qml-tricks/

Более конкретно QQmlObjectListModel (от Qt QML Models) может сделать трюк для вас.

Расширение с использованием Qt Super-Macros, это уменьшает накладные расходы на запись сеттеров / геттеров!
Эти макросы в основном расширяются до Q_PROPERTY, что приводит к доступности из QML и добавляет определение установщика, получателя и закрытой переменной.

Использование в вашем конкретном случае это может выглядеть примерно так, быстро записывается, не проверяется (проверьте, используя правильный индекс для ссылки на модель):

VehicleItem.h:

#include <QObject>
#include "QQmlVarPropertyHelpers.h" // Include library Qt Super-Macros

class VehicleItem : public QObject {
Q_OBJECT

QML_WRITABLE_VAR_PROPERTY(QString, imagePath)
QML_WRITABLE_VAR_PROPERTY(QString, name)

public:
explicit VehicleItem(QString imagePath, QString name, QObject* parent=0)
: QObject   (parent)
, m_imagePath (imagePath)
, m_name      (name)
{}

};

VehiclesModel.h:

#include <QObject>
#include "QQmlObjectListModel.h" // Include library Qt QML Models
#include "VehicleItem.h"
class VehiclesModel : public QObject {
Q_OBJECT

QML_OBJMODEL_PROPERTY(VehicleItem, modelList)

public:
explicit VehiclesModel(QObject *parent = 0);

};

VehiclesModel.c:

#include "VehiclesModel.h"
VehiclesModel::VehiclesModel(QObject *parent) :
QObject(parent), m_modelList(new QQmlObjectListModel<VehicleItem>())
{}

main.c (remains the same):

#include "VehiclesModel.h"
int main(int argc, char * argv[]) {
QGuiApplication app(argc, argv);
VehiclesModel vehiclesModel;
QQmlApplicationEngine engine;

engine.rootContext()->setContextProperty("vehiclesModel", &vehiclesModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}

main.qml:

ComboBox {
id: control
model: vehiclesModel
delegate: ItemDelegate {
contentItem: RowLayout {
Image {
source: imagePath
}
Label {
text: name
}
}
highlighted: control.highlightedIndex == index
}
contentItem: RowLayout {
Image {
source: vehiclesModel.modelList.get(index).imagePath
}
Label {
text: vehiclesModel.modelList.get(index).name
}
}
}

Поскольку modelList (а также imagePath и name) расширяется макросом до Q_PROPERTY, он доступен со стороны QML.

Чтобы узнать все подробности об этой библиотеке, обязательно посмотрите молниеносный доклад Томаса Бутру на QtWS2015: https://www.youtube.com/watch?v=96XAaH97XYo

1

Бесстыдная вилка для моего Библиотека SortFilterProxyModel.

Проблема, которую вы задаете, на самом деле является головокружительным сценарием. Я нашел способ сделать это несколько правильно, но это довольно сложно и включает в себя внешнюю библиотеку. В моем решении мы фильтруем исходную модель, чтобы выставить только элемент, соответствующий текущему индексу комбинированного списка, и создаем экземпляр делегата для этого элемента и используем его как contentItem из ComboBox,

Преимущество этого состоит в том, что вам не нужно изменять вашу модель и синхронизировать ее с изменениями модели.

import SortFilterProxyModel 0.2 // from https://github.com/oKcerG/SortFilterProxyModel
import QtQml 2.2

/*
...
*/

ComboBox {
id: control
model: vehiclesModel
delegate: ItemDelegate {
contentItem: RowLayout {
Image {
source: imagePath
}
Label {
text: name
}
}
highlighted: control.highlightedIndex == index
}
contentItem: { currentIndex; return selectedInstantiator.object; } // use currentIndex to force the binding reevaluation. When the model changes, the instantiator doesn't notify object has changed
Instantiator {
id: selectedInstantiator
model: SortFilterProxyModel {
sourceModel: control.model
filters: IndexFilter {
minimumIndex: control.currentIndex
maximumIndex: control.currentIndex
}
}
delegate: RowLayout {
Image {
source: imagePath
}
Label {
text: name
}
}
}
}
0
По вопросам рекламы [email protected]