#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
#include <QLabel>
#include <QHBoxLayout>
#include <QTimer>
#include <QPainter>
#include <QMap>
#include <QGridLayout>
#include <QLineEdit>
#include <QGraphicsDropShadowEffect>
#include <QDesktopWidget>
#include <QGroupBox>
// Toast类定义
class Toast : public QWidget
{
Q_OBJECT
public:
enum Position {
Top = 0,
Center,
Bottom,
TopLeft,
TopRight,
BottomLeft,
BottomRight
};
enum ToastType {
Info = 0,
Success,
Warning,
Error
};
static void show(const QString& text,
int duration = 2000,
Position position = Bottom,
ToastType type = Info,
QWidget* parent = nullptr) {
static QMap<QString, Toast*> activeToasts;
QString key = QString("%1-%2-%3").arg(text).arg(static_cast<int>(type)).arg(static_cast<int>(position));
if (activeToasts.contains(key)) {
Toast* existingToast = activeToasts[key];
existingToast->restartTimer(duration);
return;
}
Toast* toast = new Toast(text, duration, position, type, parent);
activeToasts[key] = toast;
QObject::connect(toast, &Toast::destroyed, [key]() {
activeToasts.remove(key);
});
toast->showToast();
}
static void info(const QString& text, int duration = 2000, Position position = Bottom, QWidget* parent = nullptr) {
show(text, duration, position, Info, parent);
}
static void success(const QString& text, int duration = 2000, Position position = Bottom, QWidget* parent = nullptr) {
show(text, duration, position, Success, parent);
}
static void warning(const QString& text, int duration = 2000, Position position = Bottom, QWidget* parent = nullptr) {
show(text, duration, position, Warning, parent);
}
static void error(const QString& text, int duration = 2000, Position position = Bottom, QWidget* parent = nullptr) {
show(text, duration, position, Error, parent);
}
protected:
void paintEvent(QPaintEvent* event) override {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
// 绘制圆角背景
painter.setBrush(getBackgroundColor());
painter.setPen(Qt::NoPen);
painter.drawRoundedRect(rect(), 8, 8);
// 绘制图标
if (!m_icon.isNull()) {
int iconSize = 20;
int iconX = 15;
int iconY = (height() - iconSize) / 2;
painter.drawPixmap(iconX, iconY, iconSize, iconSize, m_icon);
}
// 绘制文字
painter.setPen(getTextColor());
QFont font = painter.font();
font.setPixelSize(14);
painter.setFont(font);
int textX = m_icon.isNull() ? 20 : 45;
QRect textRect(textX, 0, width() - textX - 20, height());
painter.drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, m_text);
}
private:
explicit Toast(const QString& text,
int duration,
Position position,
ToastType type,
QWidget* parent = nullptr)
: QWidget(parent)
, m_text(text)
, m_duration(duration)
, m_position(position)
, m_type(type)
{
// 设置窗口属性
setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint);
setAttribute(Qt::WA_TranslucentBackground);
setAttribute(Qt::WA_DeleteOnClose);
// 不要设置为ToolTip,可能会被系统隐藏
setFocusPolicy(Qt::NoFocus);
initUI();
initTimer();
// 添加阴影效果
QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect(this);
shadowEffect->setBlurRadius(15);
shadowEffect->setColor(QColor(0, 0, 0, 80));
shadowEffect->setOffset(0, 2);
setGraphicsEffect(shadowEffect);
}
void initUI() {
QFontMetrics fm(QFont("", 14));
int textWidth = fm.width(m_text);
// 设置图标
switch (m_type) {
case Success:
m_icon = createColoredIcon(QColor(46, 125, 50), QChar(0x2713)); // 对勾
break;
case Warning:
m_icon = createColoredIcon(QColor(237, 108, 2), QChar(0x26A0)); // 警告
break;
case Error:
m_icon = createColoredIcon(QColor(211, 47, 47), QChar(0x26D4)); // 错误
break;
case Info:
default:
m_icon = createColoredIcon(QColor(2, 119, 189), QChar(0x2139)); // 信息
break;
}
// 计算宽度
int width = textWidth + 40;
if (!m_icon.isNull()) width += 25;
width = qMin(qMax(width, 150), 400);
setFixedSize(width, 50);
}
QPixmap createColoredIcon(const QColor& color, QChar unicodeChar) {
QPixmap pixmap(20, 20);
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(color);
painter.setPen(Qt::NoPen);
painter.drawEllipse(0, 0, 20, 20);
painter.setPen(Qt::white);
QFont font = painter.font();
font.setPixelSize(12);
font.setBold(true);
painter.setFont(font);
painter.drawText(pixmap.rect(), Qt::AlignCenter, unicodeChar);
return pixmap;
}
void initTimer() {
m_timer = new QTimer(this);
m_timer->setSingleShot(true);
connect(m_timer, &QTimer::timeout, this, &Toast::hideToast);
}
void adjustPosition() {
QWidget* parent = parentWidget();
QRect screenRect = QApplication::desktop()->availableGeometry();
int x = 0, y = 0;
int margin = 20;
switch (m_position) {
case Top:
x = screenRect.left() + (screenRect.width() - width()) / 2;
y = screenRect.top() + margin;
break;
case Center:
x = screenRect.left() + (screenRect.width() - width()) / 2;
y = screenRect.top() + (screenRect.height() - height()) / 2;
break;
case TopLeft:
x = screenRect.left() + margin;
y = screenRect.top() + margin;
break;
case TopRight:
x = screenRect.right() - width() - margin;
y = screenRect.top() + margin;
break;
case BottomLeft:
x = screenRect.left() + margin;
y = screenRect.bottom() - height() - margin;
break;
case BottomRight:
x = screenRect.right() - width() - margin;
y = screenRect.bottom() - height() - margin;
break;
case Bottom:
default:
x = screenRect.left() + (screenRect.width() - width()) / 2;
y = screenRect.bottom() - height() - margin;
break;
}
move(x, y);
}
void showToast() {
// 先调整位置
adjustPosition();
// 淡入效果
setWindowOpacity(0.0);
QWidget::show();
QTimer::singleShot(10, this, [this]() {
// 使用定时器来实现淡入效果
for (int i = 0; i <= 100; i += 5) {
setWindowOpacity(i / 100.0);
QApplication::processEvents();
}
setWindowOpacity(1.0);
});
m_timer->start(m_duration);
}
void restartTimer(int duration) {
if (m_timer) {
m_timer->stop();
m_timer->start(duration);
}
}
private slots:
void hideToast() {
// 淡出效果
for (int i = 100; i >= 0; i -= 5) {
setWindowOpacity(i / 100.0);
QApplication::processEvents();
}
close();
}
private:
QColor getBackgroundColor() const {
switch (m_type) {
case Success: return QColor(237, 247, 237);
case Warning: return QColor(255, 243, 224);
case Error: return QColor(253, 237, 237);
case Info:
default: return QColor(229, 246, 253);
}
}
QColor getTextColor() const {
switch (m_type) {
case Success: return QColor(30, 70, 32);
case Warning: return QColor(102, 60, 0);
case Error: return QColor(95, 33, 32);
case Info:
default: return QColor(1, 67, 97);
}
}
private:
QString m_text;
int m_duration;
Position m_position;
ToastType m_type;
QPixmap m_icon;
QTimer* m_timer = nullptr;
};