相关类型:MenuBar、Menu、MenuItem、MenuSeparator、Action

注意:本文基于Qt Quick Control 2,在帮助文档里搜索MenuBar,将弹出以下选项
【QML】菜单-编程之家
前两个是旧的MenuBar,用法跟最新的不一样,import的时候注意import QtQuick.Controls 2.x,否则会提示错误
同样的事情发生在搜索Menu、MenuItem、MenuSeparator时,也是弹出多个版本,这也是qml很让人头疼的地方,同样名字的类型,往往存在多个不同时期、互不兼容的版本,互相之间其实是两个完全不一样的类型了!


MenuBar
从Qt5.10开始加入,继承自Container,由多个Menu构成

帮助文档里给出了一个简单的例子

【QML】菜单-编程之家

  ApplicationWindow {id: windowwidth: 320height: 260visible: truemenuBar: 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") }}}}

Menu
从Qt5.7开始加入,继承自Popup,Popup是qml专门为弹出式控件定义的基类,Menu属于Popup的一种,类似的还有Dialog,Drawer, ToolTip

Menu有两种使用场景:
Context menus; for example, a menu that is shown after right clicking
Popup menus; for example, a menu that is shown after clicking a button
不管哪种,都是通过click事件弹出菜单,它们的区别是第一种是通过 Menu.popup()打开菜单,而第二种是通过Popup.open()打开菜单

一个鼠标右键菜单的例子:
【QML】菜单-编程之家

  MouseArea {anchors.fill: parentacceptedButtons: Qt.LeftButton | Qt.RightButtononClicked: {if (mouse.button === Qt.RightButton)contextMenu.popup()}onPressAndHold: {if (mouse.source === Qt.MouseEventNotSynthesized)contextMenu.popup()}Menu {id: contextMenuMenuItem { text: "Cut" }MenuItem { text: "Copy" }MenuItem { text: "Paste" }}}

上面通过MenuItem定义菜单子项,也可以通过Action来定义,效果是一样的

  MouseArea {anchors.fill: parentacceptedButtons: Qt.LeftButton | Qt.RightButtononClicked: {if (mouse.button === Qt.RightButton)contextMenu.popup()}onPressAndHold: {if (mouse.source === Qt.MouseEventNotSynthesized)contextMenu.popup()}Menu {id: contextMenuAction { text: "Cut" }Action { text: "Copy" }Action { text: "Paste" }}}

MenuItem
从Qt5.7开始加入,继承自AbstractButton,提供了一个triggered信号,通过onTriggered事件处理函数,如

Menu {id: contextMenuMenuItem {text: "Cut" onTriggered: console.log("menu-cut")}}

Action
从Qt5.10开始加入,继承自QtObject,专门用于菜单栏或者工具栏,可以check,可以分配快捷键。当作为menu的元素时,也被认为是一个MenuItem。

简单的例子:

  ToolButton {id: toolButtonaction: copyAction}Action {id: copyActiontext: qsTr("&Copy")icon.name: "edit-copy"shortcut: StandardKey.CopyonTriggered: window.activeFocusItem.copy()}

自定义菜单

Customizing MenuBar

我们可以定制MenuBar的background和delegate,其中delegate定义为 MenuBarItem

例子:
【QML】菜单-编程之家

import QtQuick 2.12
import QtQuick.Controls 2.12MenuBar {id: menuBarMenu {title: qsTr("File")Action { text: "New" }Action { text: "Open" }Action { text: "Save" }}Menu {title: qsTr("Edit")Action { text: "Undo" }Action { text: "Redo" }MenuSeparator{}Action { text: "Cut" }Action { text: "Copy" }Action { text: "Phaste" }}Menu {title: qsTr("View")Action { text: "Manager" }Action { text: "Bookmark" }Action { text: "Toolbox" }Action { text: "Log" }}Menu {title: qsTr("Help")Action { text: "Manual" }Action { text: "Update" }Action { text: "About" }}delegate: MenuBarItem {id: menuBarItemcontentItem: Text {text: menuBarItem.textfont: menuBarItem.fontopacity: enabled ? 1.0 : 0.3color: menuBarItem.highlighted ? "#ffffff" : "#21be2b"horizontalAlignment: Text.AlignLeftverticalAlignment: Text.AlignVCenterelide: Text.ElideRight}background: Rectangle {implicitWidth: 40implicitHeight: 40opacity: enabled ? 1 : 0.3color: menuBarItem.highlighted ? "#21be2b" : "transparent"}}background: Rectangle {implicitWidth: 40implicitHeight: 40color: "#ffffff"Rectangle {color: "#21be2b"width: parent.widthheight: 1anchors.bottom: parent.bottom}}
}

上面的例子只影响MenuBar的一级菜单,二级菜单需要进一步定制Menu

Customizing Menu

我们可以定制Menu的background 、MenuItem 、MenuSeparator ,其中,MenuItem是每个选项,又可以定制其 background、content item、indicator、arrow,而MenuSeparator则可以定制其 background、content item

我们在上面的MenuBar例子基础上,修改其中名为“View”的Menu:
【QML】菜单-编程之家

        Menu {title: qsTr("View")Action { text: "Manager"; checkable: true; checked: true  }Action { text: "Bookmark"; checkable: true }Menu {title: "Toolbox"Action { text: "Selector" }Action { text: "Pen" }Action { text: "Brush" }MenuSeparator {contentItem: Rectangle {implicitWidth: 200implicitHeight: 1color: "#21be2b"}}Action { text: "Zoom In" }Action { text: "Zoom Out" }Action { text: "Rotate" }Action { text: "Flip" }}Action { text: "Log" }//定义菜单每个选项的显示行为delegate: MenuItem {id: menuItemimplicitWidth: 200implicitHeight: 40//MenuItem专属的arrow属性,用于定制展开选项的箭头arrow: Canvas {x: parent.width - widthimplicitWidth: 40implicitHeight: 40visible: menuItem.subMenuonPaint: {var ctx = getContext("2d")ctx.fillStyle = menuItem.highlighted ? "#ffffff" : "#21be2b"ctx.moveTo(15, 15)ctx.lineTo(width - 15, height / 2)ctx.lineTo(15, height - 15)ctx.closePath()ctx.fill()}}//AbstractButton的属性,用于定制选项前的指示标记indicator: Item {implicitWidth: 40implicitHeight: 40Rectangle {width: 26height: 26anchors.centerIn: parentvisible: menuItem.checkableborder.color: "#21be2b"radius: 3Rectangle {width: 14height: 14anchors.centerIn: parentvisible: menuItem.checkedcolor: "#21be2b"radius: 2}}}//用于定制选项文本contentItem: Text {leftPadding: menuItem.indicator.widthrightPadding: menuItem.arrow.widthtext: menuItem.textfont: menuItem.fontopacity: enabled ? 1.0 : 0.3color: menuItem.highlighted ? "#ffffff" : "#21be2b"horizontalAlignment: Text.AlignLeftverticalAlignment: Text.AlignVCenterelide: Text.ElideRight}//用于定制选项背景background: Rectangle {implicitWidth: 200implicitHeight: 40opacity: enabled ? 1 : 0.3color: menuItem.highlighted ? "#21be2b" : "transparent"}}//定制整个菜单的背景background: Rectangle {implicitWidth: 200implicitHeight: 40color: "#ffffff"border.color: "#21be2b"radius: 2}}

这里偷懒没有定制“Toolbox”的次级菜单,可以通过把上面的例子封装为一个Menu模块,如MyMenu.qml,在创建菜单时直接使用MyMenu就可以达到定制效果


动态创建菜单

(To be continued)