Python實(shí)現(xiàn)Excel批量樣式修改器(附完整代碼)
前言
一個(gè)基于 Python tkinter 和 openpyxl 的 Excel 文件批量美化工具,支持拖拽操作和靈活的樣式配置。
功能特性
核心功能
- 批量處理:支持同時(shí)處理多個(gè) Excel 文件
- 拖拽支持:直接拖拽文件到界面即可添加
- 樣式自定義:可自定義表頭和數(shù)據(jù)行的字體、字號(hào)、背景色
- 居中對(duì)齊:支持表頭和數(shù)據(jù)行的水平居中設(shè)置
- 背景色控制:可選擇是否修改背景色
- Sheet 選擇:支持處理所有 Sheet 或指定特定 Sheet
界面特性
- 直觀的圖形用戶(hù)界面
- 實(shí)時(shí)進(jìn)度顯示
- 詳細(xì)的操作日志
- 顏色選擇器
- Sheet 列表查看功能
系統(tǒng)要求
Python 3.6+
Windows 操作系統(tǒng)
依賴(lài)庫(kù):
tkinter(Python 內(nèi)置)openpyxltkinterdnd2
安裝說(shuō)明
克隆或下載項(xiàng)目
git clone <repository-url> cd 13-Excel批量樣式修改器
安裝依賴(lài)
pip install openpyxl tkinterdnd2
運(yùn)行程序
python main.py
使用指南
基本操作流程
1.啟動(dòng)程序
- 雙擊
main.py或在命令行運(yùn)行 - 程序界面將自動(dòng)打開(kāi)
2.參數(shù)設(shè)置
- 表頭行號(hào):設(shè)置表頭所在的行號(hào)(默認(rèn)第1行)
- 字體設(shè)置:選擇表頭和數(shù)據(jù)行的字體類(lèi)型和大小
- 背景色設(shè)置:點(diǎn)擊"選色"按鈕選擇背景顏色
- 居中選項(xiàng):勾選是否需要水平居中
- 背景色控制:選擇是否修改背景色
3.Sheet 選擇
- 處理所有Sheet:默認(rèn)選項(xiàng),處理文件中的所有工作表
- 指定Sheet:輸入特定的Sheet名稱(chēng),多個(gè)用逗號(hào)分隔
- 查看Sheet:點(diǎn)擊按鈕查看文件中所有可用的Sheet列表
4.添加文件
- 拖拽添加:直接將 Excel 文件拖拽到文件列表區(qū)域
- 手動(dòng)添加:點(diǎn)擊"添加文件"按鈕選擇單個(gè)或多個(gè)文件
- 批量添加:點(diǎn)擊"添加目錄"按鈕選擇包含 Excel 文件的文件夾
5.開(kāi)始處理
- 點(diǎn)擊"開(kāi)始美化"按鈕
- 觀察進(jìn)度條和日志輸出
- 等待處理完成
高級(jí)功能
Sheet 選擇功能
- 查看 Sheet 列表:添加文件后,點(diǎn)擊"查看Sheet"按鈕可以查看第一個(gè)文件中的所有Sheet名稱(chēng)
- 指定 Sheet 處理:在"Sheet名稱(chēng)"輸入框中輸入要處理的Sheet名稱(chēng)
- 多 Sheet 處理:使用逗號(hào)分隔多個(gè)Sheet名稱(chēng),如:
Sheet1, 數(shù)據(jù)表, Summary
樣式配置技巧
- 表頭樣式:通常使用較大字號(hào)和深色背景,便于區(qū)分
- 數(shù)據(jù)行樣式:使用較小字號(hào)和淺色背景,保持可讀性
- 顏色搭配:建議使用對(duì)比度適中的顏色組合
技術(shù)實(shí)現(xiàn)
核心技術(shù)棧
- GUI框架:tkinter + tkinterdnd2(拖拽支持)
- Excel處理:openpyxl
- 多線(xiàn)程:threading(避免界面凍結(jié))
關(guān)鍵函數(shù)說(shuō)明
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):
核心美化函數(shù),負(fù)責(zé)處理單個(gè)Excel文件的樣式修改。
參數(shù)說(shuō)明:
file_path:Excel文件路徑header_row:表頭行號(hào)header_font/data_font:字體樣式對(duì)象header_fill/data_fill:背景填充對(duì)象center_header/center_data:是否居中對(duì)齊change_header_bg/change_data_bg:是否修改背景色sheet_names:指定處理的Sheet列表
get_sheet_names()
def get_sheet_names(file_path: str):
獲取Excel文件中所有Sheet名稱(chēng)的輔助函數(shù)。
設(shè)計(jì)模式
- MVC模式:界面、邏輯、數(shù)據(jù)分離
- 觀察者模式:GUI事件響應(yīng)
- 工廠(chǎng)模式:樣式對(duì)象創(chuàng)建
完整代碼
#!/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("依賴(lài)缺失", "請(qǐng)先 pip install tkinterdnd2")
sys.exit(1)
from openpyxl import load_workbook
from openpyxl.styles import Alignment, Font, PatternFill
# ---------- 工具函數(shù) ----------
def get_sheet_names(file_path: str):
"""獲取Excel文件中所有Sheet的名稱(chēng)"""
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 整數(shù)轉(zhuǎn) 16 進(jìn)制字符串"""
return f"{r:02X}{g:02X}{b:02X}"
def hex_to_rgb(hex_color: str):
"""#RRGGBB 轉(zhuǎn) (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):
"""美化單個(gè)文件"""
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')
# ----- 數(shù)據(jù)行 -----
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 = []
# ---- 頂部參數(shù)區(qū) ----
params = LabelFrame(self, text="參數(shù)設(shè)置", padx=10, pady=10)
params.pack(fill=X, padx=10, pady=5)
# 表頭行號(hào)
row_frm = Frame(params)
row_frm.pack(anchor=W)
Label(row_frm, text="表頭所在行號(hào):").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)
# 表頭字體/字號(hào)/顏色/居中/背景色控制
self.header_font_name = StringVar(value="微軟雅黑")
self.header_font_size = IntVar(value=12)
self.header_hex = StringVar(value="#4472C4") # 默認(rèn)藍(lán)色
self.center_header = BooleanVar(value=True)
self.change_header_bg = BooleanVar(value=True)
# 數(shù)據(jù)行字體/字號(hào)/顏色/居中/背景色控制
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="字號(hào):").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, "數(shù)據(jù):", self.data_font_name, self.data_font_size, self.data_hex, self.center_data, self.change_data_bg)
# Sheet選擇區(qū)域
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名稱(chēng):").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="(多個(gè)用逗號(hào)分隔)", font=("微軟雅黑", 8)).pack(side=LEFT)
# ---- 文件列表區(qū) ----
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)
# ---- 按鈕區(qū) ----
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="開(kāi)始美化", command=self.start_task, bg="#4472C4", fg="white").pack(side=RIGHT, padx=2)
# ---- 日志/進(jìn)度 ----
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選擇模式改變時(shí)的回調(diào)"""
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("提示", "請(qǐng)先添加Excel文件!")
return
# 獲取第一個(gè)文件的Sheet列表作為示例
file_path = self.file_list[0]
sheet_names = get_sheet_names(file_path)
if not sheet_names:
messagebox.showerror("錯(cuò)誤", f"無(wú)法讀取文件:{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提示:如需處理多個(gè)Sheet,請(qǐng)用逗號(hào)分隔"
)
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()
# ---------- 任務(wù) ----------
def start_task(self):
if not self.file_list:
messagebox.showwarning("提示", "請(qǐng)先添加待處理文件!")
return
# 禁用按鈕防止重復(fù)點(diǎn)擊
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" 錯(cuò)誤:{e}")
self.progress["value"] = idx
self.log_msg(">>> 全部任務(wù)結(jié)束!")
# 恢復(fù)按鈕
for child in self.winfo_children():
if isinstance(child, Frame):
for btn in child.winfo_children():
if isinstance(btn, Button):
btn.config(state=NORMAL)
# ---------- 啟動(dòng) ----------
if __name__ == "__main__":
ExcelBeautifier().mainloop()
界面布局
┌─────────────────────────────────────────┐
│ 參數(shù)設(shè)置 │
├─────────────────────────────────────────┤
│ 表頭行號(hào): [1] │
│ 表頭: [字體] [字號(hào)] [背景色] [選項(xiàng)] │
│ 數(shù)據(jù): [字體] [字號(hào)] [背景色] [選項(xiàng)] │
│ Sheet選擇: ○全部 ○指定 [輸入框] [查看] │
├─────────────────────────────────────────┤
│ 待處理文件列表 │
│ (支持拖拽添加文件) │
├─────────────────────────────────────────┤
│ [添加文件] [添加目錄](méi) [移除] [清空] [開(kāi)始] │
├─────────────────────────────────────────┤
│ 操作日志 │
│ [進(jìn)度條] │
└─────────────────────────────────────────┘
效果圖

使用示例
示例1:基礎(chǔ)美化
- 設(shè)置表頭行號(hào)為 1
- 表頭:微軟雅黑,12號(hào),藍(lán)色背景,居中
- 數(shù)據(jù):微軟雅黑,10號(hào),白色背景,不居中
- 拖拽Excel文件到列表
- 點(diǎn)擊"開(kāi)始美化"
示例2:指定Sheet處理
- 完成基礎(chǔ)設(shè)置
- 選擇"指定Sheet"單選按鈕
- 在輸入框中輸入:
工作表1, 數(shù)據(jù)統(tǒng)計(jì) - 點(diǎn)擊"查看Sheet"確認(rèn)Sheet名稱(chēng)
- 開(kāi)始處理
示例3:不修改背景色
- 完成基礎(chǔ)設(shè)置
- 取消勾選"改變背景色"選項(xiàng)
- 只修改字體樣式和對(duì)齊方式
- 開(kāi)始處理
注意事項(xiàng)
- 文件備份:處理前建議備份原始文件
- 文件格式:僅支持
.xlsx格式的Excel文件 - 文件占用:確保Excel文件未被其他程序打開(kāi)
- Sheet名稱(chēng):輸入Sheet名稱(chēng)時(shí)注意大小寫(xiě)和空格
- 內(nèi)存使用:處理大量文件時(shí)注意內(nèi)存占用
常見(jiàn)問(wèn)題
Q: 程序提示"依賴(lài)缺失"
A: 請(qǐng)安裝 tkinterdnd2:pip install tkinterdnd2
Q: 無(wú)法拖拽文件
A: 確保已正確安裝 tkinterdnd2 庫(kù)
Q: 處理時(shí)出現(xiàn)錯(cuò)誤
A: 檢查文件是否被占用,確認(rèn)文件格式為 .xlsx
Q: Sheet名稱(chēng)輸入無(wú)效
A: 點(diǎn)擊"查看Sheet"按鈕確認(rèn)正確的Sheet名稱(chēng)
Q: 程序界面凍結(jié)
A: 大文件處理時(shí)屬于正?,F(xiàn)象,請(qǐng)耐心等待
版本歷史
v2.0.0 (最新)
- 新增Sheet選擇功能
- 新增背景色控制選項(xiàng)
- 新增Sheet列表查看功能
- 修復(fù)多線(xiàn)程處理問(wèn)題
- 優(yōu)化界面布局
v1.0.0
- 初始版本發(fā)布
- 基礎(chǔ)批量美化功能
- 拖拽文件支持
- 樣式自定義功能
開(kāi)發(fā)環(huán)境設(shè)置
- Fork 項(xiàng)目
- 創(chuàng)建功能分支
- 提交更改
- 創(chuàng)建 Pull Request
代碼規(guī)范
- 遵循 PEP 8 編碼規(guī)范
- 添加適當(dāng)?shù)淖⑨尯臀臋n
- 保持代碼簡(jiǎn)潔易讀
到此這篇關(guān)于Python實(shí)現(xiàn)Excel批量樣式修改器(附完整代碼)的文章就介紹到這了,更多相關(guān)Python修改Excel樣式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python3.4學(xué)習(xí)筆記之常用操作符,條件分支和循環(huán)用法示例
這篇文章主要介紹了Python3.4常用操作符,條件分支和循環(huán)用法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Python3.4常見(jiàn)的數(shù)學(xué)運(yùn)算、邏輯運(yùn)算操作符,條件分支語(yǔ)句,循環(huán)語(yǔ)句等功能與基本用法,需要的朋友可以參考下2019-03-03
Python中集合類(lèi)型(set)學(xué)習(xí)小結(jié)
這篇文章主要介紹了Python中集合類(lèi)型(set)學(xué)習(xí)小結(jié),本文講解了set的初始化、運(yùn)算操作、基本方法等內(nèi)容,需要的朋友可以參考下2015-01-01
python基于Opencv實(shí)現(xiàn)人臉口罩檢測(cè)
最近嘗試做一個(gè)python基于Opencv實(shí)現(xiàn)人臉口罩檢測(cè),記錄一下過(guò)程,稍微整理精簡(jiǎn)一下做下分享,需要的小伙伴可以參考下2021-06-06
Python 正則表達(dá)式中re.group()使用小結(jié)
正則表達(dá)式是在處理字符串時(shí)非常有用的工具,而re.group()是在匹配到的文本中提取特定分組內(nèi)容的方法之一,這篇文章主要介紹了Python 正則表達(dá)式之re.group()用法,需要的朋友可以參考下2024-01-01
關(guān)于Numpy數(shù)據(jù)類(lèi)型對(duì)象(dtype)使用詳解
今天小編就為大家分享一篇關(guān)于Numpy數(shù)據(jù)類(lèi)型對(duì)象(dtype)使用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11
Python如何實(shí)現(xiàn)同時(shí)進(jìn)行多個(gè)瀏覽器注入
瀏覽器注入是一種技術(shù),允許我們?cè)谶\(yùn)行中的瀏覽器進(jìn)程中注入代碼,以便執(zhí)行特定的任務(wù),下面我們就來(lái)看看Python如何實(shí)現(xiàn)同時(shí)進(jìn)行多個(gè)瀏覽器注入吧2025-03-03

