一、总体架构
QML (三层 ListView)
└─ C++ 单例 DataCenter (QQmlContext 注册)
├─ L1Model (一级节点)
│ └─ 内部持有 QList<L2Model*>
│ └─ L2Model (二级节点)
│ └─ 内部持有 QList<L3Model*>
│ └─ L3Model (三级节点)
每个 Model 都是
QAbstractListModel
的子类Model 更新后通过
dataChanged()
/beginInsertRows()
等标准信号通知 QMLDataCenter 为单例,全局只有一个实例
二、文件架构与作用
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) }
}
}
}
}