From 2496694f05cf337998c4286c5dac75625a3f343d Mon Sep 17 00:00:00 2001 From: Misaka_Company Date: Mon, 9 Feb 2026 16:00:15 +0800 Subject: [PATCH] ux: add custom result dialog with visual success/failure icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add ResultDialog class with green checkmark (✓) for success - Add red X (✕) icon for failure/partial failure - Center result dialog on screen - Use color-coded backgrounds (green/red) for better visual feedback - Replace standard messagebox for more intuitive result display Co-Authored-By: Claude Sonnet 4.5 --- gui/material_type_management_dialog.py | 154 ++++++++++++++++++++++++- 1 file changed, 150 insertions(+), 4 deletions(-) diff --git a/gui/material_type_management_dialog.py b/gui/material_type_management_dialog.py index 5488339..63325f9 100644 --- a/gui/material_type_management_dialog.py +++ b/gui/material_type_management_dialog.py @@ -16,6 +16,118 @@ from tkinter import ttk, messagebox from typing import List, Dict, Any, Optional +class ResultDialog(tk.Toplevel): + """自定义结果对话框,带有成功/失败图标""" + + def __init__(self, parent, title: str, message: str, success: bool = True): + """初始化结果对话框 + + Args: + parent: 父窗口 + title: 对话框标题 + message: 显示的消息 + success: 是否成功(成功显示绿色勾,失败显示红色叉) + """ + super().__init__(parent) + self.title(title) + self.resizable(False, False) + + # 使窗口模态 + self.transient(parent) + self.grab_set() + + # 计算居中位置 + self.update_idletasks() + width = 400 + height = 150 + screen_width = self.winfo_screenwidth() + screen_height = self.winfo_screenheight() + x = (screen_width - width) // 2 + y = (screen_height - height) // 2 + self.geometry(f"{width}x{height}+{x}+{y}") + + # 主容器 + main_frame = ttk.Frame(self, padding=20) + main_frame.pack(fill=tk.BOTH, expand=True) + + # 图标和消息容器 + content_frame = ttk.Frame(main_frame) + content_frame.pack(fill=tk.BOTH, expand=True) + + # 图标标签 + if success: + # 成功图标:绿色勾勾 + icon_label = tk.Label( + content_frame, + text="✓", + font=("Arial", 48), + fg="#22c55e", # 绿色 + bg="#f0fdf4" # 浅绿背景 + ) + icon_label.pack(side=tk.LEFT, padx=(0, 20)) + + # 背景框 + icon_frame = tk.Frame( + content_frame, + bg="#f0fdf4", + width=80, + height=80 + ) + icon_frame.place(x=0, y=0) + icon_frame.pack_propagate(False) + icon_label = tk.Label( + icon_frame, + text="✓", + font=("Arial", 48), + fg="#22c55e", + bg="#f0fdf4" + ) + icon_label.place(relx=0.5, rely=0.5, anchor="center") + else: + # 失败图标:红色叉叉 + icon_frame = tk.Frame( + content_frame, + bg="#fef2f2", # 浅红背景 + width=80, + height=80 + ) + icon_frame.pack_propagate(False) + icon_frame.pack(side=tk.LEFT, padx=(0, 20)) + + icon_label = tk.Label( + icon_frame, + text="✕", + font=("Arial", 48), + fg="#ef4444", # 红色 + bg="#fef2f2" + ) + icon_label.place(relx=0.5, rely=0.5, anchor="center") + + # 消息标签 + msg_label = tk.Label( + content_frame, + text=message, + font=("Microsoft YaHei UI", 10), + justify=tk.LEFT, + wraplength=280 + ) + msg_label.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + + # 确定按钮 + button_frame = ttk.Frame(main_frame) + button_frame.pack(fill=tk.X, pady=(10, 0)) + + ttk.Button( + button_frame, + text="确定", + command=self.destroy, + width=10 + ).pack(side=tk.RIGHT) + + # 等待窗口关闭 + self.wait_window() + + class EditableTreeview(ttk.Treeview): """可编辑的 Treeview 组件 @@ -231,6 +343,16 @@ class MaterialTypeManagementDialog(tk.Toplevel): self.title(title) self.geometry("900x650") + # 使窗口居中 + self.update_idletasks() # 确保窗口已创建 + width = self.winfo_width() + height = self.winfo_height() + screen_width = self.winfo_screenwidth() + screen_height = self.winfo_screenheight() + x = (screen_width - width) // 2 + y = (screen_height - height) // 2 + self.geometry(f"{width}x{height}+{x}+{y}") + # 使窗口模态 self.transient(parent) self.grab_set() @@ -463,6 +585,9 @@ class MaterialTypeManagementDialog(tk.Toplevel): def _load_data(self): """从数据库加载数据""" try: + # 保存当前的负责人选中状态 + previously_selected_managers = self._get_selected_managers() + from db.materials_to_be_deleted_dao import MaterialsTypeToBeDeletedDAO dao = MaterialsTypeToBeDeletedDAO() @@ -474,6 +599,25 @@ class MaterialTypeManagementDialog(tk.Toplevel): # 重新创建筛选复选框 self._create_filter_checkboxes() + # 恢复之前的负责人选中状态 + if previously_selected_managers: + # 清空全选 + self.select_all_var.set(False) + for var in self.manager_checkboxes.values(): + var.set(False) + + # 恢复之前选中的负责人 + for manager in previously_selected_managers: + if manager in self.manager_checkboxes: + self.manager_checkboxes[manager].set(True) + + # 检查是否全部选中 + all_selected = all( + self.manager_checkboxes.get(m, tk.BooleanVar(value=False)).get() + for m in self.managers + ) + self.select_all_var.set(all_selected) + # 刷新显示 self._apply_filter() self._update_status() @@ -888,12 +1032,14 @@ class MaterialTypeManagementDialog(tk.Toplevel): if stats['update_failed'] > 0: result_parts.append(f"更新失败:{stats['update_failed']} 条") - result_msg = "保存完成!\n\n" + "\n".join(result_parts) + result_msg = "\n".join(result_parts) - if (stats['insert_failed'] + stats['delete_failed'] + stats['update_failed']) > 0: - messagebox.showwarning("完成(部分失败)", result_msg) + # 使用自定义对话框显示结果 + has_failures = (stats['insert_failed'] + stats['delete_failed'] + stats['update_failed']) > 0 + if has_failures: + ResultDialog(self, "保存完成(部分失败)", result_msg, success=False) else: - messagebox.showinfo("成功", result_msg) + ResultDialog(self, "保存成功", result_msg, success=True) # 重新加载数据 self._load_data()