当前位置:首页 > 公众号精选 > 码农爱学习

本篇使用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] = radio

        model_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)

        # 创建所有功能点的checkbox
        for 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_button
        for 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] = radio

        model_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)

        # 创建所有功能点的checkbox
        for 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_button
        for 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来实现了一种按钮切换关联变化的勾选框的逻辑,首先介绍了功能,然后逐步分析代码的实现逻辑,最后给出运行效果。

文章推荐

几款代码比较工具面向对象的基本概念什么是软件能力成熟度模型嵌入式基础知识-IP地址与子网划分简易Qt图片查看器

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭