33import customtkinter as ctk
44
55from pathlib import Path
6- from PIL import Image , ImageTk , ImageDraw
6+ from PIL import Image , ImageTk
77
88# --- Imports: Theme, State, and Logic ---
99from .theme import DR_BG , DR_TEXT , DR_MUTED , DR_SURFACE , DR_BORDER , DR_PURPLE
3535
3636
3737class SplashScreen (ctk .CTkToplevel ):
38- """
39- A temporary loading screen that appears before the main application window is ready.
40- """
4138 def __init__ (self , master , logo_path ):
4239 super ().__init__ (master )
4340 self .overrideredirect (True )
@@ -71,13 +68,21 @@ def __init__(self, master, logo_path):
7168
7269
7370class App (ctk .CTk ):
74- """
75- The main application window with multithreaded operations support.
76- """
7771 def __init__ (self ):
72+ # OPTIMISATION 1: Force le mode de rendu haute performance sur Windows
73+ try :
74+ if sys .platform .startswith ("win" ):
75+ import ctypes
76+ ctypes .windll .shcore .SetProcessDpiAwareness (1 )
77+ except Exception :
78+ pass
79+
7880 super ().__init__ ()
7981 self .withdraw ()
8082
83+ # Variable pour bloquer le rendu intensif durant le mouvement
84+ self ._resize_after_id = None
85+
8186 self .settings : AppSettings = load_settings ()
8287
8388 ctk .set_appearance_mode ("dark" )
@@ -95,6 +100,9 @@ def __init__(self):
95100 self .minsize (1020 , 700 )
96101 self .configure (fg_color = DR_BG )
97102
103+ # OPTIMISATION 2: Capture l'événement de redimensionnement
104+ self .bind ("<Configure>" , self ._smart_resize )
105+
98106 self .logo_path = Path (__file__ ).resolve ().parent .parent .parent / "assets" / "logo.png"
99107 self ._ensure_logo_exists (self .logo_path )
100108
@@ -137,7 +145,6 @@ def __init__(self):
137145 self .content_area .grid_rowconfigure (1 , weight = 1 )
138146 self .content_area .grid_rowconfigure (2 , weight = 0 )
139147
140- # The Topbar handles status and loading animations
141148 self .topbar_view = TopbarView (self .content_area , on_toggle_sidebar = self .toggle_terminal )
142149 self .topbar_view .grid (row = 0 , column = 0 , sticky = "ew" , padx = 18 , pady = (18 , 8 ))
143150
@@ -146,12 +153,10 @@ def __init__(self):
146153 self .page_container .grid_columnconfigure (0 , weight = 1 )
147154 self .page_container .grid_rowconfigure (0 , weight = 1 )
148155
149- # Global Terminal
150156 self .logs_view = LogsView (self .content_area , on_close = self .toggle_terminal )
151157 self .logs_view .grid (row = 2 , column = 0 , sticky = "ew" , padx = 18 , pady = (0 , 18 ))
152158 self ._terminal_visible = True
153159
154- # Link log view to state for global access
155160 self .ui_state .log_view = self .logs_view
156161
157162 self .pages : dict [str , ctk .CTkFrame ] = {}
@@ -176,40 +181,34 @@ def __init__(self):
176181
177182 self .after (2000 , self ._show_main_window )
178183
184+ def _smart_resize (self , event ):
185+ """Évite le recalcul saccadé en temps réel."""
186+ if event .widget == self :
187+ if self ._resize_after_id :
188+ self .after_cancel (self ._resize_after_id )
189+ # Attend 50ms de stabilité avant de rafraîchir
190+ self ._resize_after_id = self .after (50 , self ._do_refresh )
191+
192+ def _do_refresh (self ):
193+ """Effectue le rendu final de manière fluide."""
194+ self .update_idletasks ()
195+ self ._resize_after_id = None
196+
179197 @staticmethod
180198 def _ensure_logo_exists (png_path : Path ):
181199 ico_path = png_path .with_suffix (".ico" )
182-
183- if not png_path .exists ():
184- return
185-
200+ if not png_path .exists (): return
186201 try :
187- # On ouvre ton PNG
188202 img = Image .open (png_path ).convert ("RGBA" )
189-
190- # ÉTAPE CLÉ : On enlève les bordures vides si ton PNG en a
191203 bbox = img .getbbox ()
192- if bbox :
193- img = img .crop (bbox )
194-
195- # On crée une liste des tailles standards Windows
196- # La taille 256 est celle du bureau / La taille 32-48 est celle de la barre des tâches
204+ if bbox : img = img .crop (bbox )
197205 sizes = [256 , 128 , 64 , 48 , 32 , 16 ]
198206 layers = []
199-
200207 for s in sizes :
201- # On redimensionne proprement pour chaque taille
202208 resized = img .resize ((s , s ), Image .Resampling .LANCZOS )
203209 layers .append (resized )
204-
205- # On sauvegarde l'ICO en incluant TOUTES ces couches
206- # C'est ce qui garantit que l'icône "match" le PNG partout
207210 layers [0 ].save (ico_path , format = "ICO" , append_images = layers [1 :])
208-
209- print ("ICO synchronisé avec le PNG (taille max)." )
210- except Exception as e :
211- print (f"Erreur : { e } " )
212-
211+ except Exception : pass
213212
214213 def _show_main_window (self ):
215214 try : self .splash .destroy ()
@@ -318,9 +317,9 @@ def switch_page(self, page_name: str) -> None:
318317 self .pages [page_name ].grid (row = 0 , column = 0 , sticky = "nsew" )
319318 self ._current_page = page_name
320319 self .topbar_view .set_title (page_name )
320+ self .update_idletasks ()
321321
322322 def toggle_terminal (self ) -> None :
323- """Shows or hides the bottom terminal console."""
324323 if self ._terminal_visible :
325324 self .logs_view .grid_forget ()
326325 self ._terminal_visible = False
@@ -329,13 +328,9 @@ def toggle_terminal(self) -> None:
329328 self ._terminal_visible = True
330329
331330 def set_status (self , s : str ) -> None :
332- """Updates the status in the top bar."""
333331 self .topbar_view .set_status (s )
334332
335333 def log (self , s : str , level : str = "INFO" ) -> None :
336- """
337- Global logging method. Thread-safe log through UIState.
338- """
339334 self .ui_state .log (s , level = level )
340335
341336 @staticmethod
0 commit comments