В данном HOWTO мы подробно рассмотрим как свернуть форму приложения на Qt в системный трей на всех платформах: Windows, GNU/Linux, MacOS. Мы будем использовать только методы Qt и никаких хаков вроде вызова WinAPI функций и т.д.

Введение

Для работы мы будем использовать свободную официальную IDE для разработки на C++/Qt – Qt Creator.

Код из наших примеров корректно компилируется и работает как в Qt 4.x, так и в 5.x. Лицензия всех фрагментов кода из данной статьи – GNU General Public License version 3.

Создаём проект

Создайте новый проект на Qt: File -> New file or Project -> Application -> Qt Widgets Application. Названия и пути укажите произвольные.

Подключаем заголовочные файлы

Для начала нам потребуется подключить заголовочные файлы, которые отвечают за работу с элементом QtTray. В файлах mainwindow.h и mainwindow.cpp пропишите:

#include <QSystemTrayIcon>

В файле mainwindow.cpp также подключите (потребуются нам для создания контекстного меню иконки в трее и вывода сообщения):

#include <QMenu>
#include <QMessageBox>

Работаем с заголовочными файлами

В mainwindow.h пропишите свойства и методы, которые будут использоваться в проекте:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSystemTrayIcon>

namespace Ui {
    class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void changeEvent(QEvent*);
    void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
    void trayActionExecute();
    void setTrayIconActions();
    void showTrayIcon();

private:
    Ui::MainWindow *ui;
    QMenu *trayIconMenu;
    QAction *minimizeAction;
    QAction *restoreAction;
    QAction *quitAction;
    QSystemTrayIcon *trayIcon;
};

#endif // MAINWINDOW_H

Добавляем значок в ресурс

В меню File -> New File or Project выберите Qt -> Qt Resource File.

Редактор ресурсов Qt Creator

Редактор ресурсов Qt Creator

В открывшемся редакторе создайте новый префикс кнопкой AddAdd Prefix с именем images, перейдите в него, затем загрузите изображение в формате PNG желательно с заданным альфа-каналом с именем abc.png (можно указать любое, но без пробелов и символов юникода).

Работаем с кодом

Опишите новый метод, в котором будет создан объект класса QSystemTrayIcon, заданы его свойства, добавлено простое контекстное меню и обработчик клика по значку в трее:

void MainWindow::showTrayIcon()
{
    // Создаём экземпляр класса и задаём его свойства...
    trayIcon = new QSystemTrayIcon(this);
    QIcon trayImage(":/images/abc.png");
    trayIcon -> setIcon(trayImage);
    trayIcon -> setContextMenu(trayIconMenu);

    // Подключаем обработчик клика по иконке...
    connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));

    // Выводим значок...
    trayIcon -> show();
}

Добавьте методы обработки кликов по иконке в системном трее:

void MainWindow::trayActionExecute()
{
    QMessageBox::information(this, "TrayIcon", "Тестовое сообщение. Замените вызов этого сообщения своим кодом.");
}

void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
{
    switch (reason)
    {
        case QSystemTrayIcon::Trigger:
        case QSystemTrayIcon::DoubleClick:
            this -> trayActionExecute();
            break;
        default:
            break;
    }
}

Создайте и добавьте контекстное меню для нашего значка:

void MainWindow::setTrayIconActions()
{
    // Setting actions...
    minimizeAction = new QAction("Свернуть", this);
    restoreAction = new QAction("Восстановить", this);
    quitAction = new QAction("Выход", this);

    // Connecting actions to slots...
    connect (minimizeAction, SIGNAL(triggered()), this, SLOT(hide()));
    connect (restoreAction, SIGNAL(triggered()), this, SLOT(showNormal()));
    connect (quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));

    // Setting system tray's icon menu...
    trayIconMenu = new QMenu(this);
    trayIconMenu -> addAction (minimizeAction);
    trayIconMenu -> addAction (restoreAction);
    trayIconMenu -> addAction (quitAction);
}

Теперь напишем перехватчик события сворачивания формы на панель задач и вместо этого свернём её в трей:

void MainWindow::changeEvent(QEvent *event)
{
    QMainWindow::changeEvent(event);
    if (event -> type() == QEvent::WindowStateChange)
    {
        if (isMinimized())
        {
            this -> hide();
        }
    }
}

Наше приложение практически готово. Последний штрих – отредактируйте событие инициализации формы и пропишите вызовы созданных нами методов:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui -> setupUi(this);
    this -> setTrayIconActions();
    this -> showTrayIcon();
}

Завершение и сборка

Скомпилируйте созданное приложение и запустите. Если всё сделано правильно, в трее появится значок, при щелчке левой кнопкой мыши по которому появится сообщение, а правой – контекстное меню с тремя пунктами.

Указанные примеры кода не имеют каких-либо проверок на наличие трея в окружении (в среде Gnome например трея может не быть), восстановление значка в случае падения приложения, управляющего треем (Explorer.exe в Windows) и т.д. Это было сделано сознательно, дабы максимально упростить код.

Исходники проекта

Исходники данного демонстрационного проекта доступны на GitHub: https://github.com/xvitaly/traysample и распространяются под лицензией GNU GPL версии 3.