ux: add custom result dialog with visual success/failure icons
- 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 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,118 @@ from tkinter import ttk, messagebox
|
|||||||
from typing import List, Dict, Any, Optional
|
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):
|
class EditableTreeview(ttk.Treeview):
|
||||||
"""可编辑的 Treeview 组件
|
"""可编辑的 Treeview 组件
|
||||||
|
|
||||||
@@ -231,6 +343,16 @@ class MaterialTypeManagementDialog(tk.Toplevel):
|
|||||||
self.title(title)
|
self.title(title)
|
||||||
self.geometry("900x650")
|
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.transient(parent)
|
||||||
self.grab_set()
|
self.grab_set()
|
||||||
@@ -463,6 +585,9 @@ class MaterialTypeManagementDialog(tk.Toplevel):
|
|||||||
def _load_data(self):
|
def _load_data(self):
|
||||||
"""从数据库加载数据"""
|
"""从数据库加载数据"""
|
||||||
try:
|
try:
|
||||||
|
# 保存当前的负责人选中状态
|
||||||
|
previously_selected_managers = self._get_selected_managers()
|
||||||
|
|
||||||
from db.materials_to_be_deleted_dao import MaterialsTypeToBeDeletedDAO
|
from db.materials_to_be_deleted_dao import MaterialsTypeToBeDeletedDAO
|
||||||
|
|
||||||
dao = MaterialsTypeToBeDeletedDAO()
|
dao = MaterialsTypeToBeDeletedDAO()
|
||||||
@@ -474,6 +599,25 @@ class MaterialTypeManagementDialog(tk.Toplevel):
|
|||||||
# 重新创建筛选复选框
|
# 重新创建筛选复选框
|
||||||
self._create_filter_checkboxes()
|
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._apply_filter()
|
||||||
self._update_status()
|
self._update_status()
|
||||||
@@ -888,12 +1032,14 @@ class MaterialTypeManagementDialog(tk.Toplevel):
|
|||||||
if stats['update_failed'] > 0:
|
if stats['update_failed'] > 0:
|
||||||
result_parts.append(f"更新失败:{stats['update_failed']} 条")
|
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:
|
else:
|
||||||
messagebox.showinfo("成功", result_msg)
|
ResultDialog(self, "保存成功", result_msg, success=True)
|
||||||
|
|
||||||
# 重新加载数据
|
# 重新加载数据
|
||||||
self._load_data()
|
self._load_data()
|
||||||
|
|||||||
Reference in New Issue
Block a user