Die Funktionalität von Standard-System-Tray-Benachrichtigungen kann manchmal nicht ausreichen, um mutige Ideen für das Styling einer Anwendung umzusetzen. Betrachten Sie daher die Implementierung einer Popup-Nachricht im Stil von PopUp -Benachrichtigungen DE Gnome , und zwar wie in der folgenden Abbildung gezeigt.
Popup-Benachrichtigung im Gnome-Stil
Um die Benachrichtigung zu demonstrieren, schlage ich vor, eine Anwendung zu erstellen, in der es ein Feld zum Eingeben von Text und eine Schaltfläche gibt, bei deren Drücken eine Popup-Nachricht aufgerufen wird.
Die Meldung wird in der unteren rechten Ecke des Bildschirms über der Taskleiste angezeigt. In diesem Fall muss die Benachrichtigung entsprechend dem Inhalt skaliert werden.
Ein reibungsloses Erscheinen wird innerhalb von 150 Millisekunden und ein Verschwinden nach drei Sekunden implementiert.
Projektstruktur
- PopupWindow.pro - Projektprofil;
- mainwindow.h - Header-Datei des Hauptanwendungsfensters;
- mainwindow.cpp - Quellcodedatei für das Hauptanwendungsfenster;
- mainwindow.ui - Form des Hauptanwendungsfensters;
- main.cpp - Quelldatei der Anwendung starten;
- popup.h - Benachrichtigungs-Header-Datei;
- popup.cpp - Quellcodedatei für Popup-Meldungen.
PopupWindow.pro und mainwindow.ui
Wir verbinden nichts Besonderes im Projektprofil, sondern platzieren im Hauptanwendungsfenster einfach eine Schaltfläche und ein Texteingabefeld.
Hauptfenster.h
In der Header-Datei des Hauptfensters der Anwendung müssen Sie die Header-Datei der PopUp-Benachrichtigung verbinden und das Benachrichtigungsobjekt selbst sowie einen Slot für die Verarbeitung des Klicks auf die Schaltfläche zum Starten der Popup-Benachrichtigung deklarieren dort deklariert werden. Dieser Slot legt den Text in der Benachrichtigung fest und ändert die Position der Benachrichtigung auf dem Computerbildschirm entsprechend der Größe der Benachrichtigung.
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "popup.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void on_pushButton_clicked(); private: Ui::MainWindow *ui; PopUp *popUp; // Объявляем объект всплывающего сообщения }; #endif // MAINWINDOW_H
Hauptfenster.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); popUp = new PopUp(); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_pushButton_clicked() { popUp->setPopupText(ui->textEdit->toPlainText()); popUp->show(); }
popup.h
Um eine Benachrichtigung zu entwickeln, müssen Sie von der QWidget -Klasse erben, in der Sie die Fensterdekoration deaktivieren und einen transparenten Hintergrund festlegen müssen. Sie müssen es auch so konfigurieren, dass die Benachrichtigung immer über allen Fenstern angezeigt wird. Der halbtransparente Benachrichtigungshintergrund wird in der Methode paintEvent() gezeichnet, in der ein halbtransparentes schwarzes Rechteck mit abgerundeten Kanten über die gesamte Breite und Höhe des Benachrichtigungs-Widgets gezeichnet wird.
Die Animation des Erscheinens und Verschwindens der Benachrichtigung wird mit dem Objekt QPropertyAnimation. durchgeführt
Was wichtig ist: Die Größenänderung des Widgets sollte zum Zeitpunkt des Festlegens des Textes in der Benachrichtigung und nicht zum Zeitpunkt des Neuzeichnens oder Festlegens der Position der Benachrichtigung auf dem Bildschirm erfolgen, da sonst die Benachrichtigungsgrößen nicht korrekt sind und nicht an der erwarteten Stelle sein oder nicht mit den erwarteten Größen.
Um eine zeitlich begrenzte Anzeige einer Benachrichtigung auf einem Computerbildschirm zu implementieren, verwenden Sie QTimer .
#ifndef POPUP_H #define POPUP_H #include <QWidget> #include <QLabel> #include <QGridLayout> #include <QPropertyAnimation> #include <QTimer> class PopUp : public QWidget { Q_OBJECT // Свойство полупрозрачности Q_PROPERTY(float popupOpacity READ getPopupOpacity WRITE setPopupOpacity) void setPopupOpacity(float opacity); float getPopupOpacity() const; public: explicit PopUp(QWidget *parent = 0); protected: void paintEvent(QPaintEvent *event); // Фон будет отрисовываться через метод перерисовки public slots: void setPopupText(const QString& text); // Установка текста в уведомление void show(); /* Собственный метод показа виджета * Необходимо для преварительной настройки анимации * */ private slots: void hideAnimation(); // Слот для запуска анимации скрытия void hide(); /* По окончании анимации, в данном слоте делается проверка, * виден ли виджет, или его необходимо скрыть * */ private: QLabel label; // Label с сообщением QGridLayout layout; // Размещение для лейбла QPropertyAnimation animation; // Свойство анимации для всплывающего сообщения float popupOpacity; // Свойства полупрозрачности виджета QTimer *timer; // Таймер, по которому виджет будет скрыт }; #endif // POPUP_H
popup.cpp
#include "popup.h" #include <QPainter> #include <QApplication> #include <QDesktopWidget> #include <QDebug> PopUp::PopUp(QWidget *parent) : QWidget(parent) { setWindowFlags(Qt::FramelessWindowHint | // Отключаем оформление окна Qt::Tool | // Отменяем показ в качестве отдельного окна Qt::WindowStaysOnTopHint); // Устанавливаем поверх всех окон setAttribute(Qt::WA_TranslucentBackground); // Указываем, что фон будет прозрачным setAttribute(Qt::WA_ShowWithoutActivating); // При показе, виджет не получается фокуса автоматически animation.setTargetObject(this); // Устанавливаем целевой объект анимации animation.setPropertyName("popupOpacity"); // Устанавливаем анимируемое свойство connect(&animation, &QAbstractAnimation::finished, this, &PopUp::hide); /* Подключаем сигнал окончания * анимации к слоты скрытия * */ // Настройка текста уведомления label.setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); // Устанавливаем по центру // И настраиваем стили label.setStyleSheet("QLabel { color : white; " "margin-top: 6px;" "margin-bottom: 6px;" "margin-left: 10px;" "margin-right: 10px; }"); // Производим установку текста в размещение, ... layout.addWidget(&label, 0, 0); setLayout(&layout); // которое помещаем в виджет // По сигналу таймера будет произведено скрытие уведомления, если оно видимо timer = new QTimer(); connect(timer, &QTimer::timeout, this, &PopUp::hideAnimation); } void PopUp::paintEvent(QPaintEvent *event) { Q_UNUSED(event) /* А теперь настраиваем фон уведомления, * который является прямоугольником с чёрным фоном * */ QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // Включаем сглаживание // Подготавливаем фон. rect() возвращает внутреннюю геометрию виджета уведомления, по содержимому QRect roundedRect; roundedRect.setX(rect().x() + 5); roundedRect.setY(rect().y() + 5); roundedRect.setWidth(rect().width() - 10); roundedRect.setHeight(rect().height() - 10); // Кисть настраиваем на чёрный цвет в режиме полупрозрачности 180 из 255 painter.setBrush(QBrush(QColor(0,0,0,180))); painter.setPen(Qt::NoPen); // Край уведомления не будет выделен // Отрисовываем фон с закруглением краёв в 10px painter.drawRoundedRect(roundedRect, 10, 10); } void PopUp::setPopupText(const QString &text) { label.setText(text); // Устанавливаем текст в Label adjustSize(); // С пересчётом размеров уведомления } void PopUp::show() { setWindowOpacity(0.0); // Устанавливаем прозрачность в ноль animation.setDuration(150); // Настраиваем длительность анимации animation.setStartValue(0.0); // Стартовое значение будет 0 (полностью прозрачный виджет) animation.setEndValue(1.0); // Конечное - полностью непрозрачный виджет setGeometry(QApplication::desktop()->availableGeometry().width() - 36 - width() + QApplication::desktop() -> availableGeometry().x(), QApplication::desktop()->availableGeometry().height() - 36 - height() + QApplication::desktop() -> availableGeometry().y(), width(), height()); QWidget::show(); // Отображаем виджет, который полностью прозрачен animation.start(); // И запускаем анимацию timer->start(3000); // А также стартуем таймер, который запустит скрытие уведомления через 3 секунды } void PopUp::hideAnimation() { timer->stop(); // Останавливаем таймер animation.setDuration(1000); // Настраиваем длительность анимации animation.setStartValue(1.0); // Стартовое значение будет 1 (полностью непрозрачный виджет) animation.setEndValue(0.0); // Конечное - полностью прозрачный виджет animation.start(); // И запускаем анимацию } void PopUp::hide() { // Если виджет прозрачный, то скрываем его if(getPopupOpacity() == 0.0){ QWidget::hide(); } } void PopUp::setPopupOpacity(float opacity) { popupOpacity = opacity; setWindowOpacity(opacity); } float PopUp::getPopupOpacity() const { return popupOpacity; }
Ergebnis
Quellarchiv: PopupWindow
Als Ergebnis sieht die Nachricht wie folgt aus:
В Astra Linux вместо прозрачности черный фон. Не знаеете, что может быть?
Недоработки, вряд ли этот зверь вообще является официально поддерживаемым
Евгений, не совсем понимаю, как связывается свойство полупрозрачности Q_PROPERTY(float popupOpacity READ getPopupOpacity WRITE setPopupOpacity) со свойством анимации QPropertyAnimation animation. Проясните момент. И где про это прочитать поподробнее.
Все свойства в объектах, которые наследуются от Q_OBJECT и помечены макросом Q_PROPERTY, могут вызываться по своему имени с помощью QMetaObject::invokeMethod . Подробнее можете почитать в документации на QMetaObject .
Именно это и используется для вызова свойста внутри QPropertyAnimation. Исходники я не смотрел, но уверен но 100% что там будет QMetaObject::invokeMethod или что-то похожее.
Обычно используется так
А о том, какое свойство вызывать QPropertyAnimation узнаёт через установку целевого объекта и имени свойства объекта, которое нужно вызывать.
Видимо это был такой способ обойти шаблонизацию в своё время у них. Думаю, что сейчас это можно с помощью шаблонов решать более изящно. Но вообще, очень не удобно, когда в проекте повсеместно используют QMetaObject::invokeMethod . Трудно отлаживать и иногда вообще не ясно откуда прилетает вызов функции, а callstack так вообще уродский.
Переписал на python, но почему-то не закрывается при вызове hide()? А при вызове из другого модуля вообще не отображается окно?
Доброго времени суток.
Возник вопрос - установлена Qt 6.1.2
popup.cpp
Выводит ошибку: popup.cpp:5:10: error: 'QDesktopWidget' file not found
... в Qt 5.15 использовал метод QApplication::desktop()
На данный момент ошибка: popup.cpp:81:31: error: no member named 'desktop' in 'QApplication'
В документации - метод QApplication::desktop() устарел - никак не могу понять какой метод использовать ?
QApplication имеет метод screens() , который возвращает список экранов. А класс QScreen имеет методы geometry() и availableGeometry() . Можете попробовать через них добиться нужного результата.
Включите прозрачность в композит менеджере fly-admin-theme : fly-admin-theme ->Эффекты и всё заработает.
Добрый день, взял за основу ваш PopUp notification , и немного доработал его под свои нужды.
Добавил в отдельном eventloop'e всплывающую очередь уведомлений с анимацией и таймером удаления.
С вашего позволения оставляю здесь ссылку на git