在 PyQt5 中构建「省-市-县」级联选择器

2021-12-01 17:49:53 浏览数 (1)

一、Web 网页中的级联选择器

在各类网页中,我们经常可以看到级联选择器。

比如在购物平台填写收获地址的时候,进行省市县的选择;

又比如在一些商品分类中的商品大类、商品子类的选择:

可以说,对于一个正常的Web框架而言,级联选择器都是必不可少的组件。

二、级联选择器原理

其实普通的级联选择器其构成并不复杂。

无非是选择一级的时候,唤起二级的渲染和显示,选择二级的时候,唤起三级的渲染和显示;

至于各级是用列表还是用下拉框,都是其次的。

下面,我们就使用 PyQt5 构建一个省市县的级联选择器。

最终的效果如下所示:

三、构建一个桌面应用的级联选择器

准备数据

要实现省市县的级联选择,省市县的数据必不可少,在这里,我们使用的是一个 JSON 格式嵌套的省市县数据,如下图所示:

数据来源于:https://www.cnblogs.com/bihuijia/p/15077207.html

大家可以自行复制。

最终以data.json文件的形式提供给代码读取。

代码开发

首先,引入所需的模块:

代码语言:javascript复制
from PyQt5 import QtWidgets,QtCore
from loguru import logger
import sys
import json

接着,编写其基础界面。

通过继承QtWidgets.QWidget,定义一个Widget:

代码语言:javascript复制
class Widget(QtWidgets.QWidget):
    def __init__(self):
        super(Widget, self).__init__()
        self.setWindowTitle("PyQt5 省市县级联选择器 - 州的先生")
        self.init_data() # 初始化数据
        self.init_ui() # 初始化UI

Widget初始化的时候,我们调用了两个方法,分别用于初始化数据和初始化UI界面。

初始化数据,通过读取data.json文件进行 json 解析并赋值:

代码语言:javascript复制
    # 初始化数据
    def init_data(self):
        # 读取json数据
        with open("./data.json", 'r', encoding='utf-8') as data:
            self.data_json = json.loads(data.read(), encoding='utf-8')
            print(self.data_json)

我们的级联选择器通过三个下拉选择器QComboBox()来实现,然后三个下拉选择器用一个网格布局QGridLayout()进行水平排列。

代码语言:javascript复制
    # 初始化UI
    def init_ui(self):
        # 省选择器
        self.province = QtWidgets.QComboBox()
        self.province.addItem("--请选择省")
        self.province.currentTextChanged.connect(self.slot_province_click)
        for data in self.data_json:
            self.province.addItem(data['Name'])
        # 市选择器
        self.city = QtWidgets.QComboBox()
        self.city.addItem("--请选择市")
        self.city.currentTextChanged.connect(self.slot_city_click)
        # 县选择器
        self.county = QtWidgets.QComboBox()
        self.county.addItem("--请选择县")
        self.layout = QtWidgets.QGridLayout()
        self.setLayout(self.layout)
        self.layout.addWidget(self.province, 0, 0, 1, 1)
        self.layout.addWidget(self.city, 0, 1, 1, 1)
        self.layout.addWidget(self.county, 0, 2, 1, 1)

在这里,我们通过QComboBox()currentTextChanged信号来捕获下拉框某个选项被选择,分别连接到了两个方法:

代码语言:javascript复制
    # 省选择器点击响应
    def slot_province_click(self):
        try:
            current_province = self.province.currentText()
            if current_province.startswith('--') is False:
                for data in self.data_json:
                    if data['Name'] == current_province:
                        self.city.clear()
                        self.current_city_data = data['Cities']
                        for c in data['Cities']:
                            self.city.addItem(c['Name'])
            else:
                self.city.clear()
                self.county.clear()
        except:
            logger.exception("加载市异常")
    # 市选择器点击响应
    def slot_city_click(self):
        try:
            current_city = self.city.currentText()
            if current_city.startswith('--') is False:
                for data in self.current_city_data:
                    if data['Name'] == current_city:
                        self.county.clear()
                        for c in data['Districts']:
                            self.county.addItem(c['Name'])
        except:
            logger.error("加载县区异常")

最后,完整代码如下所示:

代码语言:javascript复制
# coding:utf-8
from PyQt5 import QtWidgets,QtCore
from loguru import logger
import sys
import json
class Widget(QtWidgets.QWidget):
    def __init__(self):
        super(Widget, self).__init__()
        self.setWindowTitle("PyQt5 省市县级联选择器 - 州的先生")
        self.init_data() # 初始化数据
        self.init_ui() # 初始化UI
    # 初始化数据
    def init_data(self):
        # 读取json数据
        with open("./data.json", 'r', encoding='utf-8') as data:
            self.data_json = json.loads(data.read(), encoding='utf-8')
            print(self.data_json)
    # 初始化UI
    def init_ui(self):
        # 省选择器
        self.province = QtWidgets.QComboBox()
        self.province.addItem("--请选择省")
        self.province.currentTextChanged.connect(self.slot_province_click)
        for data in self.data_json:
            self.province.addItem(data['Name'])
        # 市选择器
        self.city = QtWidgets.QComboBox()
        self.city.addItem("--请选择市")
        self.city.currentTextChanged.connect(self.slot_city_click)
        # 县选择器
        self.county = QtWidgets.QComboBox()
        self.county.addItem("--请选择县")
        self.layout = QtWidgets.QGridLayout()
        self.setLayout(self.layout)
        self.layout.addWidget(self.province, 0, 0, 1, 1)
        self.layout.addWidget(self.city, 0, 1, 1, 1)
        self.layout.addWidget(self.county, 0, 2, 1, 1)
    # 省选择器点击响应
    def slot_province_click(self):
        try:
            current_province = self.province.currentText()
            if current_province.startswith('--') is False:
                for data in self.data_json:
                    if data['Name'] == current_province:
                        self.city.clear()
                        self.current_city_data = data['Cities']
                        for c in data['Cities']:
                            self.city.addItem(c['Name'])
            else:
                self.city.clear()
                self.county.clear()
        except:
            logger.exception("加载市异常")
    # 市选择器点击响应
    def slot_city_click(self):
        try:
            current_city = self.city.currentText()
            if current_city.startswith('--') is False:
                for data in self.current_city_data:
                    if data['Name'] == current_city:
                        self.county.clear()
                        for c in data['Districts']:
                            self.county.addItem(c['Name'])
        except:
            logger.error("加载县区异常")
def main():
    app = QtWidgets.QApplication(sys.argv)
    gui = Widget()
    gui.show()
    sys.exit(app.exec())
if __name__ == '__main__':
    main()

四、最后

通过短短的不到100行代码,我们就在 PyQt5 上实现了一个完成的省市县级联选择器。如上述所说,级联的核心在于根据所选动态响应和渲染子级数据,至于用什么控件来实现,倒是次要的。

所以基于此,大家可以尝试使用别的控件来实现一下级联选择器,或者对这个级联选择器进行美化。

0 人点赞