Python实现Excel批量样式修改器(附完整代码)

 更新时间:2025年09月10日 08:59:13   作者:小庄-Python办公  
这篇文章主要为大家详细介绍了如何使用Python实现一个Excel批量样式修改器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

前言

一个基于 Python tkinter 和 openpyxl 的 Excel 文件批量美化工具,支持拖拽操作和灵活的样式配置。

功能特性

核心功能

  • 批量处理:支持同时处理多个 Excel 文件
  • 拖拽支持:直接拖拽文件到界面即可添加
  • 样式自定义:可自定义表头和数据行的字体、字号、背景色
  • 居中对齐:支持表头和数据行的水平居中设置
  • 背景色控制:可选择是否修改背景色
  • Sheet 选择:支持处理所有 Sheet 或指定特定 Sheet

界面特性

  • 直观的图形用户界面
  • 实时进度显示
  • 详细的操作日志
  • 颜色选择器
  • Sheet 列表查看功能

系统要求

Python 3.6+

Windows 操作系统

依赖库:

  • tkinter (Python 内置)
  • openpyxl
  • tkinterdnd2

安装说明

克隆或下载项目

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样式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python3.4学习笔记之常用操作符,条件分支和循环用法示例

    Python3.4学习笔记之常用操作符,条件分支和循环用法示例

    这篇文章主要介绍了Python3.4常用操作符,条件分支和循环用法,结合实例形式较为详细的分析了Python3.4常见的数学运算、逻辑运算操作符,条件分支语句,循环语句等功能与基本用法,需要的朋友可以参考下
    2019-03-03
  • Python中集合类型(set)学习小结

    Python中集合类型(set)学习小结

    这篇文章主要介绍了Python中集合类型(set)学习小结,本文讲解了set的初始化、运算操作、基本方法等内容,需要的朋友可以参考下
    2015-01-01
  • python图片格式转换脚本

    python图片格式转换脚本

    大家好,本篇文章主要讲的是python图片格式转换脚本,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2022-01-01
  • python基于Opencv实现人脸口罩检测

    python基于Opencv实现人脸口罩检测

    最近尝试做一个python基于Opencv实现人脸口罩检测,记录一下过程,稍微整理精简一下做下分享,需要的小伙伴可以参考下
    2021-06-06
  • 详解Python装饰器 给你的咖啡加点料

    详解Python装饰器 给你的咖啡加点料

    今天你的咖啡加糖了吗? 让我们通过一个简单的例子来引出装饰器的概念及用法。在引出装饰器之前,我们先来了解一下函数的概念,感兴趣的朋友跟随小编一起看看吧
    2021-07-07
  • python中cv2模块安装详细图文教程

    python中cv2模块安装详细图文教程

    在Python中cv2是OpenCV库的一个模块,这是一个开源的计算机视觉和机器学习软件库,下面这篇文章主要给大家介绍了关于python中cv2模块安装的相关资料,需要的朋友可以参考下
    2024-05-05
  • pandas中8种常用的index 索引设置

    pandas中8种常用的index 索引设置

    在数据处理时,经常会因为index报错而发愁,本文主要介绍了pandas中8种常用的index 索引设置,具有一定的参考价值,感兴趣的可以了解一下
    2024-07-07
  • Python 正则表达式中re.group()使用小结

    Python 正则表达式中re.group()使用小结

    正则表达式是在处理字符串时非常有用的工具,而re.group()是在匹配到的文本中提取特定分组内容的方法之一,这篇文章主要介绍了Python 正则表达式之re.group()用法,需要的朋友可以参考下
    2024-01-01
  • 关于Numpy数据类型对象(dtype)使用详解

    关于Numpy数据类型对象(dtype)使用详解

    今天小编就为大家分享一篇关于Numpy数据类型对象(dtype)使用详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-11-11
  • Python如何实现同时进行多个浏览器注入

    Python如何实现同时进行多个浏览器注入

    浏览器注入是一种技术,允许我们在运行中的浏览器进程中注入代码,以便执行特定的任务,下面我们就来看看Python如何实现同时进行多个浏览器注入吧
    2025-03-03

最新评论