qt实现菜单,简单的界面QMenu+QAction完全可以实现,在加上qss的支持,可以定制出比较美观的菜单,qt的菜单一般用在托盘、按钮和工具栏上。

当然啦,也有很多软件有比较美观的托盘菜单,比如360、电脑管家等软件,效果图如图1所示,其实qt在4.2之后也提供了定制菜单的功能,使用QWidgetAction可以定制出自己想要的菜单来,接下来是我定制菜单栏的步骤。

图1 360图盘菜单

实现效果如下图2所示,菜单是由单个条目组成的,每一个条目又由左右两部分组成,左边是一个图标,并伴有底色,右边是一个label,上边有文字描述,当有鼠标移动到项上时,项整个背景色变成红色,并且图标会替换,文字颜色也会有相应的变化。

图2 定制菜单

首先拿到这个功能,我们可以先考虑功能的拆分,既然qt支持菜单项的窗口定制功能,那我们不防把每一个项目定制成一个QWidget,这样就问题就变成了一个窗口的定制,这样看起来是不是简单多了。

首先我们来看下QSystemTrayIcon类,该类实现了windows托盘的功能,activated信号表示托盘图标有事件,我们需要处理这个信号,当messageClicked信号触发时,说明我们点击了托盘提示信息。下面是我重写的托盘类

 class CSystemTrayIcon : public QSystemTrayIcon
{
Q_OBJECT signals:
void ShowMainWidget();
void ShowMiniWidget();
void AppQuit(); public:
CSystemTrayIcon(const QIcon & icon, QObject * parent = nullptr);
~CSystemTrayIcon(); public:
void SetWaverable(bool waver);//托盘图标是否闪动
void ShowMessage(const QString & title
, const QString & message
, QSystemTrayIcon::MessageIcon icon = QSystemTrayIcon::Information
, int millisecondsTimeoutHint = );//托盘弹出气泡提示 QAction * AddAction(const QString & actName, const QIcon & icon); protected:
virtual bool event(QEvent *) Q_DECL_OVERRIDE;
virtual void timerEvent(QTimerEvent *) Q_DECL_OVERRIDE;
virtual bool eventFilter(QObject * watched, QEvent * event) Q_DECL_OVERRIDE; private slots:
void TrayActivateSlot(QSystemTrayIcon::ActivationReason);
void MessageClickedSlot(); private:
void CreateMenu(); private:
bool m_MouseLeave = true;
int m_TimeID = ;
QIcon m_IconPath;
CTrayMenu * m_Menu = nullptr;
#ifdef CustomAction
//添加定制菜单项
CActionItem * mainAct = nullptr;
CActionItem * miniAct = nullptr;
CActionItem * exitAct = nullptr;
#else
QAction * mainAct = nullptr;
QAction * miniAct = nullptr;
QAction * exitAct = nullptr;
#endif
};

通过查看CSystemTrayIcon类的接口,可以发现该类中有其他的接口,但是我在本文中不打算对其一一作出解释,因为和菜单项定制无关,如果非要追问,那我只能接单的说明下,SetWaverable接口使用类设置托盘图标是否闪动,就类似qq一样的效果。

接下来我们需要了解下QWidgetAction类,这个类继承自QAction,他拥有QAction的所有信号和槽,因此我们可以把他当QAction一样的用,不仅仅如此,我们还可以为其提供自定义的QWidget,实现代码如下

 class CActionItem : public QWidgetAction
{
Q_OBJECT
Q_PROPERTY(bool m_Hover READ IsMHover WRITE SetMHover) public:
CActionItem(const QString & text = "", QWidget * parent = nullptr);
~CActionItem(); public:
void SetContentText(const QString & text);
void SetItemIcon(const QString & icon);
void SetItemIcon(const QString & icon, const QString & hover);
void SetItemIcon(const QString & icon, const QString & hover, const QString & press); QWidget * contentWidget() const;//获取中心窗口
void SetToolTip(const QString & toolTip); public:
bool IsMHover(){ return m_Hover; }
void SetMHover(bool hover){ m_Hover = hover; } protected:
virtual QWidget * createWidget(QWidget * parent) Q_DECL_OVERRIDE;
virtual void deleteWidget(QWidget * widget) Q_DECL_OVERRIDE; private:
bool m_Hover = false;
CActionContentWidget * m_ContentWidget = nullptr;
};

当有QWidgetAction被创建时,首先在构造函数中初始化我们定制的窗口,并将其设置为缺省的窗口

CActionItem::CActionItem(const QString & text, QWidget * parent /*= nullptr*/) : QWidgetAction(parent)
{
setEnabled(true); m_ContentWidget = new CActionContentWidget();
connect(m_ContentWidget, &CActionContentWidget::IconClicked, this, [this]{this->triggered(); });
m_ContentWidget->SetContentText(text); setDefaultWidget(m_ContentWidget);
}

createWidget接口会被自动调用,因此我们可以在此接口中创建我们自己定制的QWidget。代码如下,记得把定制的窗口设置为参数所给窗口的子窗口

QWidget * CActionItem::createWidget(QWidget * parent)
{
m_ContentWidget->setParent(parent);
return m_ContentWidget;
}

QWidgetActoin只是一个QAction,想要美观的菜单项,还是需要我们自己去定制窗口的,接下来就是我自己定制的窗口

 class CActionContentWidget : public QWidget
{
Q_OBJECT
signals:
void IconClicked(); public:
CActionContentWidget(QWidget * parent = nullptr);
~CActionContentWidget(); public:
void SetContentText(const QString & text);
void SetItemIcon(const QString & icon, const QString & hover); public:
void SetBackgroundRole(bool hover); protected:
virtual void enterEvent(QEvent *) Q_DECL_OVERRIDE;
virtual void leaveEvent(QEvent *) Q_DECL_OVERRIDE;
virtual bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE; private:
void InitializeUI(); private:
QWidget * m_ContentWidget = nullptr;
QPushButton * m_ActIcon = nullptr;
QLabel * m_ActText = nullptr;
QString m_NormalIcon, m_HoverIcon, m_PressedIcon;
};

最后就是菜单的定制啦,为什么要重写菜单呢,因为我需要在指定时刻,修改菜单项的位置,因此菜单项的定制也比较简单,就是在关键时刻跑出一个信号,表示需要修改菜单位置了,代码如下:

 class CTrayMenu : public QMenu
{
Q_OBJECT signals:
void FixedPostion();//移动菜单位置 public:
CTrayMenu(QWidget * parent = nullptr);
~CTrayMenu(); protected:
virtual bool event(QEvent *) Q_DECL_OVERRIDE;
};

因为菜单是一个QWidget,在构造函数中,拿不到width和height,而在show的时候可以拿到相关信息,代码如下:

bool CTrayMenu::event(QEvent * e)
{
if (e->type() == QEvent::Show)
{
emit FixedPostion();
} return QMenu::event(e);
}

讲到这儿,qt菜单定制功能就讲完了,在菜单定制的过程中我自己也遇到了一些问题,在此记录下,希望看到并知道原因的留下您的脚印。

问题:

1、定制的QWidget中的鼠标事件异常

2、qss中的属性判断异常,例如QLabel[IsCheck=true]{border:1 solid #ff0000;},这种方式设置的鼠标变化不起作用,为了实现这个功能,我是在CSystemTrayIcon类中把定制窗口事件都注册到父类中,然后通过eventFilter来判断鼠标位置,进一步重新设置qss来到达鼠标移动换背景色的功能。代码如下:

bool CSystemTrayIcon::eventFilter(QObject * watched, QEvent * event)
{
if (watched == this)
{
m_MouseLeave = false;
}
if (watched->inherits("QWidget") && event->type() == QEvent::Paint)
{
if (CActionContentWidget * actionItem = static_cast<CActionContentWidget *>(watched))
{
if (actionItem->rect().contains(actionItem->mapFromGlobal(QCursor::pos())))
{
actionItem->SetBackgroundRole(true);
}
else
{
actionItem->SetBackgroundRole(false);
}
}
}
return QSystemTrayIcon::eventFilter(watched, event);
}
如果您觉得文章不错,不妨给个打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!! 

 

很重要--转载声明

  1. 本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords
  2. 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。

qt之菜单项定制的更多相关文章

  1. C/C++ Qt ToolBar 菜单组件应用

    ToolBar工具栏在所有窗体应用程序中都广泛被使用,使用ToolBar可以很好的规范菜单功能分类,用户可根据菜单栏来选择不同的功能,Qt中默认自带ToolBar组件,当我们以默认方式创建窗体时,To ...

  2. Android菜单项内容大全

    一.介绍: 菜单是许多应用中常见的用户界面组件. Android3.0版本以前,Android设备会提供一个专用"菜单"按钮呈现常用的一些用户操作, Android3.0版本以后, ...

  3. 添加系统右键菜单项 管理员取得所有权(W)(带盾牌)

    @color 0A @title 添加系统右键菜单项 管理员取得所有权(^&W)(带盾牌) by wjshan0808 @echo off echo * >nul reg add HKC ...

  4. SharePoint 2013:自定义ECB菜单项的添加

    本文分别介绍了两种常用的添加ECB菜单项的方式. 声明式创建 这也是微软最佳实践推荐的方式.在VS中创建一个SharePoint空解决方案,并添加一个“空元素”类型的SPI. 在Elements.xm ...

  5. TortoiseSVN菜单项功能说明

    TortoiseSVN是windows下其中一个非常优秀的SVN客户端工具.通过使用它,我们可以可视化的管理我们的版本库.不过由于它只是一个客户端,所以它不能对版本库进行权限管理. TortoiseS ...

  6. 实战android菜单项之XML加载菜单与动态菜单项[转]

    原文地址:http://blog.csdn.net/kaiwii/article/details/7767225 自定义android应用程序的菜单项首先要知道切入点.经过学习,知道主要是两个Acti ...

  7. JavaScript网站设计实践(二)实现导航栏当前所选页面的菜单项高亮显示

    一.(一)中的代码还可以修改的地方. 在(一)中,如果是运行在服务器下,如apache等,可以把head和navigation的div抽取出来,放置在另一个html文件里,然后在页面中,include ...

  8. winform 菜单项显示历史记录 分类: WinForm 2014-07-11 18:15 196人阅读 评论(0) 收藏

    (1)创建一个项目,将其命名为MenuHistory,默认窗体为Form1. (2)从工具箱中向Form1窗体添加MenuStrip控件,同时向窗体添加OpenFileDialog控件.创建一个&qu ...

  9. MFC 单文档中动态添加菜单项和响应菜单事件

    新建一个单文档程序 在查看菜单项中增加两个子菜单,分别为隐藏工具栏(ID_HIDE),新建菜单(ID_NEWMENU) 在Resource.h中增加一个ID_NEWMENU宏 #define ID_N ...

随机推荐

  1. unity一个按钮实现开和关

    using System.Collections; using System.Collections.Generic; using UnityEngine; public class ButtonCl ...

  2. webpack问题列表及解决方案

    1.提升webpack打包速度 2.cssloader顺序有先后 3.如何正确引用图片 4.打包后访问不到json文件 5.打包后如何访问项目 6.打包后的文件 7.为什么执行webpack,就可以打 ...

  3. [OC] UIcollectionView 与 UIcollectionViewCell 的使用

    UICollectionView    @interface ViewController ()<UICollectionViewDelegate,UICollectionViewDataSou ...

  4. Python获取当前日期和日期差计算

    在Python中获取日期和时间很简单,主要使用time和datetime包 1.获取当前时间并格式化 from dateutil import rrule from datetime import d ...

  5. Python数据可视化编程实战pdf

    Python数据可视化编程实战(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1vAvKwCry4P4QeofW-RqZ_A 提取码:9pcd 复制这段内容后打开百度 ...

  6. Flume+Kafka+Storm整合

    Flume+Kafka+Storm整合 1. 需求: 有一个客户端Client可以产生日志信息,我们需要通过Flume获取日志信息,再把该日志信息放入到Kafka的一个Topic:flume-to-k ...

  7. kodexplorer免费网盘安装教程

    KodExplorer是什么? KodExplorer可道云,原名芒果云,是基于Web技术的私有云和在线文件管理系统. 用户只需通过简单环境搭建,即可使用KodExplorer快速完成私有云/私有网盘 ...

  8. mysql 零碎笔记

    聚合函数的用法: concat 连接单行记录的不同字段, group_concat 连接多行记录的相同字段, concat_ws count 按条件统计: SELECT COUNT(*) AS `nu ...

  9. cadence布线约束规则设置

    DRC检查规则在布线过程中是必不可少的,包括时序规则,走线规则,间距规则,信号完整性规则和物理规则等,在绘制电路板时,设计相关规则满足设计需求,是非常关键的! https://wenku.baidu. ...

  10. 【react】---手动封装一个简易版的redux

    export let createStore = (reducer)=>{ //定义默认的state let state; //定义默认的action let actionTypes = &qu ...