Qt实现Toast效果

使用Qt开发桌面软件的过程中,有时需要用到提示语,过几秒后自动消失,这种Toast的样式,比如“网络断开连接”,“已超时”,“对方拒绝”等等,而且可能多次出现。那么用Qt怎么来实现呢,一般可能想到的是用QTimer定时器处理,但这种要写信号槽,本篇介绍用timerEvent定时器事件处理。

  1. 创建一个对话框继承QDialog,添加一个水平布局,布局上放一个QLabel,QLabel背景透明,文字居中,设置对话框菜单栏隐藏

ToastDailog()
{
        auto layout = new QHBoxLayout;//水平布局
        m_label = new QLabel;
        m_label->setStyleSheet("color:white; background:transparent");
        layout->addWidget(m_label);
        m_label->setAlignment(Qt::AlignCenter); //文字居中
        setLayout(layout);
        setWindowFlag(Qt::FramelessWindowHint); //隐藏菜单框
        setAttribute(Qt::WA_ShowWithoutActivating, true);
 }
  1. 显示的设置对话框的背景色,设置显示在窗口的最顶端。

void show(CustomToast::Level level, const QString &text)
{
        QPalette p = palette();
        p.setColor(QPalette::Window, QColor(0, 0, 0, 200));
        setPalette(p);
        m_label->setText(text);
        setWindowFlag(Qt::WindowStaysOnTopHint);//窗口置顶
        QDialog::show();
}
  1. 显示Toast启动定时器

m_toastDailog->show(level, text);
m_timerId = startTimer(interval);
  1. 定时器到时,关闭窗口

killTimer(m_timerId);
m_toastDailog->accept(); //隐藏模态对话框

完整的示例代码如下:

CustomToast.h

#ifndef CUSTOMTOAST_H
#define CUSTOMTOAST_H

#include <QObject>
#include <QRect>

class ToastDailog;

class CustomToast : public QObject
{
    Q_OBJECT
public:
    enum Level {INFO, WARN, ERROR};
    void show(Level level, const QString& text, int interval = 2000);
    static CustomToast &instance();

private:
    explicit CustomToast(QObject *parent = nullptr);
    void timerEvent(QTimerEvent *event) override;

private:
    ToastDailog *m_toastDailog;
    int m_timerId{0};
    QRect m_gemoetry;

};

#endif // CUSTOMTOAST_H

CustomToast.cpp

#include "CustomToast.h"
#include <QDialog>
#include <QHBoxLayout>
#include <QLabel>
#include <QEvent>
#include <QDebug>

class ToastDailog: public QDialog
{
public:
    ToastDailog()
    {
        auto layout = new QHBoxLayout;//水平布局
        m_label = new QLabel;
        m_label->setStyleSheet("color:white; background:transparent");
        layout->addWidget(m_label);
        m_label->setAlignment(Qt::AlignCenter); //文字居中
        setLayout(layout);
        setWindowFlag(Qt::FramelessWindowHint); //隐藏菜单框
        setAttribute(Qt::WA_ShowWithoutActivating, true);
    }

    void show(CustomToast::Level level, const QString &text)
    {
        QPalette p = palette();
        p.setColor(QPalette::Window, QColor(0, 0, 0, 200));
        switch (level) {
        case CustomToast::INFO:
            p.setColor(QPalette::Window, QColor(0, 0, 0, 200));//黑底
            break;
        case CustomToast::WARN:
            p.setColor(QPalette::Window, QColor(0, 0, 255, 200));//蓝底
            break;
        default:
            p.setColor(QPalette::Window, QColor(255, 0, 0, 200));//红底
            break;

        }

        setPalette(p);
        m_label->setText(text);
        setWindowFlag(Qt::WindowStaysOnTopHint);//窗口置顶
        QDialog::show();
    }

private:
    QLabel *m_label;
};

void CustomToast::show(CustomToast::Level level, const QString &text, int interval)
{
    m_toastDailog->show(level, text);
    if(m_timerId != 0)
    {
        //如果已开启一个定时器,先把他关掉
        killTimer(m_timerId);
    }
    m_timerId = startTimer(interval); //启动定时器,interval毫秒触发定时器事件,直到调用killTimer
}

CustomToast &CustomToast::instance()
{
    static CustomToast thisToast;//这种实例化方法会自动回收内存
    return thisToast;
}

CustomToast::CustomToast(QObject *parent) : QObject(parent)
{
    m_toastDailog = new ToastDailog;
}

void CustomToast::timerEvent(QTimerEvent *event)
{
    killTimer(m_timerId);
    m_timerId = 0;
    m_toastDailog->accept(); //隐藏模态对话框
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

public slots:
    void showInfoToast();
    void showWarnToast();
    void showErrorToast();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "CustomToast.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->pushButton_info, SIGNAL(clicked()), this, SLOT(showInfoToast()));
    connect(ui->pushButton_warn, SIGNAL(clicked()), this, SLOT(showWarnToast()));
    connect(ui->pushButton_error, SIGNAL(clicked()), this, SLOT(showErrorToast()));
}

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

void MainWindow::showInfoToast()
{
    CustomToast::instance().show(CustomToast::INFO, QStringLiteral("文件不存在"));
}

void MainWindow::showWarnToast()
{
    CustomToast::instance().show(CustomToast::WARN, QStringLiteral("登录失败"));
}

void MainWindow::showErrorToast()
{
    CustomToast::instance().show(CustomToast::ERROR, QStringLiteral("网络断开连接"));
}

main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

运行效果:

上面这个有一个缺陷,就是如果移动主窗体,弹出的toast不会在主窗体居中了,

方案二:

#ifndef MYTOAST_H
#define MYTOAST_H

#include <QLabel>
#include <QTimer>

class MyToast : public QLabel
{
    Q_OBJECT
public:
    explicit MyToast(QWidget *parent = nullptr);
    ~MyToast();
    void startToast(const QString &text, int interval = 0);

private slots:
    void slotCloseToast();

private:
    void initView();

private:
    QTimer *m_timer;
};

#endif // MYTOAST_H
#include "MyToast.h"
#include <QDebug>

MyToast::MyToast(QWidget *parent) :
    QLabel(parent),
    m_timer(new QTimer)
{
    setAttribute(Qt::WA_DeleteOnClose); //关闭后自动析构
    initView();
}

MyToast::~MyToast()
{
    qDebug() << "MyToast::~MyToast======= line=== " << __LINE__;
}

void MyToast::startToast(const QString &text, int interval)
{
    if(m_timer->isActive())
        m_timer->stop();
    //计算文本内容的宽度和高度
    QFontMetrics metrics(QFont("微软雅黑", 14));
    int nWidth = metrics.width(text);
    int nHeight = metrics.height();
    //设置窗口大小,给周边一些空间
    this->resize(nWidth + 16, nHeight + 10);
    this->setText(text);
    //和上面设置的字体一样
    this->setFont(QFont("微软雅黑", 14));
    int px = (this->parentWidget()->width() - this->width()) / 2;
    int py = (this->parentWidget()->height() - this->height()) / 2;
    this->move(px, py);
    this->show();
    m_timer->start(interval);
}

//定时器到时关闭弹窗
void MyToast::slotCloseToast()
{
    m_timer->stop();
    this->close();
}

void MyToast::initView()
{
    //设置样式
    this->setStyleSheet("background-color:#ff0000; color:#00ff00;border-radius:4px");
    //文本居中
    this->setAlignment(Qt::AlignCenter);
    connect(m_timer, &QTimer::timeout, this, &MyToast::slotCloseToast);
}

使用


void MainWindow::showErrorToast()
{
    //CustomToast::instance().show(CustomToast::ERROR, QStringLiteral("网络断开连接"));
    MyToast *toast = new MyToast(this);
    toast->startToast(QStringLiteral("网络断开连接"), 2000);

}

运行效果:

这个托动主窗口位置,弹出的都会在主窗体的中间。

这种方案没有动画,下面介绍一种带动画的QPropertyAnimation使用。

方案三:

#ifndef TOASTANIMATION_H
#define TOASTANIMATION_H

#include <QWidget>

namespace Ui {
class ToastAnimation;
}

class ToastAnimation : public QWidget
{
    Q_OBJECT

public:
    explicit ToastAnimation(QWidget *parent = nullptr);
    ~ToastAnimation();
    void initView();
    void setText(const QString &text);
    void showAnimation(int timeout = 2000); //动画方式show出,默认2秒后消失
    //静态调用
    static void showTip(const QString &text, QWidget *parent  = nullptr);

protected:
    virtual void paintEvent(QPaintEvent *event) override;

private:
    Ui::ToastAnimation *ui;
};

#endif // TOASTANIMATION_H
#include "ToastAnimation.h"
#include "ui_ToastAnimation.h"
#include <QPropertyAnimation>
#include <QScreen>
#include <QGuiApplication>
#include <QPainter>
#include <QTimer>
#include <QDebug>


ToastAnimation::ToastAnimation(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ToastAnimation)
{
    ui->setupUi(this);
    initView();
}

ToastAnimation::~ToastAnimation()
{
    qDebug() << "~ToastAnimation============line==" << __LINE__;
    delete ui;
}

void ToastAnimation::initView()
{
    setWindowFlags(windowFlags()|Qt::FramelessWindowHint|Qt::Tool); //无边框 无任务栏
    setAttribute(Qt::WA_TranslucentBackground, true);//背景透明
    ui->label->setStyleSheet("background-color:#000000; color:#ffffff;border-radius:4px");
    ui->label->setFont(QFont("Microsoft YaHei", 14));
    ui->label->setAlignment(Qt::AlignCenter);
}

void ToastAnimation::setText(const QString &text)
{
    ui->label->setText(text);
    ui->label->resize(ui->label->width() + 16, ui->label->height() + 20);
}

void ToastAnimation::showAnimation(int timeout)
{
    //开始动画
    QPropertyAnimation *animation = new QPropertyAnimation(this, "windowOpacity");
    animation->setDuration(1000);
    animation->setStartValue(0);
    animation->setEndValue(1);
    animation->start();
    show();
    QTimer::singleShot(timeout, [&]
    {
        //结束动画
        QPropertyAnimation *animation = new QPropertyAnimation(this, "windowOpacity");
        animation->setDuration(1000);
        animation->setStartValue(1);
        animation->setEndValue(0);
        animation->start();
        connect(animation, &QPropertyAnimation::finished, [&]{
            close();
            deleteLater();//关闭后析构
        });
    });
}

void ToastAnimation::showTip(const QString &text, QWidget *parent)
{
    qDebug() << "showTip======text===" << text << " line====" << __LINE__;
    ToastAnimation *toast = new ToastAnimation(parent);
    //toast->setWindowFlags(toast->windowFlags()|Qt::WindowStaysOnTopHint);//置顶
    toast->setText(text);
    toast->adjustSize();//设置完文本后调整大小

    int px = 0;
    int py = 0;
    if(parent == nullptr) {
        //主屏的60%高度位置
        QScreen *pScreen = QGuiApplication::primaryScreen();
        px = (pScreen->size().width() - toast->width()) / 2;
        py = pScreen->size().height() * 6/10;
    }
    else {
        //弹框居中
        px = (parent->width() - toast->width()) / 2 + parent->pos().x();
        py = (parent->height() - toast->height()) / 2 + parent->pos().y();
    }

    toast->move(px, py);
    toast->showAnimation();
}
//绘制背景为黑色
void ToastAnimation::paintEvent(QPaintEvent *event)
{
    QPainter paint(this);
    paint.setPen(Qt::NoPen);
    paint.setBrush(Qt::black);
    paint.setRenderHint(QPainter::Antialiasing);
    QPainterPath path;
    path.addRoundedRect(rect(), 20, 20);
    paint.setClipPath(path);  //圆角边框去掉毛刺
    QBrush brush = QBrush(QColor("#000000")); //黑色背景
    paint.setBrush(brush);
    paint.drawRect(rect());
}

测试代码:

void MainWindow::showWarnToast()
{
    //CustomToast::instance().show(CustomToast::WARN, QStringLiteral("登录失败"));
    ToastAnimation::showTip(QStringLiteral("登录失败"), this);
}

运行结果:

参考:

https://blog.csdn.net/qq_45662588/article/details/116157723

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值