Сворачиваем форму на Qt в трей на всех платформах

В данном 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

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

В меню FileNew File or Project выберите QtQt 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.

6 commentaries to post

  1. Вот это часть кода не очень понятна

    QIcon trayImage(":/images/abc.png");

    Тут указан относительный (если я ошибаюсь) путь к картинке.
    Куда бы я не загружал картинку, у меня она не выводится при запуске приложения.
    Всё работает, а картинки нет. Меню выводится на «пустом месте».

    Скажите пожалуйста — где должна располагаться картинка, в ОС windows?
    Какие требования к png? У меня размер 32 на 32 пикселя.

    Не могли бы вы более подробно расписать раздел «Добавляем значок в ресурс»
    А именно имена и пути.

  2. @Иван
    Путь указывается относительно корня ресурсного файла проекта. Об этом подробно написано в «Добавляем значок в ресурс».

    В ресурсном файле вы должны создать префикс images, перейти в него и залить файл abc.png. Только после этого он будет доступен по указанному в примере пути.

    Размер PNG может быть любым.

  3. @Vitaly
    Я делал всё вышеуказанное. Иконка не отображается.
    В Qt, в проекте «каталог» Ресурсы.
    Ресурсы->images.qrc->/images->images/key.png

    В Qt, в mainwindow.cpp строка
    QIcon trayImage(«:/images/key.png»);

    В Windows, в папке с файлами проекта (там где main.cpp и pro файл), есть папка images.
    В ней файл key.png.

    Корень ресурсного файла проекта — папка с проектом.
    В ней есть папка images. В папке файл key.png.
    По моему, путь- » :/images/key.png» путь указан верно.
    Иконка key.png используется также для формы (окно приложения).
    И в окне формы, она отображается корректно.

    Несмотря на это, куда бы я не загружал картинку,
    у меня она не выводится при запуске приложения. Выводится пустое место.

    Я загружал картинку:
    в папку images, относительно корня ресурсного файла.
    в папку images\images, относительно корня ресурсного файла.
    в папку bin — на каталог выше.
    Бесполезно.

    ОС Windows 7 ultimate, 32 бит. Официальная, «чистая» (без лишнего по).
    Специально использую её только для Qt.

  4. Добрый день.
    Скажите пожалуйста, а почему иконка в трее отображается в начале панели на месте а точнее на одной из кнопок где у окна закрыть, свернуть и развернуть.
    В трее же должно отображается вместе с остальными иконками около часов справа, а иконка весит слева. У меня Ubuntu 14.04
    Спасибо.

  5. @Alexey
    В Ubuntu Unity своё представление трея, которое по понятным причинам не поддерживает никто. Стандартный трей у них раньше вообще не выводился, но после того, как разработчики поняли, что под их дудку плясать никто не собирается, вернули, хоть и криво.

Обсуждение закрыто.