在Qt中可以通过QSS对菜单进行样式设置,而且对于非标准菜单项我也可以通过自定义Widget的方法来实现,本文讲述在Qt中QMenu的使用方法。

一、菜单基本使用

下面示例演示了右键菜单的创建方法,该菜单包含了图标、选中/未选中状态、二级菜单、互斥选择等常用菜单特性,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
QIcon settingIcon(":/MenuBeauty/setting.png");

// 定义Actions
QAction* action1 = new QAction("动作1");
QAction* action2 = new QAction(settingIcon, "动作2 有图标");

QAction* action3 = new QAction("动作3 [未选中]");
action3->setCheckable(true);
action3->setChecked(false);
connect(action3, &QAction::triggered, this, [action3](bool checked) {
action3->setText(checked ? "动作3 [选中]" : "动作3 [未选中]");
});

QAction* action4 = new QAction("Action4 动作四测试");

QAction* action5 = new QAction(settingIcon, "动作5 禁用");
action5->setEnabled(false);

QAction* action6 = new QAction(settingIcon, "动作6 子菜单");
{
// 动作6的子动作
QAction* action6_1 = new QAction("动作6.1");
action6_1->setCheckable(true);

QAction* action6_2 = new QAction("动作6.1");
action6_2->setCheckable(true);

QAction* action6_3 = new QAction("动作6.1");
action6_3->setCheckable(true);

// 动作6的子菜单
QMenu* action6SubMenu = new QMenu(this);

action6SubMenu->addAction(action6_1);
action6SubMenu->addAction(action6_2);
action6SubMenu->addAction(action6_3);

// 互斥子菜单项,同时只能选择一个
QActionGroup* action6Group = new QActionGroup(this);
action6Group->setExclusive(true);
action6Group->addAction(action6_1);
action6Group->addAction(action6_2);
action6Group->addAction(action6_3);

action6->setMenu(action6SubMenu);
}

// 定义菜单
QMenu* menu = new QMenu(this);

// 添加Action
menu->addActions({action1, action2, action3});
menu->addSeparator();
menu->addActions({action4, action5, action6});

// 鼠标右键弹出菜单
this->setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &QWidget::customContextMenuRequested, this, [this, menu](const QPoint& pos) {
menu->exec(this->mapToGlobal(pos));
});

上述菜单未经过任何美化,样式如下图所示:

二、使用QSS美化菜单

现在我们使用如下QSS对上面创建的菜单进行美化,可以美化的项包含背景、文本颜色、边距、图标等。

为了使大家不被颜色值所困扰,下面QSS中的颜色统一使用颜色名称表示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/* 菜单的属性 */
QMenu {
border: 1px solod red;
/* 圆角生效的前提是菜单窗口背景透明 */
border-radius: 10px;
background-color: blue;
font-size: 14px;
font-family: "Microsoft YaHei";
/* 设置菜单项最小宽度,此处有bug:虽可自动增长但实际宽度比所需宽度小 */
min-width: 160px;
/* 菜单内容上下边距为10px,预留圆角区域 */
padding: 10px 0px 10px 0px;
}

/* 菜单项的属性 */
QMenu::item {
border: none;
background-color: transparent;
color: white;
/* 设置菜单项最小高度,可以让菜单项的高适应内容自动增长 */
min-height: 20px;
/* 设置菜单项最小宽度,此处有bug:虽可自动增长但实际宽度比所需宽度小 */
min-width: 160px;
/* 使菜单项之间及菜单项和图标之间保持一定间隔,设置上下左右边距为8px,此处不能用margin */
padding: 8px 8px;
}

/* 菜单项-鼠标移入时的属性 */
QMenu::item:selected {
background-color: green;
color: black;
}

/* 菜单项-禁用时的属性 */
QMenu::item:disabled {
background-color: gray;
color: white;
}

/* 分割线的属性 */
QMenu::separator {
height: 1px;
background-color: red;
/* 左右边距:6px */
margin: 0x 6px 0px 6px;
}

/* 图标的属性 */
QMenu::icon {
width: 12px;
height: 12px;
/* 左边距:12px */
margin: 0 0 0 12px;
}

/* 指示器的属性 */
/* 这里将互斥和非互斥Action的指示器样式设置为一样的,也可以设置为不一样 */
QMenu::indicator:non-exclusive:checked, QMenu::indicator:exclusive:checked {
width: 12px;
height: 12px;
/* 左边距:8px */
margin: 0 0 0 8px;
image: url(:/MenuBeauty/check.png);
}

QMenu::indicator:non-exclusive:unchecked, QMenu::indicator:exclusive:unchecked {
width: 12px;
height: 12px;
/* 左边距:8px */
margin: 0 0 0 8px;
image: url(:/MenuBeauty/unchecked.png);
}

为了使菜单的圆角生效,我们还需要移除菜单的边框、阴影,并将背景设置为透明,代码如下:

1
2
3
4
5
menu->setWindowFlags(action6SubMenu->windowFlags() |
Qt::FramelessWindowHint | // 无边框
Qt::NoDropShadowWindowHint // 移除系统阴影
);
menu->setAttribute(Qt::WA_TranslucentBackground); // 背景透明

美化后的菜单如下图所示:

QSS美好QMenu

QMenu支持QSS盒子模型,其通用属性如下:

1
2
3
4
5
6
7
8
border
border-radius
margin
padding
background
color
font
border-image

QSS还可以对QMenu的子控件进行设置,支持QMenu子控件如下:

1
2
3
4
5
6
7
item
indicator
separator
right-arrow
left-arrow
scroller
tearoff

QMenu不支持伪状态,只有其子控件支持伪状态,支持的伪状态如下:

1
2
3
4
default
selected
exclusive
non-exclusive

具体的子控件和伪状态的含义,可以参考 QSS基本使用方法 文章。

三、自定义菜单项

大多数情况下,上述常规的菜单项就可以满足要求,但在有些情况下,我们的菜单项可能由更加复杂的Widget组合而成,比如下面播放器的菜单:

我们可以使用QWidgetAction来实现上述菜单效果,QWidgetAction可以将一个QWidget放入菜单项的容器内,从而实现自定义菜单项。

下面代码定义了一个包含3个按钮的菜单项,点击按钮会弹出对话框:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
QWidgetAction* action7 = new QWidgetAction(this);
{
QWidget* widget = new QWidget();
QHBoxLayout* hl = new QHBoxLayout(widget);

auto createPushButtonFn = [this](QString title) {
QPushButton* btn = new QPushButton(title);
connect(btn, &QPushButton::clicked, this, [this]() {
QMessageBox::information(this, "Clicked", ((QPushButton*)sender())->text());
});
return btn;
};

hl->addWidget(createPushButtonFn("Button1"));
hl->addWidget(createPushButtonFn("Button2"));
hl->addWidget(createPushButtonFn("Button3"));

action7->setDefaultWidget(widget);
}

menu->addAction(action7);

效果如图所示:

我们也可以使用QSS对菜单项内的控件进行样式设置,如:

1
2
3
4
5
6
QMenu QPushButton {
border: none;
border-radius: 10px;
background-color: black;
color: white;
}