【QML 多级菜单设计】利用ListView设计多级菜单, 支持动态
在本文中,我们将探讨如何使用Qt Quick和QML来实现一个多级折叠下拉导航菜单,该菜单支持动态添加和卸载子菜单项
1 import QtQuick 2.15
2 import QtQuick.Controls 2.15
3 import QtQuick.Layouts 1.15
4
5 Item {
6 id: root
7 width: 200
8 height: 40
9
10 // 示例数据:支持多层级结构
11 property var subModel: {
12 "水果": {
13 "国产": {
14 "苹果": ["红富士", "蛇果"],
15 "香蕉": ["小米蕉", "皇帝蕉"],
16 "橙子": ["脐橙", "血橙"]
17 },
18 "进口": {
19 "苹果": ["富士", "嘎啦"],
20 "香蕉": ["小香蕉", "大蕉"],
21 "橙子": ["血橙", "普通橙"]
22 }
23 },
24 "蔬菜": {
25 "国产": {
26 "土豆": ["黄心土豆", "紫土豆"],
27 "胡萝卜": ["红胡萝卜", "黄胡萝卜"],
28 "黄瓜": ["旱黄瓜", "水黄瓜"]
29 },
30 "进口": {
31 "土豆": ["外国土豆1", "外国土豆2"],
32 "胡萝卜": ["外国胡萝卜1", "外国胡萝卜2"],
33 "黄瓜": ["外国黄瓜1", "外国黄瓜2"]
34 }
35 },
36 "1": {
37 "A组": {
38 "11": ["111", "112"],
39 "12": ["121", "122"],
40 "13": ["131", "132"]
41 },
42 "B组": {
43 "11": ["211", "212"],
44 "12": ["221", "222"],
45 "13": ["231", "232"]
46 }
47 },
48 "2": {
49 "C组": {
50 "21": ["311", "312"],
51 "22": ["321", "322"],
52 "23": ["331", "332"]
53 },
54 "D组": {
55 "21": ["411", "412"],
56 "22": ["421", "422"],
57 "23": ["431", "432"]
58 }
59 }
60 }
61
62 // 当前选中的菜单项
63 property string selectedSubItem: "设备责任人"
64
65 // 用于存储所有已打开的动态菜单 Popup(索引即层级)
66 property var openMenus: []
67
68 // 关闭从指定层级开始的所有已打开菜单
69 function closeDynamicMenus(level) {
70 for (var i = openMenus.length - 1; i >= level; i--) {
71 if (openMenus[i]) {
72 openMenus[i].close();
73 openMenus[i].destroy();
74 }
75 openMenus.splice(i, 1);
76 }
77 }
78
79 // 在指定层级打开菜单
80 // level:菜单层级(主菜单点击后,子菜单为 1 级,依此类推)
81 // data:当前层级菜单数据
82 // anchor:用于定位 Popup 的参照项
83 function openDynamicMenu(level, data, anchor) {
84 closeDynamicMenus(level);
85 var menu = dynamicMenuComponent.createObject(root, { "menuData": data, "level": level });
86 var pos = anchor.mapToItem(root, anchor.width, 0);
87 menu.x = pos.x;
88 menu.y = pos.y;
89 menu.open();
90 openMenus[level] = menu;
91 }
92
93 // 主按钮:显示当前选中项
94 Rectangle {
95 id: mainButton
96 width: root.width
97 height: root.height
98 color: "lightgray"
99 border.color: "gray"
100 radius: 5
101
102 Text {
103 id: mainText
104 anchors.centerIn: parent
105 text: root.selectedSubItem || "请选择"
106 }
107
108 MouseArea {
109 anchors.fill: parent
110 onClicked: {
111 // 显示主菜单时,关闭所有子菜单
112 mainListView.visible = true;
113 closeDynamicMenus(0);
114 }
115 }
116 }
117
118 // 主菜单 ListView,显示最外层(第一层)的菜单项
119 ListView {
120 id: mainListView
121 visible: false
122 width: root.width
123 x: mainButton.x
124 y: mainButton.y + mainButton.height
125 // 根据内容高度动态调整,但最多200像素
126 height: Math.min(contentHeight, 200)
127 model: Object.keys(root.subModel)
128 delegate: Item {
129 width: mainListView.width
130 height: root.height
131 RowLayout {
132 anchors.fill: parent
133 spacing: 0
134 // 左侧显示菜单文本
135 Rectangle {
136 id: mainTextRect
137 Layout.preferredWidth: parent.width * 0.75
138 height: parent.height
139 color: "lightgray"
140 Text {
141 anchors.centerIn: parent
142 text: modelData
143 verticalAlignment: Text.AlignVCenter
144 padding: 10
145 }
146 MouseArea {
147 anchors.fill: parent
148 onClicked: {
149 var childData = root.subModel[modelData];
150 if (childData !== undefined &&
151 ((Array.isArray(childData) && childData.length > 0) ||
152 (typeof childData === "object" && Object.keys(childData).length > 0))) {
153 // 存在子菜单,打开下一级菜单(level 1)
154 // openDynamicMenu(1, childData, mainTextRect);
155 root.selectedSubItem = modelData;
156 mainText.text = modelData;
157 mainListView.visible = false;
158 closeDynamicMenus(0);
159
160 } else {
161 // 叶子节点:更新选中并关闭所有菜单
162 root.selectedSubItem = modelData;
163 mainText.text = modelData;
164 mainListView.visible = false;
165 closeDynamicMenus(0);
166 }
167 }
168 }
169 }
170 // 右侧显示箭头:如果存在子菜单则显示 "",否则为空
171 Rectangle {
172 id: mainArrowRect
173 Layout.preferredWidth: parent.width * 0.25
174 height: parent.height
175 color: "lightgray"
176 Text {
177 anchors.centerIn: parent
178 color: "green"
179 text: {
180 var childData = root.subModel[modelData];
181 if (childData !== undefined &&
182 ((Array.isArray(childData) && childData.length > 0) ||
183 (typeof childData === "object" && Object.keys(childData).length > 0))) {
184 return "";
185 }
186 return "";
187 }
188 }
189 MouseArea {
190 anchors.fill: parent
191 onClicked: {
192 var childData = root.subModel[modelData];
193 if (childData !== undefined &&
194 ((Array.isArray(childData) && childData.length > 0) ||
195 (typeof childData === "object" && Object.keys(childData).length > 0))) {
196 openDynamicMenu(1, childData, mainArrowRect);
197 }
198 }
199 }
200 }
201 }
202 }
203 }
204
205 // 通用动态菜单 Popup 组件,用于构造任意层级的子菜单
206 Component {
207 id: dynamicMenuComponent
208 Popup {
209 id: dynPopup
210 // 当前层级菜单数据(可以是数组或对象)
211 property var menuData
212 // 当前菜单层级,主菜单点击后生成的菜单 level 为 1,依次递增
213 property int level: 0
214 width: root.width*0.75
215 padding: 1
216 height: Math.min(listView.contentHeight, 200)
217 background: Rectangle {
218 color: "#ffffff"
219 border.color: "#cccccc"
220 radius: 5
221 }
222 ListView {
223 id: listView
224 anchors.fill: parent
225 model: (typeof dynPopup.menuData === "object" && !Array.isArray(dynPopup.menuData))
226 ? Object.keys(dynPopup.menuData)
227 : dynPopup.menuData
228 delegate: Item {
229 width: listView.width
230 height: root.height
231 RowLayout {
232 anchors.fill: parent
233 spacing: 0
234 Layout.margins: 0
235 // 左侧显示菜单项文本
236 Rectangle {
237 id: dynTextRect
238 Layout.fillWidth: true
239 Layout.preferredWidth: parent.width * 0.75
240 height: parent.height
241 color: "lightgray"
242 Text {
243 anchors.centerIn: parent
244 text: modelData
245 verticalAlignment: Text.AlignVCenter
246 padding: 10
247 }
248 MouseArea {
249 anchors.fill: parent
250 onClicked: {
251 var childData;
252 if (typeof dynPopup.menuData === "object" && !Array.isArray(dynPopup.menuData)) {
253 childData = dynPopup.menuData[modelData];
254 }
255 if (childData !== undefined &&
256 ((Array.isArray(childData) && childData.length > 0) ||
257 (typeof childData === "object" && Object.keys(childData).length > 0))) {
258 // 存在下一级子菜单,打开下一层
259 // root.openDynamicMenu(dynPopup.level + 1, childData, dynTextRect);
260 root.selectedSubItem = modelData;
261 mainText.text = modelData;
262 mainListView.visible = false;
263 root.closeDynamicMenus(0);
264 } else {
265 // 叶子节点:更新选中并关闭所有菜单
266 root.selectedSubItem = modelData;
267 mainText.text = modelData;
268 mainListView.visible = false;
269 root.closeDynamicMenus(0);
270 }
271 }
272 }
273 }
274 // 右侧箭头,若存在子菜单则显示,否则为空
275 Rectangle {
276 id: dynArrowRect
277 Layout.fillWidth: true
278 Layout.preferredWidth: parent.width * 0.25
279 height: parent.height
280 color: "lightgray"
281 Text {
282 anchors.centerIn: parent
283 color: "green"
284 text: {
285 var childData;
286 if (typeof dynPopup.menuData === "object" && !Array.isArray(dynPopup.menuData)) {
287 childData = dynPopup.menuData[modelData];
288 }
289 if (childData !== undefined &&
290 ((Array.isArray(childData) && childData.length > 0) ||
291 (typeof childData === "object" && Object.keys(childData).length > 0))) {
292 return "";
293 }
294 return "";
295 }
296 }
297 MouseArea {
298 anchors.fill: parent
299 onClicked: {
300 var childData;
301 if (typeof dynPopup.menuData === "object" && !Array.isArray(dynPopup.menuData)) {
302 childData = dynPopup.menuData[modelData];
303 }
304 if (childData !== undefined &&
305 ((Array.isArray(childData) && childData.length > 0) ||
306 (typeof childData === "object" && Object.keys(childData).length > 0))) {
307 root.openDynamicMenu(dynPopup.level + 1, childData, dynArrowRect);
308 }
309 }
310 }
311 }
312 }
313 }
314 }
315 }
316 }
317 }

【QML 多级菜单设计】利用ListView设计多级菜单, 支持动态的更多相关文章
- 利用jQuery设计横/纵向菜单
在网页中,菜单扮演着"指路者"的角色.怎样设计一个人性化的菜单呢.以下小编带着大家一起做. 效果图: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi ...
- 利用MVVM设计快速开发个人中心、设置等模块
我们在做iOS开发过程中,静态页面的开发比开发动态页面更让我们开发者抓狂.因为动态页面通常是一个页面一种cell样式,作为开发者只需要专注于定制好一种样式之后,就可以使用数据填充出较好的界面.而静态c ...
- 利用PYTHON设计计算器功能
通过利用PYTHON 设计处理计算器的功能如: 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 ))- (-4*3 ...
- 利用单例模式设计数据库连接Model类
之前在<[php]利用php的构造函数与析构函数编写Mysql数据库查询类>(点击打开链接)写过的Mysql数据库查询类还不够完美,利用<[Java]单例模式>(点击打开链接) ...
- QML与C++交互:登陆界面设计
QML与C++交互:登陆界面设计 本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明. 环境: 主机:WIN7 开发环境:Qt5.2.1 说明: QML设计前 ...
- FPGA学习:VHDL设计灵活性&不同设计思路比较
概要 由于VHDL编程实现数字电路具有很高的灵活性,为多种不同的思路编写实现同一种功能提供了可能.这些不同的设计思路,在耗费资源,可靠性,速度上也有很大的差异,往往需要我们根据实际需求和资源条件选择适 ...
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(43)-工作流设计-字段分类设计
原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(43)-工作流设计-字段分类设计 系列目录 建立好42节的表之后,每个字段英文表示都是有意义的说明.先建立 ...
- .NET架构设计、框架设计系列文章总结
从事.NET开发到现在已经有七个年头了.慢慢的可能会很少写.NET文章了.不知不觉竟然走了这么多年,热爱.NET热爱c#.突然想对这一路的经历进行一个总结. 是时候开始下一阶段的旅途,希望这些文章可以 ...
- (转)Android Binder设计与实现 – 设计篇
原文地址(貌似已打不开):Android Binder设计与实现 – 设计篇 ------------------------------------------------------------- ...
- WPF换肤之四:界面设计和代码设计分离
原文:WPF换肤之四:界面设计和代码设计分离 说起WPF来,除了总所周知的图形处理核心的变化外,和Winform比起来,还有一个巨大的变革,那就是真正意义上做到了界面设计和代码设计的分离.这样可以让美 ...
随机推荐
- 基于 C\# 和 .NET 的 Spread.NET 数据处理实战
引言 在当今数字化的时代,数据处理和分析在各个领域都扮演着至关重要的角色.对于开发者而言,选择一款功能强大且易于集成的表格控件来处理数据是提高开发效率和质量的关键.Spread.NET 作为 Grap ...
- Java实现密码、文件MD5加密,密码sha256、sha384、sha512Hex等加密
SHA512加密(参考:https://blog.csdn.net/zdj_Develop/article/details/89326621?utm_medium=distribute.pc_rele ...
- 东航MU5735空难事件总结与分析
东航MU5735空难事件总结与分析 事件概述 日期:2022年3月21日 航班:东方航空MU5735(昆明长水机场→广州白云机场) 机型:波音737-800(注册号B-1791,机龄6.8年) 伤亡: ...
- 数栈技术分享:用短平快的方式告诉你Flink-SQL的扩展实现
数栈是云原生-站式数据中台PaaS,我们在github和gitee上有一个有趣的开源项目:FlinkX,FlinkX是一个基于Flink的批流统一的数据同步工具,既可以采集静态的数据,也可以采集实时变 ...
- Vertx 实现webapi实战项目(二)
消息解析:消息序列化和反序列化---上传json解析和返回json编码. 整理下工程项目 一:实现消息接口,在imp文件夹下新建接口MessageFactory 1 /****** 2 * 消息编 ...
- laradock下mysql You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYS...
上图 异常报错 mysql You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_R ...
- vue2使用vue3语法
Composition API Composition API 将是 Vue 3 的核心功能,它具有许多更改和性能改进. 我们也可以在 Vue 2 中通过 npm 插件@vue/composition ...
- Games102 作业进行时 作业1 实现并分析四种拟合算法 TODO
简介 由于已经过了上课时间, 不过工程上有对于拟合的使用, 现学现用. TODO
- virtual studio 插入 函数注释头 snippet方式
简介 以前通过一个插件就可以管理所有的函数,也可以通过snippet插入 参考链接 如何使用snippet https://blog.csdn.net/weixin_30278237/article/ ...
- java group Layout 组框架
简介 https://netbeans.org/features/java/swing.html 里面有一个mattise 专门用来生成布局代码 GroupLayout code 代码先不贴了