This commit is contained in:
2025-11-23 20:41:50 +08:00
commit f7d5b7be07
65 changed files with 14986 additions and 0 deletions

View 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