我是靠谱客的博主 土豪帅哥,这篇文章主要介绍QWidgetAction实现鼠标滑过菜单项图标高亮显示,现在分享给大家,希望可以做个参考。

需求是鼠标滑过菜单项时,菜单项的文字、icon以及子菜单的小箭头都要高亮显示,qss中只能设置item背景色、文字颜色以及子菜单小箭头的样式,icon的图片不能切换,另外曾经想过用indicator(对action setCheckable(true)后,此子控件在qss中会生效)代替icon,因为indicator可以在qss中定制,但是这样一来所有的action的图标都是一致的了,这明显不符合需求。于是想着用QWidgetAction,自定义一个QWidget,在上面加入icon,菜单文字等,先看下效果:

设计的菜单项的模型如下:

其中,1为放置icon图片的widget,2为显示菜单项文字的Label,3为放置子菜单指示器的widget。

新建一个继承QWidget的类,用作QWidgetAction的defaultWidget:

QMenuWidget.h

复制代码
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
#pragma once #include <QWidget> #include <QMenu> namespace Ui { class QMenuWidget; } class QMenuWidget : public QWidget { Q_OBJECT public: QMenuWidget(QWidget *parent = Q_NULLPTR); ~QMenuWidget(); private: Ui::QMenuWidget *ui; QWidget *m_icon; QWidget *m_text; QWidget *m_submenu_indicator; public: void resizeEvent(QResizeEvent *event); void paintEvent(QPaintEvent *event); void mouseEnterEvent(QEvent *event); void mouseLeaveEvent(QEvent *event); void SetIconWidget(QWidget *widget_); void SetTextWidget(QWidget *text_); void SetSubMenuIndicatorWidget(QWidget *indicator_); void initWidgets(); };

重载resizeEvent是为了安放三个widget的位置;

QMenuWidget.cpp:

复制代码
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include "QMenuWidget.h" #include <QPainter> #include <QStyleOption> #include <QEnterEvent> #include <QDebug> #include <QMouseEvent> #include "ui_QMenuWidget.h" QMenuWidget::QMenuWidget(QWidget *parent) : QWidget(parent) ,m_submenu_indicator(NULL) ,m_icon(NULL) ,m_text(NULL) ,ui(new Ui::QMenuWidget) { ui->setupUi(this); setMouseTracking(true); } QMenuWidget::~QMenuWidget() { } void QMenuWidget::resizeEvent(QResizeEvent * event) { int icon_w = m_icon->width(); int icon_h = m_icon->height(); int left_margin = 2; int top_margin = rect().height() - icon_h; top_margin /= 2; m_icon->setGeometry(left_margin, top_margin, icon_w, icon_h); int text_margin_left = 2; int indicator_w = 0; if (m_submenu_indicator) indicator_w = m_submenu_indicator->width(); QRect text_rc; text_rc.setTop(0); text_rc.setLeft(rect().left() + left_margin + text_margin_left + icon_w); text_rc.setRight(rect().right() - indicator_w); text_rc.setBottom(rect().bottom()); m_text->setGeometry(text_rc); if (!m_submenu_indicator) return; int indicator_h = m_submenu_indicator->height(); top_margin = rect().height() - indicator_h; top_margin /= 2; m_submenu_indicator->setGeometry(rect().right() - indicator_w, top_margin, indicator_w, indicator_h); } void QMenuWidget::paintEvent(QPaintEvent * event) { QStyleOption opt; opt.init(this); QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } void QMenuWidget::mouseEnterEvent(QEvent * event) { bool ishover = this->property("hover").toBool(); if (ishover) return; qDebug() << __FUNCTION__; this->setProperty("hover", true); m_icon->setProperty("hover", true); m_text->setProperty("hover", true); m_icon->style()->unpolish(m_icon); m_text->style()->unpolish(m_text); this->style()->unpolish(this); m_icon->style()->polish(m_icon); m_text->style()->polish(m_text); this->style()->polish(this); m_icon->update(); m_text->update(); this->update(); if (m_submenu_indicator) { m_submenu_indicator->setProperty("hover", true); m_submenu_indicator->style()->unpolish(m_submenu_indicator); m_submenu_indicator->style()->polish(m_submenu_indicator); m_submenu_indicator->update(); } __super::enterEvent(event); } void QMenuWidget::mouseLeaveEvent(QEvent * event) { bool ishover = this->property("hover").toBool(); if (!ishover) return; qDebug() << __FUNCTION__; this->setProperty("hover", false); m_icon->setProperty("hover", false); m_text->setProperty("hover", false); this->style()->unpolish(this); m_icon->style()->unpolish(m_icon); m_text->style()->unpolish(m_text); this->style()->polish(this); m_icon->style()->polish(m_icon); m_text->style()->polish(m_text); this->update(); m_icon->update(); m_text->update(); if (m_submenu_indicator) { m_submenu_indicator->setProperty("hover", false); m_submenu_indicator->style()->unpolish(m_submenu_indicator); m_submenu_indicator->style()->polish(m_submenu_indicator); m_submenu_indicator->update(); } __super::leaveEvent(event); } void QMenuWidget::SetIconWidget(QWidget * widget_) { m_icon = widget_; m_icon->setMouseTracking(true); } void QMenuWidget::SetTextWidget(QWidget * text_) { m_text = text_; m_text->setMouseTracking(true); } void QMenuWidget::SetSubMenuIndicatorWidget(QWidget * indicator_) { m_submenu_indicator = indicator_; if(m_submenu_indicator) { m_submenu_indicator->setMouseTracking(true); } } void QMenuWidget::initWidgets() { this->setProperty("hover", false); m_icon->setProperty("hover", false); m_text->setProperty("hover", false); if(m_submenu_indicator) m_submenu_indicator->setProperty("hover", false); }

这里提供了接口来设置icon、label、以及indicator的widget,也可以由QMenuWidget自己来生成这三个widget。这里记录一下经验,最开始的时候我是重载QWidget的enterEvent、leaveEvent来捕捉鼠标进入以及离开事件,从而改变样式,后来发现这样做有几个问题:

第一个问题,当有子菜单时,鼠标放在菜单项上时,不能自动弹出子菜单,要点击一下才会弹出;

第二个问题,当有子菜单时,点击弹出子菜单后,鼠标移开菜单项,leaveEvent不能触发,从而菜单项一直保留hover=true的状态,样式也是hover=true的样式;

第三个问题,当QWidgetAction以及一个QAction并排放在菜单上时,从QAction移动到QWidgetAction时,QAction的状态仍为‘selected’状态;

尝试了各种方法均无法解决,后来把QMenu的源码看了下,发现触发QAction激活是在QMenu::mouseMoveEvent()里触发的,子菜单的延时弹出也是在这里触发的,于是便做个测试,我在代码中为QMenu安装了事件过滤器,发现鼠标在QMenuWidget上移动时,QMenu并没有触发到mouseMoveEvent,便断定以上的几个问题,均是因为鼠标在我自定义的QMenuWidget上移动时,QMenu无法捕捉到mouseMoveEvent。

想要让QMenu捕捉到QMenuWidget的mouseMove事件,那要怎么办呢?上网查了下,子QWidget窗口的事件如果未处理,即没有重载父类QWidget的事件处理函数,那么事件就会传播至父窗口,仔细看了下代码,发现QMenuWidget并没有重写mouseMoveEvent啊,为什么QMenu不能捕捉到呢,经过调试,终于发现要对QMenuWidget以及icon、label、indicator都设置

复制代码
1
setMouseTracking(true);

才能捕捉到mouseMoveEvent并传播给父窗口QMenu;

这样之后,第一个问题解决了,第二个、第三个问题仍然存在;经调试,第二个问题是由于子菜单弹出后,鼠标移开,leaveEvent并没有触发;又仔细看了下QMenu的实现代码,发现每个QAction的状态都是由QMenu判断鼠标事件来决定的,于是决定不在QMenuWidget中判断各种鼠标事件,统一由QMenu执行,把装有QMenuWidget的QWidgetAction当做一般的QAction来处理,最终的QMenuWidget类的代码就如上述所示。样式上使用了qss来设置,用到了动态属性,当鼠标进入时,设置自定义属性hover为true,当鼠标离开时,设置hover为false。主窗口的代码:

复制代码
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include "mainwindow.h" #include "ui_mainwindow.h" #include "QMenuWidget.h" #include <QFile> #include <QLabel> #include <QWidgetAction> #include <QDebug> #include <QMouseEvent> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); setupMenu(); QFile file_(":/qss/res/menu.qss"); file_.open(QFile::ReadOnly); m_menu->setStyleSheet(file_.readAll()); ui->pushButton->setMenu(m_menu); } MainWindow::~MainWindow() { delete ui; } void MainWindow::setupMenu() { m_menu = new QMenu(this); m_menu->setObjectName("menu_1"); QStringList menu_name; menu_name << "clock" << "map" << "home"; QStringList menu_text; menu_text << QStringLiteral("时钟") << QStringLiteral("定位服务") << QStringLiteral("主页"); for (int i = 0; i < 3;i++) { QMenuWidget *mw = new QMenuWidget(this); mw->setObjectName("menu_widget_" + menu_name.at(i)); mw->setFixedSize(120, 40); QWidget *icon = new QWidget(mw); icon->setObjectName("menu_icon_"+menu_name.at(i)); icon->setFixedSize(32, 32); QLabel *text = new QLabel(mw); text->setObjectName("menu_text_"+menu_name.at(i)); text->setText(menu_text.at(i)); QWidget* indicator = NULL; if(menu_name.at(i) == "home") { indicator = new QWidget(mw); indicator->setObjectName("menu_sub_"+menu_name.at(i)); indicator->setFixedSize(8, 12); } mw->SetIconWidget(icon); mw->SetTextWidget(text); mw->SetSubMenuIndicatorWidget(indicator); mw->initWidgets(); QWidgetAction *wa = new QWidgetAction(m_menu); wa->setObjectName("action_" + menu_name.at(i)); wa->setText(menu_name.at(i)); wa->setDefaultWidget(mw); //mw->SetAction(wa); m_widget_acts.append(wa); } QAction *act3 = new QAction(this); act3->setText(QStringLiteral("个人主页")); QMenu *submenu = new QMenu(this); submenu->addAction(act3); m_widget_acts.at(2)->setMenu(submenu); m_menu->addActions(m_widget_acts); m_menu->installEventFilter(this); connect(m_menu, &QMenu::triggered, this, &MainWindow::onMenuTriggered); } void MainWindow::onMenuTriggered(QAction *action) { qDebug() << __FUNCTION__ << action->text(); } bool MainWindow::eventFilter(QObject *watched, QEvent *event) { if (watched == m_menu) { if (event->type() == QEvent::MouseMove) { //QMenuWidget *wid = (QMenuWidget *)m_widget_act2->defaultWidget(); QPointF lp = ((QMouseEvent*)event)->localPos(); QList<QAction*>::iterator it = m_widget_acts.begin(); for (it;it != m_widget_acts.end();it++) { QWidgetAction *wa = (QWidgetAction*)(*it); QMenuWidget *wid = (QMenuWidget *)(wa)->defaultWidget(); QRect rc = m_menu->actionGeometry(wa); if (rc.contains(lp.toPoint())) { //qDebug() << rc << lp.toPoint() << wid->geometry(); wid->mouseEnterEvent(event); } else wid->mouseLeaveEvent(event); } } } return false; }

这里为m_menu添加了事件过滤器,鼠标移动时,判断鼠标在哪个action上,然后调用其对应的接口。菜单的qss文件可参考下方源码:

Qt鼠标滑过菜单图标高亮

最后

以上就是土豪帅哥最近收集整理的关于QWidgetAction实现鼠标滑过菜单项图标高亮显示的全部内容,更多相关QWidgetAction实现鼠标滑过菜单项图标高亮显示内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(90)

评论列表共有 0 条评论

立即
投稿
返回
顶部