Functionality of the standard notification system tray at times can be insufficient for the implementation of ambitious pans for styling applications. We therefore consider the embodiment of pop-up messages in the style of PopUp DE Gnome notification, namely, as shown in the following figure.
PopUp notification Gnome style
To demonstrate the notification I propose to create an application, which will be a field for entering text, and a button by pressing which will be called a pop-up message.
The message will be displayed in the lower right corner of the tray system tray. This notice must be sure to scale the contents.
Fade-in it will be implemented within 150 milliseconds and the disappearance, after three seconds.
Project structure
- PopupWindow.pro - the profile of the project;
- mainwindow.h - header file of the main application window;
- mainwindow.cpp - file source code of the main application window;
- mainwindow.ui - form the main application window;
- main.cpp - start file the application source code;
- popup.h - header file notifications;
- popup.cpp - file source pop-up message.
PopupWindow.pro и mainwindow.ui
The profile of the project, do not connect anything special, but in the main application window, just put a button and a text entry field.
mainwindow.h
The header of the main window of the application you must include the header file PopUp notification and declare the notification object itself, and there will be declared a slot for processing pressing the start button a pop-up notification. This slot will be installed in the text notification and position change notifications on the computer screen in the notification size.
#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
mainwindow.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
In order to prepare the notification must be inherited from QWidget class that you want to disable window decoration, and put a transparent background. You also need to set it up so that a notice was always on top of all windows. Rendering translucent background notification will be made in the method paintEvent() , in which the full width and height of the notification widget is drawn semi-transparent black rectangle with rounded edges.
Animation appearance and disappearance of the notification will be made through QPropertyAnimation object.
What is important: fitting the size of the widget should be made at the time the text in the notification, and not during the redrawing or installation location notifications on the screen, or to be received is not correct size and magnitude of the notice, it will not in the expected location, or not with the expected size.
To implement time-limited display notifications on the screen applies 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); // The background will be drawn through the redraw method public slots: void setPopupText(const QString& text); // Setting text notification void show(); /* own widget displaying method * It is necessary to pre-animation settings * */ private slots: void hideAnimation(); // Slot start the animation hide void hide(); /* At the end of the animation, it is checked in a given slot, * Does the widget visible, or to hide * */ private: QLabel 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 | // Disable window decoration Qt::Tool | // Discard display in a separate window Qt::WindowStaysOnTopHint); // Set on top of all windows setAttribute(Qt::WA_TranslucentBackground); // Indicates that the background will be transparent setAttribute(Qt::WA_ShowWithoutActivating); // At the show, the widget does not get the focus automatically animation.setTargetObject(this); // Set the target animation 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); QRect roundedRect; roundedRect.setX(rect().x() + 5); roundedRect.setY(rect().y() + 5); roundedRect.setWidth(rect().width() - 10); roundedRect.setHeight(rect().height() - 10); painter.setBrush(QBrush(QColor(0,0,0,180))); painter.setPen(Qt::NoPen); painter.drawRoundedRect(roundedRect, 10, 10); } void PopUp::setPopupText(const QString &text) { label.setText(text); // Set the text in the Label adjustSize(); // With the recalculation notice sizes } void PopUp::show() { setWindowOpacity(0.0); // Set the transparency to zero animation.setDuration(150); // Configuring the duration of the animation animation.setStartValue(0.0); // The start value is 0 (fully transparent widget) animation.setEndValue(1.0); // End - completely opaque widget 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); } void PopUp::hideAnimation() { timer->stop(); animation.setDuration(1000); animation.setStartValue(1.0); animation.setEndValue(0.0); animation.start(); } void PopUp::hide() { // If the widget is transparent, then hide it if(getPopupOpacity() == 0.0){ QWidget::hide(); } } void PopUp::setPopupOpacity(float opacity) { popupOpacity = opacity; setWindowOpacity(opacity); } float PopUp::getPopupOpacity() const { return popupOpacity; }
Result
Archive of the source: PopupWindow
As a result, the message will be as follows:
В 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