多层Model更新多层ListView

发布于:2025-08-06 ⋅ 阅读:(15) ⋅ 点赞:(0)

一、总体架构

QML (三层 ListView)
    └─ C++ 单例 DataCenter (QQmlContext 注册)
         ├─ L1Model (一级节点)
         │   └─ 内部持有 QList<L2Model*>
         │        └─ L2Model (二级节点)
         │            └─ 内部持有 QList<L3Model*>
         │                 └─ L3Model (三级节点)
  • 每个 Model 都是 QAbstractListModel 的子类

  • Model 更新后通过 dataChanged() / beginInsertRows() 等标准信号通知 QML

  • DataCenter 为单例,全局只有一个实例

二、文件架构与作用

ThreeLevelListView/
├── main.cpp
├── DataCenter.h / .cpp
├── CountryModel.h / .cpp
├── ProvinceModel.h / .cpp
├── CityModel.h / .cpp
├── qml.qrc
└── main.qml

1.mian.cpp 

作用:注册管理单例,在QML使用DataCenter单例

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "DataCenter.h"

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

    QQmlApplicationEngine engine;
    /* 单例注册 */
    DataCenter &dc = DataCenter::instance();
    engine.rootContext()->setContextProperty("DataCenter", &dc);

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    return app.exec();
}

2.DataCenter类

作用:管理三级model类,作为QML使用的接口

#ifndef DATACENTER_H
#define DATACENTER_H

#include <QObject>
#include "CountryModel.h"

class DataCenter : public QObject
{
    Q_OBJECT
    Q_PROPERTY(CountryModel* countryModel READ countryModel CONSTANT)
public:
    static DataCenter* instance(){
		static DataCenter g_instance;
		return &g_instance
	}
    CountryModel* countryModel() const { return m_countryModel; }

private:
    explicit DataCenter(QObject *parent = nullptr) : QObject(parent), m_countryModel(new CountryModel(this)){}
    CountryModel *m_countryModel;
};

#endif // DATACENTER_H

3.CoutryModel类,继承QAbstractListModel类

作用:第一级Model,管理第二级Model,每一个都有自己的结构提与对应的枚举

struct CountryItem {
    QString name;
    ProvinceModel *provinceModel;
};//结构体

class CountryModel : public QAbstractListModel
{
    Q_OBJECT
public:
    enum Role { NameRole = Qt::UserRole + 1, ObjRole };//及认购提对应枚举
#ifndef COUNTRYMODEL_H
#define COUNTRYMODEL_H

#include <QAbstractListModel>
#include "ProvinceModel.h"

struct CountryItem {
    QString name;
    ProvinceModel *provinceModel;
};

class CountryModel : public QAbstractListModel
{
    Q_OBJECT
public:
    enum Role { NameRole = Qt::UserRole + 1, ObjRole } : QAbstractListModel(parent) {}
    explicit CountryModel(QObject *parent = nullptr);

	//固定函数,函数里面的内容每级不一样
    int rowCount(const QModelIndex & = QModelIndex()) const override{ return m_items.size(); }
    
	QVariant data(const QModelIndex &, int role) const override{
		if (!index.isValid() || index.row() >= m_items.size()) return QVariant();
		if (role == NameRole) return m_items.at(index.row()).name;
		if (role == ObjRole) return QVariant::fromValue(m_items.at(index.row()).provinceModel);
		return QVariant();
		}	

    QHash<int, QByteArray> roleNames() const override{
		static QHash<int, QByteArray> roles{{NameRole, "name"}, {ObjRole, "obj"}};
		return roles;
	}


	//供QML调用
    Q_INVOKABLE bool add(const QString &name){
		beginInsertRows(QModelIndex(), m_items.size(), m_items.size());
		m_items.append({name, new ProvinceModel(this)});
		endInsertRows();
		return true;
	}
	
    Q_INVOKABLE bool remove(int index){
    if (index < 0 || index >= m_items.size()) return false;
    beginRemoveRows(QModelIndex(), index, index);
    delete m_items.takeAt(index).provinceModel;
    endRemoveRows();
    return true;
}

    Q_INVOKABLE bool update(int index, const QString &newName){
		if (index < 0 || index >= m_items.size()) return false;
		m_items[index].name = newName;
		emit dataChanged(createIndex(index, 0), createIndex(index, 0), {NameRole});
		return true;
	}

    Q_INVOKABLE QObject* provinceModelAt(int idx) const{
		if (idx < 0 || idx >= m_items.size()) return nullptr;
			return m_items.at(idx).provinceModel;
	}

private:
    QList<CountryItem> m_items;
};

#endif // COUNTRYMODEL_H

4.PrpvinceModel类,继承QAbstractListModel类

作用:第二级Model,管理第三级Model

#ifndef PROVINCEMODEL_H
#define PROVINCEMODEL_H

#include <QAbstractListModel>
#include "CityModel.h"

struct ProvinceItem {
    QString name;
    CityModel *cityModel;
};

class ProvinceModel : public QAbstractListModel
{
    Q_OBJECT
public:
    enum Role { NameRole = Qt::UserRole + 1, CityModelRole };
    explicit ProvinceModel(QObject *parent = nullptr) : QAbstractListModel{parent}{}

    int rowCount(const QModelIndex & = QModelIndex()) const override{
		return m_items.size();
	}

    QVariant data(const QModelIndex &, int role) const override{
		if (!index.isValid() || index.row() >= m_items.size()) return QVariant();
		if (role == NameRole) return m_items.at(index.row()).name;
		if (role == CityModelRole) return QVariant::fromValue(m_items.at(index.row()).cityModel);
		return QVariant();
	}
	
    QHash<int, QByteArray> roleNames() const override{
		static QHash<int, QByteArray> roles{{NameRole, "name"}, {CityModelRole, "obj"}};
		return roles;
	}
	
	

    Q_INVOKABLE bool add(const QString &name){
		beginInsertRows(QModelIndex(), m_items.size(), m_items.size());
		m_items.append({name, new CityModel(this)});
		endInsertRows();
		return true;
	}

    Q_INVOKABLE bool remove(int index){
		if (index < 0 || index >= m_items.size()) return false;
		beginRemoveRows(QModelIndex(), index, index);
		delete m_items.takeAt(index).cityModel;
		endRemoveRows();
		return true;
	}
	
    Q_INVOKABLE bool update(int index, const QString &newName){
		if (index < 0 || index >= m_items.size()) return false;
		m_items[index].name = newName;
		emit dataChanged(createIndex(index, 0), createIndex(index, 0), {NameRole});
		return true;
	}

    Q_INVOKABLE QObject* cityModelAt(int idx) const{
		if (idx < 0 || idx >= m_items.size()) return nullptr;
			return m_items.at(idx).cityModel;
	}

private:
    QList<ProvinceItem> m_items;
};

#endif // PROVINCEMODEL_H

5.CityModel类,继承QAbstractListModel类

作用:第三级Model,管理自己的成员

#ifndef CITYMODEL_H
#define CITYMODEL_H

#include <QAbstractListModel>

struct CityItem {
    QString name;
};

class CityModel : public QAbstractListModel
{
      Q_OBJECT
public:
    enum Role { NameRole = Qt::UserRole + 1, };
    explicit CityModel(QObject *parent = nullptr): QAbstractListModel{parent}{}

    int rowCount(const QModelIndex & = QModelIndex()) const override{
		return m_items.size();
	}

    QVariant data(const QModelIndex &, int role) const override{
		if (!index.isValid() || index.row() >= m_items.size()) return QVariant();
		if (role == NameRole) return m_items.at(index.row()).name;
		//if (role == CityModelRole) return QVariant::fromValue(m_items.at(index.row()).cityModel);
		return QVariant();
	}
    QHash<int, QByteArray> roleNames() const override{
		//static QHash<int, QByteArray> roles{{NameRole, "name"}, {CityModelRole, "obj"}};
		static QHash<int, QByteArray> roles{{NameRole, "name"}, };
		return roles;
	}



    Q_INVOKABLE bool add(const QString &name){
		beginInsertRows(QModelIndex(), m_items.size(), m_items.size());
		m_items.append({name});
		endInsertRows();
		return true;
	}
    Q_INVOKABLE bool remove(int index){
		if (index < 0 || index >= m_items.size()) return false;
		beginRemoveRows(QModelIndex(), index, index);
		m_items.takeAt(index);
		endRemoveRows();
		return true;
	}
    Q_INVOKABLE bool update(int index, const QString &newName){
		if (index < 0 || index >= m_items.size()) return false;
		m_items[index].name = newName;
		emit dataChanged(createIndex(index, 0), createIndex(index, 0), {NameRole});
		return true;
	}


private:
    QList<CityItem> m_items;
};

#endif // CITYMODEL_H

6.mian.qml

作用:显示界面

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15


Window {
    visible: true
    width: 1000;
    height: 600
    title: "3 级嵌套 Model 示例"

    RowLayout {
        anchors.fill: parent
        anchors.margins: 10
        spacing: 10

        /* ---------- Level 1 : Country ---------- */
        ColumnLayout {
            Layout.fillWidth: true
            Label { text: "国家"; font.bold: true }
            ListView {
                id: lvCountry
                Layout.fillHeight: true; Layout.fillWidth: true
                model: DataCenter.countryModel
                delegate: Rectangle {
                    width: lvCountry.width; height: 30
                    color: (lvCountry.currentIndex === index) ? "lightblue" : "white"
                    Text { text: name; anchors.centerIn: parent }
                    MouseArea {
                        anchors.fill: parent
                        onClicked: lvCountry.currentIndex = index
                    }
                }
                currentIndex: -1
            }
            TextField { id: tfCountry; Layout.fillWidth: true; placeholderText: "输入国家" }
            RowLayout {
               Button { text: "添加"; onClicked:{ DataCenter.countryModel.add(tfCountry.text); tfCountry.clear() }}
                Button { text: "删除"; enabled: lvCountry.currentIndex>=0;
                         onClicked: DataCenter.countryModel.remove(lvCountry.currentIndex) }
                Button { text: "修改"; enabled: lvCountry.currentIndex>=0;
                         onClicked: DataCenter.countryModel.update(lvCountry.currentIndex, tfCountry.text) }
            }
        }

        /* ---------- Level 2 : Province ---------- */
        ColumnLayout {
            Layout.fillWidth: true
            Label { text: "省份"; font.bold: true }
            ListView {
                id: lvProvince
                Layout.fillHeight: true; Layout.fillWidth: true
                model: lvCountry.currentIndex >= 0
                          ? DataCenter.countryModel.provinceModelAt(lvCountry.currentIndex)
                          : null
                delegate: Rectangle {
                    width: lvProvince.width; height: 30
                    color: (lvProvince.currentIndex === index) ? "lightblue" : "white"
                    Text { text: name; anchors.centerIn: parent }
                    MouseArea {
                        anchors.fill: parent
                        onClicked: lvProvince.currentIndex = index
                    }
                }
                currentIndex: -1
            }
            TextField { id: tfProvince; Layout.fillWidth: true; placeholderText: "输入省份" }
            RowLayout {
                Button { text: "添加"; enabled: lvCountry.currentIndex>=0;
                         onClicked: { lvProvince.model.add(tfProvince.text); tfProvince.clear() } }
                Button { text: "删除"; enabled: lvProvince.currentIndex>=0;
                         onClicked: lvProvince.model.remove(lvProvince.currentIndex) }
                Button { text: "修改"; enabled: lvProvince.currentIndex>=0;
                         onClicked: lvProvince.model.update(lvProvince.currentIndex, tfProvince.text) }
            }
        }

        /* ---------- Level 3 : City ---------- */
        ColumnLayout {
            Layout.fillWidth: true
            Label { text: "市"; font.bold: true }
            ListView {
                id: lvCity
                Layout.fillHeight: true; Layout.fillWidth: true
                model: lvProvince.currentIndex >= 0
                           ? lvProvince.model.cityModelAt(lvProvince.currentIndex)
                           : null
                delegate: Rectangle {
                    width: lvCity.width; height: 30
                    color: (lvCity.currentIndex === index) ? "lightblue" : "white"
                    Text { text: name; anchors.centerIn: parent }
                    MouseArea {
                        anchors.fill: parent
                        onClicked: lvCity.currentIndex = index
                    }
                }
                currentIndex: -1
            }
            TextField { id: tfCity; Layout.fillWidth: true; placeholderText: "输入市" }
            RowLayout {
                Button { text: "添加"; enabled: lvProvince.currentIndex>=0;
                         onClicked: { lvCity.model.add(tfCity.text); tfCity.clear() } }
                Button { text: "删除"; enabled: lvCity.currentIndex>=0;
                         onClicked: lvCity.model.remove(lvCity.currentIndex) }
                Button { text: "修改"; enabled: lvCity.currentIndex>=0;
                         onClicked: lvCity.model.update(lvCity.currentIndex, tfCity.text) }
            }
        }
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到