一、应用场景
在系统使用的过程中,有些应用需要长时间运行在桌面环境中,例如网络,声音,麒麟天气,电源电池,输入法,QQ,微信等,为了让用户随时访问这些程序以及了解它们的状态,任务栏除了需要快速启动图标外还需要提供一个特定的消息通知区域,即系统托盘区域,用于向用户发送消息,警告,提示,用户可以通过托盘图标快速的访问这些应用,也可以通过托盘图标的状态来了解程序运行的状态。
二、简介
任务栏(ukui-panel)会提供一个特殊区域,称为系统托盘或通知区域,长时间运行的应用程序可以在其中显示图标状态和短消息,也就是右侧输入法、声音、网络等组件的区域。
图1 任务栏托盘图标区域
在 Linux 操作系统中,“系统托盘区域”是在给定的 X 屏幕上运行的应用程序,可以显示正在运行的应用程序提供的小图标。系统托盘是一个 X 客户端,在给定的屏幕上拥有一个特殊的管理器选择并提供了容器窗口。Windows 将此功能称为“通知区域”。本文使用规范名称为“系统托盘”。
从UI的角度来看,系统托盘通常用于:
-
指示某些特殊状态的瞬态图标,例如网络,麒麟天气等;
-
一些功能强大的应用的托盘图标会实现更过丰富的功能。例如,qq托盘图标可以以告诉用户他们有新传入的即时消息或类似内容。
消息通知的基本思想是:在通知区域中创建图标比弹出对话框要更少地干扰用户。但是也不能让用户完全忽略这些消息,因此托盘区域添加了这项功能,该功能允许任务栏托盘图标弹出消息通知。
三、什么应用需要创建托盘图标
了解这些的基础上,我们可以思考什么情况下应用需要创建托盘图标。
- 用户想要通过任务栏上某些应用的图标状态可以清楚的了解该程序的运行状态,例如当前使用的输入法,网络是否连接,声音大小等。
图2 托盘图标可以看到网络连接与断开
-
某些后台进程,需要一个点击事件将其主界面迅速打开,例如麒麟影音(kylin-video)。
-
某些应用的设置,需要简洁高效。任务栏属于常驻应用,托盘图标的点击等可以很方便地完成相关设置。例如蓝牙的设置可以通过托盘区域图标右键来进行连接,断开等操作。控制面板中虽然同样可以实现此功能,但是用户通过打开控制面板进行连接带来的用户体验是不佳的。
-
某些应用需要托盘图标来接受消息通知。我们应该更好的利用托盘区域的消息通知,相比弹出框,这个区域的消息通知并不会给用户造成太大的视觉干扰,但是可起到和消息弹出框一样的效果。如果有应用频繁发送消息,那么以弹出框的形式来展示这些消息可能并不合理,这可能会给用户的其他操作带来巨大影响。所以我们应该给用户选择的空间,是否使用托盘区域来接受消息通知。实际上微信等聊天程序就是这么处理的,他们并不会弹出提示框,仅仅是通过托盘图标的闪动以及通过托盘图标的悬浮显示消息内容。
四、托盘应用创建标准
目前用户越来越难以与当前托盘区域进行交互。每个应用程序的行为各不相同(从外观我们无法判断一个托盘应用的功能是通过右键菜单项还是点击事件来实现),甚至有时它们的样式也不同。而托盘插件无法统一或改变这些托盘应用的行为和样式,因此使用较为统一的标准进行托盘应用的开发是有必要的。
-
目前 Linux 社区比较认可的托盘应用行为和样式是:图标用以显示状态,右键菜单显示功能。(甚至 gnome 为了统一这部分区域,采用了较为极端的方式:取消托盘应用的显示功能,仅保留系统应用的几个图标用以显示状态。)
-
托盘应用的功能应尽量选择右键菜单来完成(或左右键点击触发相同的界面),如果托盘应用的主要功能由主界面完成,那么也至少应该在右键菜单中包含常用功能。这样能最大限度的保持与社区托盘应用相同的交互逻辑。
-
在需要弹出主界面窗口的位置紧贴任务栏的情况下,应尽量使用 Qt 接口获取可用工作区的尺寸来使主界面正确的显示在可用工作区相应位置。
-
托盘图标的启动和需要弹出的主窗口界面的启动需要分离开来,即在任务栏启动过程中只需加载托盘上的图标,当用户在使用过程中点击了该图标,才需要去加载弹出主窗口,因此第一次加载会有一些慢,但这是在可以接受的范围内。
-
上述处理无法满足特殊设计需求的情况下,(例如 ukui-sidebar 的侧边弹出功能,)可以通过 dbus 接口获取任务栏的高度、位置来优化相关界面,但必须做好没有任务栏的情况下的异常处理。(托盘应用的弹出界面应尽量避免与任务栏交互,这样会造成当前应用在其他桌面环境下弹出界面异常的问题)。
如果确定了自己所开发的应用程序需要在托盘区域提供一个图标用于显示状态或消息提示,就要用到 QSystemTrayIcon 这个类。
五、使用QSystemTrayIcon 在麒麟系统上创建托盘图标
1. 常用函数介绍
● void setContextMenu(QMenu * menu)
给托盘图标设置功能菜单。当用户通过点击鼠标请求系统托盘的上下文菜单时,菜单会弹出,如不需要也可以不设置此菜单。
● void setIcon(const QIcon & icon)
设置图标。QIcon 这个属性保存了系统托盘的图标。托盘图标理应由系统托盘分配任何尺寸,并且应尽最大努力有效地应对任何尺寸。因此应首先考虑获取主题图标,托盘插件会根据用户的调节重新绘制图标的大小。
● void setVisible(bool visible)
设置系统托盘是否可见。一般来说我们希望系统托盘图标是可见的。设置为 true(或调用 show() )使系统托盘图标可见;设置为 false(或调用 hide())使图标隐藏。
● void showMessage(const QString & title, const QString & message, QSystemTrayIcon::MessageIcon icon = QSystemTrayIcon::Information, int millisecondsTimeoutHint = 10000)
此处显示的是一个通知消息,使用所给出的标题、消息、图标和指定的时间,标题和消息必须是纯文本字符串。
消息可以被用户点击,当用户点击时发出messageClicked()信号 。
托盘图标可能会要求系统托盘向用户显示通知消息。系统托盘会协调通知消息,以确保它们具有一致的外观,并避免一次显示多个通知消息
● void activated(QSystemTrayIcon::ActivationReason reason)
当用户激活系统托盘图标,这个信号被发射。reason 指定激活的原因。
● void messageClicked()
当使用 showMessage() 显示的消息被用户点击时,此信号被发射。
● bool isSystemTrayAvailable() [static]
如果系统托盘可用,返回 true;否则,返回 false。
如果系统托盘区域是当前不可用,但以后变为可用的时候且QSystemTrayIcon可见的情况下,它就会自动在系统托盘中添加。
在托盘应用程序启动的入口先调用 isSystemTrayAvailable()静态函数来检测是否当前系统存在可应用托盘应用显示的区域,否则可能会造成托盘应用启动早于任务栏,任务栏启动后无法显示托盘图标的问题
2. 用 Qt 注册托盘图标的步骤详解
(1) 首先创建一个 Qt 项目,命名为“traytest”,基类选择 QWidget;
(2) 在 traytest.cpp 中引入头文件“#include”;
(3) 在构造函数中 new 一个 QSystemTrayIcon 的成员变量;
(4) 给变量设置图标,如果主题库中有这个图标就从主题库中直接获取,主题库没有的情况下可以加载一张图片;
(5) 调用 show() 函数显示该图标。
这样,就实现了任务栏托盘图标的显示,下面以火狐(Firefox)图标为例,提供一个 Demo。
运行结果如下: