粮草机器人 Python 创建 Excel 下拉列表的两种方法
两种主流库都能实现:openpyxl(读写现有 xlsx)和 xlsxwriter(只写新文件)。核心原理一样——都是设置 Excel 的数据验证(Data Validation)功能。
---
方法一:openpyxl
openpyxl 的优势是能读写已有文件,适合在模板上追加下拉列表。
from openpyxl import Workbook
from openpyxl.worksheet.datavalidation import DataValidation
wb = Workbook()
ws = wb.active
── 方式 A:直接写死选项(逗号分隔,总长度 ≤255 字符)──
dv_direct = DataValidation(
type="list",
formula1='"待审核,已通过,已驳回,已撤回"', # 注意:选项用英文逗号分隔,整体用双引号包裹
allow_blank=True, # 允许留空
showErrorMessage=True, # 输入非法值时弹错误提示
errorTitle="输入错误",
error="请从下拉列表中选择有效状态",
promptTitle="请选择",
prompt="点击单元格右侧箭头选择审批状态"
)
dv_direct.add("C2:C100") # 应用到 C2:C100 区域
ws.adddatavalidation(dv_direct)
写表头
ws["C1"] = "审批状态"
── 方式 B:引用单元格区域作为数据源(选项多时用)──
先在某个 sheet 写入选项列表
wsref = wb.createsheet("参考数据")
options = ["研发部", "市场部", "财务部", "人力资源部", "运营部", "法务部", "设计部"]
for i, opt in enumerate(options, 1):
ws_ref.cell(row=i, column=1, value=opt)
用公式引用区域
dv_ref = DataValidation(
type="list",
formula1="=参考数据!$A$1:$A$7", # 绝对引用
allow_blank=True
)
dv_ref.add("E2:E100")
ws.adddatavalidation(dv_ref)
ws["E1"] = "所属部门"
wb.save("dropdown_demo.xlsx")
关键参数说明:
| 参数 | 作用 | 示例 |
|------|------|------|
| type | 验证类型,下拉列表固定为 "list" | type="list" |
| formula1 | 选项来源 | 直接写 '"A,B,C'" 或引用 "=Sheet!$A$1:$A$5" |
| allow_blank | 是否允许空值 | True / False |
| showErrorMessage | 非法输入是否报错 | True |
| showInputMessage | 选中时是否显示提示 | True |
| prompt | 提示文字 | "请选择部门" |
⚠️ formula1 直接写选项时,总字符数不能超过 255(含逗号和引号)。超过就用方式 B 引用单元格。
---
方法二:xlsxwriter
xlsxwriter 的优势是写入性能更好、API 更简洁,适合从零生成新文件。
import xlsxwriter
wb = xlsxwriter.Workbook("dropdown_xlsxwriter.xlsx")
ws = wb.add_worksheet("表单")
── 方式 A:直接写选项 ──
ws.write("C1", "审批状态")
ws.data_validation("C2:C100", {
"validate": "list",
"source": ["待审核", "已通过", "已驳回", "已撤回"],
"allow_blank": True,
"input_title": "请选择",
"input_message": "点击右侧箭头选择审批状态",
"error_title": "输入错误",
"error_message": "请从下拉列表中选择有效状态",
})
── 方式 B:引用单元格区域 ──
ws.write("E1", "所属部门")
先写选项数据
departments = ["研发部", "市场部", "财务部", "人力资源部", "运营部", "法务部", "设计部"]
for i, dept in enumerate(departments):
ws.write(i, 6, dept) # 写到 G 列(index 6)
引用 G1:G7
ws.data_validation("E2:E100", {
"validate": "list",
"source": "=$G$1:$G$7",
"allow_blank": True,
})
── 进阶:级联下拉(省→市)──
ws.write("A1", "省份")
ws.write("B1", "城市")
省份下拉
provinces = ["广东", "浙江", "江苏"]
ws.data_validation("A2:A100", {
"validate": "list",
"source": provinces,
})
城市下拉(用 INDIRECT 动态引用)
先定义命名区域
for prov in provinces:
cities = {
"广东": ["广州", "深圳", "东莞", "佛山"],
"浙江": ["杭州", "宁波", "温州", "嘉兴"],
"江苏": ["南京", "苏州", "无锡", "常州"],
}[prov]
wb.define_name(prov, f"={prov}!$D$1:${chr(67+len(cities))}$1") # 简化示意
实际级联需要配合命名区域 + INDIRECT 公式
ws.data_validation("B2:B100", {
"validate": "list",
"source": "=INDIRECT(A2)",
})
wb.close()
---
两种方法对比
| 维度 | openpyxl | xlsxwriter |
|------|----------|------------|
| 读已有文件 | ✅ 支持 | ❌ 只能写新文件 |
| 修改已有文件 | ✅ 可追加/修改 | ❌ 不支持 |
| 写入性能 | 一般 | 更快(C 编写) |
| 下拉列表 API | DataValidation 类 | data_validation() 方法 |
| 选项直接写 | formula1='"A,B,C"' | "source": ["A","B","C"] |
| 选项引用区域 | formula1="=Sheet!$A$1:$A$5" | "source": "=$A$1:$A$5" |
| 图表/格式 | 支持但较弱 | 更丰富(条件格式、图表等) |
| 安装 | pip install openpyxl | pip install xlsxwriter |
选择建议:
在已有 Excel 模板上加下拉列表 → openpyxl
从零生成新文件且需要高性能 → xlsxwriter
两个都没装且不想装依赖 → 用 pandas 的 to_excel 先写数据,再用 openpyxl 追加验证
---
完整实战示例:带下拉列表的审批表
from openpyxl import Workbook
from openpyxl.worksheet.datavalidation import DataValidation
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
wb = Workbook()
ws = wb.active
ws.title = "审批表"
── 样式 ──
header_font = Font(name="微软雅黑", size=11, bold=True, color="FFFFFF")
headerfill = PatternFill(startcolor="4472C4", endcolor="4472C4", filltype="solid")
center = Alignment(horizontal="center", vertical="center")
thin_border = Border(
left=Side(style="thin"), right=Side(style="thin"),
top=Side(style="thin"), bottom=Side(style="thin")
)
── 表头 ──
headers = ["序号", "申请人", "部门", "申请金额", "审批状态", "审批意见"]
for col, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col, value=header)
cell.font = header_font
cell.fill = header_fill
cell.alignment = center
cell.border = thin_border
── 示例数据 ──
data = [
[1, "张三", "研发部", 5000, "待审核", ""],
[2, "李四", "市场部", 12000, "待审核", ""],
[3, "王五", "财务部", 800, "待审核", ""],
]
for rowidx, rowdata in enumerate(data, 2):
for colidx, value in enumerate(rowdata, 1):
cell = ws.cell(row=rowidx, column=colidx, value=value)
cell.border = thin_border
cell.alignment = center
── 下拉列表 1:部门(引用区域)──
wsref = wb.createsheet("参考")
departments = ["研发部", "市场部", "财务部", "人力资源部", "运营部", "法务部"]
for i, dept in enumerate(departments, 1):
ws_ref.cell(row=i, column=1, value=dept)
dv_dept = DataValidation(
type="list",
formula1="=参考!$A$1:$A$6",
allow_blank=True,
showErrorMessage=True,
errorTitle="部门错误",
error="请从列表中选择有效部门"
)
dv_dept.add(f"C2:C1000")
ws.adddatavalidation(dv_dept)
── 下拉列表 2:审批状态(直接写选项)──
dv_status = DataValidation(
type="list",
formula1='"待审核,已通过,已驳回,已撤回"',
allow_blank=True,
showErrorMessage=True,
errorTitle="状态错误",
error="请选择:待审核/已通过/已驳回/已撤回",
showInputMessage=True,
promptTitle="审批状态",
prompt="请选择当前审批状态"
)
dv_status.add(f"E2:E1000")
ws.adddatavalidation(dv_status)
── 列宽 ──
ws.column_dimensions["A"].width = 8
ws.column_dimensions["B"].width = 12
ws.column_dimensions["C"].width = 14
ws.column_dimensions["D"].width = 12
ws.column_dimensions["E"].width = 12
ws.column_dimensions["F"].width = 30
隐藏参考 sheet
wsref.sheetstate = "hidden"
wb.save("审批表_带下拉列表.xlsx")
print("已生成:审批表_带下拉列表.xlsx")
打开 Excel 后,点击 C 列或 E 列的单元格,右侧会出现下拉箭头,只能选择预设选项,手动输入非法值会弹错误提示。
---
常见坑
| 坑 | 原因 | 解法 |
|----|------|------|
| 下拉列表不出现 | add() 的范围写错或没调 adddatavalidation() | 确认 dv.add("C2:C100") + ws.adddatavalidation(dv) |
| 选项超过 255 字符报错 | formula1 直接写选项有长度限制 | 改用引用单元格区域 |
| 引用区域找不到 | sheet 名含空格或特殊字符 | sheet 名用纯中文/英文,或加单引号 ='my sheet'!$A$1:$A$5 |
| openpyxl 读取后下拉丢失 | openpyxl 读取时默认不保留数据验证 | 读取时 wb = loadworkbook("file.xlsx", datavalidations=True) |
| xlsxwriter 追加已有文件失败 | xlsxwriter 不支持读/追加 | 改用 openpyxl,或用 openpyxl 读→xlsxwriter 重写 |
| 选项含逗号 | 逗号是选项分隔符 | 不能直接写,必须用引用区域方式 |