Tesseract Translator

 import subprocess

import sys

import os

import platform

import tkinter as tk

from tkinter import messagebox

from pathlib import Path


# Check if we're running as compiled exe

IS_COMPILED = getattr(sys, 'frozen', False)


def create_installer_batch():

    """

    Create a batch file to install all dependencies

    """

    batch_content = """@echo off

echo ============================================================

echo SCREEN TRANSLATOR - DEPENDENCY INSTALLER

echo ============================================================

echo.

echo This script will install the required dependencies.

echo Please wait...

echo.


echo [1/3] Installing Python packages...

python -m pip install --upgrade pip

python -m pip install Pillow pystray keyboard pytesseract requests deep-translator


if %errorlevel% neq 0 (

    echo.

    echo ERROR: Failed to install Python packages.

    echo Please make sure Python is installed and added to PATH.

    echo.

    pause

    exit /b 1

)


echo.

echo [2/3] Checking Tesseract OCR...


rem Check if Tesseract is already installed

where tesseract >nul 2>&1

if %errorlevel% equ 0 (

    echo Tesseract OCR is already installed.

    goto :done

)


rem Check common installation paths

if exist "C:\\Program Files\\Tesseract-OCR\\tesseract.exe" (

    echo Tesseract OCR found at "C:\\Program Files\\Tesseract-OCR\\"

    goto :done

)


if exist "C:\\Program Files (x86)\\Tesseract-OCR\\tesseract.exe" (

    echo Tesseract OCR found at "C:\\Program Files (x86)\\Tesseract-OCR\\"

    goto :done

)


echo Tesseract OCR not found. Downloading installer...


rem Download Tesseract installer

powershell -Command "& {[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest -Uri 'https://digi.bib.uni-mannheim.de/tesseract/tesseract-ocr-w64-setup-5.3.3.20231005.exe' -OutFile '%TEMP%\\tesseract_installer.exe'}"


if %errorlevel% neq 0 (

    echo.

    echo ERROR: Failed to download Tesseract installer.

    echo Please check your internet connection.

    echo.

    pause

    exit /b 1

)


echo.

echo [3/3] Installing Tesseract OCR...

echo.

echo IMPORTANT: Please follow the installation wizard:

echo - Click "Next" through the installer

echo - Keep the default installation path

echo - Make sure "English" language is selected

echo - Click "Install" and wait for completion

echo.

echo The installer will open in a moment...

timeout /t 3 >nul


start /wait "" "%TEMP%\\tesseract_installer.exe"


rem Clean up installer

del "%TEMP%\\tesseract_installer.exe" 2>nul


:done

echo.

echo ============================================================

echo INSTALLATION COMPLETE!

echo ============================================================

echo.

echo All dependencies have been installed successfully.

echo Please close this window and restart the Screen Translator.

echo.

pause

exit /b 0

"""

    

    batch_file = "install_dependencies.bat"

    try:

        with open(batch_file, 'w') as f:

            f.write(batch_content)

        print(f"[SETUP] Created installer batch file: {batch_file}")

        return batch_file

    except Exception as e:

        print(f"[ERROR] Failed to create batch file: {e}")

        return None


def check_dependencies():

    """

    Check if all required dependencies are installed

    """

    print("[SETUP] Checking dependencies...")

    

    missing_deps = []

    

    # Check Python packages using importlib

    import importlib.util

    

    packages_to_check = {

        'PIL': 'Pillow',

        'pystray': 'pystray', 

        'keyboard': 'keyboard',

        'pytesseract': 'pytesseract',

        'requests': 'requests',

        'deep_translator': 'deep_translator'

    }

    

    for import_name, display_name in packages_to_check.items():

        try:

            spec = importlib.util.find_spec(import_name)

            if spec is not None:

                print(f"[SETUP] ✓ {display_name} found")

            else:

                print(f"[SETUP] ✗ {display_name} missing")

                missing_deps.append(display_name)

        except (ImportError, ModuleNotFoundError, ValueError):

            print(f"[SETUP] ✗ {display_name} missing")

            missing_deps.append(display_name)

    

    # Check Tesseract

    tesseract_found = False

    tesseract_paths = [

        r"C:\Program Files\Tesseract-OCR\tesseract.exe",

        r"C:\Program Files (x86)\Tesseract-OCR\tesseract.exe",

    ]

    

    for path in tesseract_paths:

        if os.path.exists(path):

            tesseract_found = True

            print(f"[SETUP] ✓ Tesseract found at {path}")

            break

    

    if not tesseract_found:

        # Try to find in PATH

        try:

            result = subprocess.run(['where', 'tesseract'], capture_output=True, text=True)

            if result.returncode == 0:

                tesseract_found = True

                print(f"[SETUP] ✓ Tesseract found in PATH")

        except:

            pass

    

    if not tesseract_found:

        print("[SETUP] ✗ Tesseract OCR missing")

        missing_deps.append('tesseract')

    

    return len(missing_deps) == 0


def run_installer():

    """

    Create and run the installer batch file

    """

    print("\n" + "="*60)

    print("MISSING DEPENDENCIES DETECTED")

    print("="*60)

    

    # Create batch file

    batch_file = create_installer_batch()

    if not batch_file:

        show_error_dialog()

        return False

    

    print(f"\n[SETUP] Running installer: {batch_file}")

    print("[SETUP] Please follow the instructions in the installer window...")

    

    try:

        # Run batch file

        if IS_COMPILED:

            batch_path = os.path.join(os.path.dirname(sys.executable), batch_file)

        else:

            batch_path = batch_file

        

        subprocess.run([batch_path], shell=True)

        

        # Show restart message

        show_restart_dialog()

        return True

        

    except Exception as e:

        print(f"[ERROR] Failed to run installer: {e}")

        show_error_dialog()

        return False


def show_restart_dialog():

    """

    Show a dialog asking user to restart the application

    """

    root = tk.Tk()

    root.withdraw()

    

    message = """Installation Complete!


All required dependencies have been installed.


Please restart Screen Translator for the changes to take effect.


Click OK to close the application."""

    

    messagebox.showinfo("Installation Complete", message)

    root.destroy()

    sys.exit(0)


def show_error_dialog():

    """

    Show error dialog with manual installation instructions

    """

    root = tk.Tk()

    root.withdraw()

    

    message = """Failed to install dependencies automatically.


Please install manually:


1. Open Command Prompt as Administrator

2. Run these commands:

   pip install Pillow pystray keyboard pytesseract requests deep-translator


3. Download and install Tesseract OCR from:

   https://github.com/UB-Mannheim/tesseract/wiki


Then restart Screen Translator."""

    

    messagebox.showerror("Installation Failed", message)

    root.destroy()


# Main dependency check

print("="*60)

print("SCREEN TRANSLATOR - STARTING UP")

print("="*60)


if not check_dependencies():

    print("\n[SETUP] Some dependencies are missing.")

    print("[SETUP] Starting automatic installation...")

    

    if not run_installer():

        sys.exit(1)

    else:

        sys.exit(0)


print("\n[SETUP] All dependencies are installed!")

print("[SETUP] Starting application...\n")


# Now import the main application modules

import base64

import io

import json

import threading

from tkinter import ttk, filedialog

from dataclasses import dataclass

from typing import Optional

from PIL import Image, ImageTk, ImageGrab

import pystray

from PIL import ImageDraw

import keyboard

import time

import math

import traceback

import hashlib

import pytesseract

from deep_translator import GoogleTranslator


# Get Tesseract path

def get_tesseract_path():

    paths = [

        r"C:\Program Files\Tesseract-OCR\tesseract.exe",

        r"C:\Program Files (x86)\Tesseract-OCR\tesseract.exe",

    ]

    for path in paths:

        if os.path.exists(path):

            return path

    return "tesseract"


pytesseract.pytesseract.tesseract_cmd = get_tesseract_path()


CONFIG_FILE = "config.json"


DEFAULT_CONFIG = {

    "api_key": "",

    "shortcuts": ("alt+q", "alt+x"),

    "capture_theme": "classic",

    "start_as_icon": False,

    "src_lang": "auto",

    "tgt_lang": "en",

    "temperature": 0.2,

    "window_geometry": "",

    "hide_console": True

}


CAPTURE_THEMES = {

    "classic": {

        "name": "Classic (White/Black)",

        "bg_outline": "black",

        "bg_width": 3,

        "fg_outline": "white",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "red": {

        "name": "Red",

        "bg_outline": "#8B0000",

        "bg_width": 3,

        "fg_outline": "#FF0000",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "green": {

        "name": "Green",

        "bg_outline": "#006400",

        "bg_width": 3,

        "fg_outline": "#00FF00",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "blue": {

        "name": "Blue",

        "bg_outline": "#00008B",

        "bg_width": 3,

        "fg_outline": "#0000FF",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "yellow": {

        "name": "Yellow",

        "bg_outline": "#8B8B00",

        "bg_width": 3,

        "fg_outline": "#FFFF00",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "cyan": {

        "name": "Cyan",

        "bg_outline": "#008B8B",

        "bg_width": 3,

        "fg_outline": "#00FFFF",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "magenta": {

        "name": "Magenta",

        "bg_outline": "#8B008B",

        "bg_width": 3,

        "fg_outline": "#FF00FF",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "orange": {

        "name": "Orange",

        "bg_outline": "#8B4500",

        "bg_width": 3,

        "fg_outline": "#FFA500",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "purple": {

        "name": "Purple",

        "bg_outline": "#4B0082",

        "bg_width": 3,

        "fg_outline": "#9370DB",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "green_purple": {

        "name": "Green/Purple",

        "bg_outline": "#800080",

        "bg_width": 3,

        "fg_outline": "#00FF00",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "red_blue": {

        "name": "Red/Blue",

        "bg_outline": "#0000FF",

        "bg_width": 3,

        "fg_outline": "#FF0000",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "yellow_black": {

        "name": "Yellow/Black",

        "bg_outline": "black",

        "bg_width": 3,

        "fg_outline": "#FFFF00",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "cyan_magenta": {

        "name": "Cyan/Magenta",

        "bg_outline": "#FF00FF",

        "bg_width": 3,

        "fg_outline": "#00FFFF",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "orange_teal": {

        "name": "Orange/Teal",

        "bg_outline": "#008080",

        "bg_width": 3,

        "fg_outline": "#FFA500",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "pink_aqua": {

        "name": "Pink/Aquamarine",

        "bg_outline": "#7FFFD4",

        "bg_width": 3,

        "fg_outline": "#FFC0CB",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "gold_navy": {

        "name": "Gold/Navy Blue",

        "bg_outline": "#000080",

        "bg_width": 3,

        "fg_outline": "#FFD700",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": False

    },

    "rainbow": {

        "name": "Rainbow",

        "bg_outline": "black",

        "bg_width": 3,

        "fg_outline": "red",

        "fg_width": 2,

        "fg_dash": (5, 5),

        "rainbow": True

    }

}


LANGUAGE_MAP = {

    "Auto": "auto",

    "English": "en",

    "Spanish": "es",

    "French": "fr",

    "German": "de",

    "Italian": "it",

    "Portuguese": "pt",

    "Russian": "ru",

    "Japanese": "ja",

    "Chinese": "zh-CN",

    "Korean": "ko",

    "Arabic": "ar"

}



class OCRTranslator:

    def __init__(self):

        """

        Initialize OCR and Translation services

        """

        pass

    

    def extract_text_from_image(self, image):

        """

        Extract text from image using Tesseract OCR

        """

        try:

            print("[LOG] Starting OCR with Tesseract...")

            text = pytesseract.image_to_string(image)

            print(f"[LOG] OCR completed, extracted {len(text)} characters")

            return text.strip()

        except Exception as e:

            print(f"[LOG] OCR error: {str(e)}")

            return f"[Error] OCR failed: {str(e)}"

    

    def translate_text(self, text, src_lang="auto", tgt_lang="en"):

        """

        Translate text using Google Translate (free, no API key required)

        """

        try:

            print(f"[LOG] Starting translation from {src_lang} to {tgt_lang}...")

            

            if not text or text.startswith("[Error]"):

                return text

            

            translator = GoogleTranslator(source=src_lang, target=tgt_lang)

            translated = translator.translate(text)

            

            print(f"[LOG] Translation completed, {len(translated)} characters")

            return translated

        except Exception as e:

            error_str = str(e)

            print(f"[LOG] Translation error: {error_str}")

            

            # Check for permission/firewall issues

            if "WinError 10013" in error_str or "access" in error_str.lower() and "socket" in error_str.lower():

                return "[Error] Network access blocked by Windows Firewall.\n\nPlease add this application to Windows Firewall exceptions:\n1. Open Windows Defender Firewall\n2. Click 'Allow an app through firewall'\n3. Click 'Change settings' and 'Allow another app'\n4. Browse and select this application\n5. Check both Private and Public\n6. Click OK and restart the application"

            elif "ConnectionError" in error_str or "Max retries exceeded" in error_str:

                return "[Error] Cannot connect to translation service.\n\nPossible causes:\n- No internet connection\n- Firewall blocking the connection\n- Google Translate temporarily unavailable\n\nPlease check your internet connection and firewall settings."

            elif "timeout" in error_str.lower():

                return "[Error] Translation timed out. Please try again."

            else:

                return f"[Error] Translation failed: {error_str[:200]}"



class App(tk.Tk):

    def __init__(self):

        super().__init__()

        

        self.title("Screen Translator (Tesseract + Google Translate)")

        self.minsize(700, 600)

        

        self.config_data = self._load_config()

        

        # Hide console window if configured

        if self.config_data.get("hide_console", True):

            self._hide_console()

        

        if "window_geometry" in self.config_data:

            try:

                self.geometry(self.config_data["window_geometry"])

            except:

                self.geometry("900x750")

        else:

            self.geometry("900x750")

        

        self.ocr_translator = OCRTranslator()

        

        self.screenshot_preview = None

        self.screenshot_thumbnail = None

        

        self.is_capturing = False

        self.selection_window = None

        self.start_x = None

        self.start_y = None

        self.rect_id = None

        self.bg_rect_id = None

        

        self.tray_icon = None

        

        self._create_widgets()

        self._setup_shortcuts()

        

        self.protocol("WM_DELETE_WINDOW", self._on_closing)

        self.bind("<Configure>", self._on_window_configure)

        

        if self.config_data.get("start_as_icon", False):

            self.after(100, self._minimize_to_tray)

    

    def _load_config(self):

        if os.path.exists(CONFIG_FILE):

            try:

                with open(CONFIG_FILE, 'r') as f:

                    loaded = json.load(f)

                    config = DEFAULT_CONFIG.copy()

                    config.update(loaded)

                    return config

            except Exception as e:

                print(f"Error loading config: {e}")

                return DEFAULT_CONFIG.copy()

        return DEFAULT_CONFIG.copy()

    

    def _save_config(self):

        try:

            with open(CONFIG_FILE, 'w') as f:

                json.dump(self.config_data, f, indent=2)

        except Exception as e:

            print(f"Error saving config: {e}")

    

    def _create_widgets(self):

        main_frame = ttk.Frame(self, padding="10")

        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        self.columnconfigure(0, weight=1)

        self.rowconfigure(0, weight=1)

        

        notebook = ttk.Notebook(main_frame)

        notebook.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        main_frame.columnconfigure(0, weight=1)

        main_frame.rowconfigure(0, weight=1)

        

        # Screenshot Translation Tab

        self._create_screenshot_tab(notebook)

        

        # Manual Input Tab

        self._create_manual_tab(notebook)

        

        # File Upload Tab

        self._create_file_upload_tab(notebook)

        

        # Settings Tab

        self._create_settings_tab(notebook)

    

    def _create_screenshot_tab(self, notebook):

        """Create the screenshot translation tab"""

        translation_frame = ttk.Frame(notebook, padding="10")

        notebook.add(translation_frame, text="Screenshot")

        

        translation_frame.columnconfigure(0, weight=1)

        translation_frame.rowconfigure(5, weight=1)

        translation_frame.rowconfigure(7, weight=1)

        

        # Screenshot preview

        preview_label_frame = ttk.LabelFrame(translation_frame, text="Screenshot Preview", padding="5")

        preview_label_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 10))

        

        self.preview_label = ttk.Label(preview_label_frame, text="No screenshot captured", anchor="center")

        self.preview_label.pack(fill=tk.BOTH, expand=True)

        

        # Capture buttons

        button_frame = ttk.Frame(translation_frame)

        button_frame.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(0, 10))

        

        ttk.Button(button_frame, text="Capture Area (Alt+Q)", command=self._capture_area).pack(side=tk.LEFT, padx=5)

        ttk.Button(button_frame, text="Capture Fullscreen (Alt+X)", command=self._capture_fullscreen).pack(side=tk.LEFT, padx=5)

        

        # Language selection

        lang_frame = ttk.Frame(translation_frame)

        lang_frame.grid(row=2, column=0, sticky=(tk.W, tk.E), pady=(0, 10))

        

        ttk.Label(lang_frame, text="Source:").pack(side=tk.LEFT, padx=5)

        self.src_lang_var = tk.StringVar(value=self.config_data.get("src_lang", "auto"))

        self.src_lang_combo = ttk.Combobox(lang_frame, textvariable=self.src_lang_var, width=15, state="readonly")

        self.src_lang_combo['values'] = list(LANGUAGE_MAP.keys())

        for lang_name, lang_code in LANGUAGE_MAP.items():

            if lang_code == self.src_lang_var.get():

                self.src_lang_var.set(lang_name)

                break

        self.src_lang_combo.pack(side=tk.LEFT, padx=5)

        

        # Swap button

        ttk.Button(lang_frame, text="⇄ Swap", command=self._swap_languages, width=8).pack(side=tk.LEFT, padx=5)

        

        ttk.Label(lang_frame, text="Target:").pack(side=tk.LEFT, padx=5)

        self.tgt_lang_var = tk.StringVar(value=self.config_data.get("tgt_lang", "en"))

        self.tgt_lang_combo = ttk.Combobox(lang_frame, textvariable=self.tgt_lang_var, width=15, state="readonly")

        self.tgt_lang_combo['values'] = [k for k in LANGUAGE_MAP.keys() if k != "Auto"]

        for lang_name, lang_code in LANGUAGE_MAP.items():

            if lang_code == self.tgt_lang_var.get():

                self.tgt_lang_var.set(lang_name)

                break

        self.tgt_lang_combo.pack(side=tk.LEFT, padx=5)

        

        self.src_lang_combo.bind('<<ComboboxSelected>>', lambda e: self._on_language_change())

        self.tgt_lang_combo.bind('<<ComboboxSelected>>', lambda e: self._on_language_change())

        

        # Action buttons

        action_frame = ttk.Frame(translation_frame)

        action_frame.grid(row=3, column=0, sticky=(tk.W, tk.E), pady=(0, 10))

        

        ttk.Button(action_frame, text="Clear", command=self._clear_texts).pack(side=tk.LEFT, padx=5)

        ttk.Button(action_frame, text="Copy Original", command=lambda: self._copy_to_clipboard(self.source_text)).pack(side=tk.LEFT, padx=5)

        ttk.Button(action_frame, text="Copy Translation", command=lambda: self._copy_to_clipboard(self.target_text)).pack(side=tk.LEFT, padx=5)

        ttk.Button(action_frame, text="Minimize to Tray", command=self._minimize_to_tray).pack(side=tk.LEFT, padx=5)

        

        # Source text

        ttk.Label(translation_frame, text="Original Text:").grid(row=4, column=0, sticky=tk.W, pady=(5, 2))

        

        source_text_frame = ttk.Frame(translation_frame)

        source_text_frame.grid(row=5, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        source_text_frame.columnconfigure(0, weight=1)

        source_text_frame.rowconfigure(0, weight=1)

        

        self.source_text = tk.Text(source_text_frame, wrap=tk.WORD, height=10)

        self.source_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        

        source_scrollbar = ttk.Scrollbar(source_text_frame, orient=tk.VERTICAL, command=self.source_text.yview)

        source_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))

        self.source_text['yscrollcommand'] = source_scrollbar.set

        

        # Target text

        ttk.Label(translation_frame, text="Translated Text:").grid(row=6, column=0, sticky=tk.W, pady=(10, 2))

        

        target_text_frame = ttk.Frame(translation_frame)

        target_text_frame.grid(row=7, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        target_text_frame.columnconfigure(0, weight=1)

        target_text_frame.rowconfigure(0, weight=1)

        

        self.target_text = tk.Text(target_text_frame, wrap=tk.WORD, height=10)

        self.target_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        

        target_scrollbar = ttk.Scrollbar(target_text_frame, orient=tk.VERTICAL, command=self.target_text.yview)

        target_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))

        self.target_text['yscrollcommand'] = target_scrollbar.set

    

    def _create_manual_tab(self, notebook):

        """Create manual text input tab"""

        manual_frame = ttk.Frame(notebook, padding="10")

        notebook.add(manual_frame, text="Manual Input")

        

        manual_frame.columnconfigure(0, weight=1)

        manual_frame.rowconfigure(2, weight=1)

        manual_frame.rowconfigure(4, weight=1)

        

        # Instructions

        info_label = ttk.Label(manual_frame, text="Type or paste text below to translate:", font=('Arial', 10))

        info_label.grid(row=0, column=0, sticky=tk.W, pady=(0, 10))

        

        # Input text

        ttk.Label(manual_frame, text="Input Text:").grid(row=1, column=0, sticky=tk.W, pady=(5, 2))

        

        input_frame = ttk.Frame(manual_frame)

        input_frame.grid(row=2, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        input_frame.columnconfigure(0, weight=1)

        input_frame.rowconfigure(0, weight=1)

        

        self.manual_input_text = tk.Text(input_frame, wrap=tk.WORD, height=10)

        self.manual_input_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        

        input_scrollbar = ttk.Scrollbar(input_frame, orient=tk.VERTICAL, command=self.manual_input_text.yview)

        input_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))

        self.manual_input_text['yscrollcommand'] = input_scrollbar.set

        

        # Translate button

        btn_frame = ttk.Frame(manual_frame)

        btn_frame.grid(row=3, column=0, pady=10)

        

        ttk.Button(btn_frame, text="Translate", command=self._translate_manual_text, width=15).pack(side=tk.LEFT, padx=5)

        ttk.Button(btn_frame, text="Clear All", command=self._clear_manual_texts, width=15).pack(side=tk.LEFT, padx=5)

        ttk.Button(btn_frame, text="Copy Translation", command=lambda: self._copy_to_clipboard(self.manual_output_text), width=15).pack(side=tk.LEFT, padx=5)

        

        # Output text

        ttk.Label(manual_frame, text="Translation:").grid(row=4, column=0, sticky=tk.W, pady=(10, 2))

        

        output_frame = ttk.Frame(manual_frame)

        output_frame.grid(row=5, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        output_frame.columnconfigure(0, weight=1)

        output_frame.rowconfigure(0, weight=1)

        manual_frame.rowconfigure(5, weight=1)

        

        self.manual_output_text = tk.Text(output_frame, wrap=tk.WORD, height=10)

        self.manual_output_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        

        output_scrollbar = ttk.Scrollbar(output_frame, orient=tk.VERTICAL, command=self.manual_output_text.yview)

        output_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))

        self.manual_output_text['yscrollcommand'] = output_scrollbar.set

    

    def _create_file_upload_tab(self, notebook):

        """Create file upload tab"""

        file_frame = ttk.Frame(notebook, padding="10")

        notebook.add(file_frame, text="File Upload")

        

        file_frame.columnconfigure(0, weight=1)

        file_frame.rowconfigure(3, weight=1)

        file_frame.rowconfigure(5, weight=1)

        

        # Instructions

        info_label = ttk.Label(file_frame, text="Upload an image file to extract and translate text:", font=('Arial', 10))

        info_label.grid(row=0, column=0, sticky=tk.W, pady=(0, 10))

        

        # Upload button and preview

        upload_frame = ttk.Frame(file_frame)

        upload_frame.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(0, 10))

        

        ttk.Button(upload_frame, text="Select Image File", command=self._upload_image_file).pack(side=tk.LEFT, padx=5)

        self.file_name_label = ttk.Label(upload_frame, text="No file selected")

        self.file_name_label.pack(side=tk.LEFT, padx=10)

        

        # Image preview

        preview_frame = ttk.LabelFrame(file_frame, text="Image Preview", padding="5")

        preview_frame.grid(row=2, column=0, sticky=(tk.W, tk.E), pady=(0, 10))

        

        self.file_preview_label = ttk.Label(preview_frame, text="No image loaded", anchor="center")

        self.file_preview_label.pack(fill=tk.BOTH, expand=True)

        

        # Extracted text

        ttk.Label(file_frame, text="Extracted Text:").grid(row=3, column=0, sticky=tk.W, pady=(5, 2))

        

        file_source_frame = ttk.Frame(file_frame)

        file_source_frame.grid(row=4, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        file_source_frame.columnconfigure(0, weight=1)

        file_source_frame.rowconfigure(0, weight=1)

        file_frame.rowconfigure(4, weight=1)

        

        self.file_source_text = tk.Text(file_source_frame, wrap=tk.WORD, height=8)

        self.file_source_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        

        file_source_scrollbar = ttk.Scrollbar(file_source_frame, orient=tk.VERTICAL, command=self.file_source_text.yview)

        file_source_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))

        self.file_source_text['yscrollcommand'] = file_source_scrollbar.set

        

        # Translated text

        ttk.Label(file_frame, text="Translated Text:").grid(row=5, column=0, sticky=tk.W, pady=(10, 2))

        

        file_target_frame = ttk.Frame(file_frame)

        file_target_frame.grid(row=6, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        file_target_frame.columnconfigure(0, weight=1)

        file_target_frame.rowconfigure(0, weight=1)

        file_frame.rowconfigure(6, weight=1)

        

        self.file_target_text = tk.Text(file_target_frame, wrap=tk.WORD, height=8)

        self.file_target_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        

        file_target_scrollbar = ttk.Scrollbar(file_target_frame, orient=tk.VERTICAL, command=self.file_target_text.yview)

        file_target_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))

        self.file_target_text['yscrollcommand'] = file_target_scrollbar.set

        

        # Action buttons

        file_btn_frame = ttk.Frame(file_frame)

        file_btn_frame.grid(row=7, column=0, pady=10)

        

        ttk.Button(file_btn_frame, text="Copy Original", command=lambda: self._copy_to_clipboard(self.file_source_text)).pack(side=tk.LEFT, padx=5)

        ttk.Button(file_btn_frame, text="Copy Translation", command=lambda: self._copy_to_clipboard(self.file_target_text)).pack(side=tk.LEFT, padx=5)

        ttk.Button(file_btn_frame, text="Clear", command=self._clear_file_texts).pack(side=tk.LEFT, padx=5)

    

    def _create_settings_tab(self, notebook):

        """Create settings tab"""

        settings_frame = ttk.Frame(notebook, padding="10")

        notebook.add(settings_frame, text="Settings")

        

        # Service info

        info_frame = ttk.LabelFrame(settings_frame, text="Service Information", padding="10")

        info_frame.grid(row=0, column=0, columnspan=4, sticky=(tk.W, tk.E), pady=(0, 15))

        

        info_text = "OCR: Tesseract (Free, Local)\nTranslation: Google Translate (Free, No API Key)\n\nNo API key or account required!"

        ttk.Label(info_frame, text=info_text, justify=tk.LEFT).pack(anchor=tk.W)

        

        # Shortcuts

        ttk.Label(settings_frame, text="Shortcuts:").grid(row=1, column=0, sticky=tk.W, pady=5)

        shortcuts_frame = ttk.Frame(settings_frame)

        shortcuts_frame.grid(row=1, column=1, columnspan=3, sticky=(tk.W, tk.E), pady=5)

        

        ttk.Label(shortcuts_frame, text="Capture Area:").pack(side=tk.LEFT, padx=5)

        self.shortcut1_var = tk.StringVar(value=self.config_data.get("shortcuts", ("alt+q", "alt+x"))[0])

        ttk.Entry(shortcuts_frame, textvariable=self.shortcut1_var, width=15).pack(side=tk.LEFT, padx=5)

        

        ttk.Label(shortcuts_frame, text="Fullscreen:").pack(side=tk.LEFT, padx=5)

        self.shortcut2_var = tk.StringVar(value=self.config_data.get("shortcuts", ("alt+q", "alt+x"))[1])

        ttk.Entry(shortcuts_frame, textvariable=self.shortcut2_var, width=15).pack(side=tk.LEFT, padx=5)

        

        ttk.Button(shortcuts_frame, text="Save", command=self._save_shortcuts).pack(side=tk.LEFT, padx=5)

        

        # Capture Theme

        ttk.Label(settings_frame, text="Capture Theme:").grid(row=2, column=0, sticky=tk.W, pady=5)

        self.theme_var = tk.StringVar(value=self.config_data.get("capture_theme", "classic"))

        theme_combo = ttk.Combobox(settings_frame, textvariable=self.theme_var, width=30, state="readonly")

        theme_combo['values'] = [CAPTURE_THEMES[key]["name"] for key in CAPTURE_THEMES]

        

        current_theme_key = self.config_data.get("capture_theme", "classic")

        theme_combo.set(CAPTURE_THEMES[current_theme_key]["name"])

        

        theme_combo.grid(row=2, column=1, sticky=(tk.W, tk.E), pady=5, padx=5)

        

        def on_theme_change(event):

            selected_name = self.theme_var.get()

            for key, value in CAPTURE_THEMES.items():

                if value["name"] == selected_name:

                    self.config_data["capture_theme"] = key

                    self._save_config()

                    break

        

        theme_combo.bind('<<ComboboxSelected>>', on_theme_change)

        

        # Temperature slider

        ttk.Label(settings_frame, text="Translation Temperature:").grid(row=3, column=0, sticky=tk.W, pady=5)

        temp_frame = ttk.Frame(settings_frame)

        temp_frame.grid(row=3, column=1, columnspan=3, sticky=(tk.W, tk.E), pady=5)

        

        self.temperature_var = tk.DoubleVar(value=self.config_data.get("temperature", 0.2))

        temp_slider = ttk.Scale(temp_frame, from_=0.0, to=1.0, orient=tk.HORIZONTAL, 

                               variable=self.temperature_var, command=self._on_temperature_change)

        temp_slider.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)

        

        self.temp_label = ttk.Label(temp_frame, text=f"{self.temperature_var.get():.2f}")

        self.temp_label.pack(side=tk.LEFT, padx=5)

        

        ttk.Label(settings_frame, text="(Lower = More literal, Higher = More creative)", 

                 font=('Arial', 8)).grid(row=4, column=1, columnspan=3, sticky=tk.W, pady=(0, 10))

        

        # Start as Icon

        self.start_as_icon_var = tk.BooleanVar(value=self.config_data.get("start_as_icon", False))

        ttk.Checkbutton(settings_frame, text="Start minimized to tray", 

                       variable=self.start_as_icon_var, 

                       command=self._save_start_as_icon).grid(row=5, column=0, columnspan=2, sticky=tk.W, pady=5)

        

        # Hide Console

        self.hide_console_var = tk.BooleanVar(value=self.config_data.get("hide_console", True))

        ttk.Checkbutton(settings_frame, text="Hide console window (requires restart)", 

                       variable=self.hide_console_var, 

                       command=self._save_hide_console).grid(row=6, column=0, columnspan=2, sticky=tk.W, pady=5)

        

        settings_frame.columnconfigure(1, weight=1)

    

    def _on_temperature_change(self, value):

        """Handle temperature slider change"""

        temp = float(value)

        self.temp_label.config(text=f"{temp:.2f}")

        self.config_data["temperature"] = temp

        self._save_config()

    

    def _swap_languages(self):

        """Swap source and target languages"""

        src = self.src_lang_var.get()

        tgt = self.tgt_lang_var.get()

        

        if src != "Auto":

            self.src_lang_var.set(tgt)

            self.tgt_lang_var.set(src)

            self._on_language_change()

    

    def _copy_to_clipboard(self, text_widget):

        """Copy text from widget to clipboard"""

        try:

            text = text_widget.get("1.0", tk.END).strip()

            if text:

                self.clipboard_clear()

                self.clipboard_append(text)

                messagebox.showinfo("Success", "Text copied to clipboard!")

            else:

                messagebox.showwarning("Warning", "No text to copy!")

        except Exception as e:

            messagebox.showerror("Error", f"Failed to copy: {e}")

    

    def _clear_texts(self):

        """Clear screenshot tab texts"""

        self.source_text.delete("1.0", tk.END)

        self.target_text.delete("1.0", tk.END)

        self.screenshot_preview = None

        self.preview_label.config(image='', text="No screenshot captured")

    

    def _clear_manual_texts(self):

        """Clear manual input tab texts"""

        self.manual_input_text.delete("1.0", tk.END)

        self.manual_output_text.delete("1.0", tk.END)

    

    def _clear_file_texts(self):

        """Clear file upload tab texts"""

        self.file_source_text.delete("1.0", tk.END)

        self.file_target_text.delete("1.0", tk.END)

        self.file_name_label.config(text="No file selected")

        self.file_preview_label.config(image='', text="No image loaded")

    

    def _translate_manual_text(self):

        """Translate manually entered text"""

        input_text = self.manual_input_text.get("1.0", tk.END).strip()

        

        if not input_text:

            messagebox.showwarning("Warning", "Please enter some text to translate!")

            return

        

        self.manual_output_text.delete("1.0", tk.END)

        self.manual_output_text.insert("1.0", "Translating...")

        self.update()

        

        def translate_thread():

            try:

                src_lang_name = self.src_lang_var.get()

                tgt_lang_name = self.tgt_lang_var.get()

                src_lang = LANGUAGE_MAP.get(src_lang_name, "auto")

                tgt_lang = LANGUAGE_MAP.get(tgt_lang_name, "en")

                

                translated = self.ocr_translator.translate_text(input_text, src_lang, tgt_lang)

                

                self.manual_output_text.delete("1.0", tk.END)

                self.manual_output_text.insert("1.0", translated)

            except Exception as e:

                error_text = f"[Error] Translation failed: {str(e)}"

                self.manual_output_text.delete("1.0", tk.END)

                self.manual_output_text.insert("1.0", error_text)

        

        threading.Thread(target=translate_thread, daemon=True).start()

    

    def _upload_image_file(self):

        """Upload and process an image file"""

        file_path = filedialog.askopenfilename(

            title="Select Image File",

            filetypes=[

                ("Image files", "*.png *.jpg *.jpeg *.bmp *.gif *.tiff"),

                ("All files", "*.*")

            ]

        )

        

        if not file_path:

            return

        

        try:

            # Load and display image

            image = Image.open(file_path)

            self.file_name_label.config(text=os.path.basename(file_path))

            

            # Create thumbnail for preview

            thumbnail = image.copy()

            thumbnail.thumbnail((350, 350), Image.Resampling.LANCZOS)

            photo = ImageTk.PhotoImage(thumbnail)

            self.file_preview_label.config(image=photo, text="")

            self.file_preview_label.image = photo  # Keep reference

            

            # Clear previous texts

            self.file_source_text.delete("1.0", tk.END)

            self.file_target_text.delete("1.0", tk.END)

            

            # Show processing message

            self.file_source_text.insert("1.0", "Extracting text...")

            self.file_target_text.insert("1.0", "Waiting...")

            self.update()

            

            # Process in thread

            def process_thread():

                try:

                    # Extract text

                    original_text = self.ocr_translator.extract_text_from_image(image)

                    

                    self.file_source_text.delete("1.0", tk.END)

                    self.file_source_text.insert("1.0", original_text)

                    self.update()

                    

                    # Translate

                    if original_text and not original_text.startswith("[Error]"):

                        src_lang_name = self.src_lang_var.get()

                        tgt_lang_name = self.tgt_lang_var.get()

                        src_lang = LANGUAGE_MAP.get(src_lang_name, "auto")

                        tgt_lang = LANGUAGE_MAP.get(tgt_lang_name, "en")

                        

                        translated = self.ocr_translator.translate_text(original_text, src_lang, tgt_lang)

                        

                        self.file_target_text.delete("1.0", tk.END)

                        self.file_target_text.insert("1.0", translated)

                    else:

                        self.file_target_text.delete("1.0", tk.END)

                        self.file_target_text.insert("1.0", "[Error] No text extracted")

                        

                except Exception as e:

                    error_msg = f"[Error] Processing failed: {str(e)}"

                    self.file_source_text.delete("1.0", tk.END)

                    self.file_source_text.insert("1.0", error_msg)

                    self.file_target_text.delete("1.0", tk.END)

            

            threading.Thread(target=process_thread, daemon=True).start()

            

        except Exception as e:

            messagebox.showerror("Error", f"Failed to load image: {e}")

    

    def _on_language_change(self):

        """Save language selections"""

        src_lang_name = self.src_lang_var.get()

        tgt_lang_name = self.tgt_lang_var.get()

        

        self.config_data["src_lang"] = LANGUAGE_MAP.get(src_lang_name, "auto")

        self.config_data["tgt_lang"] = LANGUAGE_MAP.get(tgt_lang_name, "en")

        self._save_config()

    

    def _save_shortcuts(self):

        shortcut1 = self.shortcut1_var.get().strip()

        shortcut2 = self.shortcut2_var.get().strip()

        

        if shortcut1 and shortcut2:

            self.config_data["shortcuts"] = (shortcut1, shortcut2)

            self._save_config()

            self._setup_shortcuts()

            messagebox.showinfo("Success", "Shortcuts saved! Restart app for changes.")

        else:

            messagebox.showwarning("Warning", "Please enter both shortcuts.")

    

    def _save_start_as_icon(self):

        self.config_data["start_as_icon"] = self.start_as_icon_var.get()

        self._save_config()

    

    def _save_hide_console(self):

        self.config_data["hide_console"] = self.hide_console_var.get()

        self._save_config()

        messagebox.showinfo("Info", "Console visibility will change on next restart.")

    

    def _hide_console(self):

        """Hide the console window on Windows"""

        try:

            import ctypes

            import sys

            

            # Only works on Windows

            if sys.platform == 'win32':

                # Get console window handle

                console_window = ctypes.windll.kernel32.GetConsoleWindow()

                if console_window:

                    # SW_HIDE = 0

                    ctypes.windll.user32.ShowWindow(console_window, 0)

                    print("[INFO] Console window hidden")

        except Exception as e:

            print(f"[WARNING] Could not hide console: {e}")

    

    def _setup_shortcuts(self):

        shortcuts = self.config_data.get("shortcuts", ("alt+q", "alt+x"))

        

        try:

            keyboard.add_hotkey(shortcuts[0], self._capture_area)

            keyboard.add_hotkey(shortcuts[1], self._capture_fullscreen)

        except Exception as e:

            print(f"Error setting up shortcuts: {e}")

    

    def _capture_area(self):

        if self.is_capturing:

            return

        self.is_capturing = True

        self.withdraw()

        self.after(200, self._show_selection_overlay)

    

    def _show_selection_overlay(self):

        self.selection_window = tk.Toplevel(self)

        self.selection_window.attributes('-fullscreen', True)

        self.selection_window.attributes('-alpha', 0.3)

        self.selection_window.attributes('-topmost', True)

        

        canvas = tk.Canvas(self.selection_window, cursor="cross", bg='grey', highlightthickness=0)

        canvas.pack(fill=tk.BOTH, expand=True)

        

        self.start_x = None

        self.start_y = None

        self.rect_id = None

        self.bg_rect_id = None

        

        theme_key = self.config_data.get("capture_theme", "classic")

        theme = CAPTURE_THEMES[theme_key]

        

        rainbow_colors = ["red", "orange", "yellow", "green", "cyan", "blue", "purple", "magenta"]

        current_color_index = [0]

        

        def update_rainbow():

            if theme["rainbow"] and self.rect_id and canvas.winfo_exists():

                current_color_index[0] = (current_color_index[0] + 1) % len(rainbow_colors)

                canvas.itemconfig(self.rect_id, outline=rainbow_colors[current_color_index[0]])

                canvas.after(100, update_rainbow)

        

        def on_mouse_down(event):

            self.start_x = event.x

            self.start_y = event.y

            

            self.bg_rect_id = canvas.create_rectangle(

                self.start_x, self.start_y, self.start_x, self.start_y,

                outline=theme["bg_outline"],

                width=theme["bg_width"]

            )

            

            self.rect_id = canvas.create_rectangle(

                self.start_x, self.start_y, self.start_x, self.start_y,

                outline=theme["fg_outline"],

                width=theme["fg_width"],

                dash=theme["fg_dash"]

            )

            

            if theme["rainbow"]:

                update_rainbow()

        

        def on_mouse_move(event):

            if self.start_x and self.start_y and self.rect_id:

                canvas.coords(self.rect_id, self.start_x, self.start_y, event.x, event.y)

                if self.bg_rect_id:

                    canvas.coords(self.bg_rect_id, self.start_x, self.start_y, event.x, event.y)

        

        def on_mouse_up(event):

            if self.start_x and self.start_y:

                x1, y1 = min(self.start_x, event.x), min(self.start_y, event.y)

                x2, y2 = max(self.start_x, event.x), max(self.start_y, event.y)

                self.selection_window.destroy()

                self._capture_screen_area(x1, y1, x2, y2)

        

        def on_right_click(event):

            self._cancel_selection()

        

        def on_escape(event):

            self._cancel_selection()

        

        canvas.bind("<ButtonPress-1>", on_mouse_down)

        canvas.bind("<B1-Motion>", on_mouse_move)

        canvas.bind("<ButtonRelease-1>", on_mouse_up)

        canvas.bind("<ButtonPress-3>", on_right_click)

        canvas.bind("<Escape>", on_escape)

        

        canvas.focus_set()

    

    def _cancel_selection(self):

        if self.selection_window:

            self.selection_window.destroy()

            self.selection_window = None

        self.is_capturing = False

        self.deiconify()

    

    def _capture_screen_area(self, x1, y1, x2, y2):

        try:

            screenshot = ImageGrab.grab(bbox=(x1, y1, x2, y2))

            self._process_screenshot(screenshot)

        except Exception as e:

            messagebox.showerror("Error", f"Failed to capture: {e}")

        finally:

            self.is_capturing = False

            self.deiconify()

    

    def _capture_fullscreen(self):

        if self.is_capturing:

            return

        self.is_capturing = True

        self.withdraw()

        self.after(200, self._do_fullscreen_capture)

    

    def _do_fullscreen_capture(self):

        try:

            screenshot = ImageGrab.grab()

            self._process_screenshot(screenshot)

        except Exception as e:

            messagebox.showerror("Error", f"Failed to capture: {e}")

        finally:

            self.is_capturing = False

            self.deiconify()

    

    def _process_screenshot(self, screenshot):

        self.screenshot_preview = screenshot

        self._update_screenshot_preview()

        

        self.source_text.delete("1.0", tk.END)

        self.source_text.insert("1.0", "Processing screenshot...")

        self.target_text.delete("1.0", tk.END)

        self.target_text.insert("1.0", "Waiting for translation...")

        self.update()

        

        def process_thread():

            try:

                print("[LOG] Starting screenshot processing thread")

                

                src_lang_name = self.src_lang_var.get()

                tgt_lang_name = self.tgt_lang_var.get()

                src_lang = LANGUAGE_MAP.get(src_lang_name, "auto")

                tgt_lang = LANGUAGE_MAP.get(tgt_lang_name, "en")

                

                print(f"[LOG] Languages - Source: {src_lang}, Target: {tgt_lang}")

                

                print("[LOG] Calling Tesseract OCR...")

                original_text = self.ocr_translator.extract_text_from_image(screenshot)

                print(f"[LOG] OCR completed, text length: {len(original_text)}")

                

                self.source_text.delete("1.0", tk.END)

                self.source_text.insert("1.0", original_text)

                self.update()

                

                if original_text and not original_text.startswith("[Error]"):

                    print("[LOG] Text extracted successfully, starting translation...")

                    translated_text = self.ocr_translator.translate_text(original_text, src_lang, tgt_lang)

                    print(f"[LOG] Translation completed, text length: {len(translated_text)}")

                    

                    self.target_text.delete("1.0", tk.END)

                    self.target_text.insert("1.0", translated_text)

                else:

                    print("[LOG] OCR failed or returned error, skipping translation")

                    self.target_text.delete("1.0", tk.END)

                    self.target_text.insert("1.0", "[Error] No text extracted from image")

                

                print("[LOG] Screenshot processing completed")

                

            except Exception as e:

                print(f"[LOG] Exception in process_thread: {str(e)}")

                print(f"[LOG] Traceback: {traceback.format_exc()}")

                error_text = f"[Error] Processing failed: {str(e)}"

                self.source_text.delete("1.0", tk.END)

                self.source_text.insert("1.0", error_text)

                self.target_text.delete("1.0", tk.END)

        

        threading.Thread(target=process_thread, daemon=True).start()

    

    def _update_screenshot_preview(self):

        if not self.screenshot_preview:

            return

        

        try:

            thumbnail = self.screenshot_preview.copy()

            thumbnail.thumbnail((350, 350), Image.Resampling.LANCZOS)

            

            self.screenshot_thumbnail = ImageTk.PhotoImage(thumbnail)

            

            self.preview_label.config(image=self.screenshot_thumbnail, text="")

        except Exception as e:

            print(f"Error updating preview: {e}")

    

    def _create_tray_icon(self):

        image = Image.new('RGB', (64, 64), color='blue')

        draw = ImageDraw.Draw(image)

        draw.rectangle([16, 16, 48, 48], fill='white')

        

        menu = pystray.Menu(

            pystray.MenuItem("Show", self._show_window),

            pystray.MenuItem("Exit", self._exit_app)

        )

        

        self.tray_icon = pystray.Icon("screen_translator", image, "Screen Translator", menu)

        threading.Thread(target=self.tray_icon.run, daemon=True).start()

    

    def _minimize_to_tray(self):

        self.withdraw()

        if not self.tray_icon:

            self._create_tray_icon()

    

    def _show_window(self):

        self.deiconify()

        self.lift()

        self.focus_force()

    

    def _exit_app(self):

        self._save_window_geometry()

        if self.tray_icon:

            self.tray_icon.stop()

        self.quit()

    

    def _on_closing(self):

        if messagebox.askokcancel("Quit", "Do you want to quit?"):

            self._exit_app()

    

    def _on_window_configure(self, event):

        if event.widget == self:

            if hasattr(self, '_geometry_save_timer'):

                self.after_cancel(self._geometry_save_timer)

            self._geometry_save_timer = self.after(500, self._save_window_geometry)

    

    def _save_window_geometry(self):

        try:

            geometry = self.geometry()

            self.config_data["window_geometry"] = geometry

            self._save_config()

        except Exception as e:

            print(f"Error saving window geometry: {e}")



if __name__ == "__main__":

    print("="*60)

    print("STARTING APPLICATION")

    print("="*60)

    

    app = App()

    app.mainloop()

Commenti

Post popolari in questo blog

No Man's Sky Similar Games

Tower Defense Games

Auto Copy Folder - Source Code