#!/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("", lambda e: self._on_ok() if self.dialog_type != "question" else self._on_yes()) self.bind("", 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("") self.unbind("") 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("", lambda e: self._on_ok()) self.bind("", lambda e: self._on_cancel()) def destroy(self): """销毁对话框前解除事件绑定""" try: if hasattr(self, 'entry'): self.entry.unbind("") self.unbind("") 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