#!/usr/bin/env python3 """ 日报展示和复制工具 基于tkinter的GUI应用,用于展示日报信息和一键复制 """ import tkinter as tk from tkinter import ttk, messagebox, scrolledtext from datetime import datetime, timedelta import sys import os import threading # 添加项目根目录到路径 project_root = os.path.dirname(os.path.abspath(__file__)) if project_root not in sys.path: sys.path.insert(0, project_root) class DailyReportApp: """日报展示和复制应用""" def __init__(self, root): self.root = root self.root.title("日报管理系统") self.root.geometry("1400x900") self.root.minsize(1200, 700) # 设置默认字体大小 self.default_font_size = 14 self.title_font_size = 16 # 创建主布局 self.create_main_layout() # 初始化数据 self.current_report_date = None self.report_content = "" self.is_generating = False def create_main_layout(self): """创建主布局""" # 确保能导入项目模块 project_root = os.path.dirname(os.path.abspath(__file__)) if project_root not in sys.path: sys.path.insert(0, project_root) # 主容器 main_container = tk.Frame(self.root, padx=10, pady=10) main_container.pack(fill="both", expand=True) # 配置权重 main_container.columnconfigure(1, weight=3) # 中间区域 main_container.columnconfigure(2, weight=1) # 右侧区域 main_container.rowconfigure(1, weight=1) # 1. 顶部提示区域 self.create_notice_area(main_container) # 2. 左侧日期选择区域 self.create_left_panel(main_container) # 3. 中间日报展示区域 self.create_center_panel(main_container) # 4. 右侧复制按钮区域 self.create_right_panel(main_container) def create_notice_area(self, parent): """创建提示区域""" notice_frame = tk.Frame(parent, bd=2, relief="solid", bg="#FFF3E0") notice_frame.grid(row=0, column=0, columnspan=3, sticky="ew", pady=(0, 10)) # 静态提示标签 self.notice_label = tk.Label( notice_frame, text="重要提示:本程序需在8:00过后运行,确保最后一条船的指令结束时间超过8点,以保证日报数据完整性!", fg="#FF6B6B", bg="#FFF3E0", font=("", self.title_font_size, "bold"), padx=10, pady=10, ) self.notice_label.pack(fill="x", expand=True) def create_left_panel(self, parent): """创建左侧控制面板""" left_frame = tk.LabelFrame( parent, text="日期选择", padx=10, pady=10, font=("", self.title_font_size, "bold"), ) left_frame.grid(row=1, column=0, sticky="nsew", padx=(0, 10)) # 昨日汇总按钮 self.yesterday_btn = tk.Button( left_frame, text="昨日汇总", command=self.select_yesterday, font=("", self.default_font_size), padx=10, pady=5, bg="#4CAF50", fg="white", ) self.yesterday_btn.pack(fill="x", pady=(0, 10)) # 日期选择器 tk.Label(left_frame, text="选择日期:", font=("", self.default_font_size)).pack( anchor="w", pady=(10, 5) ) # 日期输入框 self.date_var = tk.StringVar() self.date_entry = tk.Entry( left_frame, textvariable=self.date_var, font=("", self.default_font_size) ) self.date_entry.pack(fill="x", pady=(0, 5)) # 日期提示 tk.Label( left_frame, text="格式:YYYY-MM-DD", fg="gray", font=("", self.default_font_size - 2), ).pack(anchor="w") # 生成日报按钮 self.generate_btn = tk.Button( left_frame, text="生成日报", command=self.generate_report_async, font=("", self.default_font_size), padx=10, pady=5, bg="#2196F3", fg="white", ) self.generate_btn.pack(fill="x", pady=(20, 0)) # 当前选中日期标签 self.selected_date_label = tk.Label( left_frame, text="当前选中:无", fg="#2196F3", font=("", self.default_font_size, "bold"), ) self.selected_date_label.pack(anchor="w", pady=(20, 0)) def create_center_panel(self, parent): """创建中间日报展示区域""" center_frame = tk.LabelFrame( parent, text="日报内容", padx=10, pady=10, font=("", self.title_font_size, "bold"), ) center_frame.grid(row=1, column=1, sticky="nsew", padx=(0, 10)) # 日报信息文本框 self.report_text = scrolledtext.ScrolledText( center_frame, wrap=tk.WORD, padx=10, pady=10, height=30, font=("", self.default_font_size), ) self.report_text.pack(fill="both", expand=True) # 默认提示文本 self.report_text.insert( tk.END, '请点击左侧"昨日汇总"按钮,或选择日期后点击"生成日报"按钮...' ) self.report_text.config(state=tk.DISABLED) def create_right_panel(self, parent): """创建右侧控制面板""" right_frame = tk.LabelFrame( parent, text="操作", padx=10, pady=10, font=("", self.title_font_size, "bold"), ) right_frame.grid(row=1, column=2, sticky="nsew") # 复制按钮 self.copy_btn = tk.Button( right_frame, text="复制日报", command=self.copy_report, font=("", self.default_font_size), padx=10, pady=5, bg="#4CAF50", fg="white", ) self.copy_btn.pack(fill="x", pady=(0, 20)) # 复制状态标签 self.copy_status = tk.Label( right_frame, text="", font=("", self.default_font_size) ) self.copy_status.pack(fill="x") # 分隔线 tk.Frame(right_frame, height=2, bg="gray").pack(fill="x", pady=20) # 清空按钮 self.clear_btn = tk.Button( right_frame, text="清空", command=self.clear_report, font=("", self.default_font_size), padx=10, pady=5, ) self.clear_btn.pack(fill="x", pady=(0, 10)) # 退出按钮 self.exit_btn = tk.Button( right_frame, text="退出", command=self.root.quit, font=("", self.default_font_size), padx=10, pady=5, ) self.exit_btn.pack(fill="x") def select_yesterday(self): """选择昨天""" yesterday = datetime.now() - timedelta(days=1) self.date_var.set(yesterday.strftime("%Y-%m-%d")) self.selected_date_label.config( text=f"当前选中:昨天 ({yesterday.strftime('%Y-%m-%d')})" ) self.generate_report_async() def generate_report_async(self): """异步生成日报(避免界面卡死)""" if self.is_generating: return self.is_generating = True self.generate_btn.config(state=tk.DISABLED, text="生成中...") # 在后台线程生成日报 thread = threading.Thread(target=self._generate_report_thread) thread.daemon = True thread.start() def _generate_report_thread(self): """在后台线程中生成日报""" try: date_str = self.date_var.get().strip() if not date_str: self.root.after(0, lambda: self._on_generate_error("请先选择日期")) return try: report_date = datetime.strptime(date_str, "%Y-%m-%d") except ValueError: self.root.after( 0, lambda: self._on_generate_error( "日期格式错误!请使用 YYYY-MM-DD 格式" ), ) return # 更新UI self.root.after( 0, lambda: self.selected_date_label.config( text=f"当前选中:{report_date.strftime('%Y-%m-%d')}" ), ) # 生成日报 try: from report_generator import DailyReportGenerator generator = DailyReportGenerator() report = generator.generate_daily_report(report_date) self.root.after(0, lambda: self._on_generate_success(report)) except Exception as e: error_msg = f"生成日报时出错:{str(e)}\n\n请确保:\n1. 环境变量配置正确\n2. Metabase 和飞书服务可访问\n3. 在8:00过后运行程序" self.root.after(0, lambda: self._on_generate_error(error_msg)) except Exception as e: self.root.after(0, lambda: self._on_generate_error(str(e))) def _on_generate_success(self, report): """生成成功回调""" self.report_content = report self.report_text.config(state=tk.NORMAL) self.report_text.delete(1.0, tk.END) self.report_text.insert(tk.END, report) self.report_text.config(state=tk.DISABLED) self._reset_generate_button() def _on_generate_error(self, error_msg): """生成失败回调""" self.report_content = error_msg self.report_text.config(state=tk.NORMAL) self.report_text.delete(1.0, tk.END) self.report_text.insert(tk.END, error_msg) self.report_text.config(state=tk.DISABLED) self._reset_generate_button() messagebox.showerror("错误", error_msg) def _reset_generate_button(self): """重置生成按钮状态""" self.is_generating = False self.generate_btn.config(state=tk.NORMAL, text="生成日报") def copy_report(self): """复制日报到剪贴板""" if ( not self.report_content or self.report_content == '请点击左侧"昨日汇总"按钮,或选择日期后点击"生成日报"按钮...' ): messagebox.showwarning("提示", "请先生成日报!") return try: # 复制到剪贴板 self.root.clipboard_clear() self.root.clipboard_append(self.report_content) # 显示成功提示 self.copy_status.config(text="已复制到剪贴板!", fg="#4CAF50") self.root.after(3000, lambda: self.copy_status.config(text="")) except Exception as e: messagebox.showerror("错误", f"复制失败:{str(e)}") def clear_report(self): """清空日报内容""" self.report_content = "" self.report_text.config(state=tk.NORMAL) self.report_text.delete(1.0, tk.END) self.report_text.insert( tk.END, '请点击左侧"昨日汇总"按钮,或选择日期后点击"生成日报"按钮...' ) self.report_text.config(state=tk.DISABLED) self.date_var.set("") self.selected_date_label.config(text="当前选中:无") self.copy_status.config(text="") def main(): """主函数""" # 创建主窗口 root = tk.Tk() # 创建应用 app = DailyReportApp(root) # 运行主循环 root.mainloop() if __name__ == "__main__": main()