Evgenii Legotckoi
Evgenii Legotckoi01 листопада 2015 р. 12:39

QML - Урок 008. Як працювати з системним треєм (QSystemTrayIcon)

При перекладі інтерфейсу програми c Qt/C++ на Qt/QML мої руки дісталися до іконки програми в системному треї. Завдання полягало в тому, щоб перевести іконку в System Tray з C++ на Qml, частково чи повністю. Перший варіант, який я реалізував, був обгорткою навколо QSystemTrayIcon із QMenu з використанням системи сигналів та слотів . Рішення цілком логічне, враховуючи, що в QML немає готового об'єкта, на кшталт MenuBar для System Tray . Тому робимо обгортку, з якою можна взаємодіяти з QML шару.

Після того, як обгортка була реалізована, мені довелося порадитися з програмістом з Wargamming Костянтином Ляшкевичем , який порадив мені також звернути увагу на те, що * QML може мати доступ не тільки до сигналів та слотів, але й до параметрів Q_PROPERTY, які також були в класі QSystemTrayIcon, тобто фактично можна було тільки зареєструвати цей клас, як тип у QML шар і спробувати написати практично весь код на QML. Я перевірив цю пораду і розповів про результат Костянтину. В результаті він сам зацікавився даним завданням і ми витратили вечірню годину на цікаве костилювання і спільними зусиллями запихали QSystemTrayIcon максимально в QML. *

Таким чином, у цій статті Ви побачите дві реалізації для роботи з іконкою в системному треї.

Отримана програма буде згортатися в System Tray на кліку по іконці в треї, а також натискання кнопки закриття вікна. Але тільки в тому випадку, якщо буде активний спеціальний чекбокс, для контролю процесу згортання вікна програми в трей, якщо чекбокс не активний, то програма буде закриватися. Також програму можна буде закрити при активному чекбокс через пункт меню в іконці системного трею.


Перший варіант

Варіант для роботи із системним треєм через клас обгортку.

Структура проекту для роботи з System Tray

У проект входять такі файли:

  • QmlSystemTray.pro - профайл проекту;
  • main.cpp - основний файл вихідних кодів для запуску програми;
  • systemtray.h - заголовний файл класу для роботи із системним треєм;
  • systemtray.cpp - файл вихідних кодів класу для роботи із системним треєм;
  • main.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();
    // Устанавливаем доступ к свойствам объекта класса в контексте QML
    context->setContextProperty("systemTray", systemTray);

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

    return app.exec();
}

systemtray.h

У заголовному файлі класу SystemTray оголошуємо сигнали, які будуть передавати інформацію в QML, а також об'єкт 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:
    /* Объявляем объект будущей иконки приложения для трея */
    QSystemTrayIcon         * trayIcon;
};

#endif // SYSTEMTRAY_H

systemtray.cpp

Далі прописуємо вихідний код класу для роботи з System Tray, але реалізуємо лише подачу сигналів при взаємодії з пунктами меню та іконкою системного трею. Логіка обробки сигналів буде реалізована в QML.

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

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

    // Создаём контекстное меню с двумя пунктами
    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);

    /* Инициализируем иконку трея, устанавливаем иконку,
     * а также задаем всплывающую подсказку
     * */
    trayIcon = new QSystemTrayIcon();
    trayIcon->setContextMenu(trayIconMenu);
    trayIcon->setIcon(QIcon(":/logo-min.png"));
    trayIcon->show();
    trayIcon->setToolTip("Tray Program" "\n"
                         "Работа со сворачиванием программы трей");

    /* Также подключаем сигнал нажатия на иконку к обработчику
     * данного нажатия
     * */
    connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
            this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
}

/* Метод, который обрабатывает нажатие на иконку приложения в трее
 * */
void SystemTray::iconActivated(QSystemTrayIcon::ActivationReason reason)
{
    switch (reason){
    case QSystemTrayIcon::Trigger:
        // В случае сигнала нажатия на иконку трея вызываем сигнал в QML слой
        emit signalIconActivated();
        break;
    default:
        break;
    }
}

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

main.qml

Для отримання доступу до властивостей об'єкта класу SystemTray у Qml шарі прописуємо об'єкт Connections, через яке здійснюється підключення до об'єкта SystemTray. у властивості target прописуємо ім'я, яке було оголошено у файлі main.cpp, коли в движок Qml встановлювався доступ до об'єкта системного трею через метод setContextProperty().

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")

    // Переменная для игнорирования чекбокса
    property bool ignoreCheck: false

    /* С помощью объекта Connections
     * Устанавливаем соединение с классом системного трея
     * */
    Connections {
        target: systemTray
        // Сигнал - показать окно
        onSignalShow: {
            application.show();
        }

        // Сигнал - закрыть приложения игнорируя чек-бокс
        onSignalQuit: {
            ignoreCheck = true
            close();
        }

        // Свернуть/развернуть окно через клик по системному трею
        onSignalIconActivated: {
             if(application.visibility === Window.Hidden) {
                 application.show()
             } else {
                 application.hide()
             }
        }
    }

    // Тестовый чекбокс для управления закрытием окна
    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>

// Объявляем пользовательский тип данных для работы с иконкой в QML
Q_DECLARE_METATYPE(QSystemTrayIcon::ActivationReason)

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QQmlApplicationEngine engine; // Инициализируем Qml движок

    // Регистрируем QSystemTrayIcon в качестве типа объекта в Qml
    qmlRegisterType<QSystemTrayIcon>("QSystemTrayIcon", 1, 0, "QSystemTrayIcon");
    // Регистрируем в QML тип данных для работы с получаемыми данными при клике по иконке
    qRegisterMetaType<QSystemTrayIcon::ActivationReason>("ActivationReason");
    // Устанавливаем Иконку в контекст движка
    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 ми отримуємо можливість залежно від типу переданого значення reason визначати реакцію на кліки миші по іконці програми у системному треї. Коли ми робимо клік правою кнопкою миші по іконці програми в системному треї, виникає Menu. Меню викликається функцією 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")

    // Зарегистрированный системный трей в QML слое
    QSystemTrayIcon {
        id: systemTray

        // Первоначальная инициализация системного трея
        Component.onCompleted: {
            icon = iconTray             // Устанавливаем иконку
            // Задаём подсказку для иконки трея
            toolTip = "Tray Program
Работа со сворачиванием программы трей"
            show();
        }

        /* По клику на иконку трея определяем,
         * левой или правой кнопкой мыши был клик.
         * Если левой, то скрываем или открываем окно приложения.
         * Если правой, то открываем меню системного трея
         * */
        onActivated: {
            if(reason === 1){
                trayMenu.popup()
            } else {
                if(application.visibility === Window.Hidden) {
                    application.show()
                } else {
                    application.hide()
                }
            }
        }
    }

    // Меню системного трея
    Menu {
        id: trayMenu

        MenuItem {
            text: qsTr("Развернуть окно")
            onTriggered: application.show()
        }

        MenuItem {
            text: qsTr("Выход")
            onTriggered: {
                systemTray.hide()
                Qt.quit()

            }
        }
    }

    // Тестовый чекбокс для управления закрытием окна
    CheckBox {
        id: checkTray
        anchors.centerIn: parent
        text: qsTr("Включить сворачивание в системный трей при нажатии кнопки закрытия окна")
    }

    // Обработчик события закрытия окна
    onClosing: {
        /* Если чекбокс не должен игнорироваться и он активен,
         * то скрываем приложение.
         * В противном случае закрываем приложение
         * */
        if(checkTray.checked === true){
            close.accepted = false
            application.hide()
        } else {
            // Завершаем приложение
            Qt.quit()
        }
    }
}

Підсумок

В результаті виконаної роботи, Ви отримаєте програму, яка буде згортатися в трей як на кліки по іконці в системній треї, так і натискання кнопки закриття вікна програми. Виглядатиме воно, як на нижченаведеному малюнку. Ну а демонстрацію роботи програми Ви можете побачити у відеоуроці, де пояснюється обидва варіанти програмного коду

Соавтор статті: Костянтин Ляшкевич

Відеоурок

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

o
  • 03 квітня 2017 р. 17:13

Здравствуйте. В первом варианте не выложен файл QmlSystemTray.pro без него как-то не полно.

Evgenii Legotckoi
  • 04 квітня 2017 р. 02:33

Добрый день. Добавил QmlSystemTray.pro.

o
  • 04 квітня 2017 р. 02:34

Спасибо)

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:50бали,
  • Рейтинг балів-4
m
  • molni99
  • 26 жовтня 2024 р. 01:37

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
m
  • molni99
  • 26 жовтня 2024 р. 01:29

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 11:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 жовтня 2024 р. 14:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 08:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 07:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 11:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi24 червня 2024 р. 15:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 06:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 03:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 09:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Слідкуйте за нами в соціальних мережах