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]);
}
相关推荐
用户805533698032 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner2 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz7 天前
QML Hello World 入门示例
qt
xcyxiner10 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner11 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner11 天前
DicomViewer (添加模型类)3
qt
xcyxiner12 天前
DicomViewer (目录调整) 2
qt
xcyxiner12 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00614 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术14 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript