豌畔丛 发表于 2025-10-1 19:09:32

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

在基于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:
      <strong>region_data </strong><strong>= json.load(f)</strong>主要的界面逻辑,就是动态生成省市区的标签和下拉组件,并结合事件进行级联的处理操作。
      layout = QHBoxLayout(self)
      # 动态创建多层级 ComboBox
      for i in range(levels):
            box_layout = QHBoxLayout()
            label = QLabel(self.labels + ":")
            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.itemData(index)

      # 更新下一层
      if level + 1 < self.levels:
            self.populate_level(level + 1, code)

      # 更新结果
      self.update_result()

      # 发射信号
      self.<strong>selectionChanged</strong>.emit(self.get_selection())其中的selectionChanged的信号事件,是我们为自定义类定义的一个信号变量。
class CascadeSelector(QWidget):
    <strong>selectionChanged </strong>= <strong>Signal(list)</strong>   # 信号,传递选中的 (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 = <strong>CascadeSelector</strong>(region_data, root_code="86", levels=3, labels=[<strong>"省", "市", "区"</strong>])
    # 主界面绑定信号
    <strong>win.selectionChanged.connect</strong>(lambda sel: print("主界面收到:", sel))
    win.resize(400, 200)
    win.show()运行界面效果,如下所示。

通过初始化自定义组件 CascadeSelector ,我们就可以实现类似省市区等多层级的数据级联选择的处理效果。
CascadeSelector组件的完整代码如下所示。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       self.combo_boxes :list = []      layout = QHBoxLayout(self)
      # 动态创建多层级 ComboBox
      for i in range(levels):
            box_layout = QHBoxLayout()
            label = QLabel(self.labels + ":")
            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      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.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 =       # 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())通过这样,我们把自定义组件作为一个界面元素,可以放在任何需要的地方呈现,实现了数据的封装,并获得事件的信号处理即可。

 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 使用PySide6/PyQt6实现全国省市区的级联选择组件