一个 Qml MenuBar 的问题

news2024/11/15 18:06:16

基本情况

使用 QQuick.Control 中的 MenuBar 实现主菜单栏。菜单栏包括 File、Edit、View、Help 菜单项。点击菜单项,会弹出对应的菜单。

ApplicationWindow {
    id: window
    width: 320
    height: 260
    visible: true

    menuBar: MenuBar {
        Menu {
            title: qsTr("&File")
            Action { text: qsTr("&New...") }
            Action { text: qsTr("&Open...") }
            Action { text: qsTr("&Save") }
            Action { text: qsTr("Save &As...") }
            MenuSeparator { }
            Action { text: qsTr("&Quit") }
        }
        Menu {
            title: qsTr("&Edit")
            Action { text: qsTr("Cu&t") }
            Action { text: qsTr("&Copy") }
            Action { text: qsTr("&Paste") }
        }
        Menu {
            title: qsTr("&Help")
            Action { text: qsTr("&About") }
        }
    }
}

流程1:点击菜单栏上的菜单项,该菜单项被激活(弹出),再次点击该菜单项,菜单项退出激活状态。

流程2:如果在激活状态,移动鼠标到另一个菜单项,自动激活(不需要点击)另一个菜单项,当前激活的菜单项退出激活状态。

以上都符合预期,但是问题来了。

问题现象

流程3:在弹出的菜单上,点击某一个项目,弹出菜单消失,但是对应的菜单项并没有退出激活状态。

尝试解决

首先想到的方法,就是针对性处理。在弹出菜单消失时,触发菜单项状态切换。

        delegate: MenuBarItem {
            id: menuBarItem

            property bool opened: menu.opened
            onOpenedChanged: {
                if (!opened && highlighted) {
                    highlighted = false
                    triggered()
                }
            }
        }

实测流程3是OK了,但是流程1有问题了。在激活状态,再次点击菜单项,没有退出激活状态。

代码分析

看来只能分析源代码了。相同的思路,其实源代码里面已经实现了。

注意下面代码的 aboutToHide 一行,在弹出菜单将要消失时,是有处理的。

void QQuickMenuBar::itemAdded(int index, QQuickItem *item)
{
    Q_D(QQuickMenuBar);
    QQuickContainer::itemAdded(index, item);
    if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(item)) {
        QQuickMenuBarItemPrivate::get(menuBarItem)->setMenuBar(this);
        QObjectPrivate::connect(menuBarItem, &QQuickControl::hoveredChanged, d, &QQuickMenuBarPrivate::onItemHovered);
        QObjectPrivate::connect(menuBarItem, &QQuickMenuBarItem::triggered, d, &QQuickMenuBarPrivate::onItemTriggered);
        if (QQuickMenu *menu = menuBarItem->menu())
            QObjectPrivate::connect(menu, &QQuickPopup::aboutToHide, d, &QQuickMenuBarPrivate::onMenuAboutToHide);
    }
    d->updateImplicitContentSize();
    emit menusChanged();
}

菜单栏里面维护了激活状态(即 popupMode 为 true),菜单消失时,退出激活状态。

void QQuickMenuBarPrivate::onMenuAboutToHide()
{
    if (triggering || !currentItem || (currentItem->isHovered() && currentItem->isEnabled()) || !currentItem->isHighlighted())
        return;
    popupMode = false;
    activateItem(nullptr);
}

那为什么没有生效呢?通过调试,发现上面的代码 menu 是空指针,所以没有与 aboutToHide 信号连接。调用栈如下:

1   QQuickMenuBar::itemAdded              qquickmenubar.cpp       534  0x7ffc9884f04f 
2   QQuickContainerPrivate::insertItem    qquickcontainer.cpp     250  0x7ffc98814a3b 
3   QQuickContainer::insertItem           qquickcontainer.cpp     532  0x7ffc98813378 
4   QQuickContainer::addItem              qquickcontainer.cpp     507  0x7ffc9881327c 
5   QQuickContainer::itemChange           qquickcontainer.cpp     865  0x7ffc98813da0 
6   QQuickItemPrivate::itemChange         qquickitem.cpp          6231 0x7ffc7b509ed7 
7   QQuickItemPrivate::addChild           qquickitem.cpp          2976 0x7ffc7b505cf3 
8   QQuickItem::setParentItem             qquickitem.cpp          2765 0x7ffc7b4f699c 
9   QQuickMenuBarPrivate::beginCreateItem qquickmenubar.cpp       100  0x7ffc9884f6d8 
10  QQuickMenuBarPrivate::createItem      qquickmenubar.cpp       115  0x7ffc9884f768 
11  QQuickMenuBar::addMenu                qquickmenubar.cpp       341  0x7ffc9884e9cc 
12  QQuickMenuBar::qt_static_metacall     moc_qquickmenubar_p.cpp 131  0x7ffc9884e30d 
13  QQuickMenuBar::qt_metacall            moc_qquickmenubar_p.cpp 230  0x7ffc9884e0f9 
14  QMetaObject::metacall                 qmetaobject.cpp         310  0x7ffc5d08dcb4 
15  QQmlObjectOrGadget::metacall          qqmlpropertycache.cpp   1772 0x7ffc7140ed1b 
16  CallMethod                            qv4qobjectwrapper.cpp   1297 0x7ffc711c923e 
17  CallPrecise                           qv4qobjectwrapper.cpp   1557 0x7ffc711c9f56 
18  QV4::QObjectMethod::callInternal      qv4qobjectwrapper.cpp   2118 0x7ffc711c63da 
19  QV4::QObjectMethod::virtualCall       qv4qobjectwrapper.cpp   2056 0x7ffc711c5eeb 
20  QV4::FunctionObject::call             qv4functionobject_p.h   203  0x7ffc70f8a031 
... <更多>                                                                              

为什么 menu 是空的呢,原来 itemAdded 调用得比较早,这个时候还没有 setMenu。下面的代码(Qt 5.12.4) beginCreateItem 会调用 setParentItem,此时就触发了 itemAdded。

QQuickItem *QQuickMenuBarPrivate::createItem(QQuickMenu *menu)
{
    QQuickItem *item = beginCreateItem();
    if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(item))
        menuBarItem->setMenu(menu);
    completeCreateItem();
    return item;
}

 后来看到一个比较新的 Qt5 代码,这个问题就是修复了的。他将 setMenu 放在了更前面。

QQuickItem *QQuickMenuBarPrivate::beginCreateItem(QQuickMenu *menu)
{
    ......
    if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(item))
        menuBarItem->setMenu(menu);
    item->setParentItem(q);
    QQml_setParent_noEvent(item, q);
    return item;
}

解决方案

如果不升级 Qt,有没有办法解决这个问题呢?

其实只要将 MenuItem 重新添加到 MenuBar 中就行了,这个时候 menu 就不是空的了。

看代码:

    MenuBar {
        id: menuBar

        delegate: MenuBarItem {
            id: menuBarItem

            onMenuChanged: {
                // MenuBar has BUG on addMenu, it can't detach menu on MenuBarItem which is set later
                //  Re-add the item to fix the BUG
                menuBar.addItem(menuBar.takeItem(menuBar.count - 1))
            }
        }
    }

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

List集合

首先看这个框架图&#xff1a; List集合代表一个元素有序&#xff0c;可重复的集合&#xff0c;集合中每个元素都有对应的顺序索引。List接口中增加了一些根据索引操作元素的方法&#xff1a; void add(int index,E element ) 在列表的指定位置插入该元素。 boolean addAll(in…

【数据结构】LinkedList模拟实现与简单使用

文章目录模拟实现LinkedListLinkedList的简单使用LinkedList的一些方法LinkedList的遍历简单对比一下LinkedList和ArrayList模拟实现LinkedList 在上一篇的博客中&#xff0c;我们讲解了链表的基础知识&#xff0c;并且模拟实现了一个无头单向不循环链表&#xff0c;链表的基础…

12.动态内存

文章目录动态内存12.1动态内存和智能指针12.1.1shared_ptr类make_shared函数shared_ptr的拷贝和赋值shared_ptr自动销毁所管理的对象shared_ptr还自动释放相关联的内存使用了动态生存期的资源的类12.1.2直接管理内存使用new动态分配和初始化对象动态分配的const对象内存耗尽指针…

Java集合类ArrayList应用 | 如何在字符串s1中删除有在字符串s2出现的字符?

目录 一、题干 二、题解 1. 思路 ArrayList实现 2. 代码 ArrayList实现 StringBuilder实现-1 StringBuilder实现-2 三、总结 一、题干 面试的编程题&#xff1a; s1: "welcome to Zhejiang" s2: "come" 要求输出从字符串s1删除s2中存在的字符之后…

如何更好使用markdown输出pdf

如何更好的使用markdown输出PDF 背景&#xff1a;当前很多人比较常用的 markdown 编辑方式是用vscode编写&#xff0c;再使用 vscode 中 的 MPE 的进行预览。有时候会出现这种情况&#xff1a;想要最终输出PDF给客户&#xff0c;但是通过 vscode 中的markdown 转 PDF插件生成 P…

云计算与云原生

如今是云时代&#xff0c;云计算&#xff0c;大数据&#xff0c;人工智能等新的名词在最近爆火。今天我们来了解一下&#xff0c;云计算与云原生。 在了解云原生之前&#xff0c;我们必须要了解云计算。 云计算 什么是云计算&#xff1f; 在了解云计算之前我们需要先了解一…

嵌入式实时操作系统的设计与开发(三)

基本调度机制 用户在基于RTOS开发应用前&#xff0c;首先要创建线程。 aCoral中&#xff0c;用户创建一个线程时须指定用户希望采用的调度策略&#xff0c;例如&#xff0c;用户想创建一个周期性执行的线程并希望通过周期来触发多线程的调度。 //创建一个周期性的线程 acoral…

视频播放中动画

CSS filter属性CSS的filter属性主要用于设置图像的视觉效果。语法&#xff1a;filter: none|blur()|brightness()|contrast()|drop-shadow()|grayscale()|hue-rotate()|invert()|opacity()|saturate()|sepia()|url();Filter 函数注意&#xff1a; 滤镜通常使用百分比 (如&#…

ArcGIS基础实验操作100例--实验49按分区划分栅格图层

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验49 按分区划分栅格图层 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;…

Power BI柱形图

在PowerBI中制作常见的柱形图&#xff0c;简单来说&#xff0c;柱形图就是利用水平的柱子表示不同分类数据的大小&#xff0c;与之类似的是条形图&#xff0c;它就是竖的柱形图&#xff0c;或者说把柱形图顺时针转动90度就成了条形图&#xff0c;使用以及作图方式类似&#xff…

媒体查询(@media语法、案例)详解

媒体查询media 语法1. 直接写在 CSS 样式中2. 针对不同的媒体设备&#xff0c;从外部链入不同的 stylesheets&#xff08;外部样式表&#xff09;使用 media 实现网页变色龙media 语法 media 可以直接写在 CSS 样式中&#xff0c;或者可以针对不同的媒体设备&#xff0c;从外部…

技术分享 | 一款功能全面的 MySQL Shell 插件

作者&#xff1a;杨涛涛 资深数据库专家&#xff0c;专研 MySQL 十余年。擅长 MySQL、PostgreSQL、MongoDB 等开源数据库相关的备份恢复、SQL 调优、监控运维、高可用架构设计等。目前任职于爱可生&#xff0c;为各大运营商及银行金融企业提供 MySQL 相关技术支持、MySQL 相关课…

[Java]异常处理

文章目录&#x1f97d; 异常概述&#x1f97d; 异常的分类&#x1f97d; 异常的处理&#x1f30a; 异常处理机制一&#xff1a;try-catch-finally&#x1f4a6; 语法结构&#x1f4a6; try-catch&#x1f4a6; finally&#x1f4a6; try-catch-finally处理异常的执行流程&#…

7.0、Linux-Vim编辑器以及常用命令详解

7.0、Linux-Vim编辑器以及常用命令详解 什么是 Vim 编辑器 -> Vim 是从 vi 发展出来的一个文本编辑器&#xff1b;代码补全、编译以及错误等方便编程的功能特别丰富&#xff0c;在程序员中被广泛使用&#xff1b;简单的来说&#xff0c;vi 是老式的字处理器&#xff0c;不过…

未知感知对象检测:从开放视频中学习你不知道的东西(学习笔记)

Unknown-Aware Object Detection: Learning What You Dont Know from Videos in the wild paper: https://arxiv.org/abs/2203.03800 code: https://github.com/deeplearning-wisc/stud the Wild 弄一个靠谱的目标检测器&#xff0c;完成OOD问题 什么是out of distribution (…

PHP Tools for Visual Studio 2019-2022 1.7 Crack

PHP Tools 是一个完整的 PHP 开发环境&#xff0c;位于单个软件包中。利用众所周知的行业标准 IDE 开发小型项目直至大型 PHP 应用程序。 该编辑器具有智能代码分析和快速抢占式代码完成功能。通过大量的导航功能、手边的本地化手册或快速重构操作来提高您的工作效率。 检查代码…

基于Amlogic 安卓9.0, 驱动简说(五):基于GPIO、LED子系统的LED驱动

一、篇头 本章介绍LED子系统的使用。使用LED子系统&#xff0c;可以轻松实现对LED&#xff0c;例如常见的闪烁和亮度控制功能。简单起见&#xff0c;本章先使用GPIO实现&#xff0c;在不模拟PWM的情况下&#xff0c;只能实现点亮和灭灯的效果&#xff0c;重点是介绍GPIO、LED子…

腾讯前端一面常考vue面试题汇总

vue2.x详细 1. 分析 首先找到vue的构造函数 源码位置&#xff1a;src\core\instance\index.js function Vue (options) {if (process.env.NODE_ENV ! production &&!(this instanceof Vue)) {warn(Vue is a constructor and should be called with the new keyword…

【Linux】Linux进程的理解 --- 进程描述符、状态、优先级、切换…

如果不改变自己&#xff0c;就别把跨年搞的和分水岭一样&#xff0c;记住你今年是什么吊样&#xff0c;明年就还会是什么吊样&#xff01;&#xff01;&#xff01; 文章目录一、冯诺依曼体系结构&#xff08;硬件&#xff09;二、操作系统&#xff08;软件&#xff09;1.操作…

HCIP第六天

文章目录一&#xff0c;实验要求二&#xff0c;搭建拓扑图三&#xff0c;配置IP地址和环回地址四&#xff0c;宣告OSPF五&#xff0c;抓取流量六&#xff0c;测试一&#xff0c;实验要求 所有到达目标的路径最优,互有备份二&#xff0c;搭建拓扑图 三&#xff0c;配置IP地址和环…