导读

一款流行的软件,往往会在功能渐趋完善的时候,通过改善交互界面来提高用户体验。毕竟,就算再牛逼的产品,躲藏在糟糕的用户界面之后总会让用户心生不满。界面设计需综合考虑审美学、心理学、设计学等多因素,是一份精细活。这篇博文仍然以Qt的使用为主旨,探讨一下在Qt中如何进行系统托盘的个性化定制。

介绍

首先我们看看几款知名软件的系统托盘设计:

上图是金山卫士的系统托盘菜单设计。我们稍作分析:整个托盘菜单窗口是个半透明的设计,窗口边框进行了圆角处理。底部的菜单项包含三个Button,倒数第二、三个菜单项的右部还加上了一个自定义的单选按钮。顶部菜单项则包含一个评级组件;其他菜单项则没有什么特别,加上对应的图标即可完成设计。但是可能由于整个背景色的缘故,导致整体效果看起来灰蒙蒙的,不太亮堂。

上图是360安全卫士的托盘菜单。顶部和底部的两个菜单项都将背景色设置成了360安全卫士的主题色,加上了两个标签和按钮。其他菜单项保持不变。另外,菜单的背景色也被设置成了白色。整个菜单的设计较为简洁、清爽。虽然并不喜欢用360安全卫士,但是并不妨碍我对其产品外观设计的赞赏。

原型设计

既然有了上述两款产品的参考,我们也可以试着设计下自己的系统托盘。首先我们需要一个原型设计工具,将草图绘制好我们才能用代码将最终结果显示出来。这里推荐一个原型设计工具:Balsqmiq mockup。这款工具使用简单,其提供的原型组件非常丰富,使用会觉得非常方便。

根据初步设想,我设计了如下的一个原型草图:

在布局方面基本上综合了金山卫士和360安全卫士的设计特点。顶部菜单项部署两个Label, 一个用来显示应用程序的窗口标题或产品名称,另一个显示为go to visit,可以响应鼠标点击事件。底部菜单项和金山卫士一样,设置了三个按钮:Update, about, exit,使用水平均匀布局。其他的菜单项则和普通菜单项没有区别。 基本上,一个自定义的托盘菜单已经跃然而出。

代码实现

根据上述的原型设计,我们要做的准备工作显然就是准备好图片。对于没有美工技能的程序员来说,寻找界面图片素材显然是一大难题。做不出图片显然只好去网上搜索了。本人在网上下载了一堆的图片压缩包,有一个值得推荐:异次元图标。另外还有一个图片搜索网站也值得推荐。在这里我准备的图片如下:

每个图片都取了一个别名,这样在代码中我们直接使用图片别名,从而消除与图片具体名称的藕合性。资源准备好之后我们需要开始编码了。参考本人曾经写过的一篇博文(使用Qt创建系统托盘),可以实现一个默认主题的系统托盘菜单。但是这里我们要实现自定义托盘菜单,我们从QSystemTray派生一个子类,并定义好相关的类成员变量:

QMenu* m_trayMenu;

QWidget* m_topWidget;
QWidgetAction* m_topWidgetAction;
QLabel* m_topLabel;
QLabel* m_homeBtn; QWidget* m_bottomWidget;
QWidgetAction* m_bottomWidgetAction;
QPushButton* m_updateBtn;
QPushButton* m_aboutBtn;
QPushButton* m_exitBtn; QAction* m_runOnSystemBoot;
QAction* m_helpOnline;
QAction* m_homePage;
QAction* m_notification;
QAction* m_settings;

  显然,我们注意到一个平时没有接触到的:QWidgetAction。这个类自Qt 4.2引入,继承自QAction。根据类名也可以推测出其含义:使用QWidget来充当Menu的Action。于是,我们似乎明白了自定义菜单的精髓:用Widget来做Action。这里我们主要定义顶部菜单项和底部菜单项。因此我们定义了两个QWidgetAction。另外,我们还有一个疑问就是:布局好的Widget如何"伪装"成Action插入到菜单项中去呢?我们可以使用QWidgetAction的setDefaultWidget()方法来完成这项工作。后面的代码将会有说明。

此外,我们还注意到:360安全卫士的底部菜单项和顶部菜单项的背景色都是绿色的这又该如何实现呢?一种可行的方法是,安装一个事件过滤器(Event Filter)。当过滤到绘制事件并且绘制的组件是顶部菜单项和底部菜单项时,我们改变绘制方式。代码如下:

bool SystemTray::eventFilter(QObject *obj, QEvent *event)
{
if (obj == m_topWidget && event->type() == QEvent::Paint)
{
QPainter painter(m_topWidget);
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(42, 120, 192));
painter.drawRect(m_topWidget->rect());
}
return QSystemTrayIcon::eventFilter(obj, event);
}

  在完成了我们自己的绘制工作之后,还得再调用父类的事件过滤器,以免漏掉其他过滤工作。eventFilter()是一个protected方法,我们要在头文件中进行重写。

接下来要做的工作就是完成顶部和底部菜单项的绘制工作。先看看顶部菜单项如何绘制:

void SystemTray::createTopWidget()
{
m_topWidget = new QWidget();
m_topWidgetAction = new QWidgetAction(m_trayMenu);
m_topLabel = new QLabel(QStringLiteral("HUST Information Security Lab"));
m_topLabel->setObjectName(QStringLiteral("WhiteLabel"));
m_homeBtn = new QLabel(QStringLiteral("Visit"));
m_homeBtn->setCursor(Qt::PointingHandCursor);
m_homeBtn->setObjectName(QStringLiteral("WhiteLabel")); QVBoxLayout* m_topLayout = new QVBoxLayout();
m_topLayout->addWidget(m_topLabel, 0, Qt::AlignLeft|Qt::AlignVCenter);
m_topLayout->addWidget(m_homeBtn, 0, Qt::AlignRight|Qt::AlignVCenter); m_topLayout->setSpacing(5);
m_topLayout->setContentsMargins(5, 5, 5, 5); m_topWidget->setLayout(m_topLayout);
m_topWidget->installEventFilter(this);
m_topWidgetAction->setDefaultWidget(m_topWidget);
}

  我们声明了两个Label标签,作用在上文已说明。然后用垂直布局管理器将两个标签分左右放置。注意语句:m_topWidget->installEventFilter(this)。这条语句完成了过滤器的安装。指针this表明窗口事件将先发往当前类的eventFilter()方法进行处理,如果不处理再发往其他类的过滤器进行处理。底部菜单项的初始化大致类似:

void SystemTray::createBottomWidget()
{
m_bottomWidget = new QWidget();
m_bottomWidgetAction = new QWidgetAction(m_trayMenu); m_updateBtn = new QPushButton(QIcon(":/menu/update"), QStringLiteral("Update"));
m_updateBtn->setObjectName(QStringLiteral("TrayButton"));
m_updateBtn->setFixedSize(60, 25); m_aboutBtn = new QPushButton(QIcon(":/menu/about"), QStringLiteral("About"));
m_aboutBtn->setObjectName(QStringLiteral("TrayButton"));
m_aboutBtn->setFixedSize(60, 25); m_exitBtn = new QPushButton(QIcon(":/menu/quit"), QStringLiteral("Exit"));
m_exitBtn->setObjectName(QStringLiteral("TrayButton"));
m_exitBtn->setFixedSize(60, 25); QHBoxLayout* m_bottomLayout = new QHBoxLayout();
m_bottomLayout->addWidget(m_updateBtn, 0, Qt::AlignCenter);
m_bottomLayout->addWidget(m_aboutBtn, 0, Qt::AlignCenter);
m_bottomLayout->addWidget(m_exitBtn, 0, Qt::AlignCenter); m_bottomLayout->setSpacing(5);
m_bottomLayout->setContentsMargins(5,5,5,5); m_bottomWidget->setLayout(m_bottomLayout);
m_bottomWidgetAction->setDefaultWidget(m_bottomWidget);
}

  分别对三个按钮设置了大小和图标。具体的外观样式则使用了QSS来进行控制,因此我们还为每个按钮设置了一个Object Name。这个Object Name在QSS中充当ID选择器,便于样式控制。那么样式文件该如何编写呢?具体参看如下所示:

QMenu{
background:white;
border:1px solid lightgray; # 边框为灰色
} QMenu::item{
padding:0px 20px 0px 20px;
margin-left: 5px;
height:25px;
} QMenu::item:selected:enabled{
background: lightgray; # 菜单项选中时背景色设置为浅灰色
color: white; # 文本颜色设置为白色,否则看不清文本内容了
} QMenu::separator{
height:1px;
background: lightgray; # 菜单分割线也设置为浅灰色
margin:2px 0px 2px 0px;
} QMenu::item:selected:!enabled{
background:transparent;
} QPushButton#TrayButton {
border: none; # 无边框按钮
background: transparent; # 按钮背景设置为透明,这样不会受到默认主题颜色干扰
} QPushButton#TrayButton:hover {
background: rgb(233, 237, 252); # 鼠标悬停时,按钮背景色设为淡色
color: rgb(42, 120, 192); # 鼠标悬停时,文本颜色不变
}

  基本上,使用上面的样式设置就可完成基本样式设置。其他代码就不再详细叙述。到此,我们的托盘菜单就完成了个性化定制工作。

效果图

根据上述代码,我们实现的最终效果图如下:

前面也说过:界面设计是一门学问,综合了设计学、心理学、审美学等多学科。要设计出让人耳目一新的产品界面,需要设计师具备相当的设计功力。但不管最终设计的怎么样,我们已经知道了,如何实现具备个人特点的托盘菜单!

参考

用Qt写软件系列四:定制个性化系统托盘菜单的更多相关文章

  1. 用Qt写软件系列三:一个简单的系统工具(上)

    导言 继上篇<用Qt写软件系列二:QIECookieViewer>之后,有一段时间没有更新博客了.这次要写的是一个简单的系统工具,需求来自一个内部项目.功能其实很简单,就是查看当前当前系统 ...

  2. 用Qt写软件系列五:一个安全防护软件的制作(1)

    引言 又有许久没有更新了.Qt,我心爱的Qt,为了找工作不得不抛弃一段时间,业余时间来学一学了.本来计划要写一系列关于Qt组件美化的博文,但是写了几篇之后就没坚持下去了.技术上倒是问题不大,主要是时间 ...

  3. 用Qt写软件系列三:一个简单的系统工具之界面美化

    前言 在上一篇中,我们基本上完成了主要功能的实现,剩下的一些导出.进程子模块信息等功能,留到后面再来慢慢实现.这一篇来讲述如何对主界面进行个性化的定制.Qt库提供的只是最基本的组件功能,使用这些组件开 ...

  4. 用Qt写软件系列二:QCookieViewer(浏览器Cookie查看器)

    预备 继上篇<浏览器缓存查看器QCacheViewer>之后,本篇开始QCookieViewer的编写.Cookie技术作为网站收集用户隐私信息.分析用户偏好的一种手段,广泛应用于各大网站 ...

  5. 用Qt写软件系列一:QCacheViewer(浏览器缓存查看器)

    介绍 Cache技术广泛应用于计算机行业的软硬件领域.该技术既是人们对新技术探讨的结果,也是对当前软硬件计算能力的一种妥协.在浏览器中使用cache技术,可以大幅度提高web页面的响应速度,降低数据传 ...

  6. 用Qt写软件系列五:一个安全防护软件的制作(3)

    引言 上一篇中讲述了工具箱的添加.通过一个水平布局管理器,我们将一系列的工具按钮组合到了一起,完成了工具箱的编写.本文在前面的基础上实现窗体分割效果.堆栈式窗口以及Tab选项卡. 窗体分割 窗体分割是 ...

  7. 用Qt写软件系列五:一个安全防护软件的制作(2)

    引言 在上一篇中讲述了主窗体的创建和设计.主窗体的无边框效果.阴影效果.拖动事件处理.窗体美化等工作在前面的博客中早就涉及,因此上篇博文中并未花费过多笔墨.这一篇继续讲述工具箱(Tool Button ...

  8. 如何定制Windows系统右键菜单

    今天心血来潮把几个自己常用的工具定制到了系统的右键菜单.包括notepad++,7zip,还有复制文件全路径和文件夹路径.下面简单介绍一下步骤. 1. Windows系统右键菜单对应的注册表位置 Wi ...

  9. linux kernel系列四:嵌入式系统中的文件系统以及MTD

    本节介绍File System和MTD技术 一 FS 熟知的FS有ext2,3,4.但是这些都是针对磁盘设备的.而ES中一般的存储设备为Flash,由于Flash的特殊性: Flash存储按照Bloc ...

随机推荐

  1. 连接UI到代码

    本章,你将连接FoodTracker应用程序的UI到代码并定义一些可执行的动作.当你完成时,你的应用程序将是这个样子: 学习目标在课程结束时,你将能够:1.解释一个storyboard中的场景和vie ...

  2. Python 内置彩蛋

    The Zen of Python, by Tim Peters Beautiful is better than ugly.Explicit is better than implicit.Simp ...

  3. javaweb 学习总结

    http://www.cnblogs.com/xdp-gacl/category/574705.html 这个总结很好,以前看书没搞懂的,这里基本上都清楚了,赞一个,推荐. Servlet与普通Jav ...

  4. IE6下的效果

    1. IE6有宽度border实现透明 如果想使得边框颜色透明,在其余浏览器下比较简单,直接使用:border-color:transparent;但在IE6下这个办法不行,可以通过下面的方式实现: ...

  5. EXCEL 保存之前校验

    Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean) 'MsgBox "开始检测数据.. ...

  6. Windows无法安装到GPT分区形式磁盘的解决办法

    现在很多新买的硬盘都是GTP格式,这种格式需要使用UEFI BIOS模式安装系统,我们以前传统的windows系统安装都是“MBR+legacy BIOS”模式安装 Windows无法安装到GPT分区 ...

  7. 入手Cubieboard2之制作最小Linux系统

    前言 昨天终于入手了一块Cubieboard2板子,今年4月入职从事的就是与之相关的工作,因此趁现在有时间就好好熟悉一下. 一.主机环境 1.PC主机WIN 7旗舰版 1.虚拟机VM7.0 2.ubu ...

  8. Android实战技巧:深入解析AsyncTask

    AsyncTask的介绍及基本使用方法 关于AsyncTask的介绍和基本使用方法可以参考官方文档和Android实战技巧:多线程AsyncTask这里就不重复. AsyncTask引发的一个问题 上 ...

  9. How to Debug Enterprise Portal Code in Dynamics AX 2009

    转载 To set up debugging for pages1. Log into the server that is running the AOS.2. Open the Microsoft ...

  10. springmvc下使用kaptcha做验证码

    http://www.open-open.com/lib/view/open1395238908947.html