Skip to content

Commit 6632578

Browse files
Merge pull request #20 from ZylosCore/fps-fix-problem
Sidbar fps - changing the file management
2 parents cb66b00 + 7f35ae7 commit 6632578

5 files changed

Lines changed: 155 additions & 276 deletions

File tree

project_info.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<project>
33
<name>BulkFolder</name>
4-
<version>1.7.5</version>
4+
<version>1.7.6</version>
55
<description>The safest and fastest bulk organizer and renamer.</description>
66
<author>Achraf KHABAR / Open Source Community</author>
77
<repository>https://github.com/Ashraf-Khabar/bulkfolder</repository>

src/bulkfolder/ui/app.py

Lines changed: 8 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,15 @@
66
from PIL import Image, ImageTk, ImageDraw
77

88
# --- Imports: Theme, State, and Logic ---
9-
# Importing colors to ensure the app respects our custom UI theme.
109
from .theme import DR_BG, DR_TEXT, DR_MUTED, DR_SURFACE, DR_BORDER, DR_PURPLE
11-
# UIState acts as the "memory" of the application (e.g., storing the selected folder paths).
1210
from .state import UIState
13-
# actions contains all the logical functions (the "Controller" in MVC).
1411
from . import actions
1512

1613
# --- Imports: Configuration and Project Info ---
1714
from ..config import load_settings, AppSettings
1815
from ..info import get_project_info
1916

2017
# --- Imports: Views (UI Components) ---
21-
# Importing all the visual blocks (the "View" in MVC) that make up the user interface.
2218
from .views.sidebar import SidebarView
2319
from .views.topbar import TopbarView
2420
from .views.cards import CardsRow
@@ -42,41 +38,33 @@
4238
class SplashScreen(ctk.CTkToplevel):
4339
"""
4440
A temporary loading screen that appears before the main application window is ready.
45-
It provides visual feedback to the user while settings and heavy components are loading.
4641
"""
4742
def __init__(self, master, logo_path):
4843
super().__init__(master)
49-
# Removes the standard window borders and close/minimize buttons.
5044
self.overrideredirect(True)
51-
# Keeps the splash screen always on top of other windows.
5245
self.attributes("-topmost", True)
5346
self.configure(fg_color=DR_BG)
5447

55-
# Calculate coordinates to center the splash screen on the user's monitor.
5648
width, height = 340, 380
5749
x = (self.winfo_screenwidth() // 2) - (width // 2)
5850
y = (self.winfo_screenheight() // 2) - (height // 2)
5951
self.geometry(f"{width}x{height}+{x}+{y}")
6052

61-
# Configure the grid to center the inner frame.
6253
self.grid_columnconfigure(0, weight=1)
6354
self.grid_rowconfigure(0, weight=1)
6455

6556
inner = ctk.CTkFrame(self, fg_color="transparent")
6657
inner.grid(row=0, column=0)
6758

68-
# Load and display the application logo if it exists.
6959
if logo_path and logo_path.exists():
7060
img = Image.open(logo_path)
7161
self.photo = ctk.CTkImage(light_image=img, dark_image=img, size=(120, 120))
7262
ctk.CTkLabel(inner, text="", image=self.photo).pack(pady=(0, 20))
7363

74-
# Title and subtitles
7564
ctk.CTkLabel(inner, text="BulkFolder", font=ctk.CTkFont(size=26, weight="bold"), text_color=DR_TEXT).pack()
7665
ctk.CTkLabel(inner, text="Organize & Rename safely", font=ctk.CTkFont(size=13), text_color=DR_MUTED).pack(pady=(0, 20))
7766
ctk.CTkLabel(inner, text="Loading...", font=ctk.CTkFont(size=12), text_color=DR_MUTED).pack(pady=(0, 5))
7867

79-
# Start an animated progress bar to indicate loading is happening.
8068
self.pb = ctk.CTkProgressBar(inner, width=220, progress_color=DR_PURPLE, fg_color=DR_BORDER)
8169
self.pb.pack(pady=(5, 0))
8270
self.pb.set(0)
@@ -85,77 +73,59 @@ def __init__(self, master, logo_path):
8573

8674
class App(ctk.CTk):
8775
"""
88-
The main application window. It acts as the "orchestrator", connecting
89-
the UI (Views) with the logic (Actions) and holding the shared memory (State).
76+
The main application window.
9077
"""
9178
def __init__(self):
9279
super().__init__()
93-
# Hide the main window immediately so only the splash screen is visible at startup.
9480
self.withdraw()
9581

96-
# Load user preferences from the config file.
9782
self.settings: AppSettings = load_settings()
9883

99-
# Force dark mode. The dynamic custom colors are handled by theme.py.
10084
ctk.set_appearance_mode("dark")
10185
ctk.set_default_color_theme("blue")
10286

103-
# Apply UI scaling based on user settings (e.g., 125%).
10487
try:
10588
scale_val = float(self.settings.ui_scaling.replace("%", "")) / 100.0
10689
ctk.set_widget_scaling(scale_val)
10790
ctk.set_window_scaling(scale_val)
10891
except Exception:
10992
pass
11093

111-
# Set main window properties.
11294
self.title("BulkFolder")
11395
self.geometry("1200x720")
11496
self.minsize(1020, 640)
11597
self.configure(fg_color=DR_BG)
11698

117-
# Locate the logo and generate a default one if it doesn't exist.
11899
self.logo_path = Path(__file__).resolve().parent.parent.parent / "assets" / "logo.png"
119100
self._ensure_logo_exists(self.logo_path)
120101

121-
# Set the window icon (top left corner and taskbar).
122102
if self.logo_path.exists():
123103
img = Image.open(self.logo_path)
124104
photo = ImageTk.PhotoImage(img)
125105
self.wm_iconphoto(True, photo)
126106
self.iconphoto(True, photo)
127107

128-
# --- WINDOWS TASKBAR MAGIC FIX ---
129108
if sys.platform.startswith("win"):
130-
# 1. Tell Windows this is a standalone app (AppUserModelID)
131109
try:
132110
import ctypes
133-
# Changement ici : on passe à 1.1 pour vider le cache de Windows !
134111
myappid = 'monprojet.bulkfolder.app.1.1'
135112
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
136113
except Exception:
137114
pass
138115

139116
ico_path = self.logo_path.with_suffix(".ico")
140-
141-
# 2. Force the creation of a proper .ICO file with ALL necessary sizes
142117
try:
143118
img.save(ico_path, format="ICO", sizes=[(16,16), (24,24), (32,32), (48,48), (64,64), (128,128), (256,256)])
144119
except Exception:
145120
pass
146121

147-
# 3. Apply the icon
148122
if ico_path.exists():
149123
self.iconbitmap(str(ico_path))
150124

151-
# Initialize and update the splash screen so it renders before the heavy lifting starts.
152125
self.splash = SplashScreen(self, self.logo_path)
153126
self.splash.update()
154127

155-
# Initialize the global application state (the shared memory).
156128
self.ui_state = UIState()
157-
158-
# Initialize variables to store active plans and scan results.
159129
self.last_plan = None
160130
self.last_scan = None
161131
self.large_files_scan = None
@@ -164,36 +134,28 @@ def __init__(self):
164134
self.flattener_plan = []
165135
self.dateorg_plan = []
166136

167-
# Configure the main window grid layout.
168-
# Column 0 (Sidebar) has weight 0 (fixed width). Column 1 (Main content) has weight 1 (expandable).
169137
self.grid_columnconfigure(0, weight=0)
170138
self.grid_columnconfigure(1, weight=1)
171139
self.grid_rowconfigure(0, weight=1)
172140

173-
# Build the Sidebar and pass the project_info (version, etc.) to it.
174141
info = get_project_info()
175142
self.sidebar_view = SidebarView(info, self, on_page=self.switch_page, logo_path=self.logo_path)
176143
self.sidebar_view.grid(row=0, column=0, sticky="nsw")
177144

178-
# Create the main right-side container that will hold the topbar and the pages.
179145
self.main = ctk.CTkFrame(self, corner_radius=0, fg_color=DR_BG)
180146
self.main.grid(row=0, column=1, sticky="nsew")
181147
self.main.grid_columnconfigure(0, weight=1)
182-
self.main.grid_rowconfigure(1, weight=1) # Row 1 is for the page container, it expands.
148+
self.main.grid_rowconfigure(1, weight=1)
183149

184-
# Build the Topbar (Status and title).
185-
self.topbar_view = TopbarView(self.main, on_toggle_sidebar=self.toggle_sidebar)
150+
# Removed on_toggle_sidebar callback as it's no longer needed
151+
self.topbar_view = TopbarView(self.main, on_toggle_sidebar=None)
186152
self.topbar_view.grid(row=0, column=0, sticky="ew", padx=18, pady=(18, 8))
187153

188-
# Create the specific container where different pages will be swapped in and out.
189154
self.page_container = ctk.CTkFrame(self.main, corner_radius=0, fg_color=DR_BG)
190155
self.page_container.grid(row=1, column=0, sticky="nsew")
191156
self.page_container.grid_columnconfigure(0, weight=1)
192157
self.page_container.grid_rowconfigure(0, weight=1)
193158

194-
# --- Pre-building all pages ---
195-
# We build ALL pages at startup and store them in a dictionary.
196-
# This allows instant switching between pages without losing the user's data or state on that page.
197159
self.pages: dict[str, ctk.CTkFrame] = {}
198160
self.pages["Organizer"] = self._build_page_organizer(self.page_container)
199161
self.pages["Renamer"] = self._build_page_renamer(self.page_container)
@@ -207,7 +169,6 @@ def __init__(self):
207169
self.pages["Settings"] = self._build_page_settings(self.page_container)
208170
self.pages["About"] = self._build_page_about(self.page_container)
209171

210-
# Determine which page to show first based on user settings.
211172
self._current_page = ""
212173
start_page = self.settings.default_page
213174
if start_page not in self.pages:
@@ -217,7 +178,6 @@ def __init__(self):
217178
self.log("App started.", level="DEBUG")
218179
self.set_status("Ready.")
219180

220-
# Wait 2.5 seconds before destroying the splash screen and showing the main UI.
221181
self.after(2500, self._show_main_window)
222182

223183
@staticmethod
@@ -240,26 +200,18 @@ def _ensure_logo_exists(png_path: Path):
240200
except Exception: pass
241201

242202
def _show_main_window(self):
243-
"""Transitions from the splash screen to the main application window."""
244203
try:
245204
self.splash.pb.stop()
246205
self.splash.destroy()
247206
except: pass
248-
self.deiconify() # Unhides the main window.
249-
250-
# -------------------------------------------------------------------------
251-
# Page Builder Methods
252-
# These methods create the specific UI frames for each feature and
253-
# link their buttons to the corresponding logic in actions.py using lambdas.
254-
# -------------------------------------------------------------------------
207+
self.deiconify()
255208

256209
def _build_page_organizer(self, parent) -> ctk.CTkFrame:
257210
frame = ctk.CTkFrame(parent, corner_radius=0, fg_color=DR_BG)
258211
frame.grid_columnconfigure(0, weight=0)
259212
frame.grid_columnconfigure(1, weight=1)
260213
frame.grid_rowconfigure(0, weight=1)
261214

262-
# Left panel: Controls for the Organizer
263215
self.organizer_panel = OrganizerPanel(
264216
frame,
265217
on_choose_folder=lambda: actions.choose_folder(self),
@@ -273,7 +225,6 @@ def _build_page_organizer(self, parent) -> ctk.CTkFrame:
273225
self.organizer_panel.grid(row=0, column=0, sticky="ns", padx=(18, 10), pady=(0, 18))
274226
self.organizer_panel.configure(width=320)
275227

276-
# Right panel: Data visualization (Cards and Tabs)
277228
right = ctk.CTkFrame(frame, corner_radius=0, fg_color=DR_BG)
278229
right.grid(row=0, column=1, sticky="nsew", padx=(10, 18), pady=(0, 18))
279230
right.grid_columnconfigure(0, weight=1)
@@ -380,45 +331,27 @@ def _build_page_about(self, parent) -> ctk.CTkFrame:
380331
)
381332
return self.about_page
382333

383-
# -------------------------------------------------------------------------
384-
# Utility Methods
385-
# -------------------------------------------------------------------------
386-
387334
def switch_page(self, page_name: str) -> None:
388-
"""
389-
Hides the current page and displays the requested page.
390-
Instead of destroying and recreating the UI, it uses grid_forget() to hide it,
391-
which preserves all user inputs (text fields, checkboxes, etc.).
392-
"""
393335
if page_name not in self.pages: return
394-
395-
# Hide the currently active page.
396336
if self._current_page: self.pages[self._current_page].grid_forget()
397-
398-
# Show the new page.
399337
self.pages[page_name].grid(row=0, column=0, sticky="nsew")
400338
self._current_page = page_name
401-
402-
# Update the topbar title.
403339
self.topbar_view.set_title(page_name if page_name != "Organizer" else "Organizer")
404340

405341
def toggle_sidebar(self) -> None:
406-
"""Expands or collapses the left sidebar."""
407-
self.sidebar_view.toggle()
342+
"""Disabled."""
343+
pass
408344

409345
def set_status(self, s: str) -> None:
410-
"""Updates the status message in the top navigation bar."""
411346
self.topbar_view.set_status(s)
412347

413348
def log(self, s: str, level: str = "INFO") -> None:
414-
"""Sends a message to the Logs tab in the Organizer."""
415349
if hasattr(self, "logs_view"): self.logs_view.log(s, level=level)
416350

417351
@staticmethod
418352
def human_bytes(n: int) -> str:
419-
"""Converts raw bytes into a readable format (KB, MB, GB, etc.)."""
420353
step = 1024.0
421354
for u in ["B", "KB", "MB", "GB", "TB"]:
422355
if n < step: return f"{n:.1f} {u}"
423356
n /= step
424-
return f"{n:.1f} PB"
357+
return f"{n:.1f} PB"

0 commit comments

Comments
 (0)