QAbstractListModel example with MVVM pattern

291 Views Asked by At

I am trying to display the list of items that comes from QAbstractListModel on a QML List View using the MVVM pattern. To keep it simple my reading/accessing architecture will be something like this-View(QML-ListView)->ViewModel(C++-Binding Properties defined)->Model(C++ read data from DevicesListModel)->DevicesListModel(QAbstractListModel)class.

The DevicesListModel receives items from lower layers.

I am trying with writing a sample but ended up not knowing how to move further, any help in completing the example to achieve the desired result is much helpful.

DevicesListModel.h

#ifndef DEVICESLISTMODEL_H
#define DEVICESLISTMODEL_H

#pragma once

#include <QObject>
#include <QAbstractListModel>

class QQmlEngine;
class QJSEngine;

class DevicesListModel : public QAbstractListModel
{
    Q_OBJECT
    Q_DISABLE_COPY(DevicesListModel)
public:

    enum DeviceModelRoles {
        NameRole = Qt::UserRole + 1,
        AddressRole,
        TypeRole
    };

    static QObject *qobject_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
    {
        Q_UNUSED(engine)
        Q_UNUSED(scriptEngine)
        return new DevicesListModel();
    }

    // Basic functionality:
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

signals:
    void countChanged(int count);

private slots:
    void devicesChanged(QVariantList devices);

public slots:

private:
    explicit DevicesListModel(QObject *parent = nullptr);

    ~DevicesListModel();

    QList<QVariantMap> m_device_list;
    QHash<int, QByteArray> m_roles;

};

#endif // DEVICESLISTMODEL_H

DevicesListModel.cpp

#include "deviceslistmodel.h"
#include <QDebug>

DevicesListModel::DevicesListModel(QObject *parent) : QAbstractListModel(parent){

    //QObject::connect(DeviceDiscovery::instance(), SIGNAL(devicesChanged(QVariantList)), this, SLOT(devicesChanged(QVariantList)));

    m_roles[NameRole] = "name";
    m_roles[AddressRole] = "address";
    m_roles[TypeRole] = "type";

}

DevicesListModel::~DevicesListModel()
{
    m_device_list.clear();
}

int DevicesListModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;

    return m_device_list.count();
}

QVariant DevicesListModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    QVariantMap device = m_device_list.at(index.row());

    switch (role) {
        case NameRole: {
            return QVariant::fromValue(device["name"].toString());
        }
        case AddressRole: {
            return QVariant::fromValue(device["address"].toString());
        }
        case TypeRole: {
            return QVariant::fromValue(device["type"].toString());
        }
        default: {
            return QVariant::fromValue(QString("Unkown Role"));
        }
    }
}

void DevicesListModel::devicesChanged(QVariantList devices)
{
    // Add device
    for (QVariant device: devices) {
        QVariantMap deviceMap = device.toMap();
        QString name = deviceMap["name"].toString();
        QString address = deviceMap["address"].toString();
        QString type = deviceMap["type"].toString();

        qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << name << address << type;

        if (!m_device_list.contains(deviceMap))
        {
            m_device_list.append(deviceMap);
        }
    }

    // Remove device
    for (int i = 0; i < m_device_list.count(); i++) {

        if (!devices.contains(QVariant::fromValue(m_device_list[i])))
        {
            m_device_list.removeAt(i);
        }
    }

    emit countChanged(m_device_list.count());
}

DeviceModel.h

#ifndef DEVICESMODEL_H
#define DEVICESMODEL_H

#include <QObject>

class DevicesModel : public QObject
{
    Q_OBJECT
private:
    QStringList _devicesList;

public:
    virtual ~DevicesModel() {}
    explicit DevicesModel(QObject *parent = nullptr);

    //QString getDevicesListModelItems() const {return _devicesList;}

    void  readListItemsFromDeviceListModel(){
        //how to read from devicesListModel and Store here, so that View can read the data using properties defined in the deviceViewModel
    }

signals:

public slots:
};

#endif // DEVICESMODEL_H

DeviceModel.cpp

#include "devicesmodel.h"

DevicesModel::DevicesModel(QObject *parent) : QObject(parent)
{

}

MainViewModel.h

#ifndef MAINVIEWMODEL_H
#define MAINVIEWMODEL_H

#include <QObject>
#include <QList>
#include "devicesmodel.h"

class MainViewModel : public QObject
{
    Q_OBJECT

private:

    DevicesModel _devicesModel;
    //Q_PROPERTY(QList devicesListModel READ devicesListModel WRITE setDevicesListModel NOTIFY devicesListModelChanged)

    //QList m_devicesListModel;

public:
    explicit MainViewModel(const DevicesModel& devicesModel);

    virtual ~MainViewModel(){}


//QList devicesListModel() const;

signals:

//void devicesListModelChanged(QList devicesListModel);

public slots:
//void setDevicesListModel(QList devicesListModel);
};

#endif // MAINVIEWMODEL_H

MainViewModel.cpp

#include "mainviewmodel.h"

MainViewModel::MainViewModel(const DevicesModel& devicesModel)
{

}

/*QList MainViewModel::devicesListModel() const
{
    return m_devicesListModel;
}

void MainViewModel::setDevicesListModel(QList devicesListModel)
{
    if (m_devicesListModel == devicesListModel)
        return;

    m_devicesListModel = devicesListModel;
    emit devicesListModelChanged(m_devicesListModel);
}*/

main.qml

import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.2

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    ListView{
        id: devicesView
        anchors.fill: parent.fill
        model: listModel
        delegate: Button{
            text: (listModel.Name+listModel.type+listModel.address)
            //Binding { target: listModel; property: "labelText"; value:  }

        }
    }
}

main.cpp

#include <QGuiApplication>
#include <QQmlContext>
#include <QQmlApplicationEngine>

#include "deviceviewmodel.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    DevicesModel deviceMod;
    MainViewModel deviceViewModell(deviceMod);

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("listModel", &deviceViewModell);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

Thanks in advance!!

1

There are 1 best solutions below

0
user19012733 On

Returning the m_deviceModel from mainViewModel, which contains the instance of DeviceModel which reads data from DeviceListModel.

DevicesModel *MainViewModel::deviceModel() const
{
    return m_deviceModel;
}