From 6e83f6499cf16936342afa3cd32c10ea19e4adb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A9sir=C3=A9=20Werner=20Menrath?= Date: Sat, 26 Jul 2025 22:04:14 +0200 Subject: [PATCH] commit 10 scrollen behoben --- .../custom_file_dialog.cpython-312.pyc | Bin 43071 -> 44474 bytes custom_file_dialog.py | 465 ++++++------------ 2 files changed, 159 insertions(+), 306 deletions(-) diff --git a/__pycache__/custom_file_dialog.cpython-312.pyc b/__pycache__/custom_file_dialog.cpython-312.pyc index 2723f90333668a20c7bb73aad45cf8145154c010..256943d0a1cb853c25847aa44f6bc6f1b69ea373 100644 GIT binary patch delta 9024 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 XRZ$iJRY$EEuP&}H{*LFVc$ED=o1HN6 delta 7783 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 MpZP?", 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]) + top_bar = ttk.Frame(content_frame); top_bar.grid(row=0, column=0, sticky="ew", pady=(5, 0)); 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]) - ttk.Separator(content_frame, orient='horizontal').grid( - row=1, column=0, sticky="ew", pady=5) + ttk.Separator(content_frame, orient='horizontal').grid(row=1, column=0, sticky="ew", pady=5) - self.file_list_frame = ttk.Frame(content_frame, style="Content.TFrame") - self.file_list_frame.grid(row=2, 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=2, column=0, sticky="nsew"); self.bind("", self.on_window_resize) - bottom_frame = ttk.Frame(content_frame) - bottom_frame.grid(row=3, 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) + bottom_frame = ttk.Frame(content_frame); bottom_frame.grid(row=3, 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) def on_window_resize(self, event): new_width = self.file_list_frame.winfo_width() if self.view_mode.get() == "icons" and abs(new_width - self.last_width) > 50: - if self.resize_job: - self.after_cancel(self.resize_job) + if self.resize_job: self.after_cancel(self.resize_job) self.resize_job = self.after(200, self.populate_files) self.last_width = new_width + def _unbind_mouse_wheel_events(self): + # Unbind all mouse wheel events from the root window + self.unbind_all("") + self.unbind_all("") + self.unbind_all("") + 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) + # Unbind previous global mouse wheel events + self._unbind_mouse_wheel_events() + + 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.selected_file = None 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() + 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: @@ -317,146 +249,106 @@ class CustomFileDialog(tk.Toplevel): 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) + 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, warning_message) - except PermissionError: - return ([], "Zugriff verweigert.", None) - except FileNotFoundError: - return ([], "Verzeichnis nicht gefunden.", None) + 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 = 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") 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"))) + container_frame.bind("", lambda e: canvas.configure(scrollregion=canvas.bbox("all"))) def _on_mouse_wheel(event): delta = -1 if event.num == 4 else 1 canvas.yview_scroll(delta, "units") - + + # Bind mouse wheel events specifically to the canvas canvas.bind_all("", _on_mouse_wheel) canvas.bind_all("", _on_mouse_wheel) canvas.bind_all("", _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 + if warning: self.status_bar.config(text=warning) + if error: ttk.Label(container_frame, text=error).pack(pady=20); return item_width, item_height = 110, 100 frame_width = self.file_list_frame.winfo_width() col_count = max(1, frame_width // item_width) row, col = 0, 0 for name in items: - if not self.show_hidden_files.get() and name.startswith('.'): - continue + 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) - if not is_dir and not self._matches_filetype(name): - continue + 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) - 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, 14), anchor="center", style="Item.TLabel") - name_label.pack(fill="x", expand=True) + 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) + 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, 14), 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("", 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)) col = (col + 1) % col_count - if col == 0: - row += 1 + 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") - 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) - 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.bind("", self.on_list_double_click) - self.tree.bind("<>", self.on_list_select) + 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) + 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.bind("", self.on_list_double_click); self.tree.bind("<>", self.on_list_select) + + def _on_mouse_wheel_treeview(event): + delta = -1 if event.num == 4 else 1 + self.tree.yview_scroll(delta, "units") + + # Bind mouse wheel events specifically to the treeview + self.tree.bind_all("", _on_mouse_wheel_treeview) + self.tree.bind_all("", _on_mouse_wheel_treeview) + self.tree.bind_all("", _on_mouse_wheel_treeview) items, error, warning = self._get_sorted_items() - if warning: - self.status_bar.config(text=warning) - if error: - self.tree.insert("", "end", values=(error,)) - return + 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) - if not is_dir and not self._matches_filetype(name): - continue + 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) + 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: - icon, file_type, size = self.get_file_icon( - name, 'small'), "Datei", self._format_size(stat.st_size) - self.tree.insert("", "end", text=name, image=icon, values=( - name, size, file_type, modified_time)) - except (FileNotFoundError, PermissionError): - continue + icon, file_type, size = self.get_file_icon(name, 'small'), "Datei", self._format_size(stat.st_size) + self.tree.insert("", "end", text=name, image=icon, values=(name, size, file_type, modified_time)) + except (FileNotFoundError, PermissionError): continue def on_item_select(self, path, item_frame): if hasattr(self, 'selected_item_frame') and self.selected_item_frame.winfo_exists(): 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']) + if isinstance(child, ttk.Label): child.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 + if isinstance(child, ttk.Label): child.state(['selected']) + self.selected_item_frame = item_frame; self.selected_file = path self.update_status_bar() def on_list_select(self, event): - if not self.tree.selection(): - return + if not self.tree.selection(): return item_id = self.tree.selection()[0] item_text = self.tree.item(item_id, 'values')[0] self.selected_file = os.path.join(self.current_dir, item_text) @@ -464,132 +356,93 @@ class CustomFileDialog(tk.Toplevel): def _handle_unsupported_file(self, path): if path.lower().endswith('.svg'): - self.status_bar.config( - text="SVG-Dateien werden nicht unterstützt.") + self.status_bar.config(text="SVG-Dateien werden nicht unterstützt.") return True return False 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 self._handle_unsupported_file(path): return + 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 + if not self.tree.selection(): return item_id = self.tree.selection()[0] 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 self._handle_unsupported_file(path): return + 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))) + 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) - self.forward_button.config(state=tk.NORMAL if self.history_pos < len( - self.history) - 1 else tk.DISABLED) + self.back_button.config(state=tk.NORMAL if self.history_pos > 0 else tk.DISABLED) + self.forward_button.config(state=tk.NORMAL if self.history_pos < len(self.history) - 1 else tk.DISABLED) 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): - if self._handle_unsupported_file(self.selected_file): - return + if self._handle_unsupported_file(self.selected_file): return 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.replace( - "*.", "").lower().split() + 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 + if fn_lower.endswith(p): return True return False def _format_size(self, size_bytes): - if size_bytes is None: - return "" - if size_bytes < 1024: - return f"{size_bytes} B" - if size_bytes < 1024**2: - return f"{size_bytes/1024:.1f} KB" - if size_bytes < 1024**3: - return f"{size_bytes/1024**2:.1f} MB" + if size_bytes is None: return "" + if size_bytes < 1024: return f"{size_bytes} B" + if size_bytes < 1024**2: return f"{size_bytes/1024:.1f} KB" + if size_bytes < 1024**3: return f"{size_bytes/1024**2:.1f} MB" 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] + "..." + return text if len(text) <= max_len else text[:max_len-3] + "..." \ No newline at end of file