Question

I've figured out how to bind a model derived from QAbstractListModel to a QML view.

But the next thing I tired does not work. If a new Item is added to the model the QML view will not update. Why is that?

DataObject.h

class DataObject {
    public:
        DataObject(const QString &firstName,
                   const QString &lastName):
            first(firstName),
            last(lastName) {}

        QString first;
        QString last;
};

SimpleListModel.h

class SimpleListModel : public QAbstractListModel
{
    Q_OBJECT

    enum /*class*/ Roles {
        FIRST_NAME = Qt::UserRole,
        LAST_NAME
    };

    public:
        SimpleListModel(QObject *parent=0);
        QVariant data(const QModelIndex &index, int role) const;
        Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const;
        QHash<int, QByteArray> roleNames() const;
        void addName(QString firstName, QString lastName);

    private:
        Q_DISABLE_COPY(SimpleListModel);
        QList<DataObject*> m_items;
};

SimpleListModel.cpp

SimpleListModel::SimpleListModel(QObject *parent) :
    QAbstractListModel(parent)
{
    DataObject *first = new DataObject(QString("Firstname01"), QString("Lastname01"));
    DataObject *second = new DataObject(QString("Firstname02"), QString("Lastname02"));
    DataObject *third = new DataObject(QString("Firstname03"), QString("Lastname03"));

    m_items.append(first);
    m_items.append(second);
    m_items.append(third);
}

QHash<int, QByteArray> SimpleListModel::roleNames() const
{
    QHash<int, QByteArray> roles;

    roles[/*Roles::*/FIRST_NAME] = "firstName";
    roles[/*Roles::*/LAST_NAME] = "lastName";

    return roles;
}

void SimpleListModel::addName(QString firstName, QString lastName)
{
    DataObject *dataObject = new DataObject(firstName, lastName);

    m_items.append(dataObject);

    emit dataChanged(this->index(m_items.size()), this->index(m_items.size()));
}

int SimpleListModel::rowCount(const QModelIndex &) const
{
    return m_items.size();
}

QVariant SimpleListModel::data(const QModelIndex &index, int role) const
{
    //--- Return Null variant if index is invalid
    if(!index.isValid())
        return QVariant();

    //--- Check bounds
    if(index.row() > (m_items.size() - 1))
        return QVariant();

    DataObject *dobj = m_items.at(index.row());

    switch (role)
    {
        case /*Roles::*/FIRST_NAME:
            return QVariant::fromValue(dobj->first);

        case /*Roles::*/LAST_NAME:
            return QVariant::fromValue(dobj->last);

        default:
            return QVariant();
    }
}

AppCore.h

class AppCore : public QObject
{
    Q_OBJECT
    Q_PROPERTY(SimpleListModel *simpleListModel READ simpleListModel CONSTANT)

    public:
        explicit AppCore(QObject *parent = 0);
        SimpleListModel *simpleListModel() const;

    public slots:
        void addName();

    private:
        SimpleListModel *m_SimpleListModel;

};

AppCore.cpp

AppCore::AppCore(QObject *parent) :
    QObject(parent)
{
    m_SimpleListModel = new SimpleListModel(this);
}

SimpleListModel *AppCore::simpleListModel() const
{
    return m_SimpleListModel;
}

void AppCore::addName()
{
    m_SimpleListModel->addName("FirstnameNEW", "LastnameNEW");
}

main.cpp

int main(int argc, char *argv[])
{
    QGuiApplication a(argc, argv);

    QQuickView *view = new QQuickView();
    AppCore *appCore = new AppCore();

    qRegisterMetaType<SimpleListModel *>("SimpleListModel");

    view->engine()->rootContext()->setContextProperty("appCore", appCore);
    view->setSource(QUrl::fromLocalFile("main.qml"));
    view->show();

    return a.exec();
}

main.qml

// ...
ListView {
    id: myListView
    anchors.fill: parent
    delegate: myDelegate
    model: appCore.simpleListModel
}

MouseArea {
    anchors.fill: parent
    onClicked: {
        appCore.addName()
        console.log('rowCount: ' + appCore.simpleListModel.rowCount())
    }
}
//...
Was it helpful?

Solution

you should call beginInsertRows and endInsertRows instead of emitting the signal

void SimpleListModel::addName(QString firstName, QString lastName)
{
    DataObject *dataObject = new DataObject(firstName, lastName);

    // tell QT what you will be doing
    beginInsertRows(ModelIndex(),m_items.size(),m_items.size());

    // do it
    m_items.append(dataObject);

    // tell QT you are done
    endInsertRows();

}

these 2 functions emit all needed signals

OTHER TIPS

You're ignoring the semantics of a QAbstractItemModel. There are two kinds of signals that a model must emit:

  • data change signals: They must be emitted after the data was changed. A data change is a change of value of an existing item. Other changes to the model are not called data changes - the terminology here has a specific meaning.

  • structure change signals: They must be emitted before and after any structural change. A structural change is addition or removal of any of the items. The beginXxxYyy and endXxxYyy helper functions emit those signals.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top