Qt中经常会用到提示框,用于交互操作!QMessageBox是被大多数人用到的,用起来是很方便,但是控件类型、大小、布局、样式、往往不是开发者想要的。本实例实现的Notification控件,是一种悬浮在角落的通知提醒框。

一、简述

         本文为大家分享了QT实现带动态弹出动画的自定义通知提示框的具体代码。在QT中实现带动态弹出动画的自定义通知提示框,可以使用QPropertyAnimation类来实现动画效果。

二、 设计思路        

        要实现一个带有动态弹出动画的自定义通知提示框,可以使用Qt的动画框架来实现。以下是本文介绍的实现方式:

  1. 创建一个自定义的通知提示框类。可以继承自QWidget,并在构造函数中设置窗口的初始大小和位置,以及其他相关的属性。

  2. 在通知提示框类中定义一个动画对象,例如QPropertyAnimation,用于控制弹出动画的效果。可以设置弹出动画的目标属性为窗口的位置或大小。

  3. 在通知提示框类中定义一个槽函数,用于处理通知提示框的关闭事件。可以在槽函数中使用动画对象来执行关闭动画,并在动画完成后关闭窗口。

  4. 在需要显示通知提示框的地方,创建通知提示框的实例,并调用其show()函数显示窗口。可以使用动画对象来执行弹出动画。

三、效果 

 

四、核心代码  
1、头文件
#ifndef NOTIFICATION_H
#define NOTIFICATION_H

#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QTextEdit>
#include <QTimer>
#include <mutex>
#include <vector>

//消息类型
enum NotifyType {
    Notify_Type_None = 0, // 普通
    Notify_Type_Success, // 成功
    Notify_Type_Error, // 错误
    Notify_Type_Warning, // 警告
    Notify_Type_Information // 信息
};

//消息位置
enum NotifyPosition {
    Pos_Top_Right = 0, // 右上角开始提醒,从上至下
    Pos_Top_Left, // 左上角开始提醒,从上至下
    Pos_Bottom_Left, // 左下角开始提醒,从下至上
    Pos_Bottom_Right // 右下角开始提醒,从下至上
};

class NotificationLabel;
class NotificationItem;
class Notification : public QObject
{
    Q_OBJECT
public:
    explicit Notification(QObject *parent = nullptr);
    ~Notification() override;

    /**
     * @brief Push 显示提示框
     * @param type 提示类型
     * @param pos 提示的方向(左上、左下、右上、右下)
     * @param title 提示的标题
     * @param content 提示的内容
     * @param nLive 提示框的存活时间,默认3000ms,若该值等于0,提示框不消失。
     */
    void Push(NotifyType type, NotifyPosition pos, QString title, QString content, int nLive = 3000);

private:
    const int nMargin = 15;
    QSize m_size;
    std::mutex m_vecMtx;
    std::vector<NotificationItem*> m_vecItem;

private slots:
    void itemRemoved(NotificationItem* pItem);
};

class NotificationItem : public QWidget
{
    Q_OBJECT
public:
    explicit NotificationItem(QWidget* parent = nullptr,
                                NotifyType type = Notify_Type_None,
                                NotifyPosition pos = Pos_Top_Right,
                                QString title = QString("标题"),
                                QString content = QString("这是一个提示"),
                                int nLife = 3000);
    ~NotificationItem() override;
    void Show();
    NotifyPosition GetPosType() const;
    bool IsAppearEnd() const;

private:
    void Appear();
    void Disappear();

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    const int nFixedWidth = 300;
    const int nMinHeight = 50;
    const int nTopPadding = 14;
    const int nLeftPadding = 23;
    const int nRightPadding = 26;
    const int nMargin = 20;
    NotifyPosition m_enPos;
    QTimer m_liftTimer;
    int m_nLifeTime;
    bool m_bAppearEnd;

signals:
    void itemRemoved(NotificationItem*);
};

class NotificationLabel : public QWidget
{
    Q_OBJECT
public:
    explicit NotificationLabel(QWidget* parent = nullptr, int nFixedWidth = 300, QString content = "");
    ~NotificationLabel() override;
    void Adjust();

protected:
    void paintEvent(QPaintEvent *) override;

private:
    QStringList m_strList;
    QString m_strText;
    int m_nHeight;
    int m_nMargin = 5; // 上下间距
    QColor m_conetentColor = QColor(0x606266); // 内容的字体颜色
};

#endif // NOTIFICATION_H
2、实现代码
#include "notification.h"
#include <QPainter>
#include <QStyleOption>
#include <QGraphicsDropShadowEffect>
#include <QScrollBar>
#include <QPropertyAnimation>
#include <QApplication>
#include <QScreen>

static int nAppearTime = 200; // 出现的时间200ms
static int nDisappearTime = 200; // 消失的时间200ms

Notification::Notification(QObject *parent) : QObject(parent)
{
    QWidget* pWidget = qobject_cast<QWidget*>(parent);
    if(pWidget == nullptr)
        throw std::runtime_error("parent of notification error!");
    m_size = pWidget->size();
    m_vecItem.reserve(30);
}

Notification::~Notification()
{

}

void Notification::Push(NotifyType type, NotifyPosition pos, QString title, QString content, int nLive)
{
    std::lock_guard<std::mutex> lck(m_vecMtx);
    NotificationItem* pItem = new NotificationItem(qobject_cast<QWidget*>(parent()),
                                                   type,
                                                   pos,
                                                   title,
                                                   content,
                                                   nLive);
    connect(pItem, &NotificationItem::itemRemoved, this, &Notification::itemRemoved);
    int currentHeight = 0;
    int currentX = 0;

    if(pos == NotifyPosition::Pos_Top_Right)
    {
        currentX = m_size.width();
        currentHeight = nMargin;
    }
    else if(pos == NotifyPosition::Pos_Top_Left)
    {
        currentX = -pItem->width();
        currentHeight = nMargin;
    }
    else if(pos == NotifyPosition::Pos_Bottom_Left)
    {
        currentX = -pItem->width();
        currentHeight = m_size.height() - nMargin - pItem->height();
    }
    else
    {
        currentX = m_size.width();
        currentHeight = m_size.height() - nMargin - pItem->height();
    }

    for_each(m_vecItem.begin(), m_vecItem.end(), [&](NotificationItem* item) {
        if(item->GetPosType() == pos)
        {
            if(pos == NotifyPosition::Pos_Top_Right)
            {
                currentHeight += (item->height() + nMargin);
            }
            else if(pos == NotifyPosition::Pos_Top_Left)
            {
                currentHeight += (item->height() + nMargin);
            }
            else if(pos == NotifyPosition::Pos_Bottom_Left)
            {
                currentHeight -= (item->height() + nMargin);
            }
            else
            {
                currentHeight -= (item->height() + nMargin);
            }
        }
    });
    pItem->move(currentX, currentHeight);
    m_vecItem.emplace_back(pItem);
    pItem->Show();
}

void Notification::itemRemoved(NotificationItem *pRemoved)
{
    std::unique_lock<std::mutex> lck(m_vecMtx);
    int currentY = 0;
    bool bFirst = true;
    NotifyPosition pos = pRemoved->GetPosType();
    for(auto itr = m_vecItem.begin(); itr != m_vecItem.end();)
    {
        if(*itr == pRemoved)
        {
            m_vecItem.erase(itr);
            break;
        }
        else ++itr;
    }

    for_each(m_vecItem.begin(), m_vecItem.end(), [&, pos, bFirst, currentY](NotificationItem* item) mutable {
        if(item->GetPosType() == pos)
        {
            if(bFirst)
            {
                if(pos == NotifyPosition::Pos_Top_Right)
                {
                    currentY = nMargin;
                }
                else if(pos == NotifyPosition::Pos_Top_Left)
                {
                    currentY = nMargin;
                }
                else if(pos == NotifyPosition::Pos_Bottom_Left)
                {
                    currentY = m_size.height() - nMargin - item->height();
                }
                else
                {
                    currentY = m_size.height() - nMargin - item->height();
                }
                bFirst = false;
            }
            else
            {
                if(item->IsAppearEnd())
                {
                    if(pos == NotifyPosition::Pos_Top_Right)
                    {
                        currentY += (item->height() + nMargin);
                    }
                    else if(pos == NotifyPosition::Pos_Top_Left)
                    {
                        currentY += (item->height() + nMargin);
                    }
                    else if(pos == NotifyPosition::Pos_Bottom_Left)
                    {
                        currentY -= (item->height() + nMargin);
                    }
                    else
                    {
                        currentY -= (item->height() + nMargin);
                    }
                }
            }
            if(item->IsAppearEnd())
            {
                QPropertyAnimation* pAnimation1 = new QPropertyAnimation(item, "geometry", this);
                pAnimation1->setDuration(nDisappearTime);
                pAnimation1->setStartValue(QRect(item->pos().x(),
                                                 item->pos().y(),
                                                 item->width(),
                                                 item->height()));
                pAnimation1->setEndValue(QRect(item->pos().x(),
                                               currentY,
                                               item->width(),
                                               item->height()));

                pAnimation1->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);
            }
        }
    });
}


///
NotificationItem::NotificationItem(QWidget *parent,
                                   NotifyType type,
                                   NotifyPosition pos,
                                   QString title,
                                   QString content,
                                   int nLife) : QWidget(parent),
                                                m_enPos(pos),
                                                m_bAppearEnd(false)
{
    setObjectName(QStringLiteral("notification_item"));
    QLabel* pTitle = new QLabel(title, this);
    pTitle->setObjectName(QStringLiteral("label_title"));
    NotificationLabel* pContent = new NotificationLabel(this, nFixedWidth - 10, content);
    QFont font;
    font.setPointSize(11);
    font.setFamily(QStringLiteral("Microsoft Yahei"));
    pContent->setFont(font);
    QPushButton* pClose = new QPushButton(this);
    pClose->setFixedSize(16, 16);
    pClose->setObjectName(QStringLiteral("btn_close"));
    pClose->setCursor(QCursor(Qt::PointingHandCursor));

    setStyleSheet(QStringLiteral("QWidget#notification_item{border:none;border-radius:8px;background-color:white;}"
                                 "QLabel#label_title{border:none;background-color:white;font-family:Microsoft Yahei;font-size:20px;font-weight:700;color:#303133;}"
                                 "QPushButton#btn_close{border:none;background-color:white;background-position:center;border-image:url(:/skin/notification_close.png);}"
                                 "QPushButton:hover#btn_close{border-image:url(:/skin/notification_close_hover.png);}"));

    // 标题设置
    pTitle->setAlignment(Qt::AlignLeft | Qt::AlignTop);
    QFontMetrics fontWidth(pTitle->font());
    QString elideNote = fontWidth.elidedText(pTitle->text(),
                                             Qt::ElideRight,
                                             120);
    pTitle->setText(elideNote);
    pTitle->setToolTip(title);
    pTitle->adjustSize();

    // 内容设置
    pContent->Adjust();

    // 布局
    if(type != NotifyType::Notify_Type_None)
    {
        QLabel* pIcon = new QLabel(this);
        pIcon->setStyleSheet(QStringLiteral("QLabel{border:none;background-color:white;}"));
        if(type == NotifyType::Notify_Type_Success)
        {
            pIcon->setPixmap(QPixmap(":/skin/type_success.png"));
        }
        else if(type == NotifyType::Notify_Type_Error)
        {
            pIcon->setPixmap(QPixmap(":/skin/type_error.png"));
        }
        else if(type == NotifyType::Notify_Type_Warning)
        {
            pIcon->setPixmap(QPixmap(":/skin/type_warning.png"));
        }
        else
        {
            pIcon->setPixmap(QPixmap(":/skin/type_information.png"));
        }
        pIcon->adjustSize();
        setFixedSize(nFixedWidth + nLeftPadding + nRightPadding + pIcon->width() + 10,
                     pContent->height() + pTitle->height() + 2 * nTopPadding + 5);
        pIcon->move(nLeftPadding, nTopPadding - std::abs(pIcon->height() - pTitle->height()) / 2);
        pTitle->move(pIcon->x() + pIcon->width() + 10, nTopPadding);
    }
    else
    {
        setFixedSize(nFixedWidth + nLeftPadding + nRightPadding,
                     pContent->height() + pTitle->height() + 2 * nTopPadding + 5);
        pTitle->move(nLeftPadding, nTopPadding);
    }

    pContent->move(pTitle->x(), pTitle->y() + pTitle->height() + 5);
    pClose->move(width() - pClose->width() / 2 - nRightPadding, nTopPadding);

    QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect(this);
    effect->setBlurRadius(20);        // 阴影圆角的大小
    effect->setColor(Qt::gray);      //阴影的颜色
    effect->setOffset(0,0);          //阴影的偏移量
    setGraphicsEffect(effect);

    connect(pClose, &QPushButton::clicked, this, [&](){
        m_liftTimer.stop();
        Disappear();
    });

    connect(&m_liftTimer, &QTimer::timeout, this, [&](){
        m_liftTimer.stop();
        Disappear();
    });
    m_nLifeTime = nLife;
    hide();
}

NotificationItem::~NotificationItem()
{

}

void NotificationItem::Show()
{
    show();
    Appear();
}

NotifyPosition NotificationItem::GetPosType() const
{
    return m_enPos;
}

bool NotificationItem::IsAppearEnd() const
{
    return m_bAppearEnd;
}

void NotificationItem::Appear()
{
    QPropertyAnimation *animation = new QPropertyAnimation(this, "geometry");
    animation->setDuration(nAppearTime);
    animation->setStartValue(QRect(pos().x(), pos().y(), width(), height()));
    if(m_enPos == NotifyPosition::Pos_Top_Right)
    {
        animation->setEndValue(QRect(pos().x() - width() - nMargin,
                                     pos().y(),
                                     width(),
                                     height()));
    }
    else if(m_enPos == NotifyPosition::Pos_Top_Left)
    {
        animation->setEndValue(QRect(pos().x() + width() + nMargin,
                                     pos().y(),
                                     width(),
                                     height()));
    }
    else if(m_enPos == NotifyPosition::Pos_Bottom_Left)
    {
        animation->setEndValue(QRect(pos().x() + width() + nMargin,
                                     pos().y(),
                                     width(),
                                     height()));
    }
    else
    {
        animation->setEndValue(QRect(pos().x() - width() - nMargin,
                                     pos().y(),
                                     width(),
                                     height()));
    }
    animation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);
    connect(animation, &QPropertyAnimation::finished, this, [&](){
        m_bAppearEnd = true;
        if(m_nLifeTime > 0)
            m_liftTimer.start(m_nLifeTime);
    });
}

void NotificationItem::Disappear()
{
    QGraphicsOpacityEffect *pOpacity = new QGraphicsOpacityEffect(this);
    pOpacity->setOpacity(1);
    setGraphicsEffect(pOpacity);
    QPropertyAnimation *pOpacityAnimation2 = new QPropertyAnimation(pOpacity, "opacity");
    pOpacityAnimation2->setDuration(nDisappearTime);
    pOpacityAnimation2->setStartValue(1);
    pOpacityAnimation2->setEndValue(0);
    pOpacityAnimation2->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);
    connect(pOpacityAnimation2, &QPropertyAnimation::finished, this, [&](){
        emit itemRemoved(this);
        deleteLater();
    });
}

void NotificationItem::paintEvent(QPaintEvent *event)
{
    QStyleOption opt;
    opt.init(this);
    QPainter p(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
    QWidget::paintEvent(event);
}

//
NotificationLabel::NotificationLabel(QWidget *parent,
                                     int nFixedWidth,
                                     QString content) : QWidget(parent)
{
    setObjectName(QStringLiteral("motification_label"));
    setFixedWidth(nFixedWidth);
    m_strText = content;
    m_nHeight = 0;
    setStyleSheet(QStringLiteral("QWidget#motification_label{background-color:white;border:none;}"));
}

NotificationLabel::~NotificationLabel()
{

}

void NotificationLabel::Adjust()
{
    m_strList.clear();
    QFontMetrics fm(font());
    int tpHeight = fm.height();
    int size = m_strText.length();
    QString strTp;
    for(int i = 0; i < size; i++)
    {
        strTp.append(m_strText.at(i));
        int tpWidth = fm.horizontalAdvance(strTp);
        if(tpWidth > width())
        {
            i--;
            strTp.chop(1);
            m_strList.push_back(strTp);
            strTp.clear();
            m_nHeight += tpHeight;
        }
        else
        {
            if(i == size - 1)
            {
                m_strList.push_back(strTp);
                strTp.clear();
                m_nHeight += tpHeight;
            }
        }
    }
    setFixedHeight(m_nHeight + tpHeight / 2);
    repaint();
}

void NotificationLabel::paintEvent(QPaintEvent *event)
{
    QStyleOption opt;
    opt.init(this);
    QPainter p(this);
    p.setRenderHint(QPainter::Antialiasing);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
    QFontMetrics fm(font());
    int tpHeight = fm.height();
    int height = tpHeight;
    p.setPen(m_conetentColor);
    for(int i = 0; i < m_strList.count(); i++)
    {
        p.drawText(QPoint(0, height), m_strList[i]);
        height += (tpHeight + m_nMargin);
    }
    QWidget::paintEvent(event);
}

        以上是带动态弹出动画的自定义通知提示框实现代码,在实际使用中,可以根据需要进行扩展及自定义动画文件和样式。      

五、使用示例

以下是一个简单的示例代码,演示了如何在Qt中使用此控件:

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

Notification* nf = nullptr;

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    resize(500, 360);
    nf = new Notification(this);

    QPushButton* btn1 = new QPushButton("普通", this);
    QPushButton* btn2 = new QPushButton("成功", this);
    QPushButton* btn3 = new QPushButton("错误", this);
    QPushButton* btn4 = new QPushButton("警告", this);
    QPushButton* btn5 = new QPushButton("信息", this);

    connect(btn1, &QPushButton::clicked, this, [&](){
        nf->Push(NotifyType::Notify_Type_None,
                 NotifyPosition::Pos_Top_Right,
                 "标准提示",
                 "你好,世界!Hellow world!"
                , 0);
    });
    connect(btn2, &QPushButton::clicked, this, [&](){
        nf->Push(NotifyType::Notify_Type_Success,
                 NotifyPosition::Pos_Top_Left,
                 "成功提示",
                 "你好,世界!Hellow world!");
    });
    connect(btn3, &QPushButton::clicked, this, [&](){
        nf->Push(NotifyType::Notify_Type_Error,
                 NotifyPosition::Pos_Bottom_Left,
                 "错误提示",
                 "你好,世界!Hellow world!");
    });
    connect(btn4, &QPushButton::clicked, this, [&](){
        nf->Push(NotifyType::Notify_Type_Warning,
                 NotifyPosition::Pos_Bottom_Right,
                 "警告提示",
                 "你好,世界!Hellow world!");
    });
    connect(btn5, &QPushButton::clicked, this, [&](){
        nf->Push(NotifyType::Notify_Type_Information,
                 NotifyPosition::Pos_Top_Right,
                 "信息提示",
                 "你好,世界!Hellow world!");
    });

    btn1->move(200, 50);
    btn2->move(200, 100);
    btn3->move(200, 150);
    btn4->move(200, 200);
    btn5->move(200, 250);
}

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

总结一下,QT实现带动态弹出动画的自定义通知提示框的实现步骤如下:

  1. 创建一个自定义的QWidget类作为提示框的界面。

  2. 在该QWidget类中添加需要显示的文字或图标等元素。

  3. 在QWidget类中添加动画效果,可以使用QPropertyAnimation类实现动画效果。可以使用这个类来实现提示框的弹出、缩放、渐变等效果。

  4. 在主窗口中使用QLayout来布局,并将该自定义QWidget类添加到主窗口中。

  5. 在需要显示提示框的地方,通过创建QWidget类的实例来显示提示框。可以通过调用QWidget类的show()方法来显示提示框。

  6. 可以设置定时器,在一定时间后隐藏提示框。可以使用QTimer类来实现定时器功能。可以通过调用QWidget类的hide()方法来隐藏提示框。

  7. 可以添加鼠标事件,例如鼠标点击提示框时隐藏提示框。可以通过重写QWidget类的鼠标事件来实现该功能。

 通过以上步骤,就可以实现带动态弹出动画的自定义通知提示框。可以根据需要自定义提示框的样式和动画效果,使其更加符合应用程序的需求。

        谢谢您的关注和阅读!如有任何问题或需要帮助,请随时与我联系。希望您能继续支持并享受更多精彩的内容。祝您生活愉快!

六、源代码下载
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐