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
|
||||
|
||||
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user