Qt/C++ - Lesson 002. QSystemTrayIcon - How to minimize the application to the system tray?

QSystemTrayIcon, Tray, qt, трей

Similar article on PyQt5/Python

Today will discuss the way to turn off the application, the Qt framework, written in, the operating system tray using QSystemTrayIcon class. This function is very useful for applications that need to run in the background for a long time mode. For example, video or audio playback application.

Therefore we will work with following questions:

  • How to teach your program minimized to tray;
  • How to make a popup menu for the tray icon of your applications;
  • How to disable this feature if it is not needed.

The project structure for QSystemTrayIcon

The project is created as an application Qt Widgets, where files are created by default:

  • Tray.pro - профайл;
  • mainwindow.h - header file of the main application window;
  • mainwindow.cpp - source window;
  • main.cpp - the main source file from which the application starts;
  • mainwindow.ui - form of the main application window.

mainwindow.ui

For the testing will create a simple and unremarkable mold with a check-box.

Object name QCheckBox following - tree CheckBox

Tray.pro

This file is left with the default settings.

#-------------------------------------------------
#
# Project created by QtCreator 2015-08-10T17:18:30
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = Tray
TEMPLATE = app


SOURCES += main.cpp\
        mainwindow.cpp

HEADERS  += mainwindow.h

FORMS    += mainwindow.ui

main.cpp

This file is also not subject to change

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

mainwindow.h

The header has made changes as we need to keep track of the window closing event, as well as the need to create a handler for clicks on the application icon in the system tray. Also in the file, do not forget to connect all the necessary libraries. Otherwise, the project will not compile.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QCloseEvent>
#include <QSystemTrayIcon>
#include <QAction>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

protected:
    /* Virtual function of the parent class in our class
     * Overridden to change the behavior of the application,
     * That it is minimized to tray when we want
     */
    void closeEvent(QCloseEvent * event);

private slots:
    /* The slot that will accept the signal from the event
     * Click on the application icon in the system tray
     */
    void iconActivated(QSystemTrayIcon::ActivationReason reason);

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

#endif // MAINWINDOW_H

mainwindow.cpp

The whole logic of the application under test laid down in this class. The application will minimize to the system tray only if the tick checkbox is checked. Instead checkbox can be a variable or any other way of storing information about the need for this function to work.

At the same time, to exit the application when the marked check box, the corresponding item in the context menu.

Also, when the application is minimized to tray, it pops up a message that informs the user of the event.

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowTitle("Tray Program");

    /* Initialize the tray icon, set the icon of a set of system icons,
     * As well as set a tooltip
     * */
    trayIcon = new QSystemTrayIcon(this);
    trayIcon->setIcon(this->style()->standardIcon(QStyle::SP_ComputerIcon));
    trayIcon->setToolTip("Tray Program" "\n"
                         "Работа со сворачиванием программы трей");
    /* After that create a context menu of two items */
    QMenu * menu = new QMenu(this);
    QAction * viewWindow = new QAction(trUtf8("Развернуть окно"), this);
    QAction * quitAction = new QAction(trUtf8("Выход"), this);

    /* connect the signals clicks on menu items to by appropriate slots.
     * The first menu item expands the application from the tray,
     * And the second menu item terminates the application
     * */
    connect(viewWindow, SIGNAL(triggered()), this, SLOT(show()));
    connect(quitAction, SIGNAL(triggered()), this, SLOT(close()));

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

    /* Set the context menu on the icon
     * And show the application icon in the system tray
     * */
    trayIcon->setContextMenu(menu);
    trayIcon->show();

    /* Also connect clicking on the icon to the signal processor of this press 
     * */
    connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
            this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
}

MainWindow::~MainWindow()
{
    delete ui;
}

/* The method that handles the closing event of the application window
 * */
void MainWindow::closeEvent(QCloseEvent * event)
{
    /* If the window is visible, and the checkbox is checked, then the completion of the application
     * Ignored, and the window simply hides that accompanied
     * The corresponding pop-up message
     */
    if(this->isVisible() && ui->trayCheckBox->isChecked()){
        event->ignore();
        this->hide();
        QSystemTrayIcon::MessageIcon icon = QSystemTrayIcon::MessageIcon(QSystemTrayIcon::Information);

        trayIcon->showMessage("Tray Program",
                              trUtf8("Приложение свернуто в трей. Для того чтобы, "
                                     "развернуть окно приложения, щелкните по иконке приложения в трее"),
                              icon,
                              2000);
    }
}

/* The method that handles click on the application icon in the system tray
 * */
void MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason)
{
    switch (reason){
    case QSystemTrayIcon::Trigger:
        /* The event is ignored if the checkbox is not checked
         * */
        if(ui->trayCheckBox->isChecked()){
            /* otherwise, if the window is visible, it is hidden,
             * Conversely, if hidden, it unfolds on the screen
             * */
            if(!this->isVisible()){
                this->show();
            } else {
                this->hide();
            }
        }
        break;
    default:
        break;
    }
}

Conclusion

If the project is successful build, your application will roll easily in the tray kolachikom. An example of the application QSystemTrayIcon shown in the following video:

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.
Support the author Donate
Вопрос такой, на сколько большая разница в реализации трея на Qt или на QML как у вас есть еще такое видео?
И то и другое можно применить для десктопного приложения, и как лучше строить выбор?
В QML нет собственного типа для системного трея. Поэтому нужно регистрировать через qmlRegisterType сам QSystemTrayIcon, либо класс, в который обёрнут QSystemTrayIcon.
Два примера реализации системного трея с использованием QML есть вот в этом уроке: http://www.evileg.ru/baza-znanij/qt-qml-android/rabota-s-system-tray-v-qml-qt-prilozhenii.html
SS

Добрый день.
При попытке запуска приложения в Qt Creator 4.2.0 на Qt 5.7.1 выдаёт следующие ошибки (как при ручном вводе кода, так и при копировании с сайта):



1. В файле mainwindow.cpp :

E:\Proj\Qt\Tray\mainwindow.cpp:21: ошибка: invalid use of incomplete type 'class QMenu'
QMenu *menu = new QMenu(this);
^


Помогло добавление директивы в заголовок данного файла, либо (что как мне кажется более грамотно) в mainwindow.h :

#include <QMenu>


Спасибо за урок!

Видимо, когда я работал с этим кодом, QMenu подключался в заголовочнике QSystemTrayIcon , поэтому и сработало нормально. В любом случае ваше решение верное. Внесу поправку, что если у Вас нет объявлений QMenu в заголовочнике, то лучше перенеси include в cpp файл.

SS

Спасибо! А чем ваш вариант лучше? Скажу сразу, что не совсем хорошо разбираюсь в C++ (школьная программа была пройдена, - в институте немного "подзабил")), но вроде при инклюде заголовочного файла с объявленным QMenu, например в другой .cpp, в нём тоже всё будет работать, иначе для каждого cpp-файла QMenu будет подключаться отдельно - выходит, это более оптимально?

Это к вопросу о скорости компиляции проекта. В маленьких проектах это не очень заметно, а в крупных становится очевидным.

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

Также есть ещё один хороший способ увеличить скорость компиляции, если объект какого-либо класса, например того же самого QMenu объявляется как указатель в заголовочном файле, то можно объявить класс QMenu, а заголовочный файл подключить уже в файле исходных кодов.

Это будет выглядеть следующим образом:

widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

class QMenu;

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

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

private:
    Ui::Widget *ui;
    QMenu *menu;
};

#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"

#include <QMenu>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    menu = new QMenu(this);
}
DCh

У кого при компиляции ворненги - отредактируйте код таким образом, чтобы trUtf8() не было, а было просто tr()

x
  • #
  • April 18, 2018, 7:36 a.m.

Здравствуйте, извиняюсь за вопрос немного не по теме урока, а скорее по общему синтаксису Qt, связанному с активным использованием указателей.
В частности в вашем примере, разве new QMenu, QAction и QSystemTrayIconnew без их последующего delete не должно приводить к утечке памяти?

Добрый день!

В рамках самого Qt здесь утечки не будет. Особенность фреймворка в том, что при создании объектов, которыe наследованы от QObject (подавляющее большинство классов), передаётся указатель на parent объект, как в случае с меню
new QMenu(this); // this - указатель на parent объект
Когда parent-объект будет удалён, он прихватит за собой и все объекты, которые получили указатель на parent`a. То есть автоматически подчистит память.
Если не передать указатель на парента, то утечка конечно будет. Передача указателя на парента является опциональной, можете и не передавать, если этого требует архитектура приложения, но тогда нужно будет следить за утечками.

Comments

Only authorized users can post comments.
Please, Log in or Sign up
How to become an author?

Contribute to the evolution of the EVILEG community.

Learn how to become a site author.

Learn it
Donate

Good day, Dear Users!!!

I am Evgenii Legotckoi, developer of EVILEG. And it is my hobby project, which helps to learn programming another programmers and developers

If the site helped you, and you want also support the development of the site, than you can donate by following ways

PayPalYandex.Money
Timeweb

Let me recommend you the excellent hosting on which EVILEG is located.

For many years, Timeweb has been proving his stability.

For projects on Django I recommend VDS hosting

View Hosting Timeweb
AS
May 26, 2020, 11:29 a.m.
Artem Sun-Dun-Chan

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:50points,
  • Rating points-4
MN
May 25, 2020, 11:33 a.m.
Mitja Nagibin

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:50points,
  • Rating points-4
f
May 25, 2020, 5:05 a.m.
falcon

C++ - Test 001. The first program and data types

  • Result:66points,
  • Rating points-1
Last comments
AL
May 29, 2020, 5:24 a.m.
Aleksandr Leonidov

Django - Tutorial 036. How to add authentication through social networks. VKontakte

Здравствуйте! После клика на ссылку авторизации выдает json словарь с ошибкой: {"error":"invalid_request","error_description":"redirect_uri is incorrect, check application redirect uri in …
May 28, 2020, 3:14 p.m.
Evgenij Legotskoj

Qt/C++ - Lesson 039. How to paint stroke in QSqlTableModel by value in the column?

Ну в моём примере, который в статье сработало так model->setData(model->index(1, 1), 7); Поскольку model->index(1, 0) - это индекс колонки id, которая скрыта, поэтому…
MA
May 28, 2020, 3:08 p.m.
Mihail A

Qt/C++ - Lesson 039. How to paint stroke in QSqlTableModel by value in the column?

Спасибо, завтра првоерю. А model->setData(model->index(1, 0), 7); Тоже заработало?
May 28, 2020, 3:06 p.m.
Evgenij Legotskoj

Qt/C++ - Lesson 039. How to paint stroke in QSqlTableModel by value in the column?

Да, метод data всё-таки влиял, я переписал его так и заработало удаление QVariant TableModel::data(const QModelIndex &idx, int role) const{ if (role == Qt::BackgroundColorRole) {…
May 28, 2020, 2:49 p.m.
Evgenij Legotskoj

Django - Tutorial 011. Adding comments to the site based on Django

Он более функциональный и его функционал объективно лучше поддерживается Django. Из первого, что приходит на ум: Это наличие полей типа Array Поддержка полей для JSON …
Now discuss on the forum
IP
May 29, 2020, 1:55 a.m.
Igor' Poroshin

QTablwView + QSqlQueryModel скрыть пустой столбец

Да, понятно. В данном случае лучше использовать серверную процедуру (если такие поддерживаются), в которой будет проверяться наличие всех пустых строк у нужного столбца и вызываться соответ…
RG
May 28, 2020, 6:21 p.m.
Rovshan Gurbanov

Сборка под старые версии Android

У меня SDK почти все версии есть, NDK есть версии 10, 17, 21. Но собирается приложение только с NDK v21 под Android версии 7.0 и выше Версия Qt у меня 5.14.2
May 28, 2020, 7:58 a.m.
Evgenij Legotskoj

Освобождение памяти QMainWindow::setCentralWidget

Да, соглашусь. Просто удаление происходит позже, а не сразу.
May 28, 2020, 5:43 a.m.
Mihailll

При подключении к git как указать пароль?

Нужно сделать ssh-keygen и потом полученый из файла код скопировать в ssh ключ в бикбакете
F
May 28, 2020, 1:42 a.m.
Fidan

QML

Да, проблема ушла, спасибо.
About
Services
© EVILEG 2015-2020
Recommend hosting TIMEWEB