基于MVVM模式的QAbstractListModel与QML ListView整合实现求助
完善MVVM架构下QAbstractListModel与QML ListView的绑定实现
按照你设定的View(QML-ListView)→ViewModel(C++)→Model(C++)→DevicesListModel(QAbstractListModel)架构,以下是代码的完善方案:
1. 修复DevicesListModel(底层数据模型)
DevicesListModel.h
补充角色名称重写声明,添加count属性的Q_PROPERTY,确保QML能识别模型角色并监听数据数量变化:
#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) Q_PROPERTY(int count READ rowCount NOTIFY countChanged) // 暴露数量属性 public: enum DeviceModelRoles { NameRole = Qt::UserRole + 1, AddressRole, TypeRole }; Q_ENUM(DeviceModelRoles) static QObject *qobject_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) return new DevicesListModel(); } int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QHash<int, QByteArray> roleNames() const override; // 重写角色名称方法 signals: void countChanged(int count); private slots: void devicesChanged(QVariantList devices); private: explicit DevicesListModel(QObject *parent = nullptr); ~DevicesListModel(); QList<QVariantMap> m_device_list; QHash<int, QByteArray> m_roles; }; #endif // DEVICESLISTMODEL_H
DevicesListModel.cpp
实现角色名称方法,修改devicesChanged方法,使用Qt模型的标准通知机制触发QML更新:
#include "deviceslistmodel.h" #include <QDebug> DevicesListModel::DevicesListModel(QObject *parent) : QAbstractListModel(parent){ m_roles[NameRole] = "name"; m_roles[AddressRole] = "address"; m_roles[TypeRole] = "type"; // 恢复和底层设备发现模块的连接(按需启用) // QObject::connect(DeviceDiscovery::instance(), SIGNAL(devicesChanged(QVariantList)), this, SLOT(devicesChanged(QVariantList))); } 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() || index.row() >= m_device_list.size()) return QVariant(); const QVariantMap &device = m_device_list.at(index.row()); switch (role) { case NameRole: return device["name"].toString(); case AddressRole: return device["address"].toString(); case TypeRole: return device["type"].toString(); default: return QVariant(); } } QHash<int, QByteArray> DevicesListModel::roleNames() const { return m_roles; } void DevicesListModel::devicesChanged(QVariantList devices) { beginResetModel(); // 批量修改前通知模型重置 m_device_list.clear(); // 批量添加新设备 for (const QVariant &devVar : devices) { QVariantMap devMap = devVar.toMap(); m_device_list.append(devMap); } endResetModel(); // 结束重置,通知QML更新视图 emit countChanged(m_device_list.count()); }
2. 重构DevicesModel(业务模型层)
让它持有DevicesListModel实例,作为ViewModel和底层模型的中间层:
DevicesModel.h
#ifndef DEVICESMODEL_H #define DEVICESMODEL_H #include <QObject> #include "deviceslistmodel.h" class DevicesModel : public QObject { Q_OBJECT Q_PROPERTY(DevicesListModel* deviceListModel READ deviceListModel CONSTANT) public: explicit DevicesModel(QObject *parent = nullptr); virtual ~DevicesModel() {} DevicesListModel* deviceListModel() const; private: DevicesListModel* m_deviceListModel; }; #endif // DEVICESMODEL_H
DevicesModel.cpp
#include "devicesmodel.h" DevicesModel::DevicesModel(QObject *parent) : QObject(parent) { // 获取DevicesListModel单例实例 m_deviceListModel = qobject_cast<DevicesListModel*>(DevicesListModel::qobject_singletontype_provider(nullptr, nullptr)); } DevicesListModel* DevicesModel::deviceListModel() const { return m_deviceListModel; }
3. 完善MainViewModel(视图模型层)
暴露业务模型的设备列表模型属性,供QML绑定:
MainViewModel.h
#ifndef MAINVIEWMODEL_H #define MAINVIEWMODEL_H #include <QObject> #include "devicesmodel.h" class MainViewModel : public QObject { Q_OBJECT Q_PROPERTY(DevicesListModel* deviceModel READ deviceModel CONSTANT) public: explicit MainViewModel(DevicesModel* devicesModel, QObject *parent = nullptr); virtual ~MainViewModel() {} DevicesListModel* deviceModel() const; private: DevicesModel* m_devicesModel; }; #endif // MAINVIEWMODEL_H
MainViewModel.cpp
#include "mainviewmodel.h" MainViewModel::MainViewModel(DevicesModel* devicesModel, QObject *parent) : QObject(parent), m_devicesModel(devicesModel) { } DevicesListModel* MainViewModel::deviceModel() const { return m_devicesModel->deviceListModel(); }
4. 修改QML视图(View层)
绑定ViewModel的设备模型,正确使用角色名称访问数据:
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("设备列表") ListView{ id: devicesView anchors.fill: parent model: mainViewModel.deviceModel // 绑定ViewModel的设备模型 delegate: Button{ width: parent.width text: name + " | " + type + " | " + address // 直接使用角色名称访问数据 } } }
5. 修正main.cpp(初始化流程)
注册单例,正确初始化各层对象并传递给QML:
#include <QGuiApplication> #include <QQmlContext> #include <QQmlApplicationEngine> #include "deviceslistmodel.h" #include "devicesmodel.h" #include "mainviewmodel.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); // 注册DevicesListModel为QML单例 qmlRegisterSingletonType<DevicesListModel>("com.example.devices", 1, 0, "DevicesListModel", DevicesListModel::qobject_singletontype_provider); // 初始化各层对象 DevicesModel deviceModel; MainViewModel mainViewModel(&deviceModel); QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("mainViewModel", &mainViewModel); // 暴露ViewModel到QML engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; return app.exec(); }
关键修改说明
- DevicesListModel:补充
roleNames()重写,让QML能识别自定义角色;使用beginResetModel()/endResetModel()通知数据变化,确保QML视图实时更新;添加count属性方便QML监听数量。 - DevicesModel:作为业务模型层,持有底层设备模型的实例,封装业务逻辑(可扩展添加数据过滤、转换等逻辑)。
- MainViewModel:暴露设备模型属性给QML,作为View和Model的中间层,可在此添加视图相关的业务逻辑。
- QML:直接绑定ViewModel的设备模型,delegate中通过角色名称直接访问数据,符合MVVM的视图与数据分离原则。
内容的提问来源于stack exchange,提问作者user19012733
相关产品推荐
相关产品推荐

