系统托盘图标,现代操作系统通常在桌面上提供一个特殊区域,称为系统托盘或通知区域,长时间运行的应用程序可以在其中显示图标和短消息。网上找到的例子大多太凌乱,这里总结下提供个代码封装,方便后续用到了简单使用。
QT中实现这一功能使用QSystemTrayIcon,它为应用程序在系统托盘中提供一个图标。现代操作系统通常在桌面上提供一个特殊区域,称为系统托盘或通知区域,长时间运行的应用程序可以在其中显示图标和短消息。
下面是一个SystemTrayIcon类的封装,后面介绍它在Qml中的简单使用。
systemtrayicon.h文件:
#ifndef SYSTEMTRAYICON_H
#define SYSTEMTRAYICON_H
#include
#include
#include
#include
#include
class MyAction : public QAction
{
Q_OBJECT
//Q_PROPERTY宏提供在qml中访问的信号槽等等
Q_PROPERTY(QUrl icon READ icon WRITE setIcon NOTIFY iconChanged)
public:
MyAction(QObject *parent = nullptr);
~MyAction();
QUrl icon() const;
signals:
void iconChanged();
public slots:
void setIcon(const QUrl &arg);
private:
QUrl m_icon;
};
class MySeparator : public QObject
{
public:
MySeparator(QObject *parent = nullptr);
~MySeparator();
};
class SystemTray;
class MyMenu : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(int width READ width WRITE setWidth NOTIFY widthChanged)
Q_PROPERTY(int height READ height WRITE setHeight NOTIFY heightChanged)
public:
MyMenu(QQuickItem *parent = nullptr);
~MyMenu();
int width() const;
int height() const;
void clear();
signals:
void widthChanged();
void heightChanged();
public slots:
void setWidth(int arg);
void setHeight(int arg);
void addSeparator();
void addAction(MyAction *action);
void addMenu(MyMenu *menu);
protected:
void componentComplete();
private:
friend class SystemTrayIcon; //让SystemTray能够直接访问m_menu
QMenu *m_menu;
};
class SystemTrayIcon : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(int x READ x CONSTANT)
Q_PROPERTY(int y READ y CONSTANT)
Q_PROPERTY(QUrl icon READ icon WRITE setIcon NOTIFY iconChanged)
Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip NOTIFY toolTipChanged)
Q_PROPERTY(MyMenu* menu READ menu WRITE setMenu NOTIFY menuChanged)
public:
SystemTrayIcon(QQuickItem *parent = nullptr);
~SystemTrayIcon();
int x() const;
int y() const;
QUrl icon() const;
QString toolTip() const;
MyMenu* menu() const;
signals:
void trigger();
void iconChanged();
void toolTipChanged();
void menuChanged();
public slots:
void setIcon(const QUrl &arg);
void setToolTip(const QString &arg);
void setMenu(MyMenu *arg);
void onVisibleChanged();
void onActivated(QSystemTrayIcon::ActivationReason reason);
void onExit();
private:
QSystemTrayIcon *m_systemTray;
MyMenu *m_menu;
QString m_toolTip;
QUrl m_icon;
};
#endif // SYSTEMTRAYICON_H
systemtrayicon.cpp文件:
#include
#include
#include
#include "systemtrayicon.h"
MyAction::MyAction(QObject *parent)
: QAction(parent)
{
setObjectName("MyAction");
}
MyAction::~MyAction()
{
}
QUrl MyAction::icon() const
{
return m_icon;
}
void MyAction::setIcon(const QUrl &arg)
{
if(m_icon != arg)
{
QString str = arg.toLocalFile();
if(str == "") str = arg.toString(); //如果转换失败
if( str.mid (0, 3) == "qrc")
str = str.mid (3, str.count() - 3);
QAction::setIcon(QIcon(str));
m_icon = arg;
emit iconChanged();
}
}
MySeparator::MySeparator(QObject *parent)
: QObject(parent)
{
setObjectName("MySeparator");
}
MySeparator::~MySeparator()
{
}
MyMenu::MyMenu(QQuickItem *parent)
: QQuickItem(parent)
{
setObjectName("MyMenu");
m_menu = new QMenu();
}
MyMenu::~MyMenu()
{
}
int MyMenu::width() const
{
return m_menu->width();
}
int MyMenu::height() const
{
return m_menu->height();
}
void MyMenu::clear() //清空caidan
{
m_menu->clear();
}
void MyMenu::setWidth(int arg)
{
if (m_menu->width() != arg)
{
m_menu->setFixedWidth(arg);
emit widthChanged();
}
}
void MyMenu::setHeight(int arg)
{
if (m_menu->height() != arg)
{
m_menu->setFixedHeight(arg);
emit heightChanged();
}
}
void MyMenu::addAction(MyAction *action)
{
m_menu->addAction(action);
}
void MyMenu::addSeparator()
{
m_menu->addSeparator();
}
void MyMenu::addMenu(MyMenu *menu)
{
m_menu->addMenu(menu->m_menu);
}
void MyMenu::componentComplete() //在菜单完成构建后调用,将自定义Action,Menu,Separator通过objectName判断加入
{
QQuickItem::componentComplete();
QObjectList list = children();
for (auto it : list)
{
if (it->objectName() == "MyAction")
{
MyAction *action = qobject_cast(it);
m_menu->addAction(action);
}
else if (it->objectName() == "MySeparator")
{
m_menu->addSeparator();
}
else if (it->objectName() == "MyMenu")
{
MyMenu *menu = qobject_cast(it);
m_menu->addMenu(menu->m_menu);
}
}
}
SystemTrayIcon::SystemTrayIcon(QQuickItem *parent)
: QQuickItem(parent)
{
m_systemTray = new QSystemTrayIcon(this);
connect(m_systemTray, &QSystemTrayIcon::activated, this, &SystemTrayIcon::onActivated);
connect(this, &SystemTrayIcon::visibleChanged, this, &SystemTrayIcon::onVisibleChanged);
setVisible(false); //给visible一个初始值,否则会不显示
}
SystemTrayIcon::~SystemTrayIcon()
{
}
int SystemTrayIcon::x() const
{
return m_systemTray->geometry().x();
}
int SystemTrayIcon::y() const
{
return m_systemTray->geometry().y();
}
QUrl SystemTrayIcon::icon() const
{
return m_icon;
}
QString SystemTrayIcon::toolTip() const
{
return m_systemTray->toolTip();
}
MyMenu *SystemTrayIcon::menu() const
{
return m_menu;
}
void SystemTrayIcon::setIcon(const QUrl &arg)
{
if(m_icon != arg)
{
QString str = arg.toLocalFile();
if(str == "") str = arg.toString();
if( str.mid (0, 3) == "qrc")
str = str.mid (3, str.count() - 3);
m_systemTray->setIcon(QIcon(str));
m_icon = arg;
emit iconChanged();
}
}
void SystemTrayIcon::setToolTip(const QString &arg)
{
if (m_toolTip != arg)
{
m_systemTray->setToolTip(arg);
m_toolTip = arg;
emit toolTipChanged();
}
}
void SystemTrayIcon::setMenu(MyMenu *arg)
{
if (m_menu != arg)
{
m_menu = arg;
m_systemTray->setContextMenu(m_menu->m_menu);
m_systemTray->installEventFilter(this);
emit menuChanged();
}
}
void SystemTrayIcon::onVisibleChanged() //visible可见性改变时显示/隐藏托盘
{
m_systemTray->setVisible(isVisible());
}
void SystemTrayIcon::onActivated(QSystemTrayIcon::ActivationReason reason)
{
switch (reason)
{
case QSystemTrayIcon::DoubleClick:
case QSystemTrayIcon::Trigger:
emit trigger(); //单击双击托盘图标时发送trigger()信号, reason类似还有Context,MiddleClick,Unknow
default:
break;
}
}
void SystemTrayIcon::onExit() //应在程序退出时调用,防止图标不消失
{
m_systemTray->hide();
QApplication::exit(0);
}
首先需要在main函数中把自定义的类注册到Qml中,使用qmlRegisterType。
qmlRegisterType 是一个可以将C++实现的类在QML中调用的,连接C++和QML的一个工具,是一个非常重要的函数。它总共4个参数:第一个参数* uri指的是QML中import后的内容,相当于头文件名,第二个第三个参数分别是主次版本号,第四个指的是QML中类的名字。 (注意第四个QML的类名首字母一定要大写,要不然会报错。)
它与setContextProperty的区别是:
//简单的上下文属性,对应的值为QVariant类型。
void QQmlContext::setContextProperty(const QString &name, const QVariant &value)
//相对来说稍微复杂一些,QObject*对象类型。
void QQmlContext::setContextProperty(const QString &name, QObject *value)
如果要使用某个全局类的实例来访问QML或从QML访问,需要在这之前创建此类对象。再使用setContextProperty()注册进去,然后QML中就可以直接使用这个类的对象。如:
MainController mainController;
engine.rootContext()->setContextProperty("MainController", &mainController);
但是这种方式不太好,setContextProperty要求对象实例的生命期需要我们自己管理,所以对象需要在堆上创建,否则离开了当前作用域就被析构了 。在栈上分配的对象“mainController”将在"return app.exec()"之后不久析构。正确应该是:
MainController mainController = new MainController;
engine.rootContext()->setContextProperty("MainController", mainController);
另需注意的是,这些定义的类需继承自QObject。类实例的方法需要qml中调用时,需要在函数前面加上Q_INVOKABLE宏。如:
#include
class RDBRestore : public QObject
{
Q_OBJECT
public:
explicit RDBRestore(QObject* parent = nullptr);
public:
Q_INVOKABLE int restoreRedis(const QString& fileNameWithPath);
Q_INVOKABLE bool checkRdbFileExist();
Q_INVOKABLE void removeRdbFile();
Q_INVOKABLE bool getIsRdbFileExist();
private:
bool isRdbFileExist;
};
下面开始正式使用,main中这样使用,把相关类注册,使用qmlRegisterType:
#include
#include
#include
#include "systemtrayicon.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
//auto restoreDb = new RDBRestore;
//engine.rootContext()->setContextProperty("restoreDb", restoreDb);
//系统托盘相关
qmlRegisterType("my.util", 1, 0, "MyMenu"); //注册到qml中
qmlRegisterType("my.util", 1, 0, "MyAction");
qmlRegisterType("my.util", 1, 0, "MySeparator");
qmlRegisterType("my.util", 1, 0, "SystemTrayIcon");
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(
&engine, &QQmlApplicationEngine::objectCreated, &app,
[url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
},
Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
import QtQuick 2.10
import QtQuick.Window 2.10
import my.util 1.0
Window {
id: root
width: 1280
height: 1024
color: "#e5e5e5"
visible: true
flags: Qt.Window | Qt.MSWindowsFixedSizeDialogHint
Image {
id: image
anchors.right: parent.right
anchors.rightMargin: 378
anchors.left: parent.left
anchors.leftMargin: 379
anchors.bottom: parent.bottom
anchors.bottomMargin: 578
anchors.top: parent.top
anchors.topMargin: 213
source: "image/background.png"
}
Text {
id: text2
x: 660
color: "#004695"
text: qsTr("hello world")
styleColor: "#000000"
font.weight: Font.Bold
font.family: "微软雅黑"
anchors.top: image.bottom
anchors.topMargin: 12
font.pixelSize: 18
}
Timer {
id: checkDownTimer
interval: 100
repeat: true
running: false
onTriggered: {
//定时任务
}
}
// 托盘图标
SystemTrayIcon{
id: systemTray
menu: menu
visible: true
icon: "qrc:///image/myicon.ico"
toolTip: "daemon is runing"
onTrigger:{
root.requestActivate();
root.show();
}
MyMenu{
id: menu
MyAction{
text: "显示界面"
icon: "qrc:///image/myicon.ico"
onTriggered: {
console.log("onTriggered 2")
root.requestActivate();
root.show();
}
}
MyAction{
text: "隐藏界面"
icon: "qrc:///image/myicon.ico"
onTriggered: {
console.log("onTriggered 3")
root.hide();
}
}
MySeparator {}
MyAction{
id:exitItem
icon: "qrc:///image/myicon.ico"
text: qsTr("Exit")
onTriggered: Qt.quit()
}
}
}
Component.onCompleted: {
checkDownTimer.start()
}
onClosing: {
//点击关闭按钮时阻止关闭不退出而是最小化至托盘显示
root.hide()
}
}
Qt中的系统托盘QSystemTrayIcon分析_@蓝枫的博客-CSDN博客
Qt之QSystemTrayIcon_weixin_34055910的博客-CSDN博客
Qt浅谈之三十系统托盘(QSystemTrayIcon)_乌托邦2号的博客-CSDN博客
qt 之 QSystemTrayIcon(托盘程序整个例子)_比卡丘不皮的博客-CSDN博客_qsystemtrayicon
在QML中使用QSystemTrayIcon(系统托盘)_梦起丶的博客-CSDN博客_qml 托盘
树莓派Qt系列教程29(下):Qml和C++混合编程 - 树莓派QT教程 微雪课堂
【QT】QML与C++混合编程详解_会飞的代码UP的博客-CSDN博客_qt和c++混合编程
QML与C++集成<二>——<使用C++属性及注册QML类型> - 走看看
树莓派Qt系列教程8: 信号与槽 - 树莓派QT教程 微雪课堂