pyQt实现一种按钮切换关联变化的勾选框的逻辑
本篇使用python来实现一种按钮切换关联变化的勾选框的逻辑。
1 功能说明
假设一种产品有多个型号,每个型号有各自不同的功能,想要通过一个界面,来展示不同型号的不同功能。
型号是一种单选按钮的型号,每次只能选择其中一种型号。
功能通过勾选框的方式展示,可以多选需要的功能。
2 代码实现
对于上述需要,通过pyQt来实现界面以及界面中的控件:
- 对于单选按钮,使用QRadioButton组件
- 对于勾选框,使用QCheckBox组件
- 对于将一类组件集合为一个组展示,使用QGroupBox组件
- 对于每组内组件的水平与垂直布局,使用 QHBoxLayout与 QVBoxLayout布局
组件有了之后,如何实现型号切换后,对于的功能进行对应的展示,是一个关键点,下面给出思路:
- 首先是创建出所有型号的所有功能的checkbox组件,结合其名称,存放到一个python的字典结构中
- 初始显示时(默认选择了一个型号),根据选中的型号的产品功能,将对应的checkbox组件添加到界面布局中
- 当型号切换后,则将界面布局中的原有的checkbox组件都先移除,然后再将切换后型号的产品功能对应的checkbox组件添加到界面布局中
2.1 初始化
型号的类别,以及对应的功能,使用python的字典(dictionary)数据结构来定义,如代码中的 self.model_funcs
.
字典是一种键值对(key-value)结构的数据类型,使用大括号 {}
表示,其中:
- 键(key)是唯一的,这里的键是
"型号1"
和"型号2"
- 值(value)可以是任何数据类型,这里的值是列表(list),包含了各个型号对应的功能
字典这种数据结构非常适合表示具有映射关系的数据.
列表(list)是一种有序、可变的序列数据类型,用于存储多个元素的集合。它的主要特点和常用操作如下:
- 用方括号
[]
定义,元素之间用逗号分隔 - 元素可以是不同的数据类型(整数、字符串、列表等)
- 支持索引访问(从 0 开始)和切片操作
- 长度可变,可动态添加 / 删除元素
def __init__(self):super().__init__()self.model_funcs = {"型号1" : ["功能A", "功能B", "功能C"],"型号2" : ["功能C", "功能D"]}self.model_radio_button = {}self.func_checkboxes = {}self.init_ui()
下面是具体的界面初始化代码,首先是型号的实现逻辑:
model_layout
定义一个水平布局,QRadioButton
通过model_layout.addWidget(radio)
的方式,实现按钮的水平排列model_group
定义一个组,通过model_group.setLayout(model_layout)
的方式,将水平排列的按钮添加到组中- 型号的名称,通过遍历
self.model_funcs.keys()
,创建对应的按钮:radio = QRadioButton(model_name)
- 将radio组件及名称记录到
self.model_radio_button
字典中 - 按钮的切换,通过关联槽函数
radio.clicked.connect(self.on_model_changed)
,实现按钮切换后的逻辑处理
def init_ui(self):# 型号水平布局,并集中到一个型号组中model_layout = QHBoxLayout()for i, model_name in enumerate(self.model_funcs.keys()):logger.info(f"i:{i}, model_name:{model_name}")radio = QRadioButton(model_name)# 默认第0个型号被选中if i == 0:radio.setChecked(True)# 给每个型号的radio连接槽函数radio.clicked.connect(self.on_model_changed)# 将radio组件添加到布局model_layout.addWidget(radio)# 将radio组件及名称记录到self.model_radio_button[]中self.model_radio_button[model_name] = radiomodel_group = QGroupBox("型号")model_group.setLayout(model_layout)
然后是功能点的实现逻辑:
self.funcs_layout
定义一个垂直布局,注意这里前面加了一个self,因为在该类的其它函数中需要跨函数使用funcs_group
定义了一个组,布局逻辑和上面的型号的布局类似all_funcs
定义了一个集合(set),它是一种无序、不重复的元素集合,属于可变数据类型,主要特点如下:- 用大括号
{}
定义(注意:空集合不能用{}
定义,需用set()
) - 元素必须是不可变类型(如整数、字符串、元组等),不能包含列表、字典等可变类型
- 自动去重,集合中不会有重复的元素
- 无序性,不支持索引访问,不能通过位置获取元素
- 用大括号
- 使用集合,是为了去除不同型号的功能点的重复数据,通过遍历
self.model_funcs.values()
,并通过all_funcs.update(func)
的方式,得到没有重复的功能点 - 然后遍历
all_funcs
,创建勾选框组件checkbox = QCheckBox(func)
,再将组件添加到self.func_checkboxes
字典中
# 功能点垂直布局,并集中到一个功能组中self.funcs_layout = QVBoxLayout()funcs_group = QGroupBox("功能")funcs_group.setLayout(self.funcs_layout)# 根据self.model_funcs中的定义,提取出非重复的所有功能点all_funcs = set()for func in self.model_funcs.values():all_funcs.update(func)# 创建所有功能点的checkboxfor func in sorted(all_funcs):logger.info(f"func:{func}")checkbox = QCheckBox(func)# 将checkbox组件及名称暂存到self.checkboxs[]中self.func_checkboxes[func] = checkbox
初始化显示默认型号对应的功能,默认型号是第1个,即“型号1”,其名称可通过list(self.model_funcs.keys())[0]
来获取。
功能展示的逻辑通过self.update_funcs_show
函数实现,后面介绍。
# 初始化显示默认型号对应的功能logger.info(f"list(self.model_funcs.keys())[0]:{list(self.model_funcs.keys())[0]}")self.update_funcs_show(list(self.model_funcs.keys())[0])
主布局,是垂直结构,上面是型号组,下面是功能组
# 主布局main_layout = QVBoxLayout(self)main_layout.addWidget(model_group)main_layout.addWidget(funcs_group)
2.2 按钮切换的槽函数
逻辑如下:
- 遍历
self.model_radio_button
字典,检查对应的按钮是否被按下 - 若被按下,则调用
self.update_funcs_show
函数更新对应型号的功能点的展示
# 切换型号def on_model_changed(self):# 遍历model_radio_buttonfor model_name, radio in self.model_radio_button.items():logger.debug(f"judge model_name:{model_name} ...")if radio.isChecked():logger.debug(f"model_name:{model_name} isChecked")self.update_funcs_show(model_name)break
2.3 根据型号和更新显示对应的功能
功能点的展示逻辑:
- 先清除布局中的checkbox组件,通过判断布局中组件的个数
self.funcs_layout.count()
,若非0,说明存在组件 - 然后通过
self.funcs_layout.takeAt(0)
来循环移除第一个checkbox组件 - 然后再根据型号,遍历对应的功能点
for func in self.model_funcs[model_name]
,将checkbox组件添加回布局中
# 根据型号和更新显示对应的功能def update_funcs_show(self, model_name):logger.debug(f"model_name:{model_name}")# 先清除logger.debug(f"funcs_layout.count:{self.funcs_layout.count()}")while self.funcs_layout.count():child = self.funcs_layout.takeAt(0)if child.widget():logger.debug(f"remove child:{child}")child.widget().setParent(None)# 再添加for func in self.model_funcs[model_name]:logger.debug(f"add func:{func}")# 将checkbox组件添加到布局self.funcs_layout.addWidget(self.func_checkboxes[func])
2.4 完整代码
完整的代码如下,这里使用的是PySide6(PyQt的一种替代库,使用逻辑基本是通用的),并通过loguru模块实现log调试。
import sys
from loguru import logger
from PySide6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,QRadioButton, QCheckBox, QGroupBox, QButtonGroup)class ModelSelect(QWidget):def __init__(self):super().__init__()self.model_funcs = {"型号1" : ["功能A", "功能B", "功能C"],"型号2" : ["功能C", "功能D"]}self.model_radio_button = {}self.func_checkboxes = {}self.init_ui()def init_ui(self):# 型号水平布局,并集中到一个型号组中model_layout = QHBoxLayout()for i, model_name in enumerate(self.model_funcs.keys()):logger.info(f"i:{i}, model_name:{model_name}")radio = QRadioButton(model_name)# 默认第0个型号被选中if i == 0:radio.setChecked(True)# 给每个型号的radio连接槽函数radio.clicked.connect(self.on_model_changed)# 将radio组件添加到布局model_layout.addWidget(radio)# 将radio组件及名称记录到self.model_radio_button[]中self.model_radio_button[model_name] = radiomodel_group = QGroupBox("型号")model_group.setLayout(model_layout)# 功能点垂直布局,并集中到一个功能组中self.funcs_layout = QVBoxLayout()funcs_group = QGroupBox("功能")funcs_group.setLayout(self.funcs_layout)# 根据self.model_funcs中的定义,提取出非重复的所有功能点all_funcs = set()for func in self.model_funcs.values():all_funcs.update(func)# 创建所有功能点的checkboxfor func in sorted(all_funcs):logger.info(f"func:{func}")checkbox = QCheckBox(func)# 将checkbox组件及名称暂存到self.checkboxs[]中self.func_checkboxes[func] = checkbox# 初始化显示默认型号对应的功能logger.info(f"list(self.model_funcs.keys())[0]:{list(self.model_funcs.keys())[0]}")self.update_funcs_show(list(self.model_funcs.keys())[0])# 主布局main_layout = QVBoxLayout(self)main_layout.addWidget(model_group)main_layout.addWidget(funcs_group)# 切换型号def on_model_changed(self):# 遍历model_radio_buttonfor model_name, radio in self.model_radio_button.items():logger.debug(f"judge model_name:{model_name} ...")if radio.isChecked():logger.debug(f"model_name:{model_name} isChecked")self.update_funcs_show(model_name)break# 根据型号和更新显示对应的功能def update_funcs_show(self, model_name):logger.debug(f"model_name:{model_name}")# 先清除logger.debug(f"funcs_layout.count:{self.funcs_layout.count()}")while self.funcs_layout.count():child = self.funcs_layout.takeAt(0)if child.widget():logger.debug(f"remove child:{child}")child.widget().setParent(None)# 再添加for func in self.model_funcs[model_name]:logger.debug(f"add func:{func}")# 将checkbox组件添加到布局self.funcs_layout.addWidget(self.func_checkboxes[func])if __name__ == "__main__":app = QApplication(sys.argv)window = ModelSelect()window.show()sys.exit(app.exec())
运行的打印如下,对应的界面就是文章开头的界面.
3总结
本篇使用python来实现了一种按钮切换关联变化的勾选框的逻辑,首先介绍了功能,然后逐步分析代码的实现逻辑,最后给出运行效果。