Qt 重写QRadioButton实现动态radioButton源码分享

Qt 重写QRadioButton实现动态radioButton源码分享

一、源码分享

1、效果展示


2、源码分享

2.1、工程结构

2.2、mainwindow.cpp

cpp 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"

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

    this->ui->radioButton_2->setColor("#C7E006","#809104");
    this->ui->radioButton_3->setColor("#FF0CE7","#910483");
    this->ui->radioButton_4->setColor("#0C92FF","#0761AC");
}

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

2.3、colorfulRadioButton.h

cpp 复制代码
#ifndef COLORFULRADIOBUTTON_H
#define COLORFULRADIOBUTTON_H

#include <QWidget>
#include <QRadioButton>
#include <QPainter>
#include <QPropertyAnimation>

class ColorfulRadioButton : public QRadioButton
{
    Q_OBJECT

    Q_PROPERTY(int bottomLineWidth READ getBottomLineWidth WRITE setBottomLineWidth NOTIFY bottomLineWidthChanged FINAL)
signals:
    void bottomLineWidthChanged();
public:
    explicit ColorfulRadioButton(QWidget *parent = nullptr);

    int getBottomLineWidth() const
    {
        return bottomLineWidth;
    }

    void setBottomLineWidth(int newBottomLineWidth)
    {
        if (bottomLineWidth == newBottomLineWidth)
            return;
        bottomLineWidth = newBottomLineWidth;
        emit bottomLineWidthChanged();
    }
    void setColor(QColor checked,QColor unchecked)
    {
        this->colorChecked = checked;
        this->colorUnchecked = unchecked;
    }
protected:
    void paintEvent(QPaintEvent *e) override;
private:
    QColor colorChecked = "#0BFFB2";
    QColor colorUnchecked = "#0C976B";

    int leftPadding = 5;
    int textX = 0;
    int bottomLineWidth;

    QPropertyAnimation *animBottomLineWidth = nullptr;
};

#endif // COLORFULRADIOBUTTON_H

2.4、colorfulRadioButton.cpp

cpp 复制代码
#include "colorfulRadioButton.h"

ColorfulRadioButton::ColorfulRadioButton(QWidget *parent)
    : QRadioButton{parent}
{
    connect(this,&QRadioButton::toggled,this,
            [=](bool checked)
            {
                if(checked)
                {
                    this->animBottomLineWidth->setStartValue(textX);
                    this->animBottomLineWidth->setEndValue(this->width() - textX);
                    this->animBottomLineWidth->start();
                }
                else
                {
                    this->animBottomLineWidth->setStartValue(this->width() - textX);
                    this->animBottomLineWidth->setEndValue(textX);
                    this->animBottomLineWidth->start();
                }
            });
    this->animBottomLineWidth = new QPropertyAnimation(this,"bottomLineWidth",this);
    this->animBottomLineWidth->setDuration(300);
    connect(this->animBottomLineWidth,&QPropertyAnimation::valueChanged,this,
            [=](const QVariant &value)
            {
                Q_UNUSED(value);
                this->update();
            });

}

void ColorfulRadioButton::paintEvent(QPaintEvent *e)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);

    int width = this->width();
    int height = this->height();

    QColor color = this->isChecked()?this->colorChecked:this->colorUnchecked;

    QRect rectRoundBorder(5,5,height-10,height-10);

    painter.setPen(QPen(color, this->isChecked()?4:2, Qt::SolidLine, Qt::RoundCap, Qt::MiterJoin));
    painter.setBrush(Qt::transparent);
    painter.drawEllipse(rectRoundBorder);


    if(this->isChecked())
    {
        //绘制内圆
        int roundWidth = (float)rectRoundBorder.width()/2.0f;
        QRect rectRound(5+roundWidth/2,0+5 + roundWidth/2,roundWidth,roundWidth);

        rectRound.setX(rectRoundBorder.center().x() - roundWidth/2 +1);
        rectRound.setY(rectRoundBorder.center().y() - roundWidth/2 +1);

        painter.setPen(Qt::NoPen);
        painter.setBrush(color);
        painter.drawEllipse(rectRound);

    }
    painter.setPen(QPen(color, 3, Qt::SolidLine, Qt::RoundCap, Qt::MiterJoin));
    QRect rectText(rectRoundBorder.x() + rectRoundBorder.width()+5,0,this->width(),this->height());
    painter.drawText(rectText, Qt::AlignLeft | Qt::AlignVCenter, this->text());
    textX = rectText.x();

    if(bottomLineWidth == 0)
        bottomLineWidth = textX;
    painter.drawLine(QPoint(rectText.x(), height-2),QPoint(bottomLineWidth, height-2));

}

2.5、使用方法

放置一个QRadioButton,然后右键提升:

2.6、完整工程下载

文章顶部下载。

二、实现原理

通过继承相关控件然后通过paintEvent重绘来实现。这是Qt GUI开发中非常核心的技术,用于创建具有独特外观或行为的控件。


1、Qt 控件重写的基本概念

在Qt中,所有的控件(如 QPushButtonQLineEditQComboBox 等)都是 QWidget 的子类。要自定义一个控件,通常需要继承自该控件类,并重写其特定方法。最常见的方法是重写以下事件或方法:

  1. 事件处理函数

    • mousePressEvent(QMouseEvent *event)
    • keyPressEvent(QKeyEvent *event)
    • paintEvent(QPaintEvent *event)(最核心)
  2. 大小相关方法

    • sizeHint()
    • minimumSizeHint()

2、核心重写方法:paintEvent

paintEvent 是绘制控件外观的核心方法。通过重写此方法,你可以完全自定义控件的外观。以下是一个重写 QPushButton 的简单示例:

cpp 复制代码
#include <QPainter>
#include <QPushButton>

class CustomButton : public QPushButton {
public:
    CustomButton(QWidget *parent = nullptr) : QPushButton(parent) {}

protected:
    void paintEvent(QPaintEvent *event) override {
        // 先调用父类的绘制方法(保留基础样式)
        QPushButton::paintEvent(event);

        // 自定义绘制逻辑
        QPainter painter(this);
        painter.setPen(Qt::red);
        painter.drawEllipse(rect().adjusted(5, 5, -5, -5)); // 在按钮上画一个红色圆圈
    }
};

3、完整重写示例:自定义按钮

下面是一个更完整的自定义按钮示例,支持渐变背景和圆角边框:

cpp 复制代码
class GradientButton : public QPushButton {
public:
    GradientButton(const QString &text, QWidget *parent = nullptr)
        : QPushButton(text, parent) {}

protected:
    void paintEvent(QPaintEvent *event) override {
        Q_UNUSED(event);

        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);

        // 绘制渐变背景
        QLinearGradient gradient(0, 0, width(), height());
        gradient.setColorAt(0, Qt::blue);
        gradient.setColorAt(1, Qt::cyan);
        painter.setBrush(gradient);

        // 绘制圆角矩形
        painter.drawRoundedRect(rect(), 10, 10);

        // 绘制文字(居中)
        painter.setPen(Qt::white);
        painter.drawText(rect(), Qt::AlignCenter, text());
    }
};

4、如何替换事件行为

如果你想改变控件的事件响应逻辑(如鼠标点击),可以重写对应的事件处理器。例如,实现一个双击按钮:

cpp 复制代码
class DoubleClickButton : public QPushButton {
protected:
    void mouseDoubleClickEvent(QMouseEvent *event) override {
        Q_UNUSED(event);
        qDebug() << "Button double-clicked!";
        // 触发自定义逻辑...
    }
};

5、注意事项

  1. 调用父类方法 :在重写 paintEvent 时,通常先调用父类方法(如 QPushButton::paintEvent(event))以保留基础样式,再添加自定义绘制。

  2. 性能优化 :避免在 paintEvent 中执行耗时操作,否则会导致界面卡顿。

  3. 样式表(QSS)vs 重写

    • 简单样式 :使用样式表(如 .setStyleSheet("background: blue;"))。
    • 复杂逻辑 :必须重写 paintEvent

6、总结

需求场景 推荐方法
修改简单样式(颜色、字体) 使用 QSS(样式表)
完全自定义外观 重写 paintEvent
修改事件行为 重写事件处理器

重写控件是Qt高级UI开发的核心技能,通过合理使用 paintEvent 和事件处理器,你可以实现高度定制化的界面效果。

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