From d7c4c0088b6c79f18c58ae998d130db14f0d6764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A9sir=C3=A9=20Werner=20Menrath?= Date: Sat, 26 Jul 2025 18:34:40 +0200 Subject: [PATCH] commit seven --- .../custom_file_dialog.cpython-312.pyc | Bin 39977 -> 41921 bytes custom_file_dialog.py | 238 +++++++----------- mainwindow.py | 10 +- 3 files changed, 92 insertions(+), 156 deletions(-) diff --git a/__pycache__/custom_file_dialog.cpython-312.pyc b/__pycache__/custom_file_dialog.cpython-312.pyc index 1491f4a21e5c5aa03a2769881bfd652d2a956c9e..50f3c0255b1dff740272204e2a39816cdc71d696 100644 GIT binary patch delta 7630 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 MPX2}xE28lK2C6{RmH+?% delta 5983 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", lambda e: self.navigate_to(self.path_entry.get())) - - self.hidden_files_button = ttk.Checkbutton(top_bar, text="Versteckte Dateien", variable=self.show_hidden_files, command=self.populate_files) - self.hidden_files_button.grid(row=0, column=1, padx=5) - - view_switch = ttk.Frame(top_bar, padding=(5, 0)) - view_switch.grid(row=0, column=2) + top_bar = ttk.Frame(content_frame); top_bar.grid(row=0, column=0, sticky="ew", pady=(5, 5)); top_bar.grid_columnconfigure(0, weight=1) + self.path_entry = ttk.Entry(top_bar); self.path_entry.grid(row=0, column=0, sticky="ew"); self.path_entry.bind("", lambda e: self.navigate_to(self.path_entry.get())) + self.hidden_files_button = ttk.Checkbutton(top_bar, text="Versteckte Dateien", variable=self.show_hidden_files, command=self.populate_files); self.hidden_files_button.grid(row=0, column=1, padx=5) + view_switch = ttk.Frame(top_bar, padding=(5, 0)); view_switch.grid(row=0, column=2) ttk.Radiobutton(view_switch, text="Kacheln", variable=self.view_mode, value="icons", command=self.populate_files).pack(side="left") ttk.Radiobutton(view_switch, text="Liste", variable=self.view_mode, value="list", command=self.populate_files).pack(side="left") - - self.filter_combobox = ttk.Combobox(top_bar, values=[ft[0] for ft in self.filetypes], state="readonly", width=20) - self.filter_combobox.grid(row=0, column=3, padx=5) - self.filter_combobox.bind("<>", self.on_filter_change) - self.filter_combobox.set(self.filetypes[0][0]) + self.filter_combobox = ttk.Combobox(top_bar, values=[ft[0] for ft in self.filetypes], state="readonly", width=20); self.filter_combobox.grid(row=0, column=3, padx=5) + self.filter_combobox.bind("<>", self.on_filter_change); self.filter_combobox.set(self.filetypes[0][0]) - self.file_list_frame = ttk.Frame(content_frame, style="Content.TFrame") - self.file_list_frame.grid(row=1, column=0, sticky="nsew") - self.bind("", self.on_window_resize) + self.file_list_frame = ttk.Frame(content_frame, style="Content.TFrame"); self.file_list_frame.grid(row=1, column=0, sticky="nsew"); self.bind("", self.on_window_resize) - bottom_frame = ttk.Frame(content_frame) - bottom_frame.grid(row=2, column=0, sticky="ew", pady=(5, 0)) - bottom_frame.grid_columnconfigure(0, weight=1) - self.status_bar = ttk.Label(bottom_frame, text="", anchor="w") - self.status_bar.grid(row=0, column=0, sticky="ew") - action_buttons_frame = ttk.Frame(bottom_frame) - action_buttons_frame.grid(row=0, column=1) + bottom_frame = ttk.Frame(content_frame); bottom_frame.grid(row=2, column=0, sticky="ew", pady=(5, 0)); bottom_frame.grid_columnconfigure(0, weight=1) + self.status_bar = ttk.Label(bottom_frame, text="", anchor="w"); self.status_bar.grid(row=0, column=0, sticky="ew") + action_buttons_frame = ttk.Frame(bottom_frame); action_buttons_frame.grid(row=0, column=1) ttk.Button(action_buttons_frame, text="Öffnen", command=self.on_open).pack(side="right") ttk.Button(action_buttons_frame, text="Abbrechen", command=self.on_cancel).pack(side="right", padx=5) @@ -241,37 +202,48 @@ class CustomFileDialog(tk.Toplevel): def populate_files(self): for widget in self.file_list_frame.winfo_children(): widget.destroy() - self.path_entry.delete(0, tk.END) - self.path_entry.insert(0, self.current_dir) + self.path_entry.delete(0, tk.END); self.path_entry.insert(0, self.current_dir) self.selected_file = None - self.update_status_bar() + # Clear status bar before populating, but preserve important warnings + current_status = self.status_bar.cget("text") + if not current_status.startswith("Zeige"): self.update_status_bar() if self.view_mode.get() == "list": self.populate_list_view() else: self.populate_icon_view() def _get_sorted_items(self): try: items = os.listdir(self.current_dir) + num_items = len(items) + warning_message = None + if num_items > MAX_ITEMS_TO_DISPLAY: + warning_message = f"Zeige {MAX_ITEMS_TO_DISPLAY} von {num_items} Einträgen." + items = items[:MAX_ITEMS_TO_DISPLAY] dirs = sorted([d for d in items if os.path.isdir(os.path.join(self.current_dir, d))], key=str.lower) files = sorted([f for f in items if not os.path.isdir(os.path.join(self.current_dir, f))], key=str.lower) - return (dirs + files, None) - except PermissionError: - return ([], "Zugriff verweigert.") - except FileNotFoundError: - return ([], "Verzeichnis nicht gefunden.") + return (dirs + files, None, warning_message) + except PermissionError: return ([], "Zugriff verweigert.", None) + except FileNotFoundError: return ([], "Verzeichnis nicht gefunden.", None) def populate_icon_view(self): canvas = tk.Canvas(self.file_list_frame, highlightthickness=0, bg=self.icon_bg_color) v_scrollbar = ttk.Scrollbar(self.file_list_frame, orient="vertical", command=canvas.yview) - canvas.pack(side="left", fill="both", expand=True) - v_scrollbar.pack(side="right", fill="y") + canvas.pack(side="left", fill="both", expand=True); v_scrollbar.pack(side="right", fill="y") container_frame = ttk.Frame(canvas, style="Content.TFrame") canvas.create_window((0, 0), window=container_frame, anchor="nw") container_frame.bind("", lambda e: canvas.configure(scrollregion=canvas.bbox("all"))) - items, error = self._get_sorted_items() + def _on_mouse_wheel(event): + if event.num == 4 or event.delta > 0: canvas.yview_scroll(-1, "units") + elif event.num == 5 or event.delta < 0: canvas.yview_scroll(1, "units") + + for widget in [canvas, container_frame]: + widget.bind("", _on_mouse_wheel); widget.bind("", _on_mouse_wheel); widget.bind("", _on_mouse_wheel) + + items, error, warning = self._get_sorted_items() + if warning: self.status_bar.config(text=warning) if error: ttk.Label(container_frame, text=error).pack(pady=20); return - item_width, item_height = 120, 100 + item_width, item_height = 115, 100 frame_width = self.file_list_frame.winfo_width() col_count = max(1, frame_width // item_width) row, col = 0, 0 @@ -282,47 +254,40 @@ class CustomFileDialog(tk.Toplevel): if not is_dir and not self._matches_filetype(name): continue item_frame = ttk.Frame(container_frame, width=item_width, height=item_height, style="Item.TFrame") - item_frame.grid(row=row, column=col, padx=5, pady=5) - item_frame.grid_propagate(False) + item_frame.grid(row=row, column=col, padx=5, pady=5); item_frame.grid_propagate(False) icon = self.icons['folder_large'] if is_dir else self.get_file_icon(name, 'large') - icon_label = ttk.Label(item_frame, image=icon, style="Icon.TLabel") - icon_label.pack(pady=(10, 5)) - name_label = ttk.Label(item_frame, text=self.shorten_text(name, 15), anchor="center", style="Item.TLabel") - name_label.pack(fill="x", expand=True) + icon_label = ttk.Label(item_frame, image=icon, style="Icon.TLabel"); icon_label.pack(pady=(10, 5)) + name_label = ttk.Label(item_frame, text=self.shorten_text(name, 15), anchor="center", style="Item.TLabel"); name_label.pack(fill="x", expand=True) Tooltip(item_frame, name) for widget in [item_frame, icon_label, name_label]: widget.bind("", lambda e, p=path: self.on_item_double_click(p)) widget.bind("", lambda e, p=path, f=item_frame: self.on_item_select(p, f)) + widget.bind("", _on_mouse_wheel); widget.bind("", _on_mouse_wheel); widget.bind("", _on_mouse_wheel) col = (col + 1) % col_count if col == 0: row += 1 def populate_list_view(self): - tree_frame = ttk.Frame(self.file_list_frame) - tree_frame.pack(fill='both', expand=True) - columns = ("name", "size", "type", "modified") - self.tree = ttk.Treeview(tree_frame, columns=columns, show="headings") + tree_frame = ttk.Frame(self.file_list_frame); tree_frame.pack(fill='both', expand=True) + columns = ("name", "size", "type", "modified"); self.tree = ttk.Treeview(tree_frame, columns=columns, show="headings") self.tree.heading("name", text="Name", anchor="w"); self.tree.column("name", anchor="w", width=300, stretch=True) self.tree.heading("size", text="Größe", anchor="e"); self.tree.column("size", anchor="e", width=120, stretch=False) self.tree.heading("type", text="Typ", anchor="w"); self.tree.column("type", anchor="w", width=120, stretch=False) self.tree.heading("modified", text="Geändert am", anchor="w"); self.tree.column("modified", anchor="w", width=160, stretch=False) - v_scrollbar = ttk.Scrollbar(tree_frame, orient="vertical", command=self.tree.yview) - h_scrollbar = ttk.Scrollbar(tree_frame, orient="horizontal", command=self.tree.xview) + v_scrollbar = ttk.Scrollbar(tree_frame, orient="vertical", command=self.tree.yview); h_scrollbar = ttk.Scrollbar(tree_frame, orient="horizontal", command=self.tree.xview) self.tree.configure(yscrollcommand=v_scrollbar.set, xscrollcommand=h_scrollbar.set) - self.tree.pack(side="left", fill="both", expand=True) - v_scrollbar.pack(side="right", fill="y"); h_scrollbar.pack(side="bottom", fill="x") + self.tree.pack(side="left", fill="both", expand=True); v_scrollbar.pack(side="right", fill="y"); h_scrollbar.pack(side="bottom", fill="x") self.tree.bind("", self.on_list_double_click); self.tree.bind("<>", self.on_list_select) - items, error = self._get_sorted_items() - if error: self.tree.insert("", "end", text=error); return + items, error, warning = self._get_sorted_items() + if warning: self.status_bar.config(text=warning) + if error: self.tree.insert("", "end", values=(error,)); return for name in items: if not self.show_hidden_files.get() and name.startswith('.'): continue - path = os.path.join(self.current_dir, name) - is_dir = os.path.isdir(path) + path = os.path.join(self.current_dir, name); is_dir = os.path.isdir(path) if not is_dir and not self._matches_filetype(name): continue try: - stat = os.stat(path) - modified_time = datetime.fromtimestamp(stat.st_mtime).strftime('%d.%m.%Y %H:%M') + stat = os.stat(path); modified_time = datetime.fromtimestamp(stat.st_mtime).strftime('%d.%m.%Y %H:%M') if is_dir: icon, file_type, size = self.icons['folder_small'], "Ordner", "" else: @@ -335,11 +300,10 @@ class CustomFileDialog(tk.Toplevel): self.selected_item_frame.state(['!selected']) for child in self.selected_item_frame.winfo_children(): if isinstance(child, ttk.Label): child.state(['!selected']) - item_frame.state(['selected']) + item_frame.state(['selected']); for child in item_frame.winfo_children(): if isinstance(child, ttk.Label): child.state(['selected']) - self.selected_item_frame = item_frame - self.selected_file = path + self.selected_item_frame = item_frame; self.selected_file = path self.update_status_bar() def on_list_select(self, event): @@ -357,11 +321,8 @@ class CustomFileDialog(tk.Toplevel): def on_item_double_click(self, path): if self._handle_unsupported_file(path): return - if os.path.isdir(path): - self.navigate_to(path) - else: - self.selected_file = path - self.destroy() + if os.path.isdir(path): self.navigate_to(path) + else: self.selected_file = path; self.destroy() def on_list_double_click(self, event): if not self.tree.selection(): return @@ -369,54 +330,38 @@ class CustomFileDialog(tk.Toplevel): item_text = self.tree.item(item_id, 'values')[0] path = os.path.join(self.current_dir, item_text) if self._handle_unsupported_file(path): return - if os.path.isdir(path): - self.navigate_to(path) - else: - self.selected_file = path - self.destroy() + if os.path.isdir(path): self.navigate_to(path) + else: self.selected_file = path; self.destroy() def on_filter_change(self, event): selected_desc = self.filter_combobox.get() for desc, pattern in self.filetypes: - if desc == selected_desc: - self.current_filter_pattern = pattern - break + if desc == selected_desc: self.current_filter_pattern = pattern; break self.populate_files() def navigate_to(self, path): try: real_path = os.path.realpath(os.path.abspath(os.path.expanduser(path))) if not os.path.isdir(real_path): - self.status_bar.config(text=f"Fehler: Verzeichnis '{os.path.basename(path)}' nicht gefunden.") - return + self.status_bar.config(text=f"Fehler: Verzeichnis '{os.path.basename(path)}' nicht gefunden."); return if not os.access(real_path, os.R_OK): - self.status_bar.config(text=f"Zugriff auf '{os.path.basename(path)}' verweigert.") - return - + self.status_bar.config(text=f"Zugriff auf '{os.path.basename(path)}' verweigert."); return self.current_dir = real_path - if self.history_pos < len(self.history) - 1: - self.history = self.history[:self.history_pos + 1] + if self.history_pos < len(self.history) - 1: self.history = self.history[:self.history_pos + 1] if not self.history or self.history[-1] != self.current_dir: - self.history.append(self.current_dir) - self.history_pos = len(self.history) - 1 - self.populate_files() - self.update_nav_buttons() - except Exception as e: - self.status_bar.config(text=f"Fehler: {e}") + self.history.append(self.current_dir); self.history_pos = len(self.history) - 1 + self.populate_files(); self.update_nav_buttons() + except Exception as e: self.status_bar.config(text=f"Fehler: {e}") def go_back(self): if self.history_pos > 0: - self.history_pos -= 1 - self.current_dir = self.history[self.history_pos] - self.populate_files() - self.update_nav_buttons() + self.history_pos -= 1; self.current_dir = self.history[self.history_pos] + self.populate_files(); self.update_nav_buttons() def go_forward(self): if self.history_pos < len(self.history) - 1: - self.history_pos += 1 - self.current_dir = self.history[self.history_pos] - self.populate_files() - self.update_nav_buttons() + self.history_pos += 1; self.current_dir = self.history[self.history_pos] + self.populate_files(); self.update_nav_buttons() def update_nav_buttons(self): self.back_button.config(state=tk.NORMAL if self.history_pos > 0 else tk.DISABLED) @@ -424,16 +369,13 @@ class CustomFileDialog(tk.Toplevel): def update_status_bar(self): try: - _, _, free = shutil.disk_usage(self.current_dir) - free_str = self._format_size(free) + _, _, free = shutil.disk_usage(self.current_dir); free_str = self._format_size(free) status_text = f"Freier Speicher: {free_str}" if self.selected_file and os.path.exists(self.selected_file) and not os.path.isdir(self.selected_file): - size = os.path.getsize(self.selected_file) - size_str = self._format_size(size) + size = os.path.getsize(self.selected_file); size_str = self._format_size(size) status_text += f" | '{os.path.basename(self.selected_file)}' Größe: {size_str}" self.status_bar.config(text=status_text) - except FileNotFoundError: - self.status_bar.config(text="Verzeichnis nicht gefunden") + except FileNotFoundError: self.status_bar.config(text="Verzeichnis nicht gefunden") def on_open(self): if self.selected_file and os.path.isfile(self.selected_file): @@ -441,21 +383,17 @@ class CustomFileDialog(tk.Toplevel): self.destroy() def on_cancel(self): - self.selected_file = None - self.destroy() + self.selected_file = None; self.destroy() def get_selected_file(self): return self.selected_file def _matches_filetype(self, filename): - if self.current_filter_pattern == "*.*": - return True - patterns = self.current_filter_pattern.split() - for pattern in patterns: - # Handles patterns like "*.txt" - p = pattern.lower().replace("*.", "") - if filename.lower().endswith(p): - return True + if self.current_filter_pattern == "*.*": return True + patterns = self.current_filter_pattern.replace("*.", "").lower().split() + fn_lower = filename.lower() + for p in patterns: + if fn_lower.endswith(p): return True return False def _format_size(self, size_bytes): @@ -466,4 +404,4 @@ class CustomFileDialog(tk.Toplevel): return f"{size_bytes/1024**3:.1f} GB" def shorten_text(self, text, max_len): - return text if len(text) <= max_len else text[:max_len-3] + "..." \ No newline at end of file + return text if len(text) <= max_len else text[:max_len-3] + "..." diff --git a/mainwindow.py b/mainwindow.py index 6f115d3..3d809be 100644 --- a/mainwindow.py +++ b/mainwindow.py @@ -29,16 +29,14 @@ class GlotzMol(tk.Tk): container.columnconfigure(0, weight=1) def open_custom_dialog(self): - # Initial directory can be anywhere, let's test with /backup - initial_directory = "/backup" if os.path.exists( - "/backup") else os.path.expanduser("~") dialog = CustomFileDialog(self, - initial_dir=initial_directory, - filetypes=[("Audio-Dateien", "*.mp3 *.wav"), + initial_dir=os.path.expanduser("~"), + filetypes=[("Alle Dateien", "*.*"), + ("Audio-Dateien", "*.mp3 *.wav"), ("Video-Dateien", "*.mkv *.mp4"), ("ISO-Images", "*.iso"), - ("Alle Dateien", "*.*")]) + ]) # This is the crucial part: wait for the dialog to be closed self.wait_window(dialog)