You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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();
}

关键修改说明

  1. DevicesListModel:补充roleNames()重写,让QML能识别自定义角色;使用beginResetModel()/endResetModel()通知数据变化,确保QML视图实时更新;添加count属性方便QML监听数量。
  2. DevicesModel:作为业务模型层,持有底层设备模型的实例,封装业务逻辑(可扩展添加数据过滤、转换等逻辑)。
  3. MainViewModel:暴露设备模型属性给QML,作为View和Model的中间层,可在此添加视图相关的业务逻辑。
  4. QML:直接绑定ViewModel的设备模型,delegate中通过角色名称直接访问数据,符合MVVM的视图与数据分离原则。

内容的提问来源于stack exchange,提问作者user19012733

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 07:27:02