Evgenii Legotckoi
Evgenii Legotckoi23. Januar 2016 10:48

Qt/C++ - Lektion 042. PopUp-Benachrichtigung im Gnome-Stil mit Qt

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:

Videoanleitung

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

m
  • 14. Juni 2018 10:40

В Astra Linux вместо прозрачности черный фон. Не знаеете, что может быть?

Evgenii Legotckoi
  • 18. Juni 2018 03:10

Недоработки, вряд ли этот зверь вообще является официально поддерживаемым

IscanderChe
  • 11. Juli 2019 12:27

Евгений, не совсем понимаю, как связывается свойство полупрозрачности Q_PROPERTY(float popupOpacity READ getPopupOpacity WRITE setPopupOpacity) со свойством анимации QPropertyAnimation animation. Проясните момент. И где про это прочитать поподробнее.

Evgenii Legotckoi
  • 11. Juli 2019 16:24

Все свойства в объектах, которые наследуются от Q_OBJECT и помечены макросом Q_PROPERTY, могут вызываться по своему имени с помощью QMetaObject::invokeMethod . Подробнее можете почитать в документации на QMetaObject .

Именно это и используется для вызова свойста внутри QPropertyAnimation. Исходники я не смотрел, но уверен но 100% что там будет QMetaObject::invokeMethod или что-то похожее.

Обычно используется так

QString retVal;
QMetaObject::invokeMethod(obj, "compute", Qt::DirectConnection,
                          Q_RETURN_ARG(QString, retVal),
                          Q_ARG(QString, "sqrt"),
                          Q_ARG(int, 42),
                          Q_ARG(double, 9.7));

А о том, какое свойство вызывать QPropertyAnimation узнаёт через установку целевого объекта и имени свойства объекта, которое нужно вызывать.

animation.setTargetObject(this);                // Устанавливаем целевой объект анимации
animation.setPropertyName("popupOpacity");      // Устанавливаем анимируемое свойство

Видимо это был такой способ обойти шаблонизацию в своё время у них. Думаю, что сейчас это можно с помощью шаблонов решать более изящно. Но вообще, очень не удобно, когда в проекте повсеместно используют QMetaObject::invokeMethod . Трудно отлаживать и иногда вообще не ясно откуда прилетает вызов функции, а callstack так вообще уродский.

ИК
  • 20. Juni 2020 09:15
  • (bearbeitet)

Переписал на python, но почему-то не закрывается при вызове hide()? А при вызове из другого модуля вообще не отображается окно?

# This Python file uses the following encoding: utf-8
import sys
import os

from PySide2.QtWidgets import QWidget, QLabel, QGridLayout, QApplication 
from PySide2.QtCore import QTimer, QPropertyAnimation, Qt, QRect
from PySide2.QtGui import QPainter, QBrush, QColor, QBackingStore

class Info(QWidget):
    def __init__(self, text = "", parent = None):
        super().__init__(parent)

        self.text = text
        self.label = QLabel(text)                        # Label с сообщением
        self.adjustSize()
        self.layout = QGridLayout()                  # Размещение для лейбла
        self.animation = QPropertyAnimation(self)    # Свойство анимации для всплывающего сообщения
                         # Свойства полупрозрачности виджета
        self.timer = QTimer()                        # Таймер, по которому виджет будет скрыт

        self.setWindowFlags(Qt.FramelessWindowHint |       # Отключаем оформление окна
                   Qt.Tool |                               # Отменяем показ в качестве отдельного окна
                   Qt.WindowStaysOnTopHint)                # Устанавливаем поверх всех окон
        self.setAttribute(Qt.WA_TranslucentBackground)     # Указываем, что фон будет прозрачным
        self.setAttribute(Qt.WA_ShowWithoutActivating)     # При показе, виджет не получается фокуса автоматически


        self.animation.setTargetObject(self)                 # Устанавливаем целевой объект анимации
        self.animation.setPropertyName(b'windowOpacity')       # Устанавливаем анимируемое свойство

        #self.animation.stateChanged.connect(self.hide())        # Подключаем сигнал окончания анимации к слоты скрытия     
        #connect(&animation, &QAbstractAnimation::finished, this, &PopUp::hide); 

        # Настройка текста уведомления
        self.label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) # Устанавливаем по центру
        # И настраиваем стили
        self.label.setStyleSheet("QLabel { color : white; "
                            "margin-top: 6px;"
                            "margin-bottom: 6px;"
                            "margin-left: 10px;"
                            "margin-right: 10px; }")

        # Производим установку текста в размещение, ...
        self.layout.addWidget(self.label, 0, 0)
        self.setLayout(self.layout)  # которое помещаем в виджет

        # По сигналу таймера будет произведено скрытие уведомления, если оно видимо
        #self.timer = QTimer()
        self.timer.timeout.connect(self.hideAnimation)

        #connect(timer, &QTimer::timeout, this, &PopUp::hideAnimation);

    #@property
    #def popupOpacity(self):   
    #   return self._popupOpacity

    #@popupOpacity.setter
    #def popupOpacity(self, opacity):
    #   self._popupOpacity = opacity
    #   self.setWindowOpacity(opacity)

    def paintEvent(self, event):
        painter = QPainter(self)        
        painter.setRenderHint(QPainter.Antialiasing)    # Включаем сглаживание

        # Подготавливаем фон. rect() возвращает внутреннюю геометрию виджета уведомления, по содержимому
        roundedRect = QRect()
        roundedRect.setX(self.rect().x() + 5)
        roundedRect.setY(self.rect().y() + 5)
        roundedRect.setWidth(self.rect().width() - 10)
        roundedRect.setHeight(self.rect().height() - 10)

        # Кисть настраиваем на чёрный цвет в режиме полупрозрачности 180 из 255
        painter.setBrush(QBrush(QColor(0,0,0,180)))
        painter.setPen(Qt.NoPen)     # Край уведомления не будет выделен

        # Отрисовываем фон с закруглением краёв в 10px
        painter.drawRoundedRect(roundedRect, 10, 10)

    def setPopupText(self, text):
        self.label.setText(text)       # Устанавливаем текст в Label
        self.adjustSize()          # С пересчётом размеров уведомления


    def show(self):
        self.setWindowOpacity(0.0)          # Устанавливаем прозрачность в ноль

        self.animation.setDuration(150)     # Настраиваем длительность анимации
        self.animation.setStartValue(0.0)   # Стартовое значение будет 0 (полностью прозрачный виджет)
        self.animation.setEndValue(1.0)     # Конечное - полностью непрозрачный виджет

        self.setGeometry(QApplication.desktop().availableGeometry().width() - 36 - self.width() + QApplication.desktop().availableGeometry().x(),
                    QApplication.desktop().availableGeometry().height() - 36 - self.height() + QApplication.desktop().availableGeometry().y(),
                    self.width(),
                    self.height())

        QWidget.show(self)              # Отображаем виджет, который полностью прозрачен

        self.animation.start()      # И запускаем анимацию
        self.timer.start(3000)      # А также стартуем таймер, который запустит скрытие уведомления через 3 секунды


    def hideAnimation(self):
        self.timer.stop()                   # Останавливаем таймер
        self.timer.timeout.disconnect(self.hideAnimation)
        self.animation.setDuration(100)     # Настраиваем длительность анимации
        self.animation.setStartValue(1.0)   # Стартовое значение будет 1 (полностью непрозрачный виджет)
        self.animation.setEndValue(0.0)     # Конечное - полностью прозрачный виджет
        self.timer.timeout.connect(self.hide)
        self.timer.start(3000)
        self.animation.start()              # И запускаем анимацию


    def hide(self):
        # Если виджет прозрачный, то скрываем его
        print('hide')       
        #QWidget.show(self) 
        self.timer.stop()
        self.animation.stop()
        self.close()

    def __repr__(self):
        return 'Info class'

if __name__ == "__main__":
    # app disexec теперь на pyqt
    app = QApplication(sys.argv)
    app.setStyle('Fusion')

    window = Info("This works fine")
    window.show()  

    sys.exit(app.exec_())


AC
  • 6. August 2021 12:22

Доброго времени суток.
Возник вопрос - установлена Qt 6.1.2

popup.cpp

#include <QDesktopWidget>

Выводит ошибку: popup.cpp:5:10: error: 'QDesktopWidget' file not found

... в Qt 5.15 использовал метод QApplication::desktop()

    setGeometry(QApplication::desktop()->geometry().width() - 36 - width() + QApplication::desktop()->geometry().x(),
                QApplication::desktop()->geometry().height() - 56 - height() + QApplication::desktop()->geometry().y(),
                width(),
                height());

На данный момент ошибка: popup.cpp:81:31: error: no member named 'desktop' in 'QApplication'

В документации - метод QApplication::desktop() устарел - никак не могу понять какой метод использовать ?

Evgenii Legotckoi
  • 11. Oktober 2021 01:50

QApplication имеет метод screens() , который возвращает список экранов. А класс QScreen имеет методы geometry() и availableGeometry() . Можете попробовать через них добиться нужного результата.

Включите прозрачность в композит менеджере fly-admin-theme : fly-admin-theme ->Эффекты и всё заработает.

АН
  • 26. März 2023 09:10
  • (bearbeitet)

Добрый день, взял за основу ваш PopUp notification , и немного доработал его под свои нужды.
Добавил в отдельном eventloop'e всплывающую очередь уведомлений с анимацией и таймером удаления.
С вашего позволения оставляю здесь ссылку на git

Kommentare

Nur autorisierte Benutzer können Kommentare posten.
Bitte Anmelden oder Registrieren
Letzte Kommentare
A
ALO1ZE19. Oktober 2024 08:19
Fb3-Dateileser auf Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов5. Oktober 2024 07:51
Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55. Juli 2024 11:02
QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr8. Februar 2024 18:43
Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
Jetzt im Forum diskutieren
J
JacobFib17. Oktober 2024 03:27
добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
JW
Jhon Wick1. Oktober 2024 15:52
Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
КГ
Кирилл Гусарев27. September 2024 09:09
Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
F
Fynjy22. Juli 2024 04:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

Folgen Sie uns in sozialen Netzwerken