Python实现Excel批量样式修改器(附完整代码)
作者:小庄-Python办公
前言
一个基于 Python tkinter 和 openpyxl 的 Excel 文件批量美化工具,支持拖拽操作和灵活的样式配置。
功能特性
核心功能
- 批量处理:支持同时处理多个 Excel 文件
- 拖拽支持:直接拖拽文件到界面即可添加
- 样式自定义:可自定义表头和数据行的字体、字号、背景色
- 居中对齐:支持表头和数据行的水平居中设置
- 背景色控制:可选择是否修改背景色
- Sheet 选择:支持处理所有 Sheet 或指定特定 Sheet
界面特性
- 直观的图形用户界面
- 实时进度显示
- 详细的操作日志
- 颜色选择器
- Sheet 列表查看功能
系统要求
Python 3.6+
Windows 操作系统
依赖库:
tkinter(Python 内置)openpyxltkinterdnd2
安装说明
克隆或下载项目
git clone <repository-url> cd 13-Excel批量样式修改器
安装依赖
pip install openpyxl tkinterdnd2
运行程序
python main.py
使用指南
基本操作流程
1.启动程序
- 双击
main.py或在命令行运行 - 程序界面将自动打开
2.参数设置
- 表头行号:设置表头所在的行号(默认第1行)
- 字体设置:选择表头和数据行的字体类型和大小
- 背景色设置:点击"选色"按钮选择背景颜色
- 居中选项:勾选是否需要水平居中
- 背景色控制:选择是否修改背景色
3.Sheet 选择
- 处理所有Sheet:默认选项,处理文件中的所有工作表
- 指定Sheet:输入特定的Sheet名称,多个用逗号分隔
- 查看Sheet:点击按钮查看文件中所有可用的Sheet列表
4.添加文件
- 拖拽添加:直接将 Excel 文件拖拽到文件列表区域
- 手动添加:点击"添加文件"按钮选择单个或多个文件
- 批量添加:点击"添加目录"按钮选择包含 Excel 文件的文件夹
5.开始处理
- 点击"开始美化"按钮
- 观察进度条和日志输出
- 等待处理完成
高级功能
Sheet 选择功能
- 查看 Sheet 列表:添加文件后,点击"查看Sheet"按钮可以查看第一个文件中的所有Sheet名称
- 指定 Sheet 处理:在"Sheet名称"输入框中输入要处理的Sheet名称
- 多 Sheet 处理:使用逗号分隔多个Sheet名称,如:
Sheet1, 数据表, Summary
样式配置技巧
- 表头样式:通常使用较大字号和深色背景,便于区分
- 数据行样式:使用较小字号和浅色背景,保持可读性
- 颜色搭配:建议使用对比度适中的颜色组合
技术实现
核心技术栈
- GUI框架:tkinter + tkinterdnd2(拖拽支持)
- Excel处理:openpyxl
- 多线程:threading(避免界面冻结)
关键函数说明
beautify_one_file()
def beautify_one_file(file_path: str,
header_row: int,
header_font, header_fill,
data_font, data_fill,
center_header: bool,
center_data: bool,
change_header_bg: bool = True,
change_data_bg: bool = True,
sheet_names: list = None):
核心美化函数,负责处理单个Excel文件的样式修改。
参数说明:
file_path:Excel文件路径header_row:表头行号header_font/data_font:字体样式对象header_fill/data_fill:背景填充对象center_header/center_data:是否居中对齐change_header_bg/change_data_bg:是否修改背景色sheet_names:指定处理的Sheet列表
get_sheet_names()
def get_sheet_names(file_path: str):
获取Excel文件中所有Sheet名称的辅助函数。
设计模式
- MVC模式:界面、逻辑、数据分离
- 观察者模式:GUI事件响应
- 工厂模式:样式对象创建
完整代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Excel 批量美化小工具
tkinter + openpyxl,支持拖拽
"""
import os
import sys
import threading
from tkinter import *
from tkinter import ttk, filedialog, messagebox
from tkinter.scrolledtext import ScrolledText
try:
from tkinterdnd2 import DND_FILES, TkinterDnD
except ImportError:
messagebox.showerror("依赖缺失", "请先 pip install tkinterdnd2")
sys.exit(1)
from openpyxl import load_workbook
from openpyxl.styles import Alignment, Font, PatternFill
# ---------- 工具函数 ----------
def get_sheet_names(file_path: str):
"""获取Excel文件中所有Sheet的名称"""
try:
wb = load_workbook(file_path, read_only=True)
sheet_names = [ws.title for ws in wb.worksheets]
wb.close()
return sheet_names
except Exception as e:
return []
def rgb_to_hex(r, g, b):
"""将 rgb 整数转 16 进制字符串"""
return f"{r:02X}{g:02X}{b:02X}"
def hex_to_rgb(hex_color: str):
"""#RRGGBB 转 (r,g,b)"""
hex_color = hex_color.lstrip("#")
return tuple(int(hex_color[i:i + 2], 16) for i in (0, 2, 4))
# ---------- 核心逻辑 ----------
def beautify_one_file(file_path: str,
header_row: int,
header_font, header_fill,
data_font, data_fill,
center_header: bool,
center_data: bool,
change_header_bg: bool = True,
change_data_bg: bool = True,
sheet_names: list = None):
"""美化单个文件"""
wb = load_workbook(file_path)
# 如果指定了sheet_names,只处理指定的Sheet;否则处理所有Sheet
if sheet_names:
worksheets = [ws for ws in wb.worksheets if ws.title in sheet_names]
else:
worksheets = wb.worksheets
for ws in worksheets:
max_col = ws.max_column
# ----- 表头 -----
for col in range(1, max_col + 1):
cell = ws.cell(row=header_row, column=col)
cell.font = header_font
if change_header_bg:
cell.fill = header_fill
if center_header:
cell.alignment = Alignment(horizontal='center', vertical='center')
# ----- 数据行 -----
for row in range(header_row + 1, ws.max_row + 1):
for col in range(1, max_col + 1):
cell = ws.cell(row=row, column=col)
cell.font = data_font
if change_data_bg:
cell.fill = data_fill
if center_data:
cell.alignment = Alignment(horizontal='center', vertical='center')
wb.save(file_path)
# ---------- GUI ----------
class ExcelBeautifier(TkinterDnD.Tk):
def __init__(self):
super().__init__()
self.title("Excel 批量美化工具")
self.geometry("700x600")
self.resizable(False, False)
self.file_list = []
# ---- 顶部参数区 ----
params = LabelFrame(self, text="参数设置", padx=10, pady=10)
params.pack(fill=X, padx=10, pady=5)
# 表头行号
row_frm = Frame(params)
row_frm.pack(anchor=W)
Label(row_frm, text="表头所在行号:").pack(side=LEFT)
self.header_row_var = IntVar(value=1)
Spinbox(row_frm, from_=1, to_=100, textvariable=self.header_row_var, width=5).pack(side=LEFT)
# 表头字体/字号/颜色/居中/背景色控制
self.header_font_name = StringVar(value="微软雅黑")
self.header_font_size = IntVar(value=12)
self.header_hex = StringVar(value="#4472C4") # 默认蓝色
self.center_header = BooleanVar(value=True)
self.change_header_bg = BooleanVar(value=True)
# 数据行字体/字号/颜色/居中/背景色控制
self.data_font_name = StringVar(value="微软雅黑")
self.data_font_size = IntVar(value=10)
self.data_hex = StringVar(value="#FFFFFF")
self.center_data = BooleanVar(value=False)
self.change_data_bg = BooleanVar(value=True)
# Sheet选择模式
self.sheet_mode = StringVar(value="all") # "all" 或 "specific"
self.sheet_names_input = StringVar(value="")
def _add_line(parent, label, font_name, font_size, hex_var, center_var, bg_var):
frm = Frame(parent)
frm.pack(anchor=W, pady=2)
Label(frm, text=label, width=10, anchor=W).pack(side=LEFT)
Label(frm, text="字体:").pack(side=LEFT)
ttk.Combobox(frm, textvariable=font_name, values=["微软雅黑", "宋体", "Arial", "Calibri"], width=10,
state="readonly").pack(side=LEFT)
Label(frm, text="字号:").pack(side=LEFT)
Spinbox(frm, from_=6, to_=30, textvariable=font_size, width=5).pack(side=LEFT)
Label(frm, text="背景色:").pack(side=LEFT)
Entry(frm, textvariable=hex_var, width=8).pack(side=LEFT)
Button(frm, text="选色", command=lambda: self.choose_color(hex_var)).pack(side=LEFT, padx=2)
Checkbutton(frm, text="改变背景色", variable=bg_var).pack(side=LEFT, padx=2)
Checkbutton(frm, text="水平居中", variable=center_var).pack(side=LEFT)
_add_line(params, "表头:", self.header_font_name, self.header_font_size, self.header_hex, self.center_header, self.change_header_bg)
_add_line(params, "数据:", self.data_font_name, self.data_font_size, self.data_hex, self.center_data, self.change_data_bg)
# Sheet选择区域
sheet_frm = Frame(params)
sheet_frm.pack(anchor=W, pady=5)
Label(sheet_frm, text="Sheet选择:", width=10, anchor=W).pack(side=LEFT)
Radiobutton(sheet_frm, text="处理所有Sheet", variable=self.sheet_mode, value="all",
command=self.on_sheet_mode_change).pack(side=LEFT)
Radiobutton(sheet_frm, text="指定Sheet", variable=self.sheet_mode, value="specific",
command=self.on_sheet_mode_change).pack(side=LEFT, padx=10)
Label(sheet_frm, text="Sheet名称:").pack(side=LEFT)
self.sheet_entry = Entry(sheet_frm, textvariable=self.sheet_names_input, width=20,
state=DISABLED)
self.sheet_entry.pack(side=LEFT, padx=2)
Button(sheet_frm, text="查看Sheet", command=self.show_sheets).pack(side=LEFT, padx=2)
Label(sheet_frm, text="(多个用逗号分隔)", font=("微软雅黑", 8)).pack(side=LEFT)
# ---- 文件列表区 ----
list_frame = LabelFrame(self, text="待处理文件(支持拖拽)", padx=10, pady=10)
list_frame.pack(fill=BOTH, expand=True, padx=10, pady=5)
self.listbox = Listbox(list_frame, selectmode=EXTENDED)
self.listbox.pack(side=LEFT, fill=BOTH, expand=True)
scroll = Scrollbar(list_frame, orient=VERTICAL)
scroll.pack(side=RIGHT, fill=Y)
self.listbox.config(yscrollcommand=scroll.set)
scroll.config(command=self.listbox.yview)
# 拖拽绑定
self.listbox.drop_target_register(DND_FILES)
self.listbox.dnd_bind('<<Drop>>', self.on_drop)
# ---- 按钮区 ----
btn_bar = Frame(self)
btn_bar.pack(fill=X, padx=10, pady=5)
Button(btn_bar, text="添加文件", command=self.add_files).pack(side=LEFT, padx=2)
Button(btn_bar, text="添加目录", command=self.add_folder).pack(side=LEFT, padx=2)
Button(btn_bar, text="移除选中", command=self.remove_selected).pack(side=LEFT, padx=2)
Button(btn_bar, text="清空列表", command=self.clear_list).pack(side=LEFT, padx=2)
Button(btn_bar, text="开始美化", command=self.start_task, bg="#4472C4", fg="white").pack(side=RIGHT, padx=2)
# ---- 日志/进度 ----
bottom = Frame(self)
bottom.pack(fill=X, padx=10, pady=5)
self.log = ScrolledText(bottom, height=8, state=DISABLED)
self.log.pack(fill=X)
self.progress = ttk.Progressbar(bottom, orient=HORIZONTAL, mode='determinate')
self.progress.pack(fill=X, pady=5)
# ---------- 交互 ----------
def on_sheet_mode_change(self):
"""Sheet选择模式改变时的回调"""
if self.sheet_mode.get() == "specific":
self.sheet_entry.config(state=NORMAL)
else:
self.sheet_entry.config(state=DISABLED)
self.sheet_names_input.set("") # 清空输入
def show_sheets(self):
"""显示选中文件的Sheet列表"""
if not self.file_list:
messagebox.showinfo("提示", "请先添加Excel文件!")
return
# 获取第一个文件的Sheet列表作为示例
file_path = self.file_list[0]
sheet_names = get_sheet_names(file_path)
if not sheet_names:
messagebox.showerror("错误", f"无法读取文件:{os.path.basename(file_path)}")
return
# 显示Sheet列表
sheet_list = "\n".join([f"• {name}" for name in sheet_names])
messagebox.showinfo(
f"Sheet列表 - {os.path.basename(file_path)}",
f"该文件包含以下Sheet:\n\n{sheet_list}\n\n提示:如需处理多个Sheet,请用逗号分隔"
)
def choose_color(self, var: StringVar):
from tkinter.colorchooser import askcolor
_, hex_code = askcolor(color=var.get(), parent=self)
if hex_code:
var.set(hex_code.upper())
def on_drop(self, event):
files = self.tk.splitlist(event.data)
self.add_file_list(files)
def add_files(self):
files = filedialog.askopenfilenames(filetypes=[("Excel 文件", "*.xlsx")])
self.add_file_list(files)
def add_folder(self):
folder = filedialog.askdirectory()
if not folder:
return
files = [os.path.join(folder, f) for f in os.listdir(folder)
if f.lower().endswith(".xlsx")]
self.add_file_list(files)
def add_file_list(self, files):
for f in files:
if f not in self.file_list:
self.file_list.append(f)
self.listbox.insert(END, os.path.basename(f))
def remove_selected(self):
for idx in reversed(self.listbox.curselection()):
self.listbox.delete(idx)
self.file_list.pop(idx)
def clear_list(self):
self.listbox.delete(0, END)
self.file_list.clear()
def log_msg(self, msg: str):
self.log.config(state=NORMAL)
self.log.insert(END, msg + "\n")
self.log.see(END)
self.log.config(state=DISABLED)
self.update()
# ---------- 任务 ----------
def start_task(self):
if not self.file_list:
messagebox.showwarning("提示", "请先添加待处理文件!")
return
# 禁用按钮防止重复点击
for child in self.winfo_children():
if isinstance(child, Frame):
for btn in child.winfo_children():
if isinstance(btn, Button):
btn.config(state=DISABLED)
self.progress["maximum"] = len(self.file_list)
self.progress["value"] = 0
threading.Thread(target=self.worker, daemon=True).start()
def worker(self):
hdr_font = Font(name=self.header_font_name.get(), size=self.header_font_size.get(), bold=True)
hdr_rgb = hex_to_rgb(self.header_hex.get())
hdr_fill = PatternFill(start_color=rgb_to_hex(*hdr_rgb), end_color=rgb_to_hex(*hdr_rgb), fill_type="solid")
dat_font = Font(name=self.data_font_name.get(), size=self.data_font_size.get(), bold=False)
dat_rgb = hex_to_rgb(self.data_hex.get())
dat_fill = PatternFill(start_color=rgb_to_hex(*dat_rgb), end_color=rgb_to_hex(*dat_rgb), fill_type="solid")
# 处理Sheet选择
sheet_names = None
if self.sheet_mode.get() == "specific":
sheet_input = self.sheet_names_input.get().strip()
if sheet_input:
sheet_names = [name.strip() for name in sheet_input.split(',') if name.strip()]
for idx, file_path in enumerate(self.file_list, 1):
self.log_msg(f"[{idx}/{len(self.file_list)}] 处理中:{os.path.basename(file_path)}")
try:
beautify_one_file(
file_path,
self.header_row_var.get(),
hdr_font, hdr_fill,
dat_font, dat_fill,
self.center_header.get(),
self.center_data.get(),
self.change_header_bg.get(),
self.change_data_bg.get(),
sheet_names
)
self.log_msg(" 完成!")
except Exception as e:
self.log_msg(f" 错误:{e}")
self.progress["value"] = idx
self.log_msg(">>> 全部任务结束!")
# 恢复按钮
for child in self.winfo_children():
if isinstance(child, Frame):
for btn in child.winfo_children():
if isinstance(btn, Button):
btn.config(state=NORMAL)
# ---------- 启动 ----------
if __name__ == "__main__":
ExcelBeautifier().mainloop()
界面布局
┌─────────────────────────────────────────┐
│ 参数设置 │
├─────────────────────────────────────────┤
│ 表头行号: [1] │
│ 表头: [字体] [字号] [背景色] [选项] │
│ 数据: [字体] [字号] [背景色] [选项] │
│ Sheet选择: ○全部 ○指定 [输入框] [查看] │
├─────────────────────────────────────────┤
│ 待处理文件列表 │
│ (支持拖拽添加文件) │
├─────────────────────────────────────────┤
│ [添加文件] [添加目录] [移除] [清空] [开始] │
├─────────────────────────────────────────┤
│ 操作日志 │
│ [进度条] │
└─────────────────────────────────────────┘
效果图

使用示例
示例1:基础美化
- 设置表头行号为 1
- 表头:微软雅黑,12号,蓝色背景,居中
- 数据:微软雅黑,10号,白色背景,不居中
- 拖拽Excel文件到列表
- 点击"开始美化"
示例2:指定Sheet处理
- 完成基础设置
- 选择"指定Sheet"单选按钮
- 在输入框中输入:
工作表1, 数据统计 - 点击"查看Sheet"确认Sheet名称
- 开始处理
示例3:不修改背景色
- 完成基础设置
- 取消勾选"改变背景色"选项
- 只修改字体样式和对齐方式
- 开始处理
注意事项
- 文件备份:处理前建议备份原始文件
- 文件格式:仅支持
.xlsx格式的Excel文件 - 文件占用:确保Excel文件未被其他程序打开
- Sheet名称:输入Sheet名称时注意大小写和空格
- 内存使用:处理大量文件时注意内存占用
常见问题
Q: 程序提示"依赖缺失"
A: 请安装 tkinterdnd2:pip install tkinterdnd2
Q: 无法拖拽文件
A: 确保已正确安装 tkinterdnd2 库
Q: 处理时出现错误
A: 检查文件是否被占用,确认文件格式为 .xlsx
Q: Sheet名称输入无效
A: 点击"查看Sheet"按钮确认正确的Sheet名称
Q: 程序界面冻结
A: 大文件处理时属于正常现象,请耐心等待
版本历史
v2.0.0 (最新)
- 新增Sheet选择功能
- 新增背景色控制选项
- 新增Sheet列表查看功能
- 修复多线程处理问题
- 优化界面布局
v1.0.0
- 初始版本发布
- 基础批量美化功能
- 拖拽文件支持
- 样式自定义功能
开发环境设置
- Fork 项目
- 创建功能分支
- 提交更改
- 创建 Pull Request
代码规范
- 遵循 PEP 8 编码规范
- 添加适当的注释和文档
- 保持代码简洁易读
到此这篇关于Python实现Excel批量样式修改器(附完整代码)的文章就介绍到这了,更多相关Python修改Excel样式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
