Update
This commit is contained in:
296
ui/utilities/custom_dialogs.py
Normal file
296
ui/utilities/custom_dialogs.py
Normal file
@@ -0,0 +1,296 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
自定义对话框
|
||||
提供与应用主题统一的消息对话框
|
||||
"""
|
||||
import customtkinter as ctk
|
||||
from typing import Optional
|
||||
from .base_dialog import BaseDialog
|
||||
from config.constants import (
|
||||
BG_COLOR_DARK,
|
||||
BUTTON_WIDTH_MEDIUM,
|
||||
BUTTON_HEIGHT_SMALL,
|
||||
FONT_SIZE_TINY,
|
||||
FONT_SIZE_MEDIUM,
|
||||
FONT_SIZE_LARGE
|
||||
)
|
||||
|
||||
|
||||
class MessageDialog(BaseDialog):
|
||||
"""自定义消息对话框"""
|
||||
|
||||
def __init__(self, parent, title: str, message: str, dialog_type: str = "info", **kwargs):
|
||||
"""
|
||||
初始化消息对话框
|
||||
|
||||
Args:
|
||||
parent: 父窗口
|
||||
title: 对话框标题
|
||||
message: 消息内容
|
||||
dialog_type: 对话框类型 (info, warning, error, question)
|
||||
"""
|
||||
super().__init__(parent, title, 400, 200)
|
||||
|
||||
self.result = None
|
||||
self.dialog_type = dialog_type
|
||||
|
||||
# 创建界面
|
||||
self._create_widgets(message)
|
||||
|
||||
# 显示对话框
|
||||
self.show()
|
||||
|
||||
def _create_widgets(self, message: str):
|
||||
"""创建界面组件"""
|
||||
# 主容器
|
||||
main_frame = ctk.CTkFrame(self, fg_color=BG_COLOR_DARK)
|
||||
main_frame.pack(fill="both", expand=True, padx=20, pady=20)
|
||||
|
||||
# 图标和消息容器
|
||||
content_frame = ctk.CTkFrame(main_frame, fg_color="transparent")
|
||||
content_frame.pack(fill="both", expand=True)
|
||||
|
||||
# 图标
|
||||
icon_text = self._get_icon_text()
|
||||
icon_color = self._get_icon_color()
|
||||
|
||||
icon_label = ctk.CTkLabel(
|
||||
content_frame,
|
||||
text=icon_text,
|
||||
font=ctk.CTkFont(size=40),
|
||||
text_color=icon_color,
|
||||
width=60
|
||||
)
|
||||
icon_label.pack(side="left", padx=(0, 15))
|
||||
|
||||
# 消息文本
|
||||
message_label = ctk.CTkLabel(
|
||||
content_frame,
|
||||
text=message,
|
||||
font=ctk.CTkFont(size=FONT_SIZE_LARGE),
|
||||
wraplength=280,
|
||||
justify="left"
|
||||
)
|
||||
message_label.pack(side="left", fill="both", expand=True)
|
||||
|
||||
# 按钮容器
|
||||
button_frame = ctk.CTkFrame(main_frame, fg_color="transparent")
|
||||
button_frame.pack(side="bottom", pady=(20, 0))
|
||||
|
||||
# 根据对话框类型创建按钮
|
||||
if self.dialog_type == "question":
|
||||
# 是/否按钮
|
||||
no_btn = ctk.CTkButton(
|
||||
button_frame,
|
||||
text="否",
|
||||
command=self._on_no,
|
||||
width=BUTTON_WIDTH_MEDIUM,
|
||||
height=BUTTON_HEIGHT_SMALL,
|
||||
font=ctk.CTkFont(size=FONT_SIZE_MEDIUM),
|
||||
fg_color="#666666",
|
||||
hover_color="#555555"
|
||||
)
|
||||
no_btn.pack(side="left", padx=5)
|
||||
|
||||
yes_btn = ctk.CTkButton(
|
||||
button_frame,
|
||||
text="是",
|
||||
command=self._on_yes,
|
||||
width=BUTTON_WIDTH_MEDIUM,
|
||||
height=BUTTON_HEIGHT_SMALL,
|
||||
font=ctk.CTkFont(size=FONT_SIZE_MEDIUM)
|
||||
)
|
||||
yes_btn.pack(side="left", padx=5)
|
||||
yes_btn.focus_set()
|
||||
else:
|
||||
# 确定按钮
|
||||
ok_btn = ctk.CTkButton(
|
||||
button_frame,
|
||||
text="确定",
|
||||
command=self._on_ok,
|
||||
width=BUTTON_WIDTH_MEDIUM,
|
||||
height=BUTTON_HEIGHT_SMALL,
|
||||
font=ctk.CTkFont(size=FONT_SIZE_MEDIUM)
|
||||
)
|
||||
ok_btn.pack()
|
||||
ok_btn.focus_set()
|
||||
|
||||
# 绑定 Enter 键
|
||||
self.bind("<Return>", lambda e: self._on_ok() if self.dialog_type != "question" else self._on_yes())
|
||||
self.bind("<Escape>", lambda e: self._on_no() if self.dialog_type == "question" else self._on_ok())
|
||||
|
||||
def _get_icon_text(self) -> str:
|
||||
"""获取图标文本"""
|
||||
icons = {
|
||||
"info": "i",
|
||||
"warning": "!",
|
||||
"error": "X",
|
||||
"question": "?"
|
||||
}
|
||||
return icons.get(self.dialog_type, "i")
|
||||
|
||||
def _get_icon_color(self) -> str:
|
||||
"""获取图标颜色"""
|
||||
colors = {
|
||||
"info": "#3584e4",
|
||||
"warning": "#ff9800",
|
||||
"error": "#f44336",
|
||||
"question": "#3584e4"
|
||||
}
|
||||
return colors.get(self.dialog_type, "#3584e4")
|
||||
|
||||
def destroy(self):
|
||||
"""销毁对话框前解除事件绑定"""
|
||||
try:
|
||||
self.unbind("<Return>")
|
||||
self.unbind("<Escape>")
|
||||
except Exception:
|
||||
pass
|
||||
return super().destroy()
|
||||
|
||||
def _on_ok(self):
|
||||
"""确定按钮点击"""
|
||||
self.result = True
|
||||
self.destroy()
|
||||
|
||||
def _on_yes(self):
|
||||
"""是按钮点击"""
|
||||
self.result = True
|
||||
self.destroy()
|
||||
|
||||
def _on_no(self):
|
||||
"""否按钮点击"""
|
||||
self.result = False
|
||||
self.destroy()
|
||||
|
||||
|
||||
class InputDialog(BaseDialog):
|
||||
"""自定义输入对话框"""
|
||||
|
||||
def __init__(self, parent, title: str, prompt: str, initial_value: str = "", **kwargs):
|
||||
"""
|
||||
初始化输入对话框
|
||||
|
||||
Args:
|
||||
parent: 父窗口
|
||||
title: 对话框标题
|
||||
prompt: 提示文本
|
||||
initial_value: 初始值
|
||||
"""
|
||||
super().__init__(parent, title, 400, 180)
|
||||
|
||||
self.result = None
|
||||
|
||||
# 创建界面
|
||||
self._create_widgets(prompt, initial_value)
|
||||
|
||||
# 显示对话框
|
||||
self.show()
|
||||
|
||||
def _create_widgets(self, prompt: str, initial_value: str):
|
||||
"""创建界面组件"""
|
||||
# 主容器
|
||||
main_frame = ctk.CTkFrame(self, fg_color="transparent")
|
||||
main_frame.pack(fill="both", expand=True, padx=20, pady=20)
|
||||
|
||||
# 提示文本
|
||||
prompt_label = ctk.CTkLabel(
|
||||
main_frame,
|
||||
text=prompt,
|
||||
font=ctk.CTkFont(size=FONT_SIZE_LARGE),
|
||||
wraplength=360,
|
||||
justify="left"
|
||||
)
|
||||
prompt_label.pack(pady=(0, 15))
|
||||
|
||||
# 输入框
|
||||
self.entry = ctk.CTkEntry(
|
||||
main_frame,
|
||||
height=BUTTON_HEIGHT_SMALL,
|
||||
font=ctk.CTkFont(size=FONT_SIZE_MEDIUM)
|
||||
)
|
||||
self.entry.pack(fill="x", pady=(0, 20))
|
||||
self.entry.insert(0, initial_value)
|
||||
self.entry.select_range(0, "end")
|
||||
self.entry.focus_set()
|
||||
|
||||
# 按钮容器
|
||||
button_frame = ctk.CTkFrame(main_frame, fg_color="transparent")
|
||||
button_frame.pack(side="bottom")
|
||||
|
||||
# 取消按钮
|
||||
cancel_btn = ctk.CTkButton(
|
||||
button_frame,
|
||||
text="取消",
|
||||
command=self._on_cancel,
|
||||
width=BUTTON_WIDTH_MEDIUM,
|
||||
height=BUTTON_HEIGHT_SMALL,
|
||||
font=ctk.CTkFont(size=FONT_SIZE_MEDIUM),
|
||||
fg_color="#666666",
|
||||
hover_color="#555555"
|
||||
)
|
||||
cancel_btn.pack(side="left", padx=5)
|
||||
|
||||
# 确定按钮
|
||||
ok_btn = ctk.CTkButton(
|
||||
button_frame,
|
||||
text="确定",
|
||||
command=self._on_ok,
|
||||
width=BUTTON_WIDTH_MEDIUM,
|
||||
height=BUTTON_HEIGHT_SMALL,
|
||||
font=ctk.CTkFont(size=FONT_SIZE_MEDIUM)
|
||||
)
|
||||
ok_btn.pack(side="left", padx=5)
|
||||
|
||||
# 绑定键盘事件
|
||||
self.entry.bind("<Return>", lambda e: self._on_ok())
|
||||
self.bind("<Escape>", lambda e: self._on_cancel())
|
||||
|
||||
def destroy(self):
|
||||
"""销毁对话框前解除事件绑定"""
|
||||
try:
|
||||
if hasattr(self, 'entry'):
|
||||
self.entry.unbind("<Return>")
|
||||
self.unbind("<Escape>")
|
||||
except Exception:
|
||||
pass
|
||||
return super().destroy()
|
||||
|
||||
def _on_ok(self):
|
||||
"""确定按钮点击"""
|
||||
self.result = self.entry.get().strip()
|
||||
self.destroy()
|
||||
|
||||
def _on_cancel(self):
|
||||
"""取消按钮点击"""
|
||||
self.result = None
|
||||
self.destroy()
|
||||
|
||||
|
||||
# 便捷函数
|
||||
def show_info(parent, title: str, message: str):
|
||||
"""显示信息对话框"""
|
||||
MessageDialog(parent, title, message, "info")
|
||||
|
||||
|
||||
def show_warning(parent, title: str, message: str):
|
||||
"""显示警告对话框"""
|
||||
MessageDialog(parent, title, message, "warning")
|
||||
|
||||
|
||||
def show_error(parent, title: str, message: str):
|
||||
"""显示错误对话框"""
|
||||
MessageDialog(parent, title, message, "error")
|
||||
|
||||
|
||||
def ask_yes_no(parent, title: str, message: str) -> bool:
|
||||
"""显示是/否对话框"""
|
||||
dialog = MessageDialog(parent, title, message, "question")
|
||||
return dialog.result if dialog.result is not None else False
|
||||
|
||||
|
||||
def ask_string(parent, title: str, prompt: str, initial_value: str = "") -> Optional[str]:
|
||||
"""显示输入对话框"""
|
||||
dialog = InputDialog(parent, title, prompt, initial_value)
|
||||
return dialog.result
|
||||
Reference in New Issue
Block a user