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);
}
}