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

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

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

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

这样就构建一个多级遍历的集合,可以无穷层级下去(如果必要的话)。
我们要加载这个JSON文件,在Python中很简单,使用json.load即可。
region_data = {
"86": {
"110000": "北京市",
"120000": "天津市",
},
"110000": {
"110100": "市辖区"
},
}
# 加载数据
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)
主要的界面逻辑,就是动态生成省市区的标签和下拉组件,并结合事件进行级联的处理操作。
layout = QHBoxLayout(self)
# 动态创建多层级 ComboBox
for i in range(levels):
box_layout = QHBoxLayout()
label = QLabel(self.labels[i] + ":")
combo = QComboBox()
box_layout.addWidget(label, 0)
box_layout.addWidget(combo, 0)
layout.addLayout(box_layout)
self.combo_boxes.append(combo)
# 绑定事件
combo.currentIndexChanged.connect(
lambda idx, level=i: self.on_selection_changed(level, idx)
)
上面代码就是构建多级的显示名称和下拉组件,在触发选择的事件后,更新下一级控件(下拉列表)的数据集合即可。
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())
其中的selectionChanged的信号事件,是我们为自定义类定义的一个信号变量。
class CascadeSelector(QWidget):
selectionChanged= Signal(list) # 信号,传递选中的 (code, name) 列表
通过它的绑定处理,我们就可以在调用代码中获得选择的集合。
region_data = {
"86": {
"110000": "北京市",
"120000": "天津市",
},
"110000": {
"110100": "市辖区"
},
}
# 加载数据
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()
运行界面效果,如下所示。

通过初始化自定义组件 CascadeSelector ,我们就可以实现类似省市区等多层级的数据级联选择的处理效果。
CascadeSelector组件的完整代码如下所示。
import sys
import json
from PySide6.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QComboBox, QLabel, QHBoxLayout
)
from PySide6.QtCore import Signal
from os import path
from pathlib import Path
class 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)
# 动态创建多层级 ComboBox
for i in range(levels):
box_layout = QHBoxLayout()
label = QLabel(self.labels[i] + ":")
combo = QComboBox()
box_layout.addWidget(label, 0)
box_layout.addWidget(combo, 0)
layout.addLayout(box_layout)
self.combo_boxes.append(combo)
# 绑定事件
combo.currentIndexChanged.connect(
lambda idx, level=i: self.on_selection_changed(level, idx)
)
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())
通过这样,我们把自定义组件作为一个界面元素,可以放在任何需要的地方呈现,实现了数据的封装,并获得事件的信号处理即可。
