QML如何与QSystemTrayIcon协同工作
阅读原文时间:2021年04月23日阅读:1

文章目录

问题来源

将应用程序从Qt / C ++移动到Qt / QML时,遇到系统托盘的问题。目的是将系统托盘的C ++代码部分的或全部的转换为QML代码。我意识到,第一个选项是利用信号和插槽机制包装QSystemTrayIcon,QMenu。这个解决方案非常符合逻辑,因为QML不是一个工具,比如使用MenuBar来构建System Tray。所以我们做了一个包装器,它可以在QML层中与QSystemTrayIcon进行交互。

在包装器实现之后,我注意QML不仅可以访问信号和插槽,还可以访问Q_PROPERTY的参数。也就是说,在QSystemTrayIcon类中,实际上只能将此类注册为QML中的类型层,并尝试在QML上编写几乎所有代码。

因此,在本文中,您将看到两个在QML中实现系统托盘的方法

通过单击系统托盘中的图标以及按关闭按钮,最终应用程序将最小化到系统托盘。但只有在活动时才会激活特殊复选框以控制应用程序窗口向系统托盘的折叠过程,如果未启用该复选框,则应用程序将关闭。此外,可以使用系统托盘图标中的活动复选框菜单项关闭应用程序。

第一个版本

Variant通过包装类使用系统托盘。

系统托盘项目的结构


这个项目包含以下文件:
QmlSystemTray.pro - 你知道这是啥
main.cpp -启动应用的主要文件
systemtray.h
systemtray.cpp -QSystemTrayIcon的包装
main.qml - QML代码
logo-min.png

QmlSystemTray.pro

TEMPLATE = app

QT += qml quick widgets

SOURCES += main.cpp \
    systemtray.cpp

RESOURCES += qml.qrc

# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =

# Default rules for deployment.
include(deployment.pri)

HEADERS += \
    systemtray.h

main.cpp

对单独的Qt / C ++类的对象进行声明和初始化,并从Qml层设置对它的访问。

#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QSystemTrayIcon>

#include <systemtray.h>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QQmlApplicationEngine engine;


    SystemTray * systemTray = new SystemTray();
    QQmlContext * context = engine.rootContext();
    // 将systemTray放入QML上下文中
    context->setContextProperty("systemTray", systemTray);

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

systemtray.h

头文件声明了将信息传递给QML的SystemTray类的信号,以及将要进行交互的QSystemTrayIcon对象。 另外,我们声明与此图标交互的处理程序。

#ifndef SYSTEMTRAY_H
#define SYSTEMTRAY_H

#include <QObject>
#include <QAction>
#include <QSystemTrayIcon>

class SystemTray : public QObject
{
    Q_OBJECT
public:
    explicit SystemTray(QObject *parent = 0);

signals:
    void signalIconActivated();
    void signalShow();
    void signalQuit();

private slots:
    /* 将接受来自点击系统托盘中的应用程序图标的事件的槽
     */
    void iconActivated(QSystemTrayIcon::ActivationReason reason);

public slots:
    void hideIconTray();

private:
    /* Declare the object of future applications for the tray icon*/
    QSystemTrayIcon         * trayIcon;
};

#endif // SYSTEMTRAY_H

systemtray.cpp

接下来,我们编写该类的源代码以使用系统托盘,但在与菜单项和系统托盘图标的交互中仅实现供应信号。 信号处理逻辑以QML实现。

#include "systemtray.h"
#include <QMenu>
#include <QSystemTrayIcon>

SystemTray::SystemTray(QObject *parent) : QObject(parent)
{

    // 创建两个项目的QMenu
    QMenu *trayIconMenu = new QMenu();

    QAction * viewWindow = new QAction(trUtf8("Развернуть окно"), this);
    QAction * quitAction = new QAction(trUtf8("Выход"), this);

    /* 将单击信号连接至适当的QML信号中
     * */
    connect(viewWindow, &QAction::triggered, this, &SystemTray::signalShow);
    connect(quitAction, &QAction::triggered, this, &SystemTray::signalQuit);

    trayIconMenu->addAction(viewWindow);
    trayIconMenu->addAction(quitAction);

    /* Initialize the tray icon, icon set, and specify the tooltip
     * */
    trayIcon = new QSystemTrayIcon();
    trayIcon->setContextMenu(trayIconMenu);
    trayIcon->setIcon(QIcon(":/logo-min.png"));
    trayIcon->show();
    trayIcon->setToolTip("Tray Program" "\n"
                         "Work with winimizing program to tray");

    /* 连接点击在托盘图标上的信号
     * */
    connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
            this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
}

/* 点击托盘图标的事件处理
 * */
void SystemTray::iconActivated(QSystemTrayIcon::ActivationReason reason)
{
    switch (reason){
    case QSystemTrayIcon::Trigger:
        // In the case of pressing the signal on the icon tray in the call signal QML layer
        emit signalIconActivated();
        break;
    default:
        break;
    }
}

void SystemTray::hideIconTray()
{
    trayIcon->hide();
}

main.qml

要访问Qml层中SystemTray类的对象的属性,请指定对象Connections,通过该对象连接到SystemTray对象。 制定目标属性名称是在main.cpp文件中进行的的,当安装引擎时,通过系统托盘setContextProperty()方法访问对象的Qml。

import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Window 2.0

ApplicationWindow {
    id: application
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    // Chance to ignore the checkbox
    property bool ignoreCheck: false

    /* 将systemTray的信号连接至本地
     * */
    Connections {
        target: systemTray
        // 这些都是上面我们定义的systemTray的信号
        onSignalShow: {
            application.show();
        }

        // The signal - close the application by ignoring the check-box
        onSignalQuit: {
            ignoreCheck = true
            close();
        }

        // 通过点击系统托盘图标显示隐藏窗口
        onSignalIconActivated: {
             if(application.visibility === Window.Hidden) {
                 application.show()
             } else {
                 application.hide()
             }
        }
    }

    // 控制关闭窗口的CheckBox
    CheckBox {
        id: checkTray
        anchors.centerIn: parent
        text: qsTr("关闭窗口时,最小化至系统托盘")
    }

    //处理窗口关闭
    onClosing: {
        if(checkTray.checked === true && ignoreCheck === false){
            close.accepted = false
            application.hide()
        } else {
            Qt.quit()
        }
    }
}

第二个变种

系统托盘项目的结构


在这个版本中,包含以下文件

  • QmlSystemTray.pro -你知道这是啥
  • main.cpp - 你也知道
  • main.qml -你应该还知道
  • logo-min.png -你肯定知道

QmlSystemTray_2.pro

在这种情况下,我建议您注意项目中连接的模块。 因为没有quickwidgets模块不起作用。

TEMPLATE = app

QT += qml quick widgets quickwidgets

SOURCES += main.cpp

RESOURCES += qml.qrc

# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =

# Default rules for deployment.
include(deployment.pri)

main.cpp

还需要将库连接到QQuickWidget main.cpp源文件。 有必要使用qmlRegisterType函数。

#include <QApplication>
#include <QQmlApplicationEngine>
#include <QIcon>
#include <QQuickWidget>
#include <QSystemTrayIcon>
#include <QQmlContext>

// Declare a user-defined data type to work with an icon in QML
Q_DECLARE_METATYPE(QSystemTrayIcon::ActivationReason)

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QQmlApplicationEngine engine; 

    // Register QSystemTrayIcon in Qml
    qmlRegisterType<QSystemTrayIcon>("QSystemTrayIcon", 1, 0, "QSystemTrayIcon");
    // Register in QML the data type of click by tray icon
    qRegisterMetaType<QSystemTrayIcon::ActivationReason>("ActivationReason");
    // Set icon in the context of the engine
    engine.rootContext()->setContextProperty("iconTray", QIcon(":/logo-min.png"));
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

main.qml

接下来,我们声明QSystemTrayIcon对象并在onCompleted方法中对其进行配置。 由于我们已经注册了类型QSystemTrayIcon :: ActivationReason,因此根据发送的值的类型,我们能够使用onActivated方法来确定对系统托盘中的应用程序图标的鼠标点击的响应。 当我们右键单击系统托盘中的应用程序图标时,会出现一个菜单。 菜单是一个函数popup()。 功能特征是它在鼠标光标所在的位置调出菜单,因此菜单出现在系统托盘图标的位置。

import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Window 2.0
import QSystemTrayIcon 1.0

ApplicationWindow {
    id: application
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    // Registered in the system tray QML layer
    QSystemTrayIcon {
        id: systemTray

        // Initial initialization of the system tray
        Component.onCompleted: {
            icon = iconTray             // Set icon
            toolTip = "Tray Program"
            show();
        }

        /*  鼠标在托盘上左击或者右击分别处理不同的相应
         * 1是QSystemTrayIcon::Context
         * */
        onActivated: {
            if(reason === 1){
                trayMenu.popup()
            } else {
                if(application.visibility === Window.Hidden) {
                    application.show()
                } else {
                    application.hide()
                }
            }
        }
    }

    // Menu system tray
    Menu {
        id: trayMenu

        MenuItem {
            text: qsTr("Maximize window")
            onTriggered: application.show()
        }

        MenuItem {
            text: qsTr("Exit")
            onTriggered: {
                systemTray.hide()
                Qt.quit()

            }
        }
    }

    // Test check box to control the closing of the window
    CheckBox {
        id: checkTray
        anchors.centerIn: parent
        text: qsTr("Enable minimizing to system tray during the closing the window")
    }
    onClosing: {
        /* If the check box should not be ignored and it is active, then hide the application. 
         * Otherwise, close the application
         * */
        if(checkTray.checked === true){
            close.accepted = false
            application.hide()
        } else {
            Qt.quit()
        }
    }
}

手机扫一扫

移动阅读更方便

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