PyQt 重写QRadioButton实现动态radioButton源码分享
📅 2026/7/16 6:04:48
👁️ 阅读次数
📝 编程学习
PyQt 重写QRadioButton实现动态radioButton源码分享
- 一、源码分享
- 1、效果展示
- 2、源码分享
- 2.1、colorful_radio_button.py
- 2.2、main.py
- 2.3、库安装
- 二、QRadioButton详解
- 1、QRadioButton 概述
- 主要特性:
- 2、 基本用法
- 2.1 、创建单选按钮
- 2.2、 布局管理
- 3、 核心方法详解
- 3.1 、状态控制方法
- 3.2 、文本和图标
- 3.3、 信号与槽
- 4、分组管理
- 4.1、 自动分组(默认)
- 4.2、 使用 QButtonGroup 手动分组
- 5、样式定制
- 5.1、 使用 QSS(Qt样式表)
- 5.2 、自定义绘制(如示例中的 ColorfulRadioButton)
- 6、实战技巧
- 6.1、 动态创建单选按钮
- 6.2、 获取选中值
- 6.3 、启用/禁用控制
- 7、常见问题与解决方案
- 问题1:单选按钮不互斥
- 问题2:文字显示不完整
- 问题3:自定义样式不生效
- 8、 与相关控件的对比
- 9、性能优化建议
- 10、扩展应用
- 10.1 、与数据模型绑定
- 10.2、 响应式单选按钮组
- 11、总结
一、源码分享
1、效果展示
2、源码分享
2.1、colorful_radio_button.py
fromPyQt6.QtWidgetsimportQRadioButton,QWidgetfromPyQt6.QtCoreimportQPropertyAnimation,pyqtProperty,pyqtSignal,Qt,QRect,QPointfromPyQt6.QtGuiimportQPainter,QColor,QPenclassColorfulRadioButton(QRadioButton):# 信号:bottomLineWidth 变更通知bottomLineWidthChanged=pyqtSignal()def__init__(self,parent:QWidget=None):super().__init__(parent)# 成员变量初始化self._bottom_line_width=0self.color_checked=QColor("#0BFFB2")self.color_unchecked=QColor("#0C976B")self.left_padding=5self.text_x=0# 创建属性动画,绑定 bottomLineWidthself.anim_bottom_line_width=QPropertyAnimation(self,b"bottomLineWidth")self.anim_bottom_line_width.setDuration(300)# 选中切换触发动画self.toggled.connect(self._on_toggled)# 动画数值变化时重绘控件self.anim_bottom_line_width.valueChanged.connect(lambdav:self.update())# -------------------------- Q_PROPERTY 封装 --------------------------@pyqtProperty(int,notify=bottomLineWidthChanged)defbottomLineWidth(self)->int:returnself._bottom_line_width@bottomLineWidth.setterdefbottomLineWidth(self,val:int):ifself._bottom_line_width==val:returnself._bottom_line_width=val self.bottomLineWidthChanged.emit()# -------------------------- 外部接口 --------------------------defsetColor(self,checked_color:QColor,unchecked_color:QColor):"""设置选中/未选中颜色"""self.color_checked=checked_color self.color_unchecked=unchecked_color# -------------------------- 槽函数 --------------------------def_on_toggled(self,checked:bool):"""单选框选中状态切换,启动下划线动画"""text_x=self.text_x total_width=self.width()ifchecked:self.anim_bottom_line_width.setStartValue(text_x)self.anim_bottom_line_width.setEndValue(total_width)else:self.anim_bottom_line_width.setStartValue(total_width)self.anim_bottom_line_width.setEndValue(text_x)self.anim_bottom_line_width.start()# -------------------------- 重绘事件 --------------------------defpaintEvent(self,event):painter=QPainter(self)painter.setRenderHint(QPainter.RenderHint.Antialiasing)w=self.width()h=self.height()is_checked=self.isChecked()current_color=self.color_checkedifis_checkedelseself.color_unchecked# 外圆圈矩形border_rect=QRect(5,5,h-10,h-10)# 绘制外圈边框pen_width=4ifis_checkedelse2pen=QPen(current_color,pen_width,Qt.PenStyle.SolidLine)pen.setCapStyle(Qt.PenCapStyle.RoundCap)pen.setJoinStyle(Qt.PenJoinStyle.MiterJoin)painter.setPen(pen)painter.setBrush(Qt.BrushStyle.NoBrush)painter.drawEllipse(border_rect)# 选中时绘制内部实心圆ifis_checked:round_w=border_rect.width()/2.0inner_rect=QRect()inner_rect.setX(int(border_rect.center().x()-round_w/2+1))inner_rect.setY(int(border_rect.center().y()-round_w/2+1))inner_rect.setWidth(int(round_w))inner_rect.setHeight(int(round_w))painter.setPen(Qt.PenStyle.NoPen)painter.setBrush(current_color)painter.drawEllipse(inner_rect)# 绘制文字text_rect=QRect(border_rect.x()+border_rect.width()+5,0,w,h)painter.setPen(QPen(current_color,3,Qt.PenStyle.SolidLine))painter.drawText(text_rect,Qt.AlignmentFlag.AlignLeft|Qt.AlignmentFlag.AlignVCenter,self.text())# 记录文字起始X坐标self.text_x=text_rect.x()# 初始化下划线宽度ifself._bottom_line_width==0:self._bottom_line_width=self.text_x# 绘制底部变色下划线line_start=QPoint(self.text_x,h-2)line_end=QPoint(self._bottom_line_width,h-2)painter.drawLine(line_start,line_end)2.2、main.py
importsysfromPyQt6.QtWidgetsimport(QApplication,QMainWindow,QWidget,QVBoxLayout,QHBoxLayout,QLabel,QSpacerItem,QSizePolicy)fromPyQt6.QtGuiimportQColor,QFontfromPyQt6.QtCoreimportQtfromcolorful_radio_buttonimportColorfulRadioButtonif__name__=="__main__":app=QApplication(sys.argv)# 主窗口黑色背景window=QMainWindow()window.setStyleSheet("background-color: #000000;")window.resize(1100,750)central_widget=QWidget()window.setCentralWidget(central_widget)main_layout=QVBoxLayout(central_widget)main_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)main_layout.setSpacing(35)# 顶部标题title_label=QLabel("请选择英雄:")title_font=QFont()title_font.setPointSize(48)title_label.setFont(title_font)title_label.setStyleSheet("color: white;")title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)main_layout.addWidget(title_label)main_layout.addSpacerItem(QSpacerItem(20,50,QSizePolicy.Policy.Minimum,QSizePolicy.Policy.Fixed))# 单选按钮外层容器radio_container=QWidget()h_layout=QHBoxLayout(radio_container)h_layout.setContentsMargins(0,0,0,0)h_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)# 垂直布局存放单选框,统一左对齐v_radio=QVBoxLayout()v_radio.setSpacing(24)v_radio.setAlignment(Qt.AlignmentFlag.AlignLeft)# 加宽容器,避免文字挤压截断radio_container.setMaximumWidth(700)font_hero=QFont()font_hero.setPointSize(24)# 封装创建按钮函数,统一设置最小宽高,保证文字完整显示defcreate_radio(text,color_check,color_uncheck,checked=False):radio=ColorfulRadioButton(text)radio.setColor(color_check,color_uncheck)radio.setFont(font_hero)# 关键:设置最小尺寸,防止文字被截断radio.setMinimumWidth(320)radio.setMinimumHeight(45)radio.setChecked(checked)returnradio# 1. 黑暗之女 安妮radio_annie=create_radio("黑暗之女 安妮",QColor("#0BFFB2"),QColor("#0C976B"))v_radio.addWidget(radio_annie)# 2. 远古恐惧 费德提克radio_fiddles=create_radio("远古恐惧 费德提克",QColor("#FFEE33"),QColor("#A89A00"))v_radio.addWidget(radio_fiddles)# 3. 符文法师 瑞兹(默认选中)radio_ryze=create_radio("符文法师 瑞兹",QColor("#FF22DD"),QColor("#A00088"),checked=True)v_radio.addWidget(radio_ryze)# 4. 复仇焰魂 布兰德radio_brand=create_radio("复仇焰魂 布兰德",QColor("#33AAFF"),QColor("#0055AA"))v_radio.addWidget(radio_brand)h_layout.addLayout(v_radio)main_layout.addWidget(radio_container)window.show()sys.exit(app.exec())2.3、库安装
二、QRadioButton详解
1、QRadioButton 概述
QRadioButton 是 PyQt6 中用于创建单选按钮的控件,它允许用户从一组互斥的选项中选择一个。与 QCheckBox(复选框)不同,单选按钮通常成组出现,同一时间只能有一个被选中。
主要特性:
- 互斥性:同一父容器中的单选按钮默认互斥
- 状态管理:选中/未选中两种状态
- 信号机制:状态变化时发射信号
- 可定制性:支持样式表、自定义绘制等
2、 基本用法
2.1 、创建单选按钮
fromPyQt6.QtWidgetsimportQApplication,QWidget,QVBoxLayout,QRadioButton# 创建单选按钮radio1=QRadioButton("选项一")radio2=QRadioButton("选项二")radio3=QRadioButton("选项三")# 设置默认选中radio1.setChecked(True)2.2、 布局管理
layout=QVBoxLayout()layout.addWidget(radio1)layout.addWidget(radio2)layout.addWidget(radio3)window=QWidget()window.setLayout(layout)window.show()3、 核心方法详解
3.1 、状态控制方法
| 方法 | 说明 | 示例 |
|---|---|---|
setChecked(bool) | 设置选中状态 | radio.setChecked(True) |
isChecked() -> bool | 获取选中状态 | if radio.isChecked(): |
toggle() | 切换选中状态 | radio.toggle() |
setAutoExclusive(bool) | 设置自动互斥 | radio.setAutoExclusive(False) |
3.2 、文本和图标
# 设置文本radio.setText("新的选项文本")# 设置图标fromPyQt6.QtGuiimportQIcon radio.setIcon(QIcon("icon.png"))# 设置图标大小radio.setIconSize(QSize(32,32))3.3、 信号与槽
# 状态变化信号radio.toggled.connect(self.on_radio_toggled)defon_radio_toggled(self,checked):ifchecked:print(f"单选按钮被选中:{self.sender().text()}")4、分组管理
4.1、 自动分组(默认)
同一父容器中的单选按钮自动形成互斥组。
4.2、 使用 QButtonGroup 手动分组
fromPyQt6.QtWidgetsimportQButtonGroup# 创建按钮组button_group=QButtonGroup()# 添加单选按钮到组button_group.addButton(radio1,1)# id=1button_group.addButton(radio2,2)# id=2button_group.addButton(radio3,3)# id=3# 获取选中按钮的IDselected_id=button_group.checkedId()# 信号:选中按钮变化button_group.buttonClicked.connect(self.on_button_clicked)5、样式定制
5.1、 使用 QSS(Qt样式表)
# 基本样式radio.setStyleSheet(""" QRadioButton { color: white; font-size: 16px; spacing: 8px; } QRadioButton::indicator { width: 20px; height: 20px; } QRadioButton::indicator:unchecked { border: 2px solid #999; border-radius: 10px; background-color: white; } QRadioButton::indicator:checked { border: 2px solid #0078D7; border-radius: 10px; background-color: #0078D7; } """)5.2 、自定义绘制(如示例中的 ColorfulRadioButton)
继承 QRadioButton 并重写paintEvent方法可以实现完全自定义的外观:
classCustomRadioButton(QRadioButton):defpaintEvent(self,event):painter=QPainter(self)# 自定义绘制逻辑# ...6、实战技巧
6.1、 动态创建单选按钮
defcreate_radio_buttons(self,options):"""根据选项列表动态创建单选按钮"""fori,optioninenumerate(options):radio=QRadioButton(option)radio.setObjectName(f"radio_{i}")self.layout().addWidget(radio)6.2、 获取选中值
defget_selected_option(self):"""获取当前选中的单选按钮文本"""forradioinself.findChildren(QRadioButton):ifradio.isChecked():returnradio.text()returnNone6.3 、启用/禁用控制
# 禁用单个按钮radio.setEnabled(False)# 禁用整个组forradioinbutton_group.buttons():radio.setEnabled(False)7、常见问题与解决方案
问题1:单选按钮不互斥
原因:可能设置了setAutoExclusive(False)或按钮不在同一父容器中
解决:确保按钮在同一布局中,或使用 QButtonGroup 显式分组
问题2:文字显示不完整
原因:控件尺寸不足
解决:设置最小尺寸或使用setSizePolicy
radio.setMinimumWidth(200)radio.setSizePolicy(QSizePolicy.Policy.Expanding,QSizePolicy.Policy.Fixed)问题3:自定义样式不生效
原因:样式表优先级或选择器错误
解决:检查样式表语法,使用!important提高优先级
8、 与相关控件的对比
| 控件 | 用途 | 选择模式 | 典型场景 |
|---|---|---|---|
| QRadioButton | 单选 | 互斥(多选一) | 性别选择、评分等级 |
| QCheckBox | 多选 | 独立(可多选) | 兴趣爱好、功能开关 |
| QComboBox | 下拉选择 | 单选(下拉列表) | 国家选择、分类筛选 |
| QListWidget | 列表选择 | 单选/多选 | 文件列表、项目选择 |
9、性能优化建议
- 批量操作:大量单选按钮时使用
setUpdatesEnabled(False)暂停重绘 - 内存管理:动态创建的按钮及时删除
- 信号连接:避免重复连接,使用
disconnect()断开旧连接 - 样式缓存:相同样式的按钮共享样式表字符串
10、扩展应用
10.1 、与数据模型绑定
# 将单选按钮与数据模型关联classRadioButtonModel:def__init__(self):self.options=["选项A","选项B","选项C"]self.selected_index=0defcreate_radio_buttons(self,parent):buttons=[]fori,optioninenumerate(self.options):radio=QRadioButton(option,parent)radio.setChecked(i==self.selected_index)radio.toggled.connect(lambdachecked,idx=i:self.on_selected(idx)ifcheckedelseNone)buttons.append(radio)returnbuttonsdefon_selected(self,index):self.selected_index=indexprint(f"选中了:{self.options[index]}")10.2、 响应式单选按钮组
创建根据用户选择动态显示/隐藏相关控件的交互界面。
11、总结
QRadioButton 是 PyQt6 中构建单选界面的核心控件。通过掌握其基本用法、分组管理、样式定制和信号机制,可以创建出功能完善、外观美观的单选界面。本文示例中的 ColorfulRadioButton 展示了如何通过自定义绘制实现独特的视觉效果,这为高级 UI 设计提供了思路。
编程学习
技术分享
实战经验