大家好!我是CSDN的Python新手博主~

前面我们已经完成了办公看板、AI智能助手、邮件自动机器人、数据清洗报表生成四大核心办公自动化工具,彻底解决了日常办公的重复劳动。
但很多新手同学反馈了一个核心痛点:

  • 脚本都是命令行运行,新手改参数不方便
  • 发给同事用,对方电脑没装Python,根本跑不起来
  • 功能分散在不同脚本里,切换起来麻烦
  • 没有可视化界面,操作不直观,容易出错

今天就用极简代码,把我们前面所有的办公自动化工具,整合成一个带图形界面的桌面应用,最后打包成EXE文件,无需安装Python,双击就能用,发给同事也能直接跑!


一、本次你能学到什么

  1. ttkbootstrap 快速开发美观的Python桌面界面(新手友好,比PyQt5门槛低)
  2. 把之前的邮件机器人、数据处理工具完整整合到界面中
  3. 实现配置保存、日志实时展示、文件选择等实用功能
  4. pyinstaller 一键打包成EXE文件,无Python环境也能运行
  5. 学会模块化开发,后续可以无限扩展新功能

二、前期准备(2分钟搞定)

1. 安装核心依赖

打开命令行执行,一键安装所有需要的库:

# 界面开发库(基于tkinter,Python自带内核,新手零门槛)
pip install ttkbootstrap
# 打包EXE工具
pip install pyinstaller
# 之前功能用到的依赖,确保都安装
pip install pandas openpyxl imaplib2 email python-dotenv

2. 提前准备

把我们之前写的 email_robot.pydata_processor.py 放到同一个文件夹里,后面直接导入复用,不用重复写代码。


三、核心代码:完整桌面应用(复制就能跑)

新建文件 office_automation_app.py,这是我们的主程序,完整代码如下:

# -*- coding: utf-8 -*-
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from ttkbootstrap.dialogs import Messagebox
import tkinter.filedialog as filedialog
import os
import json
from datetime import datetime
from dotenv import load_dotenv

# 导入我们之前写好的功能模块
from email_robot import run_email_robot
from data_processor import run_data_processor

# 加载配置文件
load_dotenv()
CONFIG_FILE = "app_config.json"

# ---------------------------
# 1. 配置管理(保存/读取用户设置)
# ---------------------------
def load_config():
    """读取用户配置,没有则创建默认配置"""
    if os.path.exists(CONFIG_FILE):
        with open(CONFIG_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    # 默认配置
    default_config = {
        "email": os.getenv("EMAIL", ""),
        "auth_code": os.getenv("EMAIL_AUTH_CODE", ""),
        "imap_server": "imap.163.com",
        "raw_data_path": "raw_data",
        "output_path": "processed_reports",
        "alert_keywords": ["紧急", "重要", "发票", "合同", "报表"]
    }
    save_config(default_config)
    return default_config

def save_config(config):
    """保存用户配置到文件"""
    with open(CONFIG_FILE, "w", encoding="utf-8") as f:
        json.dump(config, f, ensure_ascii=False, indent=2)

# 全局配置
app_config = load_config()

# ---------------------------
# 2. 主应用窗口
# ---------------------------
class OfficeAutomationApp(ttk.Window):
    def __init__(self):
        super().__init__(themename="cosmo")  # 主题:cosmo/flatly/solar/darkly
        self.title("Python办公自动化工具箱 v1.0")
        self.geometry("900x600")  # 窗口大小
        self.resizable(True, True)

        # 全局变量
        self.selected_file = None
        self.log_text = None

        # 构建界面
        self.build_ui()

    def build_ui(self):
        """构建完整界面布局"""
        # 顶部标题
        title_label = ttk.Label(
            self, text="办公自动化工具箱", font=("微软雅黑", 20, "bold"), bootstyle=PRIMARY
        )
        title_label.pack(pady=10)

        # 选项卡(分功能模块)
        notebook = ttk.Notebook(self)
        notebook.pack(fill=BOTH, expand=YES, padx=10, pady=5)

        # 选项卡1:邮件自动机器人
        email_frame = ttk.Frame(notebook, padding=10)
        notebook.add(email_frame, text="邮件自动机器人")
        self.build_email_tab(email_frame)

        # 选项卡2:数据处理与报表生成
        data_frame = ttk.Frame(notebook, padding=10)
        notebook.add(data_frame, text="数据处理与报表生成")
        self.build_data_tab(data_frame)

        # 选项卡3:系统设置
        config_frame = ttk.Frame(notebook, padding=10)
        notebook.add(config_frame, text="系统设置")
        self.build_config_tab(config_frame)

        # 底部日志区域
        log_frame = ttk.LabelFrame(self, text="运行日志", padding=10)
        log_frame.pack(fill=BOTH, expand=YES, padx=10, pady=5)

        # 日志文本框
        self.log_text = ttk.ScrolledText(log_frame, height=8)
        self.log_text.pack(fill=BOTH, expand=YES)
        self.log_text.insert(END, f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 程序启动成功!\n")
        self.log_text.configure(state=DISABLED)  # 只读

    # ---------------------------
    # 选项卡1:邮件机器人界面
    # ---------------------------
    def build_email_tab(self, parent):
        # 功能说明
        desc_label = ttk.Label(
            parent, text="一键自动收邮件、下载附件、关键词告警、归档到Excel",
            font=("微软雅黑", 10), bootstyle=SECONDARY
        )
        desc_label.grid(row=0, column=0, columnspan=2, sticky=W, pady=5)

        # 运行按钮
        run_btn = ttk.Button(
            parent, text="🚀 一键运行邮件机器人", bootstyle=PRIMARY,
            command=self.run_email_robot_ui, width=30
        )
        run_btn.grid(row=1, column=0, sticky=W, pady=10)

        # 打开附件文件夹按钮
        open_folder_btn = ttk.Button(
            parent, text="📂 打开附件文件夹", bootstyle=INFO,
            command=lambda: os.startfile("mail_attachments") if os.path.exists("mail_attachments") else Messagebox.show_warning("文件夹不存在!")
        )
        open_folder_btn.grid(row=1, column=1, sticky=W, pady=10, padx=10)

        # 打开归档Excel按钮
        open_excel_btn = ttk.Button(
            parent, text="📊 打开邮件归档Excel", bootstyle=SUCCESS,
            command=lambda: os.startfile("邮件自动归档.xlsx") if os.path.exists("邮件自动归档.xlsx") else Messagebox.show_warning("文件不存在!")
        )
        open_excel_btn.grid(row=2, column=0, sticky=W, pady=5)

    def run_email_robot_ui(self):
        """界面调用邮件机器人"""
        self.update_log("开始运行邮件机器人...")
        try:
            # 调用之前写好的函数
            run_email_robot()
            self.update_log("邮件机器人运行完成!附件已下载,归档已完成")
            Messagebox.show_info("运行成功", "邮件机器人运行完成!")
        except Exception as e:
            self.update_log(f"邮件机器人运行失败:{str(e)}")
            Messagebox.show_error("运行失败", f"错误:{str(e)}")

    # ---------------------------
    # 选项卡2:数据处理界面
    # ---------------------------
    def build_data_tab(self, parent):
        # 功能说明
        desc_label = ttk.Label(
            parent, text="自动读取原始数据、清洗、生成统计报表、插入图表、一键美化",
            font=("微软雅黑", 10), bootstyle=SECONDARY
        )
        desc_label.grid(row=0, column=0, columnspan=4, sticky=W, pady=5)

        # 1. 选择数据文件
        file_frame = ttk.Frame(parent)
        file_frame.grid(row=1, column=0, columnspan=4, sticky=W, pady=5)

        ttk.Label(file_frame, text="原始数据文件:", font=("微软雅黑", 10)).pack(side=LEFT, padx=5)
        self.file_path_label = ttk.Label(file_frame, text="未选择文件", bootstyle=DANGER)
        self.file_path_label.pack(side=LEFT, padx=5)
        select_file_btn = ttk.Button(
            file_frame, text="选择文件", bootstyle=INFO,
            command=self.select_data_file
        )
        select_file_btn.pack(side=LEFT, padx=10)

        # 2. 统计参数设置
        param_frame = ttk.LabelFrame(parent, text="统计参数设置", padding=10)
        param_frame.grid(row=2, column=0, columnspan=4, sticky=W+E, pady=10)

        # 分组列
        ttk.Label(param_frame, text="分组列名:", font=("微软雅黑", 10)).grid(row=0, column=0, sticky=W, pady=5, padx=5)
        self.group_col_entry = ttk.Entry(param_frame, width=20)
        self.group_col_entry.grid(row=0, column=1, sticky=W, padx=5)
        self.group_col_entry.insert(0, app_config.get("group_col", "产品"))

        # 统计值列
        ttk.Label(param_frame, text="统计值列名:", font=("微软雅黑", 10)).grid(row=0, column=2, sticky=W, pady=5, padx=5)
        self.value_col_entry = ttk.Entry(param_frame, width=20)
        self.value_col_entry.grid(row=0, column=3, sticky=W, padx=5)
        self.value_col_entry.insert(0, app_config.get("value_col", "销售额"))

        # 图表类型
        ttk.Label(param_frame, text="图表类型:", font=("微软雅黑", 10)).grid(row=0, column=4, sticky=W, pady=5, padx=5)
        self.chart_type_combo = ttk.Combobox(param_frame, values=["柱状图", "折线图", "饼图"], width=10)
        self.chart_type_combo.grid(row=0, column=5, sticky=W, padx=5)
        self.chart_type_combo.current(0)

        # 3. 运行按钮
        run_btn = ttk.Button(
            parent, text="🚀 一键生成报表", bootstyle=PRIMARY,
            command=self.run_data_processor_ui, width=30
        )
        run_btn.grid(row=3, column=0, sticky=W, pady=10)

        # 打开报表文件夹按钮
        open_folder_btn = ttk.Button(
            parent, text="📂 打开报表文件夹", bootstyle=INFO,
            command=lambda: os.startfile(app_config["output_path"]) if os.path.exists(app_config["output_path"]) else Messagebox.show_warning("文件夹不存在!")
        )
        open_folder_btn.grid(row=3, column=1, sticky=W, pady=10, padx=10)

    def select_data_file(self):
        """选择原始数据文件"""
        file_path = filedialog.askopenfilename(
            title="选择原始数据文件",
            filetypes=[("Excel文件", "*.xlsx"), ("CSV文件", "*.csv"), ("所有文件", "*.*")]
        )
        if file_path:
            self.selected_file = file_path
            self.file_path_label.config(text=os.path.basename(file_path), bootstyle=SUCCESS)
            self.update_log(f"已选择数据文件:{file_path}")

    def run_data_processor_ui(self):
        """界面调用数据处理工具"""
        if not self.selected_file:
            Messagebox.show_warning("警告", "请先选择原始数据文件!")
            return
        
        # 获取参数
        group_col = self.group_col_entry.get().strip()
        value_col = self.value_col_entry.get().strip()
        chart_type_map = {"柱状图": "bar", "折线图": "line", "饼图": "pie"}
        chart_type = chart_type_map[self.chart_type_combo.get()]

        if not group_col or not value_col:
            Messagebox.show_warning("警告", "请填写分组列名和统计值列名!")
            return

        self.update_log("开始运行数据处理工具...")
        try:
            # 调用之前写好的函数
            file_name = os.path.basename(self.selected_file)
            # 把文件复制到raw_data文件夹
            if not os.path.exists(app_config["raw_data_path"]):
                os.mkdir(app_config["raw_data_path"])
            target_path = os.path.join(app_config["raw_data_path"], file_name)
            with open(self.selected_file, "rb") as f1, open(target_path, "wb") as f2:
                f2.write(f1.read())
            
            # 运行数据处理
            output_file = run_data_processor(file_name, group_col, value_col, chart_type)
            self.update_log(f"数据处理完成!报表已保存至:{output_file}")
            Messagebox.show_info("运行成功", "报表生成完成!")
        except Exception as e:
            self.update_log(f"数据处理失败:{str(e)}")
            Messagebox.show_error("运行失败", f"错误:{str(e)}")

    # ---------------------------
    # 选项卡3:系统设置界面
    # ---------------------------
    def build_config_tab(self, parent):
        # 邮箱设置
        email_frame = ttk.LabelFrame(parent, text="邮箱配置", padding=10)
        email_frame.pack(fill=X, pady=5)

        # 邮箱地址
        ttk.Label(email_frame, text="邮箱地址:", font=("微软雅黑", 10)).grid(row=0, column=0, sticky=W, pady=5, padx=5)
        self.email_entry = ttk.Entry(email_frame, width=30)
        self.email_entry.grid(row=0, column=1, sticky=W, padx=5)
        self.email_entry.insert(0, app_config["email"])

        # 授权码
        ttk.Label(email_frame, text="邮箱授权码:", font=("微软雅黑", 10)).grid(row=0, column=2, sticky=W, pady=5, padx=5)
        self.auth_code_entry = ttk.Entry(email_frame, width=30, show="*")
        self.auth_code_entry.grid(row=0, column=3, sticky=W, padx=5)
        self.auth_code_entry.insert(0, app_config["auth_code"])

        # IMAP服务器
        ttk.Label(email_frame, text="IMAP服务器:", font=("微软雅黑", 10)).grid(row=1, column=0, sticky=W, pady=5, padx=5)
        self.imap_entry = ttk.Entry(email_frame, width=30)
        self.imap_entry.grid(row=1, column=1, sticky=W, padx=5)
        self.imap_entry.insert(0, app_config["imap_server"])

        # 告警关键词
        ttk.Label(email_frame, text="告警关键词(逗号分隔):", font=("微软雅黑", 10)).grid(row=1, column=2, sticky=W, pady=5, padx=5)
        self.keywords_entry = ttk.Entry(email_frame, width=30)
        self.keywords_entry.grid(row=1, column=3, sticky=W, padx=5)
        self.keywords_entry.insert(0, ",".join(app_config["alert_keywords"]))

        # 保存设置按钮
        save_btn = ttk.Button(
            parent, text="💾 保存设置", bootstyle=SUCCESS,
            command=self.save_config_ui, width=20
        )
        save_btn.pack(pady=10)

        # 主题切换
        theme_frame = ttk.LabelFrame(parent, text="界面主题", padding=10)
        theme_frame.pack(fill=X, pady=5)
        ttk.Label(theme_frame, text="选择主题:", font=("微软雅黑", 10)).pack(side=LEFT, padx=5)
        self.theme_combo = ttk.Combobox(
            theme_frame, 
            values=["cosmo", "flatly", "solar", "darkly", "vapor", "minty"],
            width=15
        )
        self.theme_combo.pack(side=LEFT, padx=5)
        self.theme_combo.current(0)
        change_theme_btn = ttk.Button(
            theme_frame, text="切换主题", bootstyle=INFO,
            command=self.change_theme
        )
        change_theme_btn.pack(side=LEFT, padx=10)

    def save_config_ui(self):
        """保存用户设置"""
        global app_config
        app_config = {
            "email": self.email_entry.get().strip(),
            "auth_code": self.auth_code_entry.get().strip(),
            "imap_server": self.imap_entry.get().strip(),
            "raw_data_path": "raw_data",
            "output_path": "processed_reports",
            "alert_keywords": [kw.strip() for kw in self.keywords_entry.get().split(",") if kw.strip()],
            "group_col": self.group_col_entry.get().strip(),
            "value_col": self.value_col_entry.get().strip()
        }
        save_config(app_config)
        # 更新环境变量,给邮件机器人用
        os.environ["EMAIL"] = app_config["email"]
        os.environ["EMAIL_AUTH_CODE"] = app_config["auth_code"]
        self.update_log("系统设置已保存!")
        Messagebox.show_info("保存成功", "系统设置已保存!")

    def change_theme(self):
        """切换界面主题"""
        new_theme = self.theme_combo.get()
        self.style.theme_use(new_theme)
        self.update_log(f"界面主题已切换为:{new_theme}")

    # ---------------------------
    # 工具函数:更新日志
    # ---------------------------
    def update_log(self, msg):
        """实时更新日志区域"""
        self.log_text.configure(state=NORMAL)
        self.log_text.insert(END, f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {msg}\n")
        self.log_text.see(END)  # 自动滚动到底部
        self.log_text.configure(state=DISABLED)

# ---------------------------
# 程序入口
# ---------------------------
if __name__ == "__main__":
    app = OfficeAutomationApp()
    app.mainloop()

四、运行效果(直接看得到)

  1. 运行代码后,会弹出一个美观的桌面应用窗口,分为3个核心选项卡:
    • 邮件自动机器人:一键运行、打开附件、打开归档Excel
    • 数据处理与报表生成:选择文件、设置参数、一键生成报表
    • 系统设置:配置邮箱、切换主题、保存参数
  2. 底部有实时运行日志,每一步操作都有记录,出错了能快速定位
  3. 所有按钮都有友好的提示,无需懂代码,点点鼠标就能用
  4. 支持主题切换,浅色/深色主题都有,界面美观不卡顿

五、一键打包成EXE(无Python环境也能运行)

代码跑通之后,我们把它打包成EXE文件,发给同事、领导都能直接用,无需安装Python。

1. 打包命令

在命令行执行(确保在代码所在的文件夹里):

pyinstaller -F -w -i app.ico office_automation_app.py

参数说明:

  • -F:打包成单个EXE文件,方便分发
  • -w:运行时不显示命令行黑窗口,只显示界面
  • -i app.ico:可选,给EXE设置图标(需要准备一个ico格式的图标文件)

2. 打包完成

执行完成后,文件夹里会生成一个 dist 文件夹,里面的 office_automation_app.exe 就是我们的成品!

  • 双击就能运行,无需安装Python
  • 把之前的 email_robot.pydata_processor.pyapp_config.json 和EXE放在同一个文件夹里,就能正常使用
  • 发给任何Windows电脑都能直接打开

六、可以直接扩展的进阶功能

你可以在这篇基础上随便加,非常适合写进博客:

  1. 新增功能模块:把之前的AI智能助手、定时任务、看板数据查看都做成新的选项卡
  2. 增加登录界面:给应用加账号密码,只有授权用户能使用
  3. 定时任务功能:在界面上设置定时任务,每天自动跑邮件机器人、生成日报
  4. 数据可视化界面:在界面上直接展示报表图表,不用打开Excel
  5. 多语言支持:增加中英文切换,适配不同用户

七、博客总结文案(可直接复制)

本篇我们完成了办公自动化工具箱的最终形态:把之前所有分散的脚本,整合成了一个带图形界面的桌面应用,最后打包成了可分发的EXE文件。

从最开始的单功能脚本,到现在的完整桌面应用,我们完成了一整套企业级办公自动化解决方案:

  • 底层:数据自动清洗、报表自动生成
  • 中间层:邮件自动处理、AI智能交互
  • 顶层:可视化桌面应用、一键分发

代码全程新手友好,注释详细,复制就能跑,打包就能用,彻底解决了日常办公的重复劳动,哪怕是完全不懂代码的同事,也能轻松使用。

至此,我们的Python办公自动化系列就完成了从0到1的全流程实战,新手可以基于这个系列,快速打造属于自己的办公自动化神器,告别无效加班!

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐