QT之Qml使用QSystemTrayIcon实现系统托盘

Posted 特立独行的猫a

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了QT之Qml使用QSystemTrayIcon实现系统托盘相关的知识,希望对你有一定的参考价值。

系统托盘图标,现代操作系统通常在桌面上提供一个特殊区域,称为系统托盘或通知区域,长时间运行的应用程序可以在其中显示图标和短消息。网上找到的例子大多太凌乱,这里总结下提供个代码封装,方便后续用到了简单使用。

 QT中实现这一功能使用QSystemTrayIcon,它为应用程序在系统托盘中提供一个图标。现代操作系统通常在桌面上提供一个特殊区域,称为系统托盘或通知区域,长时间运行的应用程序可以在其中显示图标和短消息。

下面是一个SystemTrayIcon类的封装,后面介绍它在Qml中的简单使用。

代码封装

systemtrayicon.h文件:

#ifndef SYSTEMTRAYICON_H
#define SYSTEMTRAYICON_H

#include <QObject>
#include <QActionEvent>
#include <QAction>
#include <QQuickItem>
#include <QSystemTrayIcon>

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 <QApplication>
#include <QMenu>
#include <QAction>
#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<MyAction *>(it);
            m_menu->addAction(action);
        
        else if (it->objectName() == "MySeparator")
        
            m_menu->addSeparator();
        
        else if (it->objectName() == "MyMenu")
        
            MyMenu *menu = qobject_cast<MyMenu *>(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 <QObject>

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 <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#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<MyMenu>("my.util", 1, 0, "MyMenu");        //注册到qml中
  qmlRegisterType<MyAction>("my.util", 1, 0, "MyAction");
  qmlRegisterType<MySeparator>("my.util", 1, 0, "MySeparator");
  qmlRegisterType<SystemTrayIcon>("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();

在Qml文件中使用

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教程 微雪课堂

以上是关于QT之Qml使用QSystemTrayIcon实现系统托盘的主要内容,如果未能解决你的问题,请参考以下文章

QT Quick QML 实例之 Popup 弹出界面

QT Quick QML 实例之 Popup 弹出界面

QT Quick QML 实例之 Popup 弹出界面

Qt基于QmL操作Windows任务栏按钮WinExtras实现

Qt 系统托盘(加hover效果)

Qt 5.3 QSystemTrayIcon 无法正常工作[Linux]