QT 实现按钮多样化

1.界面实现效果

以下是具体的项目需要用到的效果展示,可以根据需要,实例化想要的按钮。

2.简介

原理:使用Qt的QPropertyAnimation动画类,这里简单来说就是切换两个按钮样式。

请看以下结构体:

cpp 复制代码
#define MAX_LINE_COUNT 3

struct PurelinStatus
{
    QSizeF bgSize;                       // 背景尺寸
    double bgRadius = 0;                 // 背景圆角
    QColor bgColor = Qt::white;          // 背景颜色
    int useLine = 0;                     // 使用的线条数量
    QLineF linePoss[MAX_LINE_COUNT];     // 各线条位置
    QPointF lineHide[MAX_LINE_COUNT];     // 默认消失的位置
    QColor lineColors[MAX_LINE_COUNT];   // 各线条颜色

    PurelinStatus(int useLine = 0) : useLine(useLine)
    {
        for (int i = useLine; i < MAX_LINE_COUNT; i++)
        {
            linePoss[i] = QLineF(0, 0, 0, 0);
            lineColors[i] = Qt::transparent;
        }
    }
};

根据线条的数量、颜色、背景颜色、线条消失的位置等属性,在paintEvent中进行绘制图形。

cpp 复制代码
void PurelinButton::paintEvent(QPaintEvent *)
{
    // 背景位置
    QSizeF bgSize = currentStatus.bgSize;
    if (bgSize.isEmpty())
        bgSize = this->size();
    double left = (this->width() - bgSize.width()) / 2;
    double top = (this->height() - bgSize.height()) / 2;
    QRectF rect(left, top, bgSize.width(), bgSize.height());

    // 绘制背景
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    QPainterPath path;
    path.addRoundedRect(rect, currentStatus.bgRadius, currentStatus.bgRadius);
    painter.fillPath(path, currentStatus.bgColor);

    // 绘制前景
    const double penWidth = 3.0;
    for (int i = 0; i < currentStatus.useLine; i++)
    {
        const QLineF& line = currentStatus.linePoss[i];
        if ((line.length() < 1e-4))
            continue;

        painter.setPen(QPen(currentStatus.lineColors[i], penWidth, Qt::SolidLine, Qt::RoundCap));
        painter.drawLine(currentStatus.linePoss[i]);
    }
}

3.Button类实现

cpp 复制代码
#ifndef PURELINBUTTON_H
#define PURELINBUTTON_H

#include <QPushButton>
#include <QList>

#define MAX_LINE_COUNT 3

struct PurelinStatus
{
    QSizeF bgSize;                       // 背景尺寸
    double bgRadius = 0;                 // 背景圆角
    QColor bgColor = Qt::white;          // 背景颜色
    int useLine = 0;                     // 使用的线条数量
    QLineF linePoss[MAX_LINE_COUNT];     // 各线条位置
    QPointF lineHide[MAX_LINE_COUNT];     // 默认消失的位置
    QColor lineColors[MAX_LINE_COUNT];   // 各线条颜色

    PurelinStatus(int useLine = 0) : useLine(useLine)
    {
        for (int i = useLine; i < MAX_LINE_COUNT; i++)
        {
            linePoss[i] = QLineF(0, 0, 0, 0);
            lineColors[i] = Qt::transparent;
        }
    }
};

class PurelinButton : public QPushButton
{
    Q_OBJECT
public:
    PurelinButton(QWidget* parent = nullptr);

    const PurelinStatus& getCurrentStatus() const;
    void setCurrentStatus(PurelinStatus status);

    /// 加载显示状态
    void load(PurelinStatus status);

protected:
    void paintEvent(QPaintEvent *) override;

private slots:
    virtual void aniProgChanged(const QVariant& var);
    void aniProgFinished();

private:
    bool animating = false;
    double animationProg = 0;

    PurelinStatus currentStatus;
    PurelinStatus prevStatus;
    PurelinStatus nextStatus;
};

#endif // PURELINBUTTON_H

#include "purelinbutton.h"
#include <QPainter>
#include <QPainterPath>
#include <QDebug>
#include <QPropertyAnimation>

PurelinButton::PurelinButton(QWidget *parent) : QPushButton(parent)
{

}

const PurelinStatus &PurelinButton::getCurrentStatus() const
{
    return currentStatus;
}

void PurelinButton::setCurrentStatus(PurelinStatus status)
{
    this->currentStatus = status;
}

void PurelinButton::load(PurelinStatus status)
{
    prevStatus = currentStatus;
    nextStatus = status;

    if (prevStatus.useLine < nextStatus.useLine)
    {
        for (int i = prevStatus.useLine; i < nextStatus.useLine; i++)
        {
            prevStatus.linePoss[i] = QLineF(nextStatus.lineHide[i], nextStatus.lineHide[i]);
            prevStatus.lineColors[i] = nextStatus.lineColors[i];
        }
    }
    else
    {
        for (int i = nextStatus.useLine; i < prevStatus.useLine; i++)
        {
            nextStatus.linePoss[i] = QLineF(prevStatus.lineHide[i], prevStatus.lineHide[i]);
            nextStatus.lineColors[i] = prevStatus.lineColors[i];
        }
    }
    if (prevStatus.bgSize.isEmpty())
        prevStatus.bgSize = this->size();
    if (nextStatus.bgSize.isEmpty())
        nextStatus.bgSize = this->size();

    QPropertyAnimation* ani = new QPropertyAnimation(this, "");
    ani->setStartValue(0.0);
    ani->setEndValue(1.0);
    ani->setDuration(500);
    ani->setEasingCurve(QEasingCurve::OutQuad);
    connect(ani, &QPropertyAnimation::valueChanged, this, &PurelinButton::aniProgChanged);
    connect(ani, SIGNAL(finished()), ani, SLOT(deleteLater()));
    connect(ani, SIGNAL(finished()), this, SLOT(aniProgFinished()));
    ani->start();
}

void PurelinButton::paintEvent(QPaintEvent *)
{
    // 背景位置
    QSizeF bgSize = currentStatus.bgSize;
    if (bgSize.isEmpty())
        bgSize = this->size();
    double left = (this->width() - bgSize.width()) / 2;
    double top = (this->height() - bgSize.height()) / 2;
    QRectF rect(left, top, bgSize.width(), bgSize.height());

    // 绘制背景
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    QPainterPath path;
    path.addRoundedRect(rect, currentStatus.bgRadius, currentStatus.bgRadius);
    painter.fillPath(path, currentStatus.bgColor);

    // 绘制前景
    const double penWidth = 3.0;
    for (int i = 0; i < currentStatus.useLine; i++)
    {
        const QLineF& line = currentStatus.linePoss[i];
        if ((line.length() < 1e-4))
            continue;

        painter.setPen(QPen(currentStatus.lineColors[i], penWidth, Qt::SolidLine, Qt::RoundCap));
        painter.drawLine(currentStatus.linePoss[i]);
    }
}


void PurelinButton::aniProgChanged(const QVariant &var)
{
    // 可以重新这个方法,自定义尺寸
    // 比如,按照控件的百分比大小进行调整,而不是像素数值
    double prog = var.toDouble();

    currentStatus.bgRadius = prevStatus.bgRadius + prog * (nextStatus.bgRadius - prevStatus.bgRadius);
    currentStatus.bgSize = QSizeF(prevStatus.bgSize.width() + prog * (nextStatus.bgSize.width() - prevStatus.bgSize.width()),
                                 prevStatus.bgSize.height() + prog * (nextStatus.bgSize.height() - prevStatus.bgSize.height()));
    currentStatus.bgColor.setRgbF(
                prevStatus.bgColor.redF() + prog * (nextStatus.bgColor.redF() - prevStatus.bgColor.redF()),
                prevStatus.bgColor.greenF() + prog * (nextStatus.bgColor.greenF() - prevStatus.bgColor.greenF()),
                prevStatus.bgColor.blueF() + prog * (nextStatus.bgColor.blueF() - prevStatus.bgColor.blueF()),
                prevStatus.bgColor.alphaF() + prog * (nextStatus.bgColor.alphaF() - prevStatus.bgColor.alphaF()));
    int lineCount = qMax(prevStatus.useLine, nextStatus.useLine);
    currentStatus.useLine = lineCount;
    for (int i = 0; i < lineCount; i++)
    {
        currentStatus.linePoss[i].setLine(
                    prevStatus.linePoss[i].x1() + prog * (nextStatus.linePoss[i].x1() - prevStatus.linePoss[i].x1()),
                    prevStatus.linePoss[i].y1() + prog * (nextStatus.linePoss[i].y1() - prevStatus.linePoss[i].y1()),
                    prevStatus.linePoss[i].x2() + prog * (nextStatus.linePoss[i].x2() - prevStatus.linePoss[i].x2()),
                    prevStatus.linePoss[i].y2() + prog * (nextStatus.linePoss[i].y2() - prevStatus.linePoss[i].y2()));

        currentStatus.lineColors[i].setRgbF(
                    prevStatus.lineColors[i].redF() + prog * (nextStatus.lineColors[i].redF() - prevStatus.lineColors[i].redF()),
                    prevStatus.lineColors[i].greenF() + prog * (nextStatus.lineColors[i].greenF() - prevStatus.lineColors[i].greenF()),
                    prevStatus.lineColors[i].blueF() + prog * (nextStatus.lineColors[i].blueF() - prevStatus.lineColors[i].blueF()),
                    prevStatus.lineColors[i].alphaF() + prog * (nextStatus.lineColors[i].alphaF() - prevStatus.lineColors[i].alphaF()));
    }

    update();
}

void PurelinButton::aniProgFinished()
{
    currentStatus = nextStatus;
    animating = false;
}

4.使用

MainWindow界面上拖动一个QPushButton按钮,将这个按钮提升为我们自定义的类。

cpp 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QGraphicsDropShadowEffect>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(this);
    shadow->setOffset(3, 5);
    shadow->setColor(QColor("#44888888"));
    shadow->setBlurRadius(20);
    ui->pushButton->setGraphicsEffect(shadow);
}

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


void MainWindow::on_pushButton_clicked()
{
    const int totalCount = 8;
    static int index = -1;
    int dx = 0;

    // -
    PurelinStatus status[totalCount];
    int i = 0;
    status[i].bgSize = QSize(100, 60);
    status[i].bgColor = Qt::white;
    status[i].bgRadius = 30;
    status[i].useLine = 1;
    status[i].linePoss[0] = QLineF(35, 50, 65, 50);
    status[i].lineHide[0] = QPointF(50, 50);
    status[i].lineColors[0] =  QColor("#ed657d");

    // +
    i++;
    status[i].bgSize = QSize(-1, -1);
    status[i].bgColor = Qt::white;
    status[i].bgRadius = 30;
    status[i].useLine = 2;
    status[i].linePoss[0] = QLineF(35, 50, 65, 50);
    status[i].linePoss[1] = QLineF(50, 65, 50, 35);
    status[i].lineHide[0] = QPointF(50, 50);
    status[i].lineHide[1] = QPointF(50, 50);
    status[i].lineColors[0] = QColor("#5baaf8");
    status[i].lineColors[1] = QColor("#5baaf8");

    // x
    i++;
    status[i].bgSize = QSize(-1, -1);
    status[i].bgColor = Qt::white;
    status[i].bgRadius = 50;
    status[i].useLine = 2;
    status[i].linePoss[0] = QLineF(40, 40, 60, 60);
    status[i].linePoss[1] = QLineF(40, 60, 60, 40);
    status[i].lineHide[0] = QPointF(50, 50);
    status[i].lineHide[1] = QPointF(50, 50);
    status[i].lineColors[0] = QColor("#b9b9b9");
    status[i].lineColors[1] = QColor("#b9b9b9");

    // √
    i++;
    dx = -2;
    status[i].bgSize = QSize(-1, -1);
    status[i].bgColor = Qt::white;
    status[i].bgRadius = 50;
    status[i].useLine = 2;
    status[i].linePoss[0] = QLineF(40 + dx, 50, 50 + dx, 60);
    status[i].linePoss[1] = QLineF(50 + dx, 60, 70 + dx, 40);
    status[i].lineHide[0] = QPointF(50 + dx, 50);
    status[i].lineHide[1] = QPointF(50 + dx, 50);
    status[i].lineColors[0] = QColor("#59ce84");
    status[i].lineColors[1] = QColor("#59ce84");

    // <
    i++;
    dx = -4;
    status[i].bgSize = QSize(-1, -1);
    status[i].bgColor = Qt::white;
    status[i].bgRadius = 50;
    status[i].useLine = 2;
    status[i].linePoss[0] = QLineF(42 + dx, 50, 58 + dx, 65);
    status[i].linePoss[1] = QLineF(42 + dx, 50, 58 + dx, 35);
    status[i].lineHide[0] = QPointF(50, 50);
    status[i].lineHide[1] = QPointF(50, 50);
    status[i].lineColors[0] = QColor("#4b6fea");
    status[i].lineColors[1] = QColor("#4b6fea");

    // =
    i++;
    status[i].bgSize = QSize(100, 60);
    status[i].bgColor = Qt::white;
    status[i].bgRadius = 30;
    status[i].useLine = 2;
    status[i].linePoss[0] = QLineF(35, 55, 65, 55);
    status[i].linePoss[1] = QLineF(35, 45, 65, 45);
    status[i].lineHide[0] = QPointF(50, 55);
    status[i].lineHide[1] = QPointF(50, 45);
    status[i].lineColors[0] = QColor("#eda244");
    status[i].lineColors[1] = QColor("#eda244");

    // 三
    i++;
    status[i].bgSize = QSize(-1, -1);
    status[i].bgColor = Qt::white;
    status[i].bgRadius = 20;
    status[i].useLine = 3;
    status[i].linePoss[1] = QLineF(34, 37, 66, 37);
    status[i].linePoss[0] = QLineF(40, 50, 60, 50);
    status[i].linePoss[2] = QLineF(46, 63, 54, 63);
    status[i].lineHide[1] = QPointF(50, 37);
    status[i].lineHide[0] = QPointF(50, 50);
    status[i].lineHide[2] = QPointF(50, 63);
    status[i].lineColors[1] = QColor("#7248e3");
    status[i].lineColors[0] = QColor("#7248e3");
    status[i].lineColors[2] = QColor("#7248e3");

    // ≡
    i++;
    status[i].bgSize = QSize(-1, -1);
    status[i].bgColor = Qt::white;
    status[i].bgRadius = 20;
    status[i].useLine = 3;
    status[i].linePoss[1] = QLineF(35, 37, 65, 37);
    status[i].linePoss[0] = QLineF(35, 50, 55, 50);
    status[i].linePoss[2] = QLineF(35, 63, 60, 63);
    status[i].lineHide[1] = QPointF(35, 37);
    status[i].lineHide[0] = QPointF(35, 50);
    status[i].lineHide[2] = QPointF(35, 63);
    status[i].lineColors[1] = QColor("#424649");
    status[i].lineColors[0] = QColor("#424649");
    status[i].lineColors[2] = QColor("#424649");


    if (++index >= totalCount)
        index = 0;
    ui->pushButton->load(status[index]);
}
相关推荐
小魏冬琅7 分钟前
探索面向对象的高级特性与设计模式(2/5)
java·开发语言
lihao lihao10 分钟前
C++stack和queue的模拟实现
开发语言·c++
TT哇21 分钟前
【Java】数组的定义与使用
java·开发语言·笔记
天天进步201526 分钟前
Lodash:现代 JavaScript 开发的瑞士军刀
开发语言·javascript·ecmascript
姆路34 分钟前
QT中使用图表之QChart概述
c++·qt
假装我不帅35 分钟前
js实现类似与jquery的find方法
开发语言·javascript·jquery
wang_chao11838 分钟前
FFMPEG+Qt 实时显示本机USB摄像头1080p画面以及同步录制mp4视频
qt·ffmpeg·音视频
look_outs39 分钟前
JavaSE笔记2】面向对象
java·开发语言
萧鼎39 分钟前
【Python】高效数据处理:使用Dask处理大规模数据
开发语言·python
Ellie陈1 小时前
Java已死,大模型才是未来?
java·开发语言·前端·后端·python