PyQt 重写QRadioButton实现动态radioButton源码分享一、源码分享1、效果展示2、源码分享2.1、colorful_radio_button.py2.2、main.py2.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、 使用 QSSQt样式表5.2 、自定义绘制如示例中的 ColorfulRadioButton6、实战技巧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.pyfromPyQt6.QtWidgetsimportQRadioButton,QWidgetfromPyQt6.QtCoreimportQPropertyAnimation,pyqtProperty,pyqtSignal,Qt,QRect,QPointfromPyQt6.QtGuiimportQPainter,QColor,QPenclassColorfulRadioButton(QRadioButton):# 信号bottomLineWidth 变更通知bottomLineWidthChangedpyqtSignal()def__init__(self,parent:QWidgetNone):super().__init__(parent)# 成员变量初始化self._bottom_line_width0self.color_checkedQColor(#0BFFB2)self.color_uncheckedQColor(#0C976B)self.left_padding5self.text_x0# 创建属性动画绑定 bottomLineWidthself.anim_bottom_line_widthQPropertyAnimation(self,bbottomLineWidth)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,notifybottomLineWidthChanged)defbottomLineWidth(self)-int:returnself._bottom_line_widthbottomLineWidth.setterdefbottomLineWidth(self,val:int):ifself._bottom_line_widthval:returnself._bottom_line_widthval self.bottomLineWidthChanged.emit()# -------------------------- 外部接口 --------------------------defsetColor(self,checked_color:QColor,unchecked_color:QColor):设置选中/未选中颜色self.color_checkedchecked_color self.color_uncheckedunchecked_color# -------------------------- 槽函数 --------------------------def_on_toggled(self,checked:bool):单选框选中状态切换启动下划线动画text_xself.text_x total_widthself.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):painterQPainter(self)painter.setRenderHint(QPainter.RenderHint.Antialiasing)wself.width()hself.height()is_checkedself.isChecked()current_colorself.color_checkedifis_checkedelseself.color_unchecked# 外圆圈矩形border_rectQRect(5,5,h-10,h-10)# 绘制外圈边框pen_width4ifis_checkedelse2penQPen(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_wborder_rect.width()/2.0inner_rectQRect()inner_rect.setX(int(border_rect.center().x()-round_w/21))inner_rect.setY(int(border_rect.center().y()-round_w/21))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_rectQRect(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_xtext_rect.x()# 初始化下划线宽度ifself._bottom_line_width0:self._bottom_line_widthself.text_x# 绘制底部变色下划线line_startQPoint(self.text_x,h-2)line_endQPoint(self._bottom_line_width,h-2)painter.drawLine(line_start,line_end)2.2、main.pyimportsysfromPyQt6.QtWidgetsimport(QApplication,QMainWindow,QWidget,QVBoxLayout,QHBoxLayout,QLabel,QSpacerItem,QSizePolicy)fromPyQt6.QtGuiimportQColor,QFontfromPyQt6.QtCoreimportQtfromcolorful_radio_buttonimportColorfulRadioButtonif__name____main__:appQApplication(sys.argv)# 主窗口黑色背景windowQMainWindow()window.setStyleSheet(background-color: #000000;)window.resize(1100,750)central_widgetQWidget()window.setCentralWidget(central_widget)main_layoutQVBoxLayout(central_widget)main_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)main_layout.setSpacing(35)# 顶部标题title_labelQLabel(请选择英雄)title_fontQFont()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_containerQWidget()h_layoutQHBoxLayout(radio_container)h_layout.setContentsMargins(0,0,0,0)h_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)# 垂直布局存放单选框统一左对齐v_radioQVBoxLayout()v_radio.setSpacing(24)v_radio.setAlignment(Qt.AlignmentFlag.AlignLeft)# 加宽容器避免文字挤压截断radio_container.setMaximumWidth(700)font_heroQFont()font_hero.setPointSize(24)# 封装创建按钮函数统一设置最小宽高保证文字完整显示defcreate_radio(text,color_check,color_uncheck,checkedFalse):radioColorfulRadioButton(text)radio.setColor(color_check,color_uncheck)radio.setFont(font_hero)# 关键设置最小尺寸防止文字被截断radio.setMinimumWidth(320)radio.setMinimumHeight(45)radio.setChecked(checked)returnradio# 1. 黑暗之女 安妮radio_anniecreate_radio(黑暗之女 安妮,QColor(#0BFFB2),QColor(#0C976B))v_radio.addWidget(radio_annie)# 2. 远古恐惧 费德提克radio_fiddlescreate_radio(远古恐惧 费德提克,QColor(#FFEE33),QColor(#A89A00))v_radio.addWidget(radio_fiddles)# 3. 符文法师 瑞兹默认选中radio_ryzecreate_radio(符文法师 瑞兹,QColor(#FF22DD),QColor(#A00088),checkedTrue)v_radio.addWidget(radio_ryze)# 4. 复仇焰魂 布兰德radio_brandcreate_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# 创建单选按钮radio1QRadioButton(选项一)radio2QRadioButton(选项二)radio3QRadioButton(选项三)# 设置默认选中radio1.setChecked(True)2.2、 布局管理layoutQVBoxLayout()layout.addWidget(radio1)layout.addWidget(radio2)layout.addWidget(radio3)windowQWidget()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_groupQButtonGroup()# 添加单选按钮到组button_group.addButton(radio1,1)# id1button_group.addButton(radio2,2)# id2button_group.addButton(radio3,3)# id3# 获取选中按钮的IDselected_idbutton_group.checkedId()# 信号选中按钮变化button_group.buttonClicked.connect(self.on_button_clicked)5、样式定制5.1、 使用 QSSQt样式表# 基本样式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):painterQPainter(self)# 自定义绘制逻辑# ...6、实战技巧6.1、 动态创建单选按钮defcreate_radio_buttons(self,options):根据选项列表动态创建单选按钮fori,optioninenumerate(options):radioQRadioButton(option)radio.setObjectName(fradio_{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文字显示不完整原因控件尺寸不足解决设置最小尺寸或使用setSizePolicyradio.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_index0defcreate_radio_buttons(self,parent):buttons[]fori,optioninenumerate(self.options):radioQRadioButton(option,parent)radio.setChecked(iself.selected_index)radio.toggled.connect(lambdachecked,idxi:self.on_selected(idx)ifcheckedelseNone)buttons.append(radio)returnbuttonsdefon_selected(self,index):self.selected_indexindexprint(f选中了:{self.options[index]})10.2、 响应式单选按钮组创建根据用户选择动态显示/隐藏相关控件的交互界面。11、总结QRadioButton 是 PyQt6 中构建单选界面的核心控件。通过掌握其基本用法、分组管理、样式定制和信号机制可以创建出功能完善、外观美观的单选界面。本文示例中的 ColorfulRadioButton 展示了如何通过自定义绘制实现独特的视觉效果这为高级 UI 设计提供了思路。