Spaces:
Running
Running
| #splah_utils.py | |
| import time | |
| import atexit | |
| class SplashManager: | |
| """Simple splash screen manager that works with main thread""" | |
| def __init__(self): | |
| self.splash_window = None | |
| self._status_text = "Initializing..." | |
| self.progress_value = 0 # Track actual progress 0-100 | |
| self.canvas_width = 320 # Progress bar dimensions (increased from 300) | |
| self.canvas_height = 36 # Increased from 30 | |
| self._after_id = None | |
| def start_splash(self): | |
| """Create splash window on main thread""" | |
| try: | |
| import tkinter as tk | |
| print("🎨 Starting splash screen...") | |
| # Create splash window on main thread | |
| self.splash_window = tk.Tk() | |
| self.splash_window.title("Loading Glossarion...") | |
| self.splash_window.geometry("450x350") | |
| self.splash_window.configure(bg='#2b2b2b') | |
| self.splash_window.resizable(False, False) | |
| self.splash_window.overrideredirect(True) | |
| # Center the window | |
| self.splash_window.update_idletasks() | |
| x = (self.splash_window.winfo_screenwidth() // 2) - 225 | |
| y = (self.splash_window.winfo_screenheight() // 2) - 175 | |
| self.splash_window.geometry(f"450x350+{x}+{y}") | |
| # Add content | |
| main_frame = tk.Frame(self.splash_window, bg='#2b2b2b', relief='raised', bd=2) | |
| main_frame.pack(fill='both', expand=True, padx=2, pady=2) | |
| # Load the actual Halgakos.ico icon | |
| self._load_icon(main_frame) | |
| # Title | |
| title_label = tk.Label(main_frame, text="Glossarion v4.8.5", | |
| bg='#2b2b2b', fg='#4a9eff', font=('Arial', 20, 'bold')) | |
| title_label.pack(pady=(10, 5)) | |
| # Subtitle | |
| subtitle_label = tk.Label(main_frame, text="Advanced AI Translation Suite", | |
| bg='#2b2b2b', fg='#cccccc', font=('Arial', 12)) | |
| subtitle_label.pack(pady=(0, 15)) | |
| # Status | |
| self.status_label = tk.Label(main_frame, text=self._status_text, | |
| bg='#2b2b2b', fg='#ffffff', font=('Arial', 11)) | |
| self.status_label.pack(pady=(10, 10)) | |
| # Progress bar container | |
| progress_frame = tk.Frame(main_frame, bg='#2b2b2b') | |
| progress_frame.pack(pady=(5, 15)) # Adjusted padding for larger bar | |
| # Progress bar background | |
| self.progress_bg = tk.Canvas(progress_frame, width=self.canvas_width, height=self.canvas_height, | |
| bg='#2b2b2b', highlightthickness=0) | |
| self.progress_bg.pack() | |
| # Create border | |
| self.progress_bg.create_rectangle(1, 1, self.canvas_width-1, self.canvas_height-1, | |
| outline='#666666', width=2) | |
| # Create background | |
| self.progress_bg.create_rectangle(3, 3, self.canvas_width-3, self.canvas_height-3, | |
| fill='#1a1a1a', outline='') | |
| # Progress bar fill (will be updated) | |
| self.progress_fill = None | |
| # Progress percentage text - moved up and with better font | |
| text_x = self.canvas_width // 2 # 160 for 320px width | |
| text_y = 13.5 # Positioned slightly above center for visual balance | |
| # Use a cleaner, more modern font | |
| progress_font = ('Montserrat', 12, 'bold') # Increased size to 12 | |
| # Create outline for better readability | |
| for dx in [-1, 0, 1]: | |
| for dy in [-1, 0, 1]: | |
| if dx != 0 or dy != 0: | |
| self.progress_bg.create_text(text_x + dx, text_y + dy, text="0%", | |
| fill='#000000', font=progress_font, | |
| tags="outline", anchor='center') | |
| # Main text on top (white) | |
| self.progress_text = self.progress_bg.create_text(text_x, text_y, text="0%", | |
| fill='#ffffff', font=progress_font, | |
| anchor='center') | |
| # Version info | |
| version_label = tk.Label(main_frame, text="Starting up...", | |
| bg='#2b2b2b', fg='#888888', font=('Arial', 9)) | |
| version_label.pack(side='bottom', pady=(0, 15)) | |
| # Start progress animation | |
| self._animate_progress() | |
| # Update the display | |
| self.splash_window.update() | |
| # Register cleanup | |
| atexit.register(self.close_splash) | |
| return True | |
| except Exception as e: | |
| print(f"⚠️ Could not start splash: {e}") | |
| return False | |
| def _load_icon(self, parent): | |
| """Load the Halgakos.ico icon""" | |
| try: | |
| # Get icon path - handle both development and packaged modes | |
| import os | |
| import sys | |
| import tkinter as tk | |
| if getattr(sys, 'frozen', False): | |
| # Running as .exe | |
| base_dir = sys._MEIPASS | |
| else: | |
| # Running as .py files | |
| base_dir = os.path.dirname(os.path.abspath(__file__)) | |
| ico_path = os.path.join(base_dir, 'Halgakos.ico') | |
| if os.path.isfile(ico_path): | |
| try: | |
| # Try PIL first for better quality | |
| from PIL import Image, ImageTk | |
| pil_image = Image.open(ico_path) | |
| pil_image = pil_image.resize((128, 128), Image.Resampling.LANCZOS) | |
| icon_photo = ImageTk.PhotoImage(pil_image, master=self.splash_window) | |
| icon_label = tk.Label(parent, image=icon_photo, bg='#2b2b2b') | |
| icon_label.image = icon_photo # Keep reference | |
| icon_label.pack(pady=(20, 10)) | |
| return | |
| except ImportError: | |
| # Fallback to basic tkinter | |
| try: | |
| icon_image = tk.PhotoImage(file=ico_path) | |
| icon_label = tk.Label(parent, image=icon_image, bg='#2b2b2b') | |
| icon_label.image = icon_image | |
| icon_label.pack(pady=(20, 10)) | |
| return | |
| except tk.TclError: | |
| pass | |
| except Exception: | |
| pass | |
| # Fallback emoji if icon loading fails | |
| import tkinter as tk | |
| icon_frame = tk.Frame(parent, bg='#4a9eff', width=128, height=128) | |
| icon_frame.pack(pady=(20, 10)) | |
| icon_frame.pack_propagate(False) | |
| icon_label = tk.Label(icon_frame, text="📚", font=('Arial', 64), | |
| bg='#4a9eff', fg='white') | |
| icon_label.pack(expand=True) | |
| def _animate_progress(self): | |
| """Animate progress bar filling up""" | |
| # Cancel any existing after callback first | |
| if self._after_id: | |
| try: | |
| self.splash_window.after_cancel(self._after_id) | |
| except: | |
| pass | |
| self._after_id = None | |
| if self.splash_window and self.splash_window.winfo_exists(): | |
| try: | |
| # Auto-increment progress for visual effect during startup | |
| if self.progress_value < 100: | |
| # Increment at different rates for different phases | |
| if self.progress_value < 30: | |
| self.progress_value += 8 # Fast initial progress | |
| elif self.progress_value < 70: | |
| self.progress_value += 4 # Medium progress | |
| elif self.progress_value < 90: | |
| self.progress_value += 2 # Slow progress | |
| else: | |
| self.progress_value += 1 # Very slow final progress | |
| # Cap at 99% until explicitly set to 100% | |
| if self.progress_value >= 99: | |
| self.progress_value = 99 | |
| # Update progress bar fill | |
| if self.progress_fill: | |
| self.progress_bg.delete(self.progress_fill) | |
| # Also delete old highlight | |
| self.progress_bg.delete("highlight") | |
| # Calculate fill width (3 to canvas_width-3) | |
| fill_width = int((self.progress_value / 100) * (self.canvas_width - 6)) # -6 for borders | |
| if fill_width > 0: | |
| # Create gradient effect | |
| self.progress_fill = self.progress_bg.create_rectangle( | |
| 3, 3, 3 + fill_width, self.canvas_height - 3, | |
| fill='#4a9eff', outline='' | |
| ) | |
| # Add a highlight effect (adjusted for new height) | |
| if fill_width > 10: | |
| self.progress_bg.create_rectangle( | |
| 3, 3, min(13, 3 + fill_width), 12, | |
| fill='#6bb6ff', outline='', tags="highlight" | |
| ) | |
| # Update percentage text without changing position | |
| percent_text = f"{self.progress_value}%" | |
| # Update main text | |
| self.progress_bg.itemconfig(self.progress_text, text=percent_text) | |
| # Update all outline layers | |
| for item in self.progress_bg.find_withtag("outline"): | |
| self.progress_bg.itemconfig(item, text=percent_text) | |
| # Ensure text stays on top of progress fill | |
| self.progress_bg.tag_raise("outline") | |
| self.progress_bg.tag_raise(self.progress_text) | |
| # Store the after ID so we can cancel it later | |
| self._after_id = self.splash_window.after(100, self._animate_progress) | |
| except Exception: | |
| self._after_id = None | |
| pass | |
| def update_status(self, message): | |
| """Update splash status and progress with enhanced module loading support""" | |
| self._status_text = message | |
| try: | |
| if self.splash_window and hasattr(self, 'status_label'): | |
| self.status_label.config(text=message) | |
| # Enhanced progress mapping starting module loading at 10% | |
| progress_map = { | |
| "Loading theme framework...": 5, | |
| "Loading UI framework...": 8, | |
| # Module loading phase - starts at 10% and goes to 85% | |
| "Loading translation modules...": 10, | |
| "Initializing module system...": 15, | |
| "Loading translation engine...": 20, | |
| "Validating translation engine...": 30, | |
| "✅ translation engine loaded": 40, | |
| "Loading glossary extractor...": 45, | |
| "Validating glossary extractor...": 55, | |
| "✅ glossary extractor loaded": 65, | |
| "Loading EPUB converter...": 70, | |
| "✅ EPUB converter loaded": 75, | |
| "Loading QA scanner...": 78, | |
| "✅ QA scanner loaded": 82, | |
| "Finalizing module initialization...": 85, | |
| "✅ All modules loaded successfully": 88, | |
| "Creating main window...": 92, | |
| "Ready!": 100 | |
| } | |
| # Check for exact matches first | |
| if message in progress_map: | |
| self.set_progress(progress_map[message]) | |
| else: | |
| # Check for partial matches | |
| for key, value in progress_map.items(): | |
| if key in message: | |
| self.set_progress(value) | |
| break | |
| self.splash_window.update() | |
| except: | |
| pass | |
| def set_progress(self, value): | |
| """Manually set progress value (0-100)""" | |
| self.progress_value = max(0, min(100, value)) | |
| def close_splash(self): | |
| """Close the splash screen with proper text visibility""" | |
| try: | |
| # IMPORTANT: Cancel the animation first | |
| if self._after_id and self.splash_window: | |
| try: | |
| self.splash_window.after_cancel(self._after_id) | |
| except: | |
| pass | |
| self._after_id = None | |
| if self.splash_window and self.splash_window.winfo_exists(): | |
| # Set to 100% and ensure text is visible | |
| self.progress_value = 100 | |
| # Update display one last time without scheduling another callback | |
| if hasattr(self, 'progress_fill') and self.progress_fill: | |
| self.progress_bg.delete(self.progress_fill) | |
| self.progress_bg.delete("highlight") | |
| # Create the 100% progress bar (but leave space for text) | |
| fill_width = int((self.progress_value / 100) * (self.canvas_width - 6)) | |
| if fill_width > 0: | |
| # Create progress fill that doesn't cover the text area | |
| self.progress_fill = self.progress_bg.create_rectangle( | |
| 3, 3, 3 + fill_width, self.canvas_height - 3, | |
| fill='#4a9eff', outline='' | |
| ) | |
| # Add highlight effect | |
| if fill_width > 10: | |
| self.progress_bg.create_rectangle( | |
| 3, 3, min(13, 3 + fill_width), 12, | |
| fill='#6bb6ff', outline='', tags="highlight" | |
| ) | |
| # CRITICAL: Make sure text stays on top and is visible | |
| if hasattr(self, 'progress_text'): | |
| self.progress_bg.itemconfig(self.progress_text, text="100%", fill='#ffffff') | |
| # Update all outline layers for better visibility | |
| for item in self.progress_bg.find_withtag("outline"): | |
| self.progress_bg.itemconfig(item, text="100%", fill='#000000') | |
| # Ensure text layers are on top of progress fill | |
| self.progress_bg.tag_raise("outline") | |
| if hasattr(self, 'progress_text'): | |
| self.progress_bg.tag_raise(self.progress_text) | |
| self.splash_window.update() | |
| time.sleep(0.1) | |
| self.splash_window.destroy() | |
| self.splash_window = None | |
| except: | |
| # Ensure cleanup even on error | |
| self._after_id = None | |
| self.splash_window = None | |