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!!
Returning the
m_deviceModelfrommainViewModel, which contains the instance ofDeviceModelwhich reads data fromDeviceListModel.