1 主要目的
在Excel中,经常会遇到需要制作多级联动下拉菜单的情况,要求单元格内填写的内容只能从指定的多个选项中进行选择,并且需要设置多级目录,其中下级目录的选项内容要根据上级目录的填写内容确定,如下图所示:
上图中的数据区域均要求通过下拉菜单实现。其中A列要通过下拉菜单选择省份信息,B列要根据相应的省份信息,通过下拉菜单选择相应的地市信息。
之前的博客已经讲过多级联动下拉菜单在Excel中的设置步骤。但考虑到数据量较大的情况,手动在Excel文件中进行设置的工作量较大,本文通过Python中的openpyxl模块实现多级联动下拉菜单的自动化设置。
2 基本原理
2.1 Excel中多级联动下拉菜单设置的主要步骤
Excel中的下拉菜单主要通过名称管理器以及数据验证界面进行设置。
名称管理器的界面如下图所示。其中“名称”栏记录各选项集的名称,“数值”栏记录各选项集中的所有选项,“引用位置”栏记录选项集中各选项在Excel文件中所在的单元格。
数据验证界面如下图所示。其中“来源”栏可通过indirect函数与名称管理器中的选项集名称进行关联,以此来设置该单元格的选项范围为该选项集中的选项;也可通过indirect函数关联上一级目录对应的单位格位置,以设置本单元格的选项应根据上一级单元格的内容来确定,实现多级联动下拉菜单设置。
2.2 使用openpyxl完成自动化设置的主要思路
由于Excel中的名称管理器以及数据验证相关设置均可由openpyxl模块完成,可以通过该模块对相关数据进行处理,并完成对Excel文件的相关设置,以实现多级联动下拉菜单设置的自动化。主要包括三个关键步骤:
(1)将各选项集的信息写入Excel文件。通过openpyxl模块的相关操作,将各选项集信息写入Excel文件中指定的页面。
(2)将各选项集的信息加载至名称管理器。由于openpyxl.workbook.defined_name模块中的DefinedName函数可以进行Excel名称管理器相关设置,可以将各选项集信息加载至名称管理器。
(3)进行数据验证相关内容设置。由于openpyxl.worksheet.datavalidation模块中的DataValidation函数可以对数据验证相关内容进行设置,可以通过该方法完成下拉菜单的制作。
3 选项数据准备
由于Excel中的多级联动下拉菜单选项通常需要通过名称管理器进行关联,为了便于将数据选项写入Excel中的名称管理器,在Python中建立有序字典(OrderedDict),以保存下拉菜单的选项信息。
字典中的每条数据作为一个选项集合,键(key)为选项集名称,值(value)为选项集中的选项。
代码如下:
# data_options.py
from collections import OrderedDict
dic = OrderedDict()
dic["省份"] = ["河北省","河南省","山西省"]
dic["河北省"] = ["石家庄市","保定市","张家口市"]
dic["河南省"] = ["郑州市","开封市","洛阳市"]
dic["山西省"] = ["太原市","运城市","大同市"]
if __name__ == "__main__":
for key, value in dic.items():
print(f"key = {key}, value = {value}")
如果选项集较多、数据量较大,可以使用Pandas模块进行读取和处理,并转换为上述OrderedDict格式。具体过程不再赘述。
4 主要步骤
4.1 将选项数据写入Excel文件
为了制作Excel中的下拉菜单,需要获取每个下拉菜单的选项信息,即选项信息需要保存在Excel中。这里使用openpyxl模块将选项信息写入Excel文件。代码如下:
# write_options.py
from data_options import dic
from openpyxl import Workbook
from openpyxl.utils import get_column_letter
path = r"D:\temp\table01.xlsx" # 文件输出路径
sheet_name_dic = "dic" # 保存在Excel中页面的名称
book = Workbook() # 新建Workbook
for sheet in book.sheetnames:
del book[sheet] # 删除其他页面
sheet_dic = book.create_sheet(sheet_name_dic) # 新建sheet
for num, (key, value) in enumerate(dic.items(), start=1):
sheet_dic[f"A{num}"] = key # 第1列的值为key值(选项集名称)
for i in range(len(value)):
sheet_dic[f"{get_column_letter(i+2)}{num}"] = value[i]
# (上一行)依次将该选项集中的选项填充在该行后续列的单元格中
book.save(path) # 保存Excel文件
运行上述代码后,打开保存的Excel文件(table01.xlsx)中的dic页面如下:
其中A列为各选项集的名称,B列及后续列为各选项集中的选项名称。
4.2 Excel中名称管理器设置
在将选项信息写入Excel文件后,需要通过名称管理器将选项信息与下拉菜单进行关联,因此需要将各选项集的选项信息写入Excel的名称管理器中。
该步骤可以结合上一步(将选项数据写入Excel文件)进行,在遍历每个选项集的过程中,将该选项集的信息写入Excel文件相应位置后,同步保存在Excel的名称管理器中。
因此可以改写上一步的代码(write_options.py文件),加入保存名称管理器相关步骤。主要添加以下代码:
attr_text = f"{sheet_name_dic}!$B${num}:${get_column_letter(len(value)+1)}${num}"
# (上一行)设置名称管理器中每个选项集的取值区域(如:attr_text = "dic!$B$1:$D$1)
value_range = DefinedName(key, attr_text=attr_text) # 将选项集的名称和选项映射为Excel名称管理器中的格式
book.defined_names.append(value_range) # 加入Excel中的名称管理器
上述代码中,第1行设置了每个选项集的选项取值区域,第3行将选项集名称与选项信息转换为Excel名称管理器相关格式,第4行将该选项集信息加入Excel的名称管理器。
改写后的代码如下:
# write_options.py
from data_options import dic
from openpyxl import Workbook
from openpyxl.utils import get_column_letter
from openpyxl.workbook.defined_name import DefinedName
path = r"D:\temp\table01.xlsx" # 文件输出路径
dic_name = "dic" # 保存在Excel中页面的名称
book = Workbook() # 新建Workbook
for sheet in book.sheetnames:
del book[sheet] # 删除其他页面
sheet_dic = book.create_sheet(dic_name) # 新建sheet
for num, (key, value) in enumerate(dic.items(), start=1):
sheet_dic[f"A{num}"] = key # 第1列的值为key值(选项集名称)
for i in range(len(value)):
sheet_dic[f"{get_column_letter(i+2)}{num}"] = value[i]
# (上一行)依次将该选项集中的选项填充在该行后续列的单元格中
attr_text = f"{dic_name}!$B${num}:${get_column_letter(len(value)+1)}${num}"
# (上一行)设置名称管理器中每个选项集的取值区域(如:attr_text = "dic!$B$1:$D$1)
value_range = DefinedName(key, attr_text=attr_text) # 将选项集的名称和选项映射为Excel名称管理器中的格式
book.defined_names.append(value_range) # 加入Excel中的名称管理器
sheet_dic.sheet_state = "hidden" # 隐藏该页面
book.save(path) # 保存Excel文件
运行上述代码后,打开保存的Excel文件(table01.xlsx)中的名称管理器如下:
可知各选项集的选项信息以保存在Excel文件的名称管理器中。
4.3 Excel中数据验证设置
为了设置Excel中的下拉菜单,通常需要手动在Excel中的数据验证界面完成相应的设置,如下图所示:
为了完成数据验证的自动化设置,需要使用openpyxl模块,具体代码如下:
# set_validation.py
from openpyxl import load_workbook
from openpyxl.worksheet.datavalidation import DataValidation
"""参数设置"""
path = r"D:\temp\table01.xlsx" # 文件路径
table_name = "table" # 数据表页面名称
row_num = 3 # 数据区域的行数(计划在数据表中录入多少条数据)
row_start = 1 # 数据区域从第几行开始(如果第1行需要设置为标题行,则数据区域的起始行数应往后放)
name1 = "省份" # Excel名称管理器中,一级目录选项关联的选项集名称
"""文件读取"""
book = load_workbook(path) # 读取Excel文件
sheet = book.create_sheet(table_name) # 创建数据表页面
"""一级目录的数据验证设置"""
validation1 = DataValidation(type="list", allow_blank=True, showDropDown=False) # 数据验证选项
validation1.formula1 = f'=indirect("{name1}")' # 数据验证表达式
validation1.sqref = f"$A{row_start}:$A{row_start+row_num-1}" # 数据验证规则的应用区域
sheet.add_data_validation(validation1) # 添加改规则
"""二级目录的数据验证设置"""
validation2 = DataValidation(type="list", allow_blank=True, showDropDown=False) # 数据验证选项
validation2.formula1 = f'=indirect($A{row_start}:$A{row_start+row_num-1})' # 数据验证表达式
validation2.sqref = f"$B{row_start}:$B{row_start+row_num-1}" # 数据验证规则的应用区域
sheet.add_data_validation(validation2) # 添加改规则
"""保存文件"""
book.save(path)
其中DataValidation函数对应着一个Excel数据验证界面设置;formula1对应着Excel数据验证界面的“来源”一栏;sqref对应着该数据验证规则的应用区域。
运行程序后,重新打开该Excel文件,发现已经成功新建了“table”页面。打开该页面,发现指定的区域已经成功设置了下拉菜单,如下图所示:
分别打开A列(省份)、B列(地市)单元格的数据验证界面,发现其设置符合预期要求。
5 小结
本文通过Python中的openpyxl模块分别完成“选项数据写入”“名称管理器设置”“数据验证设置”等步骤,实现了Excel多级联动下拉菜单的自动化设置,在录入数据量较大的情况下能够减少人工录入的工作。
本文通过data_options.py、write_options.py以及set_validation.py共3个Python脚本文件,分别实现了选项数据准备、选项数据写入及Excel名称管理器设置、数据验证设置功能。在实际操作时,可以将上述步骤合并为一个Python文件运行,以避免不必要的文件读写操作。合并后的总代码如下:
from collections import OrderedDict
from openpyxl import Workbook, load_workbook
from openpyxl.utils import get_column_letter
from openpyxl.workbook.defined_name import DefinedName
from openpyxl.worksheet.datavalidation import DataValidation
"""参数设置"""
path = r"D:\temp\table03.xlsx" # 输出文件路径
dic_name = "dic" # Excel中保存选项的页面名称
table_name = "table" # Excel中数据表页面名称
row_num = 3 # 数据表中数据区域的行数(计划在数据表中录入多少条数据)
row_start = 1 # 数据表中数据区域从第几行开始(如果第1行需要设置为标题行,则数据区域的起始行数应往后放)
name1 = "省份" # Excel名称管理器中,一级目录选项关联的选项集名称
"""选项数据整理"""
dic = OrderedDict()
dic[name1] = ["河北省","河南省","山西省"]
dic["河北省"] = ["石家庄市","保定市","张家口市"]
dic["河南省"] = ["郑州市","开封市","洛阳市"]
dic["山西省"] = ["太原市","运城市","大同市"]
"""选项数据写入以及Excel名称管理器设置"""
book = Workbook() # 新建Workbook
for sheet in book.sheetnames:
del book[sheet] # 删除其他页面
sheet_dic = book.create_sheet(dic_name) # 新建页面,用于存放选项数据
for num, (key, value) in enumerate(dic.items(), start=1):
sheet_dic[f"A{num}"] = key # 第1列的值为key值(选项集名称)
for i in range(len(value)):
sheet_dic[f"{get_column_letter(i+2)}{num}"] = value[i]
# (上一行)依次将该选项集中的选项填充在该行后续列的单元格中
attr_text = f"{dic_name}!$B${num}:${get_column_letter(len(value)+1)}${num}"
# (上一行)设置名称管理器中每个选项集的取值区域(如:attr_text = "dic!$B$1:$D$1)
value_range = DefinedName(key, attr_text=attr_text) # 将选项集的名称和选项映射为Excel名称管理器中的格式
book.defined_names.append(value_range) # 加入Excel中的名称管理器
sheet_dic.sheet_state = "hidden" # 隐藏该页面
"""数据验证设置"""
sheet_table = book.create_sheet(table_name) # 创建数据表页面
# 一级目录
validation1 = DataValidation(type="list", allow_blank=True, showDropDown=False) # 数据验证选项
validation1.formula1 = f'=indirect("{name1}")' # 数据验证表达式
validation1.sqref = f"$A{row_start}:$A{row_start+row_num-1}" # 数据验证规则的应用区域
sheet_table.add_data_validation(validation1) # 添加改规则
# 二级目录
validation2 = DataValidation(type="list", allow_blank=True, showDropDown=False) # 数据验证选项
validation2.formula1 = f'=indirect($A{row_start}:$A{row_start+row_num-1})' # 数据验证表达式
validation2.sqref = f"$B{row_start}:$B{row_start+row_num-1}" # 数据验证规则的应用区域
sheet_table.add_data_validation(validation2) # 添加改规则
"""保存文件"""
book.save(path)