Skip to content

Commit f5ba2d2

Browse files
Merge pull request #27 from ZylosCore/perfo-fixing
Update app.py
2 parents 71eff08 + c7745e8 commit f5ba2d2

1 file changed

Lines changed: 24 additions & 161 deletions

File tree

src/bulkfolder/ui/app.py

Lines changed: 24 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22
import sys
33
import customtkinter as ctk
4-
54
from pathlib import Path
65
from PIL import Image, ImageTk
76

@@ -33,132 +32,76 @@
3332
from .views.large_files_page import LargeFilesPage
3433
from .views.about_page import AboutPage
3534

36-
3735
class SplashScreen(ctk.CTkToplevel):
3836
def __init__(self, master, logo_path):
3937
super().__init__(master)
4038
self.overrideredirect(True)
4139
self.attributes("-topmost", True)
4240
self.configure(fg_color=DR_BG)
43-
4441
width, height = 340, 380
4542
x = (self.winfo_screenwidth() // 2) - (width // 2)
4643
y = (self.winfo_screenheight() // 2) - (height // 2)
4744
self.geometry(f"{width}x{height}+{x}+{y}")
48-
4945
self.grid_columnconfigure(0, weight=1)
5046
self.grid_rowconfigure(0, weight=1)
51-
5247
inner = ctk.CTkFrame(self, fg_color="transparent")
5348
inner.grid(row=0, column=0)
54-
5549
if logo_path and logo_path.exists():
5650
img = Image.open(logo_path)
5751
self.photo = ctk.CTkImage(light_image=img, dark_image=img, size=(120, 120))
5852
ctk.CTkLabel(inner, text="", image=self.photo).pack(pady=(0, 20))
59-
6053
ctk.CTkLabel(inner, text="BulkFolder", font=ctk.CTkFont(size=26, weight="bold"), text_color=DR_TEXT).pack()
6154
ctk.CTkLabel(inner, text="Organize & Rename safely", font=ctk.CTkFont(size=13), text_color=DR_MUTED).pack(pady=(0, 20))
62-
ctk.CTkLabel(inner, text="Loading...", font=ctk.CTkFont(size=12), text_color=DR_MUTED).pack(pady=(0, 5))
63-
6455
self.pb = ctk.CTkProgressBar(inner, width=220, progress_color=DR_PURPLE, fg_color=DR_BORDER)
6556
self.pb.pack(pady=(5, 0))
6657
self.pb.set(0)
6758
self.pb.start()
6859

69-
7060
class App(ctk.CTk):
7161
def __init__(self):
72-
# OPTIMISATION 1: Force le mode de rendu haute performance sur Windows
7362
try:
7463
if sys.platform.startswith("win"):
7564
import ctypes
7665
ctypes.windll.shcore.SetProcessDpiAwareness(1)
77-
except Exception:
78-
pass
79-
66+
except Exception: pass
8067
super().__init__()
8168
self.withdraw()
82-
83-
# Variable pour bloquer le rendu intensif durant le mouvement
8469
self._resize_after_id = None
85-
8670
self.settings: AppSettings = load_settings()
87-
8871
ctk.set_appearance_mode("dark")
89-
ctk.set_default_color_theme("blue")
90-
91-
try:
92-
scale_val = float(self.settings.ui_scaling.replace("%", "")) / 100.0
93-
ctk.set_widget_scaling(scale_val)
94-
ctk.set_window_scaling(scale_val)
95-
except Exception:
96-
pass
97-
9872
self.title("BulkFolder")
9973
self.geometry("1240x800")
10074
self.minsize(1020, 700)
10175
self.configure(fg_color=DR_BG)
102-
103-
# OPTIMISATION 2: Capture l'événement de redimensionnement
10476
self.bind("<Configure>", self._smart_resize)
105-
10677
self.logo_path = Path(__file__).resolve().parent.parent.parent / "assets" / "logo.png"
10778
self._ensure_logo_exists(self.logo_path)
108-
109-
if self.logo_path.exists():
110-
img = Image.open(self.logo_path)
111-
photo = ImageTk.PhotoImage(img)
112-
self.wm_iconphoto(True, photo)
113-
114-
if sys.platform.startswith("win"):
115-
try:
116-
import ctypes
117-
myappid = 'zyloscore.bulkfolder.app.1.1'
118-
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
119-
except Exception: pass
120-
ico_path = self.logo_path.with_suffix(".ico")
121-
if ico_path.exists(): self.iconbitmap(str(ico_path))
122-
123-
self.splash = SplashScreen(self, self.logo_path)
124-
self.splash.update()
125-
12679
self.ui_state = UIState()
12780
self.last_plan = None
12881
self.last_scan = None
129-
self.renamer_plan = []
130-
self.chunker_plan = []
131-
132-
# Grid config
13382
self.grid_columnconfigure(0, weight=0)
13483
self.grid_columnconfigure(1, weight=1)
13584
self.grid_rowconfigure(0, weight=1)
136-
85+
self.splash = SplashScreen(self, self.logo_path)
86+
self.splash.update()
13787
info = get_project_info()
13888
self.sidebar_view = SidebarView(info, self, on_page=self.switch_page, logo_path=self.logo_path)
13989
self.sidebar_view.grid(row=0, column=0, rowspan=2, sticky="nsw")
140-
141-
# Right side container
14290
self.content_area = ctk.CTkFrame(self, corner_radius=0, fg_color=DR_BG)
14391
self.content_area.grid(row=0, column=1, sticky="nsew")
14492
self.content_area.grid_columnconfigure(0, weight=1)
14593
self.content_area.grid_rowconfigure(1, weight=1)
14694
self.content_area.grid_rowconfigure(2, weight=0)
147-
14895
self.topbar_view = TopbarView(self.content_area, on_toggle_sidebar=self.toggle_terminal)
14996
self.topbar_view.grid(row=0, column=0, sticky="ew", padx=18, pady=(18, 8))
150-
15197
self.page_container = ctk.CTkFrame(self.content_area, corner_radius=0, fg_color=DR_BG)
15298
self.page_container.grid(row=1, column=0, sticky="nsew")
15399
self.page_container.grid_columnconfigure(0, weight=1)
154100
self.page_container.grid_rowconfigure(0, weight=1)
155-
156101
self.logs_view = LogsView(self.content_area, on_close=self.toggle_terminal)
157102
self.logs_view.grid(row=2, column=0, sticky="ew", padx=18, pady=(0, 18))
158103
self._terminal_visible = True
159-
160104
self.ui_state.log_view = self.logs_view
161-
162105
self.pages: dict[str, ctk.CTkFrame] = {}
163106
self.pages["Organizer"] = self._build_page_organizer(self.page_container)
164107
self.pages["Renamer"] = self._build_page_renamer(self.page_container)
@@ -171,26 +114,16 @@ def __init__(self):
171114
self.pages["LargeFiles"] = self._build_page_large_files(self.page_container)
172115
self.pages["Settings"] = self._build_page_settings(self.page_container)
173116
self.pages["About"] = self._build_page_about(self.page_container)
174-
175117
self._current_page = ""
176-
self.switch_page(self.settings.default_page if self.settings.default_page in self.pages else "Organizer")
177-
178-
self.log("System initialized.", level="DEBUG")
179-
self.log("Terminal functional.", level="SUCCESS")
180-
self.set_status("Ready.")
181-
118+
self.switch_page("Organizer")
182119
self.after(2000, self._show_main_window)
183120

184121
def _smart_resize(self, event):
185-
"""Évite le recalcul saccadé en temps réel."""
186122
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
123+
if self._resize_after_id: self.after_cancel(self._resize_after_id)
190124
self._resize_after_id = self.after(50, self._do_refresh)
191125

192126
def _do_refresh(self):
193-
"""Effectue le rendu final de manière fluide."""
194127
self.update_idletasks()
195128
self._resize_after_id = None
196129

@@ -202,11 +135,7 @@ def _ensure_logo_exists(png_path: Path):
202135
img = Image.open(png_path).convert("RGBA")
203136
bbox = img.getbbox()
204137
if bbox: img = img.crop(bbox)
205-
sizes = [256, 128, 64, 48, 32, 16]
206-
layers = []
207-
for s in sizes:
208-
resized = img.resize((s, s), Image.Resampling.LANCZOS)
209-
layers.append(resized)
138+
layers = [img.resize((s, s), Image.Resampling.LANCZOS) for s in [256, 128, 64, 48, 32, 16]]
210139
layers[0].save(ico_path, format="ICO", append_images=layers[1:])
211140
except Exception: pass
212141

@@ -217,28 +146,20 @@ def _show_main_window(self):
217146

218147
def _build_page_organizer(self, parent) -> ctk.CTkFrame:
219148
frame = ctk.CTkFrame(parent, corner_radius=0, fg_color=DR_BG)
220-
frame.grid_columnconfigure(0, weight=0)
221-
frame.grid_columnconfigure(1, weight=1)
222-
frame.grid_rowconfigure(0, weight=1)
223-
224-
self.organizer_panel = OrganizerPanel(
225-
frame,
149+
frame.grid_columnconfigure(0, weight=0); frame.grid_columnconfigure(1, weight=1); frame.grid_rowconfigure(0, weight=1)
150+
self.organizer_panel = OrganizerPanel(frame,
226151
on_choose_folder=lambda: actions.choose_folder(self),
227152
on_scan=lambda: actions.scan_and_plan(self),
228153
on_apply=lambda: actions.apply_plan(self),
229154
on_undo=lambda: actions.undo_last_ops(self),
230155
on_toggle_subfolders=lambda enabled: actions.toggle_subfolders(self, enabled),
231156
)
232157
self.organizer_panel.grid(row=0, column=0, sticky="ns", padx=(18, 10), pady=(0, 18))
233-
234158
right = ctk.CTkFrame(frame, corner_radius=0, fg_color=DR_BG)
235159
right.grid(row=0, column=1, sticky="nsew", padx=(10, 18), pady=(0, 18))
236-
right.grid_columnconfigure(0, weight=1)
237-
right.grid_rowconfigure(2, weight=1)
238-
160+
right.grid_columnconfigure(0, weight=1); right.grid_rowconfigure(2, weight=1)
239161
self.cards_view = CardsRow(right)
240162
self.cards_view.grid(row=0, column=0, sticky="ew", pady=(0, 8))
241-
242163
self.tabs = ctk.CTkTabview(right, fg_color=DR_BG)
243164
self.tabs.grid(row=2, column=0, sticky="nsew", pady=(8, 0))
244165
self.dashboard_view = DashboardView(self.tabs.add("Dashboard"))
@@ -247,69 +168,16 @@ def _build_page_organizer(self, parent) -> ctk.CTkFrame:
247168
self.preview_view.pack(fill="both", expand=True)
248169
return frame
249170

250-
def _build_page_renamer(self, parent) -> ctk.CTkFrame:
251-
self.renamer_page = RenamerPage(parent,
252-
on_choose_folder=lambda: actions.renamer_choose_folder(self),
253-
on_preview=lambda: actions.renamer_preview(self),
254-
on_apply=lambda: actions.renamer_apply(self))
255-
return self.renamer_page
256-
257-
def _build_page_chunker(self, parent) -> ctk.CTkFrame:
258-
self.chunker_page = ChunkerPage(parent,
259-
on_choose_folder=lambda: actions.chunker_choose_folder(self),
260-
on_preview=lambda: actions.chunker_preview(self),
261-
on_apply=lambda: actions.chunker_apply(self))
262-
return self.chunker_page
263-
264-
def _build_page_flattener(self, parent) -> ctk.CTkFrame:
265-
self.flattener_page = FlattenerPage(parent,
266-
on_choose_folder=lambda: actions.flattener_choose_folder(self),
267-
on_preview=lambda: actions.flattener_preview(self),
268-
on_apply=lambda: actions.flattener_apply(self))
269-
return self.flattener_page
270-
271-
def _build_page_unzipper(self, parent) -> ctk.CTkFrame:
272-
self.unzipper_page = UnzipperPage(parent,
273-
on_choose_folder=lambda: actions.unzipper_choose_folder(self),
274-
on_refresh=lambda: actions.unzipper_refresh(self),
275-
on_extract_selected=lambda paths: actions.unzipper_extract_selected(self, paths))
276-
return self.unzipper_page
277-
278-
def _build_page_pdf(self, parent) -> ctk.CTkFrame:
279-
self.pdf_page = PdfPage(parent,
280-
on_choose_folder=lambda: actions.pdf_choose_folder(self),
281-
on_refresh=lambda: actions.pdf_refresh(self),
282-
on_convert=lambda paths: actions.pdf_convert_selected(self, paths))
283-
return self.pdf_page
284-
285-
def _build_page_dateorg(self, parent) -> ctk.CTkFrame:
286-
self.dateorg_page = DateOrgPage(parent,
287-
on_choose_folder=lambda: actions.dateorg_choose_folder(self),
288-
on_preview=lambda: actions.dateorg_preview(self),
289-
on_apply=lambda: actions.dateorg_apply(self))
290-
return self.dateorg_page
291-
292-
def _build_page_empty_folders(self, parent) -> ctk.CTkFrame:
293-
self.empty_folders_page = EmptyFoldersPage(parent,
294-
on_choose_folder=lambda: actions.empty_folders_choose_folder(self),
295-
on_refresh=lambda: actions.empty_folders_refresh(self),
296-
on_delete_selected=lambda paths: actions.empty_folders_delete_selected(self, paths))
297-
return self.empty_folders_page
298-
299-
def _build_page_large_files(self, parent) -> ctk.CTkFrame:
300-
self.large_files_page = LargeFilesPage(parent,
301-
on_choose_folder=lambda: actions.large_files_choose_folder(self),
302-
on_refresh=lambda min_mb: actions.large_files_refresh(self, min_mb),
303-
on_delete_selected=lambda paths: actions.large_files_delete_selected(self, paths))
304-
return self.large_files_page
305-
306-
def _build_page_settings(self, parent) -> ctk.CTkFrame:
307-
self.settings_page = SettingsPage(parent, settings=self.settings, on_save=lambda: actions.settings_save(self))
308-
return self.settings_page
309-
310-
def _build_page_about(self, parent) -> ctk.CTkFrame:
311-
self.about_page = AboutPage(parent, project_info=get_project_info(), on_open_github=lambda url: actions.open_github(self, url))
312-
return self.about_page
171+
def _build_page_renamer(self, p): return RenamerPage(p, on_choose_folder=lambda: actions.renamer_choose_folder(self), on_preview=lambda: actions.renamer_preview(self), on_apply=lambda: actions.renamer_apply(self))
172+
def _build_page_chunker(self, p): return ChunkerPage(p, on_choose_folder=lambda: actions.chunker_choose_folder(self), on_preview=lambda: actions.chunker_preview(self), on_apply=lambda: actions.chunker_apply(self))
173+
def _build_page_flattener(self, p): return FlattenerPage(p, on_choose_folder=lambda: actions.flattener_choose_folder(self), on_preview=lambda: actions.flattener_preview(self), on_apply=lambda: actions.flattener_apply(self))
174+
def _build_page_unzipper(self, p): return UnzipperPage(p, on_choose_folder=lambda: actions.unzipper_choose_folder(self), on_refresh=lambda: actions.unzipper_refresh(self), on_extract_selected=lambda paths: actions.unzipper_extract_selected(self, paths))
175+
def _build_page_pdf(self, p): return PdfPage(p, on_choose_folder=lambda: actions.pdf_choose_folder(self), on_refresh=lambda: actions.pdf_refresh(self), on_convert=lambda paths: actions.pdf_convert_selected(self, paths))
176+
def _build_page_dateorg(self, p): return DateOrgPage(p, on_choose_folder=lambda: actions.dateorg_choose_folder(self), on_preview=lambda: actions.dateorg_preview(self), on_apply=lambda: actions.dateorg_apply(self))
177+
def _build_page_empty_folders(self, p): return EmptyFoldersPage(p, on_choose_folder=lambda: actions.empty_folders_choose_folder(self), on_refresh=lambda: actions.empty_folders_refresh(self), on_delete_selected=lambda paths: actions.empty_folders_delete_selected(self, paths))
178+
def _build_page_large_files(self, p): return LargeFilesPage(p, on_choose_folder=lambda: actions.large_files_choose_folder(self), on_refresh=lambda m: actions.large_files_refresh(self, m), on_delete_selected=lambda paths: actions.large_files_delete_selected(self, paths))
179+
def _build_page_settings(self, p): return SettingsPage(p, settings=self.settings, on_save=lambda: actions.settings_save(self))
180+
def _build_page_about(self, p): return AboutPage(p, project_info=get_project_info(), on_open_github=lambda url: actions.open_github(self, url))
313181

314182
def switch_page(self, page_name: str) -> None:
315183
if page_name not in self.pages: return
@@ -321,22 +189,17 @@ def switch_page(self, page_name: str) -> None:
321189

322190
def toggle_terminal(self) -> None:
323191
if self._terminal_visible:
324-
self.logs_view.grid_forget()
325-
self._terminal_visible = False
192+
self.logs_view.grid_forget(); self._terminal_visible = False
326193
else:
327-
self.logs_view.grid(row=2, column=0, sticky="ew", padx=18, pady=(0, 18))
328-
self._terminal_visible = True
329-
330-
def set_status(self, s: str) -> None:
331-
self.topbar_view.set_status(s)
194+
self.logs_view.grid(row=2, column=0, sticky="ew", padx=18, pady=(0, 18)); self._terminal_visible = True
332195

333-
def log(self, s: str, level: str = "INFO") -> None:
334-
self.ui_state.log(s, level=level)
196+
def set_status(self, s: str) -> None: self.topbar_view.set_status(s)
197+
def log(self, s: str, level: str = "INFO") -> None: self.ui_state.log(s, level=level)
335198

336199
@staticmethod
337200
def human_bytes(n: int) -> str:
338201
step = 1024.0
339202
for u in ["B", "KB", "MB", "GB", "TB"]:
340203
if n < step: return f"{n:.1f} {u}"
341204
n /= step
342-
return f"{n:.1f} PB"
205+
return f"{n:.1f} PB"

0 commit comments

Comments
 (0)