297 lines
8.7 KiB
Python
297 lines
8.7 KiB
Python
#!/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
|