QT 实现按钮多样化

news2024/10/19 6:24:20

1.界面实现效果

以下是具体的项目需要用到的效果展示,可以根据需要,实例化想要的按钮。
在这里插入图片描述

2.简介

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

#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中进行绘制图形。

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类实现

#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按钮,将这个按钮提升为我们自定义的类。
在这里插入图片描述

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2217507.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么Java中1==1为真,而128==128为假?基于享元模式的整数缓存原理分析

❃博主首页 &#xff1a; 「码到三十五」 &#xff0c;同名公众号 :「码到三十五」&#xff0c;wx号 : 「liwu0213」 ☠博主专栏 &#xff1a; <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 &#xff1a…

从零开始搭建你的DolphinScheduler分布式任务调度平台实战指南

文章目录 前言1. 安装部署DolphinScheduler1.1 启动服务 2. 登录DolphinScheduler界面3. 安装内网穿透工具4. 配置Dolphin Scheduler公网地址5. 固定DolphinScheduler公网地址 前言 本篇教程和大家分享一下DolphinScheduler的安装部署及如何实现公网远程访问&#xff0c;结合内…

React速成

useRef获取DOM 组件通讯 子传父 function Son({ onGetMsg }){const sonMsg this is son msgreturn (<div>{/* 在子组件中执行父组件传递过来的函数 */}<button onClick{()>onGetMsg(sonMsg)}>send</button></div>) }function App(){const getMsg…

厨房老鼠数据集:掀起餐饮卫生监测的科技浪潮

厨房老鼠数据集&#xff1a;掀起餐饮卫生监测的科技浪潮 摘要&#xff1a;本文深入探讨了厨房老鼠数据集在餐饮行业卫生管理中的重要性及其相关技术应用。厨房老鼠数据集通过收集夜间厨房图像、老鼠标注信息以及环境数据&#xff0c;为深度学习模型提供了丰富的训练样本。基于…

两个案例全面阐述全链路测试怎么做

首先我们先针对全链路功能测试部分进行一下讲解。去年的时候&#xff0c;有一家电商公司可能知道我一直在帮银行做相关的测试&#xff0c;就请我帮他们去做一些规划。这个平台有虚拟订单&#xff0c;也有实体订单&#xff0c;方式不太一样。 还涉及到分账分佣以及跟银行的对接…

基于SpringBoot+Vue+uniapp的涪陵区特色农产品交易系统的详细设计和实现(源码+lw+部署文档+讲解等)

详细视频演示 请联系我获取更详细的视频演示 项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不…

【IC设计】复旦微行业分析

文章目录 概述各产品线安全与识别芯片&#xff1a;非挥发存储器&#xff1a;智能电表 MCU &#xff1a;集成电路测试服务&#xff1a; 前景公司是FPGA领军企业&#xff0c;在国产替代背景下深度受益优势 1&#xff1a;公司最早推出亿门级 FPGA 产品&#xff0c;提前卡位 28nm 赛…

python的多线程和多进程

首先需要明确的是&#xff0c;多进程和其他语言的一样&#xff0c;能够利用多核cpu&#xff0c;但是python由于GIL的存在&#xff0c;多线程在执行的时候&#xff0c;实际上&#xff0c;每一时刻只有一个线程在执行。相当于是单线程。然而多线程在某些情况下&#xff0c;还是能…

爬虫逆向-js进阶

1.作用域和闭包 //作用域 // var a 3 // // function test(a){ // var a 1; // console.log(函数内部,a) // } // test(2) // // console.log(a)//闭包 // function jiami(){ // function encrypt(){ // console.log(在这里进行加密了) // } // p…

GaussDB高智能--自治运维技术(中)

目录 2.4 日志分析 &#xff08;1&#xff09;日志解析阶段 &#xff08;2&#xff09;日志分析模型的训练 &#xff08;3&#xff09;在线检测模块 2.5 慢SQL发现 &#xff08;1&#xff09;训练阶段 &#xff08;2&#xff09;预测流程 2.6 慢SQL诊断 &#x…

只想简单跑个 AI 大模型,却发现并不简单

之前我用 Ollama 在本地跑大语言模型&#xff08;可以参考《AI LLM 利器 Ollama 架构和对话处理流程解析》&#xff09;。这次想再捣鼓点进阶操作&#xff0c;比如 fine-tuning。 我的想法是&#xff1a;既然有现成的大模型&#xff0c;为什么不自己整理些特定领域的数据集&am…

如何捕捉行情爆发的前兆

在金融市场的激烈角逐中&#xff0c;每一次行情的爆发都是投资者获取丰厚回报的关键时刻。然而&#xff0c;如何识别并把握这些时刻&#xff0c;却是一门需要深厚金融专业知识和敏锐洞察力的艺术。今天&#xff0c;我们就来深入探讨行情爆发的初期信号&#xff0c;揭示那些能够…

锥线性规划【分布鲁棒、两阶段鲁棒方向知识点】

1 锥线性对偶理论 本部分看似和分布鲁棒、两阶段鲁棒优化没什么关系&#xff0c;但值得优先学习&#xff0c;原因将在最后揭晓。 二阶锥 二阶锥&#xff08;second-order cone&#xff0c;又称ice-cream/Lorentz cone&#xff09;的形式为&#xff1a; 非负象限锥 半正定锥 …

初入Linux网络

1.网络发展 独立模式&#xff1a;计算机之间相互独立——>网络互联&#xff1a;多台计算机连接在一起完成数据共享——>局域网LAN&#xff1a;更多的计算机通过交换机和路由器连接在一起——>广域网WAN&#xff1a;将相隔万里的计算机连在一起。 2.协议 计算机之间…

【AI论文精读5】知识图谱与LLM结合的路线图-P3

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】 P1&#xff0c;P2&#xff0c;P4 5 LLM增强的知识图谱 知识图谱&#xff08;KGs&#xff09; 以其结构化的方式呈现知识而闻名&#xff0c;它们已被广泛应用于许多下游任务&#xff0c;如问答系统、推荐系统和网页搜索等…

(一)Mysql篇---Mysql整体架构

MySql框架浅析 首先&#xff0c;上一张图先让各位看看大致结构&#xff1a; 从上到下&#xff0c;依次说一下结构&#xff1a; 连接层&#xff1a;这里主要是处理客户端和数据库连接的&#xff0c;直接使用的Tomcat的连接池&#xff0c;可以调整最大连接数&#xff1b; 服务…

OpenEuler 软件安装与服务管理全攻略

在 OpenEuler 操作系统的日常使用中&#xff0c;软件安装和服务管理是至关重要的操作环节。本文将以严谨的方式为大家详细阐述 OpenEuler 中安装软件的多种方法&#xff0c;涵盖 RPM、DNF 的概念与操作命令以及操作实验&#xff0c;同时还包括源代码软件的安装方法和使用 syste…

JDK-23与JavaFX的安装

一、JDK-23的安装 1.下载 JDK-23 官网直接下载&#xff0c;页面下如图&#xff1a; 2.安装 JDK-23 2.1、解压下载的文件 找到下载的 ZIP 文件&#xff0c;右键点击并选择“解压到指定文件夹”&#xff0c;将其解压缩到您希望的目录&#xff0c;例如 C:\Program Files\Java\…

react18中如何实现同步的setState来实现所见即所得的效果

在react项目中&#xff0c;实现添加列表项&#xff0c;最后一项自动显示在可视区域范围&#xff01;&#xff01; 实现效果 代码实现 import { useState, useRef } from "react"; import { flushSync } from "react-dom"; function FlushSyncRef() {con…

MySQL面试专题-索引

一、MySQL为什么要选择B树来存储索引&#xff1f; MySQL的索引选择B树作为数据结构来进行存储&#xff0c;其本质原因在于可以减少IO次数&#xff0c;提高查询效率&#xff0c;简单来说就是保证在树的高度不变的情况下可以存储更多的数据。 &#xff08;一&#xff09;IO角度 在…