找回密码
 立即注册
首页 业界区 业界 使用PySide6/PyQt6实现全国省市区的级联选择组件 ...

使用PySide6/PyQt6实现全国省市区的级联选择组件

豌畔丛 2025-10-1 19:09:32
在基于BS或者H5实现全国省市区的级联选择组件,相对比较容易,一般都要有现成的封装,如对于移动端H5或者小程序的Vant4界面库,他们直接安装使用内置的数据即可进行调用。参考对应组件的数据,我们可以使用PySide6/PyQt6实现全国省市区的级联选择组件案例。
1、Vant4界面库使用的省市区组件数据

如参考Vant4的Area省市区选择组件:https://vant-ui.github.io/vant/#/zh-CN/area
1.png

 Vant 提供了一份中国省市区数据,你可以安装 @vant/area-data npm 包来引入即可使用,跟踪其使用的代码,可以看到对应的全国省市区的数据集合。
2.png

 
2、使用PySide6/PyQt6实现全国省市区的级联选择组件案例

参考对应组件的数据,我们可以使用PySide6/PyQt6实现全国省市区的级联选择组件案例,首先我们根据上面的数据集合,定义一个JSON文件,用于Python组件进行加载的数据源。
我们来构建一个全国86编码下的省份集合,如下所示。
3.png

对应省份下的城市,城市下的分区,分区下的乡镇,都可以如此遍历下去。
4.png

 这样就构建一个多级遍历的集合,可以无穷层级下去(如果必要的话)。
我们要加载这个JSON文件,在Python中很简单,使用json.load即可。
  1.     region_data = {
  2.           "86": {
  3.             "110000": "北京市",
  4.             "120000": "天津市",
  5.         },
  6.         "110000": {
  7.             "110100": "市辖区"
  8.         },
  9.     }
  10.     # 加载数据
  11.     region_file = "china_area_data.json"
  12.     data_file_path = path.join(path.dirname(__file__), region_file)
  13.    
  14.     with open(data_file_path, "r", encoding="utf-8") as f:
  15.         <strong>region_data </strong><strong>= json.load(f)</strong>
复制代码
主要的界面逻辑,就是动态生成省市区的标签和下拉组件,并结合事件进行级联的处理操作。
  1.         layout = QHBoxLayout(self)
  2.         # 动态创建多层级 ComboBox
  3.         for i in range(levels):
  4.             box_layout = QHBoxLayout()
  5.             label = QLabel(self.labels[i] + ":")
  6.             combo = QComboBox()
  7.             box_layout.addWidget(label, 0)
  8.             box_layout.addWidget(combo, 0)
  9.             layout.addLayout(box_layout)
  10.             self.combo_boxes.append(combo)
  11.             # 绑定事件
  12.             combo.currentIndexChanged.connect(
  13.                 lambda idx, level=i: self.on_selection_changed(level, idx)
  14.             )
复制代码
上面代码就是构建多级的显示名称和下拉组件,在触发选择的事件后,更新下一级控件(下拉列表)的数据集合即可。
  1.     def on_selection_changed(self, level, index):
  2.         """当某一层选择变化时,更新下级"""
  3.         if index < 0:
  4.             return
  5.         code = self.combo_boxes[level].itemData(index)
  6.         # 更新下一层
  7.         if level + 1 < self.levels:
  8.             self.populate_level(level + 1, code)
  9.         # 更新结果
  10.         self.update_result()
  11.         # 发射信号
  12.         self.<strong>selectionChanged</strong>.emit(self.get_selection())
复制代码
其中的selectionChanged的信号事件,是我们为自定义类定义的一个信号变量。
  1. class CascadeSelector(QWidget):
  2.     <strong>selectionChanged </strong>= <strong>Signal(list)</strong>   # 信号,传递选中的 (code, name) 列表
复制代码
通过它的绑定处理,我们就可以在调用代码中获得选择的集合。
  1.     region_data = {
  2.           "86": {
  3.             "110000": "北京市",
  4.             "120000": "天津市",
  5.         },
  6.         "110000": {
  7.             "110100": "市辖区"
  8.         },
  9.     }
  10.     # 加载数据
  11.     region_file = "china_area_data.json"
  12.     data_file_path = path.join(path.dirname(__file__), region_file)
  13.    
  14.     with open(data_file_path, "r", encoding="utf-8") as f:
  15.         region_data = json.load(f)
  16.     win = <strong>CascadeSelector</strong>(region_data, root_code="86", levels=3, labels=[<strong>"省", "市", "区"</strong>])
  17.     # 主界面绑定信号
  18.     <strong>win.selectionChanged.connect</strong>(lambda sel: print("主界面收到:", sel))
  19.     win.resize(400, 200)
  20.     win.show()
复制代码
运行界面效果,如下所示。
5.png

通过初始化自定义组件 CascadeSelector ,我们就可以实现类似省市区等多层级的数据级联选择的处理效果。
CascadeSelector组件的完整代码如下所示。
  1. import sysimport jsonfrom PySide6.QtWidgets import (    QApplication, QWidget, QVBoxLayout, QComboBox, QLabel, QHBoxLayout)from PySide6.QtCore import Signalfrom os import pathfrom pathlib import Pathclass CascadeSelector(QWidget):    selectionChanged = Signal(list)   # 信号,传递选中的 (code, name) 列表    def __init__(self, data, root_code="86", levels=3, labels=None, parent=None):        """        :param data: 行政区划映射数据 (dict)        :param root_code: 起始父节点 (默认 "86" -> 全国)        :param levels: 层级数量 (如 3 = 省、市、区)        :param labels: 每层的提示文字 (如 ["省", "市", "区"])        """        super().__init__(parent)        self.data = data        self.root_code = root_code        self.levels = levels        self.labels = labels or [f"Level{i+1}" for i in range(levels)]        self.combo_boxes :list[QComboBox] = []        layout = QHBoxLayout(self)
  2.         # 动态创建多层级 ComboBox
  3.         for i in range(levels):
  4.             box_layout = QHBoxLayout()
  5.             label = QLabel(self.labels[i] + ":")
  6.             combo = QComboBox()
  7.             box_layout.addWidget(label, 0)
  8.             box_layout.addWidget(combo, 0)
  9.             layout.addLayout(box_layout)
  10.             self.combo_boxes.append(combo)
  11.             # 绑定事件
  12.             combo.currentIndexChanged.connect(
  13.                 lambda idx, level=i: self.on_selection_changed(level, idx)
  14.             )        layout.addStretch(1)        # 结果显示        # self.result_label = QLabel("选择结果:")        # layout.addWidget(self.result_label)        # 初始化第一级        self.populate_level(0, self.root_code)    def populate_level(self, level, parent_code):        """填充某一层级的 ComboBox"""        if level >= self.levels:            return        combo : QComboBox = self.combo_boxes[level]        combo.blockSignals(True)        combo.clear()        children = self.data.get(parent_code, {})        for code, name in children.items():            combo.addItem(name, code)        combo.blockSignals(False)        # 自动联动下一层        if combo.count() > 0:            self.on_selection_changed(level, 0)    def on_selection_changed(self, level, index):        """当某一层选择变化时,更新下级"""        if index < 0:            return        code = self.combo_boxes[level].itemData(index)        # 更新下一层        if level + 1 < self.levels:            self.populate_level(level + 1, code)        # 更新结果        self.update_result()        # 发射信号        self.selectionChanged.emit(self.get_selection())    def update_result(self):        names = [cb.currentText() for cb in self.combo_boxes if cb.currentIndex() >= 0]        # self.result_label.setText("选择结果:" + " - ".join(names))    def get_selection(self):        """获取完整的选择结果 (code 和 name)"""        result = []        for cb in self.combo_boxes:            cb:QComboBox            idx = cb.currentIndex()            if idx >= 0:                result.append((cb.itemData(idx), cb.currentText()))        return result# -------------------- 测试 --------------------if __name__ == "__main__":    app = QApplication(sys.argv)    # 模拟数据(和你给的格式一致)    region_data = {          "86": {            "110000": "北京市",            "120000": "天津市",        },        "110000": {            "110100": "市辖区"        },        "110100": {            "110101": "东城区",            "110102": "西城区",            "110105": "朝阳区",            "110106": "丰台区",            "110107": "石景山区",            "110108": "海淀区",            "110109": "门头沟区",            "110111": "房山区",            "110112": "通州区",            "110113": "顺义区",            "110114": "昌平区",            "110115": "大兴区",            "110116": "怀柔区",            "110117": "平谷区",            "110118": "密云区",            "110119": "延庆区"        },        "120000": {            "120100": "市辖区"        },        "120100": {            "120101": "和平区",            "120102": "河东区",            "120103": "河西区",            "120104": "南开区",            "120105": "河北区",            "120106": "红桥区",            "120110": "东丽区",            "120111": "西青区",            "120112": "津南区",            "120113": "北辰区",            "120114": "武清区",            "120115": "宝坻区",            "120116": "滨海新区",            "120117": "宁河区",            "120118": "静海区",            "120119": "蓟州区"        },    }    # 加载数据    region_file = "china_area_data.json"    data_file_path = path.join(path.dirname(__file__), region_file)        with open(data_file_path, "r", encoding="utf-8") as f:        region_data = json.load(f)    win = CascadeSelector(region_data, root_code="86", levels=3, labels=["省", "市", "区"])    # 主界面绑定信号    win.selectionChanged.connect(lambda sel: print("主界面收到:", sel))    win.resize(400, 200)    win.show()    sys.exit(app.exec())
复制代码
通过这样,我们把自定义组件作为一个界面元素,可以放在任何需要的地方呈现,实现了数据的封装,并获得事件的信号处理即可。
6.png

 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册