Использование 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. Вы можете найти подробное руководство, как это работает здесь.

Другие вопросы по тегам