Использование QAbstractListModel в ListView
Я новичок в Qt, поэтому, пожалуйста, потерпите меня.
Мне успешно удалось заполнить ListView из StringList и QList объекта *
Теперь я борюсь с тем, чтобы заполнить ListView в QML, используя класс, определенный в C++, который наследует QAbstractListModel.
Вот прототип моего класса CPP:
class MessageListEntryModel : public QAbstractListModel
{
Q_OBJECT
public:
enum eMLERoleTypes
{
MLERT_MSG = Qt::UserRole+1,
MLERT_COLOR
};
MessageListEntryModel(QObject* parent=0);
virtual ~MessageListEntryModel();
void AddEntry(QString aMessage, QColor aColor);
// pure virtuals implementations
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const ;
int columnCount(const QModelIndex &parent = QModelIndex()) const ;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &child) const ;
QHash<int,QByteArray> roleNames();
private:
QList<MessageEntry*> m_vpMessages;
MessageEntry - это простой класс, который содержит 2 члена, QColor и QString (класс не расширяет QObject).
Мне пришлось реализовать все вышеперечисленные функции, поскольку они чисто виртуальные в базовом классе (это нормально? До сих пор в уроках / примерах люди упоминали только о roleNames и data).
Реализация roleNames и data следующие:
QHash<int,QByteArray> MessageListEntryModel::roleNames()
{
QHash<int,QByteArray> rez;
rez[MLERT_MSG]="message";
rez[MLERT_COLOR]="messagecolor";
return rez;
}
QVariant MessageListEntryModel::data(const QModelIndex &index, int role) const
{
qDebug()<<" Data asked for "<<index.row()<<" and role "<<role;
if (index.row()<0 || index.row()>=m_vpMessages.size())
{
return QVariant();
}
MessageEntry* entry = m_vpMessages[index.row()];
if (role == MLERT_MSG)
{
return QVariant::fromValue(entry->message);
} else if (role == MLERT_COLOR)
{
return QVariant::fromValue(entry->messageColor);
}
// should be unreachable code
return QVariant();
}
QML-часть представления списка выглядит примерно так:
ListView {
id: quickMessageListdata
model: quickListModel
delegate: Rectangle {
width: 400
height: 25
color:"#000000"
Text{
text: model.message
color: model.messagecolor
}
}
Пока это мое понимание того, как реализовать вещи в CPP и QML. Для связи этих двух я использую следующий код:
MessageListEntryModel* model =new MessageListEntryModel();
// Add various entries
...
// assign model in QML
m_pViewRef->rootContext()->setContextProperty("quickListModel",model);
С кодом выше, при запуске ничего не отображается в ListView, и я получаю следующие ошибки:
Unable to assign [undefined] to QString
Unable to assign [undefined] to QColor
Я также регистрирую класс модели для экспорта в QML (не знаю, нужно ли это):
qmlRegisterType<MessageListEntryModel> ("dlti.exported",1,0,"MessageListEntryModel");
Таким образом, совершенно очевидно, что либо я неправильно понял правильное использование класса, производного от QAbstractList Item, либо Я пропустил простую важную информацию о ключах.
Я был бы признателен за некоторые ссылки на некоторые соответствующие примеры / учебные пособия (в которых также показано, как правильно получить доступ к данным из модели в QML, поскольку я заметил, что в CPP он никогда не проходит через функцию данных).
Также обратите внимание, что я использую qt5, поэтому сэмплы qt4.8 не сработают.
РЕДАКТИРОВАТЬ
После долгих часов разочарований я, наконец, решил, что не так с этой чертовой штукой:
Моя подпись функции roleNames была неверной! Правильная подпись для перегрузки:
protected :
QHash<int,QByteArray> roleNames() const;
Обратите внимание на защищенные и константные модификаторы.
После правильного объявления функции все заработало нормально.
Для дальнейшего уведомления, реализации данных и rowCount было достаточно:).
Спасибо за помощь. Я приму ответ BaCaRoZzo, так как мне удалось выяснить это только после просмотра кода из примера.
Как примечание, он хорошо работает как с message, так и с model.message.
2 ответа
Как вы реализуете метод сложения? Вы должны использовать метод, как в примере, приведенном в моем комментарии.
Из документов:
Реализация insertRows() должна вызывать beginInsertRows() перед вставкой новых строк в структуру данных, и она должна вызывать endInsertRows() сразу после этого.
Вы должны иметь что-то вроде:
void MessageListEntryModel::add(params...)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount()); // kindly provided by superclass
// object creation based on params...
m_vpMessages << objectCreated;
endInsertRows(); // kindly provided by superclass
}
с
int MessageListEntryModel::rowCount(const QModelIndex & parent) const {
Q_UNUSED(parent);
return m_vpMessages.count();
}
Кроме того, комментарий @Jonathan Mee является правильным: используйте только имена ролей внутри делегата, как вы их определили в модели. Если все остальное правильно , это способ доступа к данным.
Один из лучших документов для понимания реализации и использования моделей C++ - это ссылка на подклассы Model. В этом документе четко изображено, какие методы являются наиболее важными для подкласса и для каких целей.
Что касается методов реализации, то это действительно зависит от потребностей. В зависимости от возможных действий на модели, должны быть реализованы различные методы (подробности см. По ссылке выше). Модель для ListView
в котором элементы могут быть добавлены / удалены могут наследовать от QAbstractListModel
и просто полагаться на реализации по умолчанию для большинства функций. Вам просто нужно data()
, roleNames()
а также rowCount()
, как вы уже видели в большинстве примеров.
Если вместо этого вам также нужно отредактировать данные, а не просто добавить / удалить их, вам также понадобятся другие функции, в частности setData()
, Вы также обязаны уведомить приложенный вид (ы) о любой модификации модели, произошедшей в setData()
через сигнал dataChanged()
, Обратитесь снова к приведенной выше ссылке на подклассы.
Обратите внимание также, что если add
метод изменяется с Q_INVOKABLE
модификатор, т.е. он объявлен как
Q_INVOKABLE void add(params...);
в заголовке модели вы также можете вызвать его из QML (так как модель установлена как свойство контекста), и вы можете написать, например:
ListView {
id: quickMessageListdata
model: quickListModel
delegate: Rectangle {
width: 400
height: 25
color:"#000000"
Text{
text: model.message
color: model.messagecolor
}
}
Component.onCompleted: {
quickListModel.add(params)
quickListModel.add(params)
}
}
вставить элементы в представление, как только представление будет создано. Очевидно, что тот же подход может быть применен к другим сигналам QML, так что вы можете реагировать на события QML и запускать режимы добавления / удаления.
Наконец, вам не нужно регистрировать модель с qmlRegisterType
, Для вашего текущего требования это излишне.
Хммм... Я не очень знаком с QML, но я считаю, что это ваша проблема: http://qt-project.org/doc/qt-5/qtquick-modelviewsdata-cppmodels.html
По ссылке похоже, что нужно поменять свой Text
блок для:
Text{
text: message
color: messagecolor
}
Дополнительная информация: http://qt-project.org/forums/viewthread/5491
Я также испытывал тяжелые времена, когда создавал собственные модели списков с помощью Qt C++. Чтобы избежать накладных расходов на разработку модели C++, я начал использовать QSyncable (существующая реализация QAbstractListModel от Ben Lau). Вы можете найти его на GitHub здесь.
Лучшая часть проекта - это тип QSON JsonListModel. Он может преобразовать любой вариант списка JSON, который вы создаете или выбираете в QML, в полнофункциональную QML ListModel. Это экономит много времени и усилий для приложений, которые, например, работают со службами JSON или REST. Вы можете найти подробное руководство, как это работает здесь.