From 116974ed3634484acafd7ec7557ed9514fe03eb3 Mon Sep 17 00:00:00 2001 From: "qichi.liang" Date: Mon, 29 Dec 2025 02:41:08 +0800 Subject: [PATCH] Improve GUI: add visual report display with formatting --- src/gui.py | 173 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 150 insertions(+), 23 deletions(-) diff --git a/src/gui.py b/src/gui.py index 43c1b27..ea70313 100644 --- a/src/gui.py +++ b/src/gui.py @@ -19,13 +19,136 @@ from src.database import DailyLogsDatabase from src.report import DailyReportGenerator +class ReportFrame(ttk.LabelFrame): + """日报显示框架""" + + def __init__(self, parent): + super().__init__(parent, text="日报预览", padding="10") + self.parent = parent + self._create_widgets() + + def _create_widgets(self): + """创建组件""" + # 日期标题 + self.date_label = ttk.Label(self, text="--月--日", font=('SimHei', 14, 'bold')) + self.date_label.pack(pady=(0, 10)) + + # 船次信息表格 + self.ships_frame = ttk.Frame(self) + self.ships_frame.pack(fill=tk.X, pady=5) + + # 统计信息 + self.stats_frame = ttk.Frame(self) + self.stats_frame.pack(fill=tk.X, pady=10) + + # 人员信息 + self.personnel_frame = ttk.Frame(self) + self.personnel_frame.pack(fill=tk.X, pady=10) + + def display_report(self, report_text: str, date: str): + """显示日报""" + # 解析日报内容 + lines = report_text.strip().split('\n') + + # 更新日期 + self.date_label.config(text=f"{date[5:7]}月{date[8:10]}日") + + # 清空旧数据 + for widget in self.ships_frame.winfo_children(): + widget.destroy() + for widget in self.stats_frame.winfo_children(): + widget.destroy() + for widget in self.personnel_frame.winfo_children(): + widget.destroy() + + # 解析并显示各部分 + current_section = None + ships_data = [] + stats_data = {} + + for line in lines: + line = line.strip() + if not line: + continue + + if line.startswith('日期:'): + continue + elif line.startswith('船名:'): + current_section = 'ships' + ships_data.append({'name': line.replace('船名:', '').strip(), 'teu': None}) + elif line.startswith('作业量:'): + if ships_data: + ships_data[-1]['teu'] = line.replace('作业量:', '').replace('TEU', '').strip() + elif line.startswith('当日实际作业量:'): + stats_data['daily_total'] = line.replace('当日实际作业量:', '').replace('TEU', '').strip() + elif line.startswith('当月计划作业量:'): + stats_data['monthly_plan'] = line.replace('当月计划作业量:', '').replace('TEU', '').replace(' (用天数*300TEU)', '').strip() + elif line.startswith('当月实际作业量:'): + stats_data['monthly_actual'] = line.replace('当月实际作业量:', '').replace('TEU', '').strip() + elif line.startswith('当月完成比例:'): + stats_data['completion'] = line.replace('当月完成比例:', '').replace('%', '').strip() + elif '白班人员' in line: + stats_data['day_shift'] = line.split(':')[1].strip() if ':' in line else '' + elif '夜班人员' in line: + stats_data['night_shift'] = line.split(':')[1].strip() if ':' in line else '' + elif '值班手机' in line: + stats_data['phone'] = line.split(':')[1].strip() if ':' in line else '' + + # 显示船次信息 + if ships_data: + ttk.Label(self.ships_frame, text="船次信息", font=('', 10, 'bold')).pack(anchor=tk.W) + for ship in ships_data: + ship_frame = ttk.Frame(self.ships_frame) + ship_frame.pack(fill=tk.X, pady=2) + ttk.Label(ship_frame, text=f"🚢 {ship['name']}", foreground='#1976D2').pack(side=tk.LEFT) + ttk.Label(ship_frame, text=f"{ship['teu']} TEU", font=('', 10, 'bold'), foreground='#388E3C').pack(side=tk.RIGHT) + + # 显示统计信息 + if stats_data: + ttk.Label(self.stats_frame, text="统计信息", font=('', 10, 'bold')).pack(anchor=tk.W) + + stats_grid = ttk.Frame(self.stats_frame) + stats_grid.pack(fill=tk.X, pady=5) + + # 第一行 + ttk.Label(stats_grid, text="当日作业:").grid(row=0, column=0, sticky=tk.W, padx=(0, 10)) + ttk.Label(stats_grid, text=f"{stats_data.get('daily_total', '0')} TEU", foreground='#388E3C', font=('', 10, 'bold')).grid(row=0, column=1, sticky=tk.W) + + ttk.Label(stats_grid, text="月计划:").grid(row=0, column=2, sticky=tk.W, padx=(20, 10)) + ttk.Label(stats_grid, text=f"{stats_data.get('monthly_plan', '0')} TEU").grid(row=0, column=3, sticky=tk.W) + + # 第二行 + ttk.Label(stats_grid, text="月实际:").grid(row=1, column=0, sticky=tk.W, padx=(0, 10), pady=(5, 0)) + ttk.Label(stats_grid, text=f"{stats_data.get('monthly_actual', '0')} TEU", foreground='#388E3C', font=('', 10, 'bold')).grid(row=1, column=1, sticky=tk.W, pady=(5, 0)) + + ttk.Label(stats_grid, text="完成率:").grid(row=1, column=2, sticky=tk.W, padx=(20, 10), pady=(5, 0)) + completion = float(stats_data.get('completion', 0)) + color = '#388E3C' if completion >= 100 else '#FF9800' if completion >= 80 else '#F44336' + ttk.Label(stats_grid, text=f"{stats_data.get('completion', '0')}%", foreground=color, font=('', 10, 'bold')).grid(row=1, column=3, sticky=tk.W, pady=(5, 0)) + + # 显示人员信息 + ttk.Label(self.personnel_frame, text="班次人员", font=('', 10, 'bold')).pack(anchor=tk.W) + + personnel_grid = ttk.Frame(self.personnel_frame) + personnel_grid.pack(fill=tk.X, pady=5) + + ttk.Label(personnel_grid, text="☀️ 白班:").grid(row=0, column=0, sticky=tk.W, padx=(0, 10)) + ttk.Label(personnel_grid, text=stats_data.get('day_shift', '-')).grid(row=0, column=1, sticky=tk.W) + + ttk.Label(personnel_grid, text="🌙 夜班:").grid(row=1, column=0, sticky=tk.W, padx=(0, 10), pady=(5, 0)) + ttk.Label(personnel_grid, text=stats_data.get('night_shift', '-')).grid(row=1, column=1, sticky=tk.W, pady=(5, 0)) + + ttk.Label(personnel_grid, text="📞 值班:").grid(row=2, column=0, sticky=tk.W, padx=(0, 10), pady=(5, 0)) + ttk.Label(personnel_grid, text=stats_data.get('phone', '-'), foreground='#1976D2').grid(row=2, column=1, sticky=tk.W, pady=(5, 0)) + + class OrbitInGUI: """码头作业日志管理工具 GUI""" def __init__(self, root): self.root = root self.root.title("码头作业日志管理工具 - OrbitIn") - self.root.geometry("800x600") + self.root.geometry("900x650") self.root.resizable(True, True) # 设置样式 @@ -37,10 +160,10 @@ class OrbitInGUI: main_frame.pack(fill=tk.BOTH, expand=True) # 左侧控制面板 - left_frame = ttk.Frame(main_frame) + left_frame = ttk.LabelFrame(main_frame, text="操作面板", padding="10") left_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10)) - # 右侧输出区域 + # 右侧主区域 right_frame = ttk.Frame(main_frame) right_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) @@ -140,19 +263,25 @@ class OrbitInGUI: ) btn_clear.pack(pady=5) - # === 右侧输出区域 === + # === 右侧主区域 === # 状态标签 self.status_var = tk.StringVar(value="就绪") status_label = ttk.Label(right_frame, textvariable=self.status_var) status_label.pack(anchor=tk.W) + # 日报显示框架 + self.report_frame = ReportFrame(right_frame) + self.report_frame.pack(fill=tk.X, pady=(5, 10)) + # 输出文本框 + ttk.Label(right_frame, text="日志输出:").pack(anchor=tk.W) self.output_text = scrolledtext.ScrolledText( right_frame, wrap=tk.WORD, - font=('Consolas', 10), - bg='#f5f5f5' + font=('Consolas', 9), + bg='#f5f5f5', + height=10 ) self.output_text.pack(fill=tk.BOTH, expand=True, pady=(5, 0)) @@ -162,10 +291,10 @@ class OrbitInGUI: # 初始消息 self.log_message("码头作业日志管理工具 - OrbitIn") self.log_message("=" * 50) - self.log_message("日期格式: YYYY-MM-DD") - self.log_message("月份格式: YYYY-MM") - self.log_message("") self.log_message("按 Ctrl+Enter 快速获取数据") + + # 默认显示今日日报 + self.generate_today_report() def log_message(self, message, is_error=False): """输出日志消息""" @@ -185,20 +314,6 @@ class OrbitInGUI: self.status_var.set(status) self.root.update() - def run_in_thread(self, func, *args, **kwargs): - """在后台线程中运行函数""" - def worker(): - try: - func(*args, **kwargs) - except Exception as e: - self.root.after(0, lambda: self.log_message(str(e), is_error=True)) - self.root.after(0, lambda: self.set_status("错误")) - finally: - self.root.after(0, lambda: self.set_status("就绪")) - - thread = threading.Thread(target=worker, daemon=True) - thread.start() - def fetch_data(self): """获取并处理数据""" self.set_status("正在获取数据...") @@ -253,6 +368,9 @@ class OrbitInGUI: stats = db.get_stats() db.close() self.log_message(f"数据库总计: {stats['total']} 条记录, {len(stats['ships'])} 艘船") + + # 刷新日报显示 + self.generate_today_report() else: self.log_message("未解析到任何记录") @@ -296,6 +414,9 @@ class OrbitInGUI: count = db.insert_many([log.to_dict() for log in logs]) db.close() self.log_message(f"已保存 {count} 条记录") + + # 刷新日报显示 + self.generate_today_report() self.set_status("完成") @@ -325,11 +446,15 @@ class OrbitInGUI: report = g.generate_report(date) g.close() + # 在日志中显示原始内容 self.log_message("") self.log_message("=" * 40) self.log_message(report) self.log_message("=" * 40) + # 在可视化区域显示 + self.report_frame.display_report(report, date) + self.set_status("完成") except Exception as e: @@ -367,6 +492,8 @@ class OrbitInGUI: if result: self.log_message("添加成功!") + # 刷新日报显示 + self.generate_today_report() else: self.log_message("添加失败!", is_error=True)