Аналогічна стаття на PyQt5/Python
Сьогодні обговоримо те, як згортати додаток, написаний на фреймворку Qt, у третій операційній системі за допомогою класу QSystemTrayIcon. Ця функція є дуже корисною для програм, які мають виконуватися у фоновому режимі тривалий час. Наприклад, програма відео або аудіо відтворення.
Тому займемося такими питаннями:
- Як навчити Вашу програму згортатися у трей;
- Як зробити контекстне меню для іконки трею Вашої програми;
- Як відключати цю функцію, якщо в ній немає потреби.
Програмний код був написаний QtCreator 3.3.1 на основі Qt 5.4.1.
Структура проекту для QSystemTrayIcon
Проект створюється як Програма Qt Widgets, в якій за замовчуванням створюються файли:
- Tray.pro – профайл;
- mainwindow.h - заголовний файл основного вікна програми;
- mainwindow.cpp - вихідний код вікна;
- main.cpp - основний вихідний файл, з якого стартує програма;
- mainwindow.ui - форма основного вікна програми.
*Примітка. Більшість інтерфейсу створюю в дизайнері, щоб не захаращувати логіку основного коду зайвою інформацією. По суті це лише справа смаку та звички.
mainwindow.ui
Формочка вікна для перевірки трею Для тесту створимо просту і нічим не примітну форму з чек-боксом.
Назва об'єкта QCheckBox наступна - trayCheckBox
Tray.pro
Даний файл залишаємо з налаштуваннями за замовчуванням.
#------------------------------------------------- # # 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
Цей файл також не змінюється
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
mainwindow.h
У заголовному файлі вже вносяться зміни, оскільки нам необхідно відслідковувати подію закриття вікна, а також створити обробник натискань на іконку програми в треї. Також у цьому файлі не забуваємо підключити всі необхідні бібліотеки. Інакше проект не скомпілюється.
#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: /* Виртуальная функция родительского класса в нашем классе * переопределяется для изменения поведения приложения, * чтобы оно сворачивалось в трей, когда мы этого хотим */ void closeEvent(QCloseEvent * event); private slots: /* Слот, который будет принимать сигнал от события * нажатия на иконку приложения в трее */ void iconActivated(QSystemTrayIcon::ActivationReason reason); private: Ui::MainWindow * ui; /* Объявляем объект будущей иконки приложения для трея */ QSystemTrayIcon * trayIcon; }; #endif // MAINWINDOW_H
mainwindow.cpp
Вся логіка роботи програми, що тестується, закладена в даному класі. Програма згортатиметься в трей тільки в тому випадку, якщо галочкою відзначений чекбокс. Замість чекбокса може бути змінна або будь-який інший спосіб зберігання інформації про необхідність роботи даної функції.
іконка програми у треї з контестним меню При цьому, щоб вийти з програми при позначеному чекбоксі, використовується відповідний пункт у контекстному меню.
Також, коли програма згортає в трей, то спливає відповідне повідомлення, яке інформує користувача про цю подію.
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); this->setWindowTitle("Tray Program"); /* Инициализируем иконку трея, устанавливаем иконку из набора системных иконок, * а также задаем всплывающую подсказку * */ trayIcon = new QSystemTrayIcon(this); trayIcon->setIcon(this->style()->standardIcon(QStyle::SP_ComputerIcon)); trayIcon->setToolTip("Tray Program" "\n" "Работа со сворачиванием программы трей"); /* После чего создаем контекстное меню из двух пунктов*/ QMenu * menu = new QMenu(this); QAction * viewWindow = new QAction(trUtf8("Развернуть окно"), this); QAction * quitAction = new QAction(trUtf8("Выход"), this); /* подключаем сигналы нажатий на пункты меню к соответсвующим слотам. * Первый пункт меню разворачивает приложение из трея, * а второй пункт меню завершает приложение * */ connect(viewWindow, SIGNAL(triggered()), this, SLOT(show())); connect(quitAction, SIGNAL(triggered()), this, SLOT(close())); menu->addAction(viewWindow); menu->addAction(quitAction); /* Устанавливаем контекстное меню на иконку * и показываем иконку приложения в трее * */ trayIcon->setContextMenu(menu); trayIcon->show(); /* Также подключаем сигнал нажатия на иконку к обработчику * данного нажатия * */ connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason))); } MainWindow::~MainWindow() { delete ui; } /* Метод, который обрабатывает событие закрытия окна приложения * */ void MainWindow::closeEvent(QCloseEvent * event) { /* Если окно видимо и чекбокс отмечен, то завершение приложения * игнорируется, а окно просто скрывается, что сопровождается * соответствующим всплывающим сообщением */ if(this->isVisible() && ui->trayCheckBox->isChecked()){ event->ignore(); this->hide(); QSystemTrayIcon::MessageIcon icon = QSystemTrayIcon::MessageIcon(QSystemTrayIcon::Information); trayIcon->showMessage("Tray Program", trUtf8("Приложение свернуто в трей. Для того чтобы, " "развернуть окно приложения, щелкните по иконке приложения в трее"), icon, 2000); } } /* Метод, который обрабатывает нажатие на иконку приложения в трее * */ void MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason) { switch (reason){ case QSystemTrayIcon::Trigger: /* Событие игнорируется в том случае, если чекбокс не отмечен * */ if(ui->trayCheckBox->isChecked()){ /* иначе, если окно видимо, то оно скрывается, * и наоборот, если скрыто, то разворачивается на экран * */ if(!this->isVisible()){ this->show(); } else { this->hide(); } } break; default: break; } }
Підсумок
У разі успішного складання проекту, Ваша програма з легкістю згорнеться в третій колочик. Приклад роботи програми з QSystemTrayIcon показаний на наступному відео:
И то и другое можно применить для десктопного приложения, и как лучше строить выбор?
Два примера реализации системного трея с использованием QML есть вот в этом уроке: http://www.evileg.ru/baza-znanij/qt-qml-android/rabota-s-system-tray-v-qml-qt-prilozhenii.html
Добрый день.
При попытке запуска приложения в Qt Creator 4.2.0 на Qt 5.7.1 выдаёт следующие ошибки (как при ручном вводе кода, так и при копировании с сайта):
1. В файле mainwindow.cpp :
Помогло добавление директивы
Спасибо за урок!
Видимо, когда я работал с этим кодом, QMenu подключался в заголовочнике QSystemTrayIcon , поэтому и сработало нормально. В любом случае ваше решение верное. Внесу поправку, что если у Вас нет объявлений QMenu в заголовочнике, то лучше перенеси include в cpp файл.
Спасибо! А чем ваш вариант лучше? Скажу сразу, что не совсем хорошо разбираюсь в C++ (школьная программа была пройдена, - в институте немного "подзабил")), но вроде при инклюде заголовочного файла с объявленным QMenu, например в другой .cpp, в нём тоже всё будет работать, иначе для каждого cpp-файла QMenu будет подключаться отдельно - выходит, это более оптимально?
Это к вопросу о скорости компиляции проекта. В маленьких проектах это не очень заметно, а в крупных становится очевидным.
Если всё подключать в заголовочниках, то много времени тратится на проверку того, был ли уже тот или иной заголовочник подключён в в конкретном заголовочном файле. А если стараться подключать заголовочные файлы в файле исходных кодов, то количество таких проверок снижается, соответственно уменьшается время на сборку проекта.
Также есть ещё один хороший способ увеличить скорость компиляции, если объект какого-либо класса, например того же самого QMenu объявляется как указатель в заголовочном файле, то можно объявить класс QMenu, а заголовочный файл подключить уже в файле исходных кодов.
Это будет выглядеть следующим образом:
widget.h widget.cppУ кого при компиляции ворненги - отредактируйте код таким образом, чтобы trUtf8() не было, а было просто tr()
Здравствуйте, извиняюсь за вопрос немного не по теме урока, а скорее по общему синтаксису Qt, связанному с активным использованием указателей.
В частности в вашем примере, разве new QMenu, QAction и QSystemTrayIconnew без их последующего delete не должно приводить к утечке памяти?
Добрый день!