在QML中使用QSystemTrayIcon(系统托盘)
阅读原文时间:2021年04月23日阅读:1

【写在前面】

写这篇文章的时候应该不算晚,因为 qml 中并没有正式的系统托盘组件。

但是,在 Qt Labs Platform 模块中( 实验模块,Qt 5.8引入 ),已经有了 SystemTrayIcon,相信以后会作为正式的模块进入 qml。

即便如此,本文仍提供了一种在qml中使用 QSystemTrayIcon 的方法,或者也可以作为 qml 与 c++ 交互的例子来看。


【正文开始】

先上完成后的 qml 代码:

main.qml

import QtQuick 2.7
import QtQuick.Window 2.2
import an.utility 1.0

Window
{
    id: root
    visible: true
    width: 400
    height: 200
    title: qsTr("Hello World")

    Rectangle
    {
        anchors.fill: parent
        color: "red"
    }

    SystemTray
    {
        id: systemTray
        menu: menu
        visible: true
        icon: "qrc:/winIcon.png"
        toolTip: "托盘运行中..."
        onTrigger:
        {
            root.requestActivate();
            root.show();
        }

        MyMenu
        {
            id: menu

            MyAction
            {
                text: "我在线上"
                icon: "qrc:/winIcon.png"
            }

            MyAction
            {
                text: "Q我吧"
                icon: "qrc:/winIcon.png"
            }

            MySeparator {}

            MyAction
            {
                text: "离开"
                icon: "qrc:/winIcon.png"
            }

            MyAction
            {
                text: "忙碌"
                icon: "qrc:/winIcon.png"
            }
        }
    }
}

效果图:

可以看到,效果和 widget 下使用是一样的,但用 qml 更简单更方便。

自定义的 SystemTrayIcon 我注册到 an.utility 1.0 中。下面讲解其实现。

systemtrayicon.h

#ifndef SYSTEMTRAYICON_H
#define SYSTEMTRAYICON_H

#include <QAction>
#include <QQuickItem>
#include <QSystemTrayIcon>

class MyAction : public QAction
{
    Q_OBJECT
    Q_PROPERTY(QUrl icon READ icon WRITE setIcon NOTIFY iconChanged)    //Q_PROPERTY宏提供在qml中访问的信号槽等等

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

要将 c++ 类导入到 qml 必须继承自 QObject 或其派生类,并使用 Q_OBJECT 宏。

而继承 QQuickItem 是为了得到 item 中的其他一些属性和信号,以及重实现 void componentComplete()。

该函数在组件完成时调用,重新实现来添加必要的操作。

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()&nbsp;&nbsp;&nbsp;&nbsp;//清空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);          &nbsp;&nbsp;&nbsp;&nbsp;//给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)&nbsp;&nbsp;&nbsp;&nbsp;//设置托盘上下文菜单
{
    if (m_menu != arg)
    {
        m_menu = arg;
        m_systemTray->setContextMenu(m_menu->m_menu);
        m_systemTray->installEventFilter(this);
        emit menuChanged();
    }
}

void SystemTrayIcon::onVisibleChanged()&nbsp;&nbsp;&nbsp;&nbsp;//visible可见性改变时显示/隐藏托盘
{
    m_systemTray->setVisible(isVisible());
}

void SystemTrayIcon::onActivated(QSystemTrayIcon::ActivationReason reason)
{
    switch (reason)
    {
    case QSystemTrayIcon::DoubleClick:
    case QSystemTrayIcon::Trigger:
        emit trigger();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//单击双击托盘图标时发送trigger()信号, reason类似还有Context,MiddleClick,Unknow

    default:
        break;
    }
}

void SystemTrayIcon::onExit()&nbsp;&nbsp;&nbsp;&nbsp;//应在程序退出时调用,防止图标不消失
{
    m_systemTray->hide();
    QApplication::exit(0);
}

main.cpp

#include <QApplication>
#include <QQmlApplicationEngine>
#include "systemtrayicon.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QApplication app(argc, argv);

    QQmlApplicationEngine engine;
    qmlRegisterType<MyMenu>("an.utility", 1, 0, "MyMenu");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//注册到qml中
    qmlRegisterType<MyAction>("an.utility", 1, 0, "MyAction");
    qmlRegisterType<MySeparator>("an.utility", 1, 0, "MySeparator");
    qmlRegisterType<SystemTrayIcon>("an.utility", 1, 0, "SystemTrayIcon");
    engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

【结语】

好吧,还有很多地方没有完善,比如快捷键。

其实写自定义组件和写 widget 差不多。。不过写好之后,在qml中使用起来就非常方便了。

当然,最后我自己试了试 Qt.labs.platform 1.0 中的 SystemTrayIcon,嗯基本上差不多吧。。所以期待以后的版本正式加入咯。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章