""" 右侧统计面板 """ import customtkinter as ctk from typing import Dict, Callable, Optional class StatisticsPanel(ctk.CTkFrame): """右侧统计面板""" def __init__( self, parent, on_refresh_callback: Callable, on_download_history_callback: Callable, on_export_callback: Callable, on_settings_callback: Callable, on_item_click_callback: Optional[Callable] = None, **kwargs ): super().__init__(parent, **kwargs) self.on_refresh_callback = on_refresh_callback self.on_download_history_callback = on_download_history_callback self.on_export_callback = on_export_callback self.on_settings_callback = on_settings_callback self.on_item_click_callback = on_item_click_callback self._create_widgets() def _create_widgets(self): """创建UI组件""" # 标题 title_label = ctk.CTkLabel( self, text="统计信息", font=("Arial", 16, "bold") ) title_label.pack(pady=(15, 10), padx=15) # ==================== 当前数据源信息 ==================== datasource_frame = ctk.CTkFrame(self) datasource_frame.pack(fill="x", padx=15, pady=(0, 10)) datasource_title = ctk.CTkLabel( datasource_frame, text="当前数据源", font=("Arial", 12, "bold") ) datasource_title.pack(pady=(10, 5), padx=10) self.datasource_label = ctk.CTkLabel( datasource_frame, text="菜品", font=("Arial", 14, "bold"), text_color="blue" ) self.datasource_label.pack(pady=(0, 10), padx=10) # ==================== 数据统计 ==================== stats_frame = ctk.CTkFrame(self) stats_frame.pack(fill="x", padx=15, pady=(0, 10)) stats_title = ctk.CTkLabel( stats_frame, text="数据统计", font=("Arial", 12, "bold") ) stats_title.pack(pady=(10, 5), padx=10) # 查询结果 self.query_result_label = ctk.CTkLabel( stats_frame, text="查询结果: 0 张", font=("Arial", 11), anchor="w" ) self.query_result_label.pack(fill="x", pady=2, padx=15) # 已选择 self.selected_label = ctk.CTkLabel( stats_frame, text="已选择: 0 张", font=("Arial", 11), anchor="w" ) self.selected_label.pack(fill="x", pady=2, padx=15) # 分隔线 separator1 = ctk.CTkFrame(stats_frame, height=2) separator1.pack(fill="x", pady=10, padx=15) # ==================== 时间分布 ==================== time_frame = ctk.CTkFrame(self) time_frame.pack(fill="x", padx=15, pady=(0, 10)) time_title = ctk.CTkLabel( time_frame, text="时间分布", font=("Arial", 12, "bold") ) time_title.pack(pady=(10, 5), padx=10) # 今日 self.today_label = ctk.CTkLabel( time_frame, text="今日: 0 张", font=("Arial", 11), anchor="w" ) self.today_label.pack(fill="x", pady=2, padx=15) # 本周 self.week_label = ctk.CTkLabel( time_frame, text="本周: 0 张", font=("Arial", 11), anchor="w" ) self.week_label.pack(fill="x", pady=2, padx=15) # 本月 self.month_label = ctk.CTkLabel( time_frame, text="本月: 0 张", font=("Arial", 11), anchor="w" ) self.month_label.pack(fill="x", pady=2, padx=15) # 分隔线 separator2 = ctk.CTkFrame(time_frame, height=2) separator2.pack(fill="x", pady=10, padx=15) # ==================== 物品排行(全部) ==================== top_frame = ctk.CTkFrame(self) top_frame.pack(fill="both", expand=True, padx=15, pady=(0, 10)) top_title = ctk.CTkLabel( top_frame, text="物品排行(全部)", font=("Arial", 12, "bold") ) top_title.pack(pady=(10, 5), padx=10) # 可滚动的排行榜 self.top_scroll = ctk.CTkScrollableFrame(top_frame, height=200) self.top_scroll.pack(fill="both", expand=True, padx=10, pady=(0, 10)) # ==================== 快捷操作 ==================== actions_frame = ctk.CTkFrame(self) actions_frame.pack(fill="x", padx=15, pady=(0, 15)) actions_title = ctk.CTkLabel( actions_frame, text="快捷操作", font=("Arial", 12, "bold") ) actions_title.pack(pady=(10, 5), padx=10) # 刷新按钮 refresh_btn = ctk.CTkButton( actions_frame, text="🔄 刷新", command=self.on_refresh_callback, height=30 ) refresh_btn.pack(fill="x", padx=10, pady=2) # 下载历史按钮 history_btn = ctk.CTkButton( actions_frame, text="📜 下载历史", command=self.on_download_history_callback, height=30, fg_color="purple", hover_color="darkviolet" ) history_btn.pack(fill="x", padx=10, pady=2) # 导出按钮 export_btn = ctk.CTkButton( actions_frame, text="📊 导出数据", command=self.on_export_callback, height=30, fg_color="orange", hover_color="darkorange" ) export_btn.pack(fill="x", padx=10, pady=2) # 设置按钮 settings_btn = ctk.CTkButton( actions_frame, text="⚙️ 设置", command=self.on_settings_callback, height=30, fg_color="gray", hover_color="darkgray" ) settings_btn.pack(fill="x", padx=10, pady=(2, 10)) def update_datasource(self, datasource: str): """更新当前数据源显示""" self.datasource_label.configure(text=datasource) def update_statistics(self, stats: Dict): """ 更新统计信息 Args: stats: 统计数据字典,包含: - total_count: 总数量 - today_count: 今日数量 - week_count: 本周数量 - month_count: 本月数量 - top_items: 全部物品列表 [{'name': str, 'count': int}, ...] """ # 查询结果 total_count = stats.get('total_count', 0) self.query_result_label.configure(text=f"查询结果: {total_count} 张") # 时间分布 today_count = stats.get('today_count', 0) self.today_label.configure(text=f"今日: {today_count} 张") week_count = stats.get('week_count', 0) self.week_label.configure(text=f"本周: {week_count} 张") month_count = stats.get('month_count', 0) self.month_label.configure(text=f"本月: {month_count} 张") # TOP 10排行 self._update_top_items(stats.get('top_items', [])) def _update_top_items(self, top_items: list): """更新物品排行(全部)""" # 清空现有内容 for widget in self.top_scroll.winfo_children(): widget.destroy() if not top_items: no_data_label = ctk.CTkLabel( self.top_scroll, text="暂无数据", font=("Arial", 11), text_color="gray" ) no_data_label.pack(pady=10) return # 显示全部物品 for i, item in enumerate(top_items, 1): item_frame = ctk.CTkFrame(self.top_scroll) item_frame.pack(fill="x", pady=2, padx=5) # 排名 rank_label = ctk.CTkLabel( item_frame, text=f"{i}.", font=("Arial", 11, "bold"), width=30 ) rank_label.pack(side="left", padx=(5, 2)) # 物品名称(可点击筛选) name_label = ctk.CTkLabel( item_frame, text=item['name'], font=("Arial", 10), anchor="w", cursor="hand2" # 鼠标悬浮时显示手型 ) name_label.pack(side="left", fill="x", expand=True, padx=5) # 绑定点击与悬浮事件 if self.on_item_click_callback: item_name = item['name'] name_label.bind( "", lambda e, name=item_name: self.on_item_click_callback(name) ) name_label.bind( "", lambda e, lbl=name_label: lbl.configure(text_color="#4FC3F7") ) name_label.bind( "", lambda e, lbl=name_label: lbl.configure(text_color=ctk.ThemeManager.theme["CTkLabel"]["text_color"]) ) # 数量 count_label = ctk.CTkLabel( item_frame, text=f"{item['count']}张", font=("Arial", 10), text_color="blue" ) count_label.pack(side="right", padx=5) def update_selected_count(self, count: int): """更新已选择数量""" self.selected_label.configure(text=f"已选择: {count} 张") def clear_statistics(self): """清空统计信息""" self.query_result_label.configure(text="查询结果: 0 张") self.selected_label.configure(text="已选择: 0 张") self.today_label.configure(text="今日: 0 张") self.week_label.configure(text="本周: 0 张") self.month_label.configure(text="本月: 0 张") # 清空全部物品排行 for widget in self.top_scroll.winfo_children(): widget.destroy()