一、总体架构

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_OBJECTQ_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.15Window {visible: truewidth: 1000;height: 600title: "3 级嵌套 Model 示例"RowLayout {anchors.fill: parentanchors.margins: 10spacing: 10/* ---------- Level 1 : Country ---------- */ColumnLayout {Layout.fillWidth: trueLabel { text: "国家"; font.bold: true }ListView {id: lvCountryLayout.fillHeight: true; Layout.fillWidth: truemodel: DataCenter.countryModeldelegate: Rectangle {width: lvCountry.width; height: 30color: (lvCountry.currentIndex === index) ? "lightblue" : "white"Text { text: name; anchors.centerIn: parent }MouseArea {anchors.fill: parentonClicked: 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: trueLabel { text: "省份"; font.bold: true }ListView {id: lvProvinceLayout.fillHeight: true; Layout.fillWidth: truemodel: lvCountry.currentIndex >= 0? DataCenter.countryModel.provinceModelAt(lvCountry.currentIndex): nulldelegate: Rectangle {width: lvProvince.width; height: 30color: (lvProvince.currentIndex === index) ? "lightblue" : "white"Text { text: name; anchors.centerIn: parent }MouseArea {anchors.fill: parentonClicked: 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: trueLabel { text: "市"; font.bold: true }ListView {id: lvCityLayout.fillHeight: true; Layout.fillWidth: truemodel: lvProvince.currentIndex >= 0? lvProvince.model.cityModelAt(lvProvince.currentIndex): nulldelegate: Rectangle {width: lvCity.width; height: 30color: (lvCity.currentIndex === index) ? "lightblue" : "white"Text { text: name; anchors.centerIn: parent }MouseArea {anchors.fill: parentonClicked: 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) }}}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/92032.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/92032.shtml
英文地址,请注明出处:http://en.pswp.cn/web/92032.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Git基础操作教程

本文目的是掌握Git基础操作教程一、Git简介Git&#xff1a;分布式版本控制系统&#xff0c;使用仓库(Repository)来记录文件的变化最流行的版本控制系统有两种&#xff1a;集中式&#xff08;SVN&#xff09;、分布式&#xff08;Git&#xff09;二、Git操作1.创建仓库仓库(Rep…

Android 之 Kotlin

变量变量的声明Kotlin使用var&#xff0c;val来声明变量&#xff0c;注意&#xff1a;Kotlin不再需要;来结尾var 可变变量&#xff0c;对应java的非final变量var b 1val不可变变量&#xff0c;对应java的final变量val a 1两种变量并未声明类型&#xff0c;这是因为Kotlin存在…

Design Compiler:布图规划探索(ICC)

相关阅读 Design Compilerhttps://blog.csdn.net/weixin_45791458/category_12738116.html?spm1001.2014.3001.5482 简介 在Design Compiler Graphical中&#xff0c;可以用布图规划探索(Floorplan Exploration)功能&#xff0c;打开IC Compiler进行布图规划的创建、修改与分…

《蓝牙低功耗音频技术架构解析》

《2025GAS声学大讲堂—音频产业创新技术公益讲座》低功耗蓝牙音频系列专题LE Audio & Auracast™专题讲座第1讲将于8月7日周四19点开讲&#xff0c;本次邀请了蓝牙技术联盟 技术与市场经理 鲁公羽 演讲&#xff0c;讲座主题&#xff1a;《蓝牙低功耗音频技术架构解析》。&…

ubuntu apt安装与dpkg安装相互之间的关系

0. 问题解释 在linux系统中&#xff0c;使用neofetch命令可以看到现在系统中使用dpkg, flatpak, snap安装的包的数量&#xff0c;那么使用apt安装的包被统计在什么位置了呢&#xff0c;使用apt的安装流程和使用flatpak的安装流程有什么关系和区别呢?1. apt 安装的包在哪里&…

YooAsset源码阅读-Downloader篇

YooAsset源码阅读-Downloader 继续 YooAsset 的 Downloader &#xff0c;本文将详细介绍如何创建下载器相关代码 CreateResourceDownloaderByAll 关键类 PlayModeImpl.csResourceDownloaderOperation.csDownloaderOperation.csBundleInfo.cs CreateResourceDownloaderByAll 方法…

豆包新模型与 PromptPilot 实操体验测评,AI 辅助创作的新范式探索

摘要&#xff1a;在 AI 技术飞速发展的当下&#xff0c;各类大模型及辅助工具层出不穷&#xff0c;为开发者和创作者带来了全新的体验。2025 年 7 月 30 日厦门站的火山方舟线下 Meetup&#xff0c;为我们提供了近距离接触豆包新模型与 PromptPilot 的机会。本次重点体验了实验…

深入探讨AI在测试领域的三大核心应用:自动化测试框架、智能缺陷检测和A/B测试优化,并通过代码示例、流程图和图表详细解析其实现原理和应用场景。

引言随着人工智能技术的飞速发展&#xff0c;软件测试领域正在经历一场深刻的变革。AI技术不仅提高了测试效率&#xff0c;还增强了测试的准确性和覆盖范围。本文将深入探讨AI在测试领域的三大核心应用&#xff1a;自动化测试框架、智能缺陷检测和A/B测试优化&#xff0c;并通过…

音视频学习笔记

0.vs应用其他库配置1基础 1.1视频基础 音视频录制原理音视频播放原理图像表示rgb图像表示yuvhttps://blog.51cto.com/u_7335580/2059670 https://blog.51cto.com/cto521/1944224 https://blog.csdn.net/mandagod/article/details/78605586?locationNum7&fps1 视频主要概念…

LLM隐藏层状态: outputs.hidden_states 是 MLP Residual 还是 Layer Norm

outputs.hidden_states 是 MLP Residual 还是 Layer Norm outputs.hidden_states 既不是单纯的 MLP Residual,也不是单纯的 Layer Norm,而是每一层所有组件(包括 Layer Norm、注意力、MLP、残差连接等)处理后的最终隐藏状态。具体需结合 Transformer 层的结构理解: 1. T…

XML 用途

XML 用途 引言 XML&#xff08;可扩展标记语言&#xff09;是一种用于存储和传输数据的标记语言。自1998年推出以来&#xff0c;XML因其灵活性和可扩展性&#xff0c;在众多领域得到了广泛应用。本文将详细介绍XML的用途&#xff0c;帮助读者全面了解这一重要技术。 一、数据存…

亚马逊撤离Google购物广告:重构流量生态的战略博弈

战略突变&#xff1a;从渐进收缩到全面退潮的背后逻辑亚马逊在2025年7月突然全面停止Google Shopping广告投放&#xff0c;这场看似 abrupt 的决策实则经历了一年多的战略铺垫&#xff0c;从2024年Q1开始的预算削减&#xff0c;到2025年Q2美国市场支出减半&#xff0c;直至核心…

【QT】常⽤控件详解(三)常用按钮控件PushButton RadioButton CheckButton Tool Button

文章目录前言一、PushButton1.1 QAbstractButton1.2 添加图标的按钮1.3 给按钮添加快捷键1.4 代码⽰例:按钮的重复触发二、 RadioButtion2.1简介2.2 几个槽函数 click,press,release, toggled 的区别2.2 模拟分组点餐三、 CheckBox四、Tool Button&#x1f6a9;总结前言 一、P…

数据结构:反转链表(reverse the linked list)

目录 通过交换元素值实现反转&#xff08;reverse by swapping elements&#xff09; 滑动指针&#xff08;sliding pointers&#xff09; 使用滑动指针反转链表&#xff08;Reversing a Linked List using Sliding Pointers&#xff09; 对比分析 如何用递归&#xff08;R…

【C#】基于SharpCompress实现压缩包解压功能

1.SharpCompress安装 在vs的nuget下搜索安装SharpCompress&#xff0c;如图所示2.解压缩包功能实现 /// <summary> /// 解压压缩包 /// </summary> /// <param name"filePath">压缩包文件路径</param> /// <param name"directoryPat…

mybatis连接PGSQL中对于json和jsonb的处理方法

pgsql数据库表字段设置了jsonb格式&#xff1b;在java的实体里使用String或者对象转换会一直提示一个错误&#xff1a; Caused by: org.postgresql.util.PSQLException: ERROR: column “xx” is of type jsonb but expression is of type character varying 需要加一个转换方法…

Spring AI Alibaba Graph 深度解析:原理、架构与应用实践

1. 引言概述 1.1 什么是 Spring AI Alibaba Graph Spring AI Alibaba Graph 是阿里云团队基于 Spring AI 生态开发的一个强大的工作流编排框架&#xff0c;专门用于构建复杂的 AI 应用。它采用声明式编程模型&#xff0c;通过图结构来定义和管理 AI 工作流&#xff0c;让开发…

C++少儿编程(二十一)—软件执行流程

让我们将以下程序视为用C编写的示例程序。步骤1&#xff1a;预处理器将源代码转换为扩展代码。当您运行程序时&#xff0c;源代码首先被发送到称为预处理器的工具。预处理器主要做两件事&#xff1a;它会从程序中删除注释。它扩展了预处理器指令&#xff0c;如宏或文件包含。它…

精通Webpack搭建Vue2.0项目脚手架指南

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;在Web应用程序开发中&#xff0c;Vue 2.0因其虚拟DOM、单文件组件、增强的生命周期钩子和Vuex及Vue Router状态管理与路由解决方案&#xff0c;成为了提高开发效率和代码组织性的关键。Webpack作为必不可少的模…

无偿分享120套开源数据可视化大屏H5模板

数据可视化跨越了语言、技术和专业的边界&#xff0c;是能够推动实现跨界沟通&#xff0c;实现国际间跨行业的创新的工具。正如画家用颜料表达自我&#xff0c;作者用文字讲述故事&#xff0c;而统计人员用数字沟通 ...... 同样&#xff0c;数据可视化的核心还是传达信息。而设…