概述
需求是鼠标滑过菜单项时,菜单项的文字、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
#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:
#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都设置
setMouseTracking(true);
才能捕捉到mouseMoveEvent并传播给父窗口QMenu;
这样之后,第一个问题解决了,第二个、第三个问题仍然存在;经调试,第二个问题是由于子菜单弹出后,鼠标移开,leaveEvent并没有触发;又仔细看了下QMenu的实现代码,发现每个QAction的状态都是由QMenu判断鼠标事件来决定的,于是决定不在QMenuWidget中判断各种鼠标事件,统一由QMenu执行,把装有QMenuWidget的QWidgetAction当做一般的QAction来处理,最终的QMenuWidget类的代码就如上述所示。样式上使用了qss来设置,用到了动态属性,当鼠标进入时,设置自定义属性hover为true,当鼠标离开时,设置hover为false。主窗口的代码:
#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实现鼠标滑过菜单项图标高亮显示所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复