【QML 多级菜单设计】利用ListView设计多级菜单

news/2025/2/24 9:09:47/文章来源:https://www.cnblogs.com/Zhouce/p/18733338

 

 

  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 }

 

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

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

相关文章

web课程

<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>这是一个标题</title> </head> &…

解密ZAB协议:Zookeeper一致性的核心实现

一致性问题 设计一个分布式系统必定会遇到一个问题—— 因为分区容忍性(partition tolerance)的存在,就必定要求我们需要在系统可用性(availability)和数据一致性(consistency)中做出权衡 。这就是著名的 CAP 定理。 ZooKeeper 的处理方式,保证了 CP(数据一致性) 一致…

解密prompt系列49. 回顾R1之前的思维链发展路线

我先按照自己的思路来梳理下R1之前整个模型思维链的发展过程,可以分成3个阶段:大模型能思考,外生慢思考,内生慢思考在所有人都在谈论R1的今天,作为算法也是有些千头万绪无从抓起。所以这一章先复盘,我先按照自己的思路来梳理下R1之前整个模型思维链的发展过程。下一章再展…

CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比

CSnakes 是一个用于在.NET项目中嵌入Python代码的工具,由.NET源生成器和运行时组成,能够实现高效的跨语言调用,Github:https://github.com/tonybaloney/CSnakes。以下是关键信息整理:核心特性跨版本支持:兼容.NET 8-9、Python 3.9-3.13,支持Windows/macOS/Linux系统高性…

Plotly.NET 一个为 .NET 打造的强大开源交互式图表库

前言 今天大姚给大家分享一个 .NET 强大、免费、开源的交互式图表库:Plotly.NET。 项目介绍 Plotly.NET 一个为 .NET 打造的强大、免费、开源的交互式图表库,支持 C# 和 F#编程语言,它构建在 plotly.js 之上并提供多个 API 层,用于创建、样式化和渲染美观的数据可视化图表。…

Apple Store 无法更新 App 解决方案 All In One

Apple Store 无法更新 App 解决方案 All In One Apple store 中可以查看新版 App, 但是无法更新新版 App, 会一直在转圈加载, 然后更新失败 ❌ 新版 Apps 与当前操作系统版本不匹配,需要先更新系统,才能安装新版的 Apps 更新 iOS 系统 ✅Apple Store 无法更新 App 解决方案 A…

合宙 ESP32C3 首个ardunio程序

https://blog.csdn.net/dpjcn1990/article/details/136085443 #define LED_BUILTIN1 12 #define LED_BUILTIN2 13//使用USB转串口即可 #define RS485_RX_PIN 1 #define RS485_TX_PIN 0 #define RS485_ENABLE_PIN 18 #define RS485 Serial1 void setup() { Serial.begin(9…

全网仅存方案,跟限速说拜拜!

关注A梦的小伙伴们都知道,A梦资源分享主要夸克网盘。有很多小伙伴也留言过想要其它网盘的链接,这方面确实众口难调,每个人的偏好和需求不同;另一个原因是夸克提供了较大的存储空间,能够更好地满足A梦的资源分享需求。 但网盘严重的限速也是很多小伙伴吐槽的点,考虑到A梦粉…

uniapp+h5---进行混合开发

uniapp和h5进行混合开发,最近在接手开发微信小程序,在技术选型的时候,拟采用uniapp+h5进行混合开发。 想必为啥要使用uniapp开发,就不用多说了?就是为了进行跨平台开发。如果使用微信小程序的开发方式,开发出来的应用就只能在微信小程序使用,但是使用uniapp就可以多端开…

dokcer-compose方式部署 mongo集群

声明:本人在单台机器上部署的mongo机器, mongo版本为8.0, 这玩意居然部署了一天,终于搞好了, 希望对后面想要部署的人有帮助 先创建一个mongodb目录, 后续的配置,数据都存放在这个目录中: 1. 创建mongo-secrets目录,mkdir mongo-secrets 2. 创建keyfile文件, openssl…

两个终端小玩具:Yazi和elinks

1. 终端文件管理器yazi 可以很方便地查找文件,跳转,并且很容易看出文件 1.1 下载安装 参考官方地址:鸭子官方安装手册安装rust编译环境curl --proto =https --tlsv1.2 -sSf https://sh.rustup.rs | sh rustup update编译yazigit clone https://github.com/sxyazi/yazi.git c…

两个终端小玩具

1. 终端浏览器 1.1 m3w 1.2 elinks 2. 终端文件管理器yazi 可以很方便地查找文件,跳转,并且很容易看出文件 2.1 下载安装 参考官方地址:鸭子官方安装手册安装rust编译环境curl --proto =https --tlsv1.2 -sSf https://sh.rustup.rs | sh rustup update编译yazigit clone htt…