非阻塞式消息提示框 MMessageToast

MMessageToast是一个Qt框架下的自定义组件,用于显示信息、警告、错误和成功消息。它支持淡入淡出效果,可设置显示持续时间和是否阻塞。组件能自适应宽高,并能根据屏幕位置进行智能布局。此外,还提供了信息、警告、错误和成功四种不同类型的样式。
摘要由CSDN通过智能技术生成

1、支持淡入淡出

2、支持阻塞和非阻塞

3、宽高自适应

效果示例:

 

 

 MMessageToast.h

//********************************************************
/// @brief 消息提示框
/// @author y974183789@gmail.com
/// @date 2021/8/31
/// @note
/// @version 1.0.0
//********************************************************

#pragma once

#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE
class QTimer;
class QEventLoop;
class QPropertyAnimation;
QT_END_NAMESPACE

namespace Ui {
class MMessageToast;
}

class MMessageToast : public QWidget {
    Q_OBJECT

public:
    explicit MMessageToast(QWidget *pParent = Q_NULLPTR);
    ~MMessageToast();

    //设置显示持续时间
    void setShowDuration(int nMSecs);
    //获取显示持续时间
    int getShowDuration() const;

    //信息消息框
    static void information(QWidget *pParent, const QString &strText, bool bShowModal = false, int nShowDuration = 2000);

    //警告消息框
    static void warning(QWidget *pParent, const QString &strText, bool bShowModal = false, int nShowDuration = 2000);
    // 错误消息框
    static void error(QWidget *pParent, const QString &strText, bool bShowModal = false, int nShowDuration = 2000);
    //成功消息框
    static void success(QWidget *pParent, const QString &strText, bool bShowModal = false, int nShowDuration = 2000);

private:
    //初始化窗口
    void initWidget();
    //调整位置
    void adjustPosition(QWidget *pParent);

    //显示消息提示框
    void showMessageToast(const QString &strIcon, const QString &strText, const QString strStyleValue,
                          bool bShowModal, int nShowDuration = 2000);
    //获取消息提示框
    static MMessageToast *getMessageToast(QWidget *pParent);

    void exec();

    void setVisible(bool visible) Q_DECL_OVERRIDE;

private Q_SLOTS:
    //开始淡入动画
    void startFadeInAnimation();
    //开始淡出动画
    void startFadeOutAnimation();

private:
    Ui::MMessageToast *ui;

    int m_nShowDuration;                  //显示持续时间
    QTimer *m_pShowTimer;                 //显示定时器
    QPropertyAnimation *m_pShowAnimation; //显示动画
    QEventLoop* m_pLoop;
};

MMessageToast.cpp

//********************************************************
/// @brief 消息提示框
/// @author y974183789@gmail.com
/// @date 2021/8/31
/// @note
/// @version 1.0.0
//********************************************************

#include  "MMessageToast.h"
#include "MGlobal.h"
#include "MDefine.h"
#include "ui_MMessageToast.h"
#include <QtCore/QPropertyAnimation>
#include <QtCore/QTimer>
#include <QtGui/QWindow>
#include <QtWidgets/QApplication>
#include <QtCore/QEventLoop>
#include <QtGui/QScreen>

#define M_TOAST_STYLE_KEY "type"

MMessageToast::MMessageToast(QWidget *pParent)
    : QWidget(pParent)
    , ui(new Ui::MMessageToast)
    , m_nShowDuration(2000)
    , m_pShowTimer(Q_NULLPTR)
    , m_pShowAnimation(Q_NULLPTR)
    , m_pLoop(Q_NULLPTR)
    {
    ui->setupUi(this);

    // 初始化窗口
    setAttribute(Qt::WA_DeleteOnClose);
    // TODO(yanhj): 中标麒麟下不支持设置透明背景
    setAttribute(Qt::WA_TranslucentBackground);
    setWindowFlags(Qt::FramelessWindowHint | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint);
    setAttribute(Qt::WA_AlwaysStackOnTop);
    setWindowOpacity(0);
    initWidget();
}

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

// 初始化窗口
void MMessageToast::initWidget() {
    // 显示定时器
    m_pShowTimer = new QTimer(this);
    m_pShowTimer->setSingleShot(true);
    connect(m_pShowTimer, SIGNAL(timeout()), this, SLOT(startFadeOutAnimation()));

    // 显示动画
    m_pShowAnimation = new QPropertyAnimation(this);
    m_pShowAnimation->setTargetObject(this);
    m_pShowAnimation->setPropertyName("windowOpacity");
}

// 调整位置
void MMessageToast::adjustPosition(QWidget *pParent) {
    QPoint p(0, 0);
    int nExtraW = 0;
    int nExtraH = 0;
    int nScrn   = 0;

    //修改此值可调整对话框弹出位置,默认为居中
    int nExtraMaxW = 0;
    int nExtraMaxH = 0;

    if (pParent != Q_NULLPTR) {
        pParent = pParent->window();
    }

    QRect rectDesktop = getScreenRect();
    QWidgetList lstWidget = QApplication::topLevelWidgets();
    for (int i = 0; (0 == nExtraW || 0 == nExtraH) && i < lstWidget.size(); ++i) {
        QWidget *pCurrent = lstWidget.at(i);
        if (pCurrent->isVisible()) {
            int nFrameW = pCurrent->geometry().x() - pCurrent->x();
            int nFrameH = pCurrent->geometry().y() - pCurrent->y();
            nExtraW     = qMax(nExtraW, nFrameW);
            nExtraH     = qMax(nExtraH, nFrameH);
        }
    }

    // sanity check for decoration frames. With embedding, we might get extraordinary values
    if (0 == nExtraW || 0 == nExtraH || nExtraW >= nExtraMaxW || nExtraH >= nExtraMaxH) {
        nExtraW = nExtraMaxW;
        nExtraH = nExtraMaxH;
    }

    if (pParent != Q_NULLPTR) {
        // Use pos() if the widget is embedded into a native window
        QPoint pp;
        if(testAttribute(Qt::WA_ShowModal)) {
            if (pParent->windowHandle() &&
                pParent->windowHandle()->property("_q_embedded_native_parent_handle").value<WId>()) {
                pp = pParent->pos();
            } else {
                pp = pParent->mapToGlobal(QPoint(0, 0));
            }
        }
        p = QPoint(pp.x() + pParent->width() / 2, pp.y() + pParent->height() / 2);
    } else {
        // p = middle of the desktop
        p = QPoint(rectDesktop.x() + rectDesktop.width() / 2, rectDesktop.y() + rectDesktop.height() / 2);
    }

    // p = origin of this
    p = QPoint(p.x() - width() / 2 - nExtraW, p.y() - height() / 2 - nExtraH);
    if (p.x() + nExtraW + width() > rectDesktop.x() + rectDesktop.width()) {
        p.setX(rectDesktop.x() + rectDesktop.width() - width() - nExtraW);
    }
    if (p.x() < rectDesktop.x()) {
        p.setX(rectDesktop.x());
    }
    if (p.y() + nExtraH + height() > rectDesktop.y() + rectDesktop.height()) {
        p.setY(rectDesktop.y() + rectDesktop.height() - height() - nExtraH);
    }
    if (p.y() < rectDesktop.y()) {
        p.setY(rectDesktop.y());
    }

    // QTBUG-52735: Manually set the correct target screen since scaling in a
    // subsequent call to QWindow::resize() may otherwise use the wrong factor
    // if the screen changed notification is still in an event queue.
    if (nScrn >= 0) {
        if (QWindow *pWindow = windowHandle()) {
            pWindow->setScreen(QGuiApplication::screens().at(nScrn));
        }
    }
    move(p);
}

// 显示消息提示框
void MMessageToast::showMessageToast(const QString &strIcon, const QString &strText, const QString strStyleValue,
                                     bool bShowModal, int nShowDuration) {
    // 设置消息信息
    ui->lblToastIcon->setPixmap(QPixmap(strIcon).scaled(36, 36, Qt::KeepAspectRatio, Qt::SmoothTransformation));
    ui->lblToastText->setText(strText);

    //是否使用模态
    if(bShowModal)
    {
#ifndef Q_OS_WIN
        setWindowFlag(Qt::Popup, true);
#endif
        setAttribute(Qt::WA_ShowModal);
        setWindowFlag(Qt::Dialog);
        setWindowModality(Qt::ApplicationModal);
    }

    // 获取最小和最大宽度
    int nWidthMin = 198;
    int nWidthMax = 600;

    // 获取不换行宽度
    ui->lblToastText->setWordWrap(false);
    ui->hlayMessageToast->activate();
    int nWidth = ui->hlayMessageToast->totalMinimumSize().width();

    // 如果不换行宽度小于最小宽度,则取最小宽度
    if (nWidth < nWidthMin) {
        nWidth = nWidthMin;
    }
    // 如果不换行宽度大小最大宽度,则取最大宽度,并开启换行
    else if (nWidth > nWidthMax) {
        nWidth = nWidthMax;
        ui->lblToastText->setWordWrap(true);
    }

    // 获取高度
    ui->hlayMessageToast->activate();
    int nHeight = ui->hlayMessageToast->hasHeightForWidth() ? ui->hlayMessageToast->totalHeightForWidth(nWidth)
                                                            : ui->hlayMessageToast->totalMinimumSize().height();
    // 设置大小
    setFixedSize((nWidth), (nHeight));
    ui->wMessageToast->setStyleSheet(
        QString("QWidget#%1 {border-radius: %2px;}").arg(ui->wMessageToast->objectName()).arg(qMin(6, (nHeight) / 2) - 1));

    // 调整位置
    adjustPosition(parentWidget());

    // 设置显示持续时间
    setShowDuration(nShowDuration);

    // 更新样式
    updateProperty(ui->wMessageToast, M_TOAST_STYLE_KEY, strStyleValue);

    // 开始显示
    if (!m_pShowTimer->isActive()) {
        show();
        startFadeInAnimation();
    }
    m_pShowTimer->start(nShowDuration);
    raise();
    if(bShowModal)
    {
        exec();
    }
}

void MMessageToast::exec()
{
    QEventLoop loop;
    m_pLoop = &loop;
    loop.exec();
    m_pLoop = Q_NULLPTR;
}

void MMessageToast::setVisible(bool visible)
{
    QWidget::setVisible(visible);
    if (!visible && m_pLoop)
    {
        m_pLoop->exit(0);
    }
}

// 设置显示持续时间
void MMessageToast::setShowDuration(int nMSecs) {
    m_nShowDuration = nMSecs;
}

// 获取显示持续时间
int MMessageToast::getShowDuration() const {
    return m_nShowDuration;
}

// 开始淡入动画
void MMessageToast::startFadeInAnimation() {
    disconnect(m_pShowAnimation, SIGNAL(finished()), this, SLOT(close()));

    m_pShowAnimation->stop();
    m_pShowAnimation->setDuration(750);
    m_pShowAnimation->setStartValue(windowOpacity());
    m_pShowAnimation->setEndValue(1);
    m_pShowAnimation->start();
}

// 开始淡出动画
void MMessageToast::startFadeOutAnimation() {
    connect(m_pShowAnimation, SIGNAL(finished()), this, SLOT(close()));

    m_pShowAnimation->stop();
    m_pShowAnimation->setDuration(750);
    m_pShowAnimation->setStartValue(1);
    m_pShowAnimation->setEndValue(0);
    m_pShowAnimation->start();
}

// 获取消息提示框
MMessageToast *MMessageToast::getMessageToast(QWidget *pParent) {
    MMessageToast *pMessageToast = Q_NULLPTR;

    // get Top widget
    while (Q_NULLPTR != pParent && pParent->parentWidget() != Q_NULLPTR)
    {
        if(pParent->isModal())
        {
            //父窗口为第一个模态窗口
            break;
        }
        pParent = pParent->parentWidget();
    }
    if (pParent != Q_NULLPTR) {
        pMessageToast = pParent->findChild<MMessageToast *>();
    }

    if (Q_NULLPTR == pMessageToast) {
        pMessageToast = new MMessageToast(pParent);
    }

    return pMessageToast;
}

// 信息消息框
void MMessageToast::information(QWidget *pParent, const QString &strText, bool bShowModal, int nShowDuration) {
    MMessageToast *pMessageToast = getMessageToast(pParent);
    if (pMessageToast != Q_NULLPTR) {
        pMessageToast->showMessageToast(M_PNG_MSG_TOAST_INFO, strText, QString("information"), bShowModal, nShowDuration);
    }
}

// 警告消息框
void MMessageToast::warning(QWidget *pParent, const QString &strText, bool bShowModal, int nShowDuration) {
    MMessageToast *pMessageToast = getMessageToast(pParent);
    if (pMessageToast != Q_NULLPTR) {
        pMessageToast->showMessageToast(M_PNG_MSG_TOAST_WARNING, strText, QString("warning"), bShowModal, nShowDuration);
    }
}

// 错误消息框
void MMessageToast::error(QWidget *pParent, const QString &strText, bool bShowModal, int nShowDuration) {
    MMessageToast *pMessageToast = getMessageToast(pParent);
    if (pMessageToast != Q_NULLPTR) {
        pMessageToast->showMessageToast(M_PNG_MSG_TOAST_ERROR, strText, QString("error"), bShowModal, nShowDuration);
    }
}

// 成功消息框
void MMessageToast::success(QWidget *pParent, const QString &strText, bool bShowModal, int nShowDuration) {
    MMessageToast *pMessageToast = getMessageToast(pParent);
    if (pMessageToast != Q_NULLPTR) {
        pMessageToast->showMessageToast(M_PNG_MSG_TOAST_SUCCESS, strText, QString("success"), bShowModal, nShowDuration);
    }
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值