diff --git a/__pycache__/custom_file_dialog.cpython-312.pyc b/__pycache__/custom_file_dialog.cpython-312.pyc index 2723f90..256943d 100644 Binary files a/__pycache__/custom_file_dialog.cpython-312.pyc and b/__pycache__/custom_file_dialog.cpython-312.pyc differ diff --git a/custom_file_dialog.py b/custom_file_dialog.py index 2a311e6..b6b2530 100644 --- a/custom_file_dialog.py +++ b/custom_file_dialog.py @@ -8,11 +8,9 @@ from datetime import datetime SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) MAX_ITEMS_TO_DISPLAY = 1000 - def get_icon_path(icon_name): return os.path.join(SCRIPT_DIR, icon_name) - def get_xdg_user_dir(dir_key, fallback_name): home = os.path.expanduser("~") fallback_path = os.path.join(home, fallback_name) @@ -35,7 +33,6 @@ def get_xdg_user_dir(dir_key, fallback_name): pass return fallback_path - class Tooltip: def __init__(self, widget, text, wraplength=250): self.widget = widget @@ -49,15 +46,11 @@ class Tooltip: def enter(self, event=None): self.schedule() def leave(self, event=None): self.unschedule(); self.hide_tooltip() - - def schedule(self): self.unschedule( - ); self.id = self.widget.after(500, self.show_tooltip) - + def schedule(self): self.unschedule(); self.id = self.widget.after(500, self.show_tooltip) def unschedule(self): id = self.id self.id = None - if id: - self.widget.after_cancel(id) + if id: self.widget.after_cancel(id) def show_tooltip(self, event=None): x, y, _, _ = self.widget.bbox("insert") @@ -73,9 +66,7 @@ class Tooltip: def hide_tooltip(self): tw = self.tooltip_window self.tooltip_window = None - if tw: - tw.destroy() - + if tw: tw.destroy() class CustomFileDialog(tk.Toplevel): def __init__(self, parent, initial_dir=None, filetypes=None): @@ -88,8 +79,7 @@ class CustomFileDialog(tk.Toplevel): self.grab_set() self.selected_file = None - self.current_dir = os.path.abspath( - initial_dir) if initial_dir else os.path.expanduser("~") + self.current_dir = os.path.abspath(initial_dir) if initial_dir else os.path.expanduser("~") self.filetypes = filetypes if filetypes else [("Alle Dateien", "*.*")] self.current_filter_pattern = self.filetypes[0][1] self.history = [] @@ -110,7 +100,7 @@ class CustomFileDialog(tk.Toplevel): 'computer_small': '/usr/share/icons/lx-icons/32/computer-32.png', 'computer_large': '/usr/share/icons/lx-icons/48/computer-48.png', 'downloads_small': '/usr/share/icons/lx-icons/32/folder-water-download-32.png', - 'downloads_large': '/usr/share/icons/lx-icons/482/folder-water-download-48.png', + 'downloads_large': '/usr/share/icons/lx-icons/48/folder-water-download-48.png', 'documents_small': '/usr/share/icons/lx-icons/32/folder-water-documents-32.png', 'documents_large': '/usr/share/icons/lx-icons/48/folder-water-documents-48.png', 'pictures_small': '/usr/share/icons/lx-icons/32/folder-water-pictures-32.png', @@ -139,22 +129,14 @@ class CustomFileDialog(tk.Toplevel): def get_file_icon(self, filename, size='large'): ext = os.path.splitext(filename)[1].lower() - if ext == '.svg': - return self.icons[f'warning_{size}'] - if ext == '.py': - return self.icons[f'python_{size}'] - if ext == '.pdf': - return self.icons[f'pdf_{size}'] - if ext in ['.tar', '.zip', '.rar', '.7z', '.gz']: - return self.icons[f'archive_{size}'] - if ext in ['.mp3', '.wav', '.ogg', '.flac']: - return self.icons[f'audio_{size}'] - if ext in ['.mp4', '.mkv', '.avi', '.mov']: - return self.icons[f'video_{size}'] if size == 'large' else self.icons['video_small_file'] - if ext in ['.jpg', '.jpeg', '.png', '.gif', '.bmp']: - return self.icons[f'picture_{size}'] - if ext == '.iso': - return self.icons[f'iso_{size}'] + if ext == '.svg': return self.icons[f'warning_{size}'] + if ext == '.py': return self.icons[f'python_{size}'] + if ext == '.pdf': return self.icons[f'pdf_{size}'] + if ext in ['.tar', '.zip', '.rar', '.7z', '.gz']: return self.icons[f'archive_{size}'] + if ext in ['.mp3', '.wav', '.ogg', '.flac']: return self.icons[f'audio_{size}'] + if ext in ['.mp4', '.mkv', '.avi', '.mov']: return self.icons[f'video_{size}'] if size == 'large' else self.icons['video_small_file'] + if ext in ['.jpg', '.jpeg', '.png', '.gif', '.bmp']: return self.icons[f'picture_{size}'] + if ext == '.iso': return self.icons[f'iso_{size}'] return self.icons[f'file_{size}'] def create_styles(self): @@ -172,142 +154,92 @@ class CustomFileDialog(tk.Toplevel): self.icon_bg_color = base_bg # Main background for content style.configure("Sidebar.TFrame", background=self.sidebar_color) - style.configure("Sidebar.TButton", - background=self.sidebar_color, anchor="w", padding=5) - style.map("Sidebar.TButton", background=[ - ('active', self.selection_color)]) + style.configure("Sidebar.TButton", background=self.sidebar_color, anchor="w", padding=5) + style.map("Sidebar.TButton", background=[('active', self.selection_color)]) style.configure("Content.TFrame", background=self.icon_bg_color) style.configure("Item.TFrame", background=self.icon_bg_color) - style.map('Item.TFrame', background=[ - ('selected', self.selection_color)]) + style.map('Item.TFrame', background=[('selected', self.selection_color)]) style.configure("Item.TLabel", background=self.icon_bg_color) - style.map('Item.TLabel', background=[('selected', self.selection_color)], foreground=[ - ('selected', "black" if not is_dark else "white")]) + style.map('Item.TLabel', background=[('selected', self.selection_color)], foreground=[('selected', "black" if not is_dark else "white")]) style.configure("Icon.TLabel", background=self.icon_bg_color) - style.map('Icon.TLabel', background=[ - ('selected', self.selection_color)]) - - style.configure("Treeview.Heading", relief="raised", - borderwidth=1, font=('TkDefaultFont', 10, 'bold')) - style.configure("Treeview", rowheight=28, - background=self.icon_bg_color, fieldbackground=self.icon_bg_color) - style.map("Treeview", background=[('selected', self.selection_color)], foreground=[ - ('selected', "black" if not is_dark else "white")]) + style.map('Icon.TLabel', background=[('selected', self.selection_color)]) + + style.configure("Treeview.Heading", relief="raised", borderwidth=1, font=('TkDefaultFont', 10, 'bold')) + style.configure("Treeview", rowheight=28, background=self.icon_bg_color, fieldbackground=self.icon_bg_color) + style.map("Treeview", background=[('selected', self.selection_color)], foreground=[('selected', "black" if not is_dark else "white")]) def create_widgets(self): - main_frame = ttk.Frame(self, padding="10") - main_frame.pack(fill="both", expand=True) - paned_window = ttk.PanedWindow(main_frame, orient=tk.HORIZONTAL) + main_frame = ttk.Frame(self, padding="10"); main_frame.pack(fill="both", expand=True) + paned_window = ttk.PanedWindow(main_frame, orient=tk.HORIZONTAL); paned_window.pack(fill="both", expand=True) - sidebar_frame = ttk.Frame( - paned_window, padding=5, style="Sidebar.TFrame") - paned_window.add(sidebar_frame, weight=0) + sidebar_frame = ttk.Frame(paned_window, padding=5, style="Sidebar.TFrame"); paned_window.add(sidebar_frame, weight=0) sidebar_frame.grid_rowconfigure(1, weight=1) - sidebar_nav_frame = ttk.Frame(sidebar_frame, style="Sidebar.TFrame") - sidebar_nav_frame.grid(row=0, column=0, sticky="ew", pady=(0, 10)) - self.back_button = ttk.Button( - sidebar_nav_frame, text="◀", command=self.go_back, state=tk.DISABLED, width=3) - self.back_button.pack(side="left", fill="x", expand=True) - self.home_button = ttk.Button(sidebar_nav_frame, text="🏠", command=lambda: self.navigate_to( - os.path.expanduser("~")), width=3) - self.home_button.pack(side="left", fill="x", expand=True, padx=2) - self.forward_button = ttk.Button( - sidebar_nav_frame, text="▶", command=self.go_forward, state=tk.DISABLED, width=3) - self.forward_button.pack(side="left", fill="x", expand=True) + sidebar_nav_frame = ttk.Frame(sidebar_frame, style="Sidebar.TFrame"); sidebar_nav_frame.grid(row=0, column=0, sticky="ew", pady=(0, 10)) + self.back_button = ttk.Button(sidebar_nav_frame, text="◀", command=self.go_back, state=tk.DISABLED, width=3); self.back_button.pack(side="left", fill="x", expand=True) + self.home_button = ttk.Button(sidebar_nav_frame, text="🏠", command=lambda: self.navigate_to(os.path.expanduser("~")), width=3); self.home_button.pack(side="left", fill="x", expand=True, padx=2) + self.forward_button = ttk.Button(sidebar_nav_frame, text="▶", command=self.go_forward, state=tk.DISABLED, width=3); self.forward_button.pack(side="left", fill="x", expand=True) - sidebar_buttons_frame = ttk.Frame( - sidebar_frame, style="Sidebar.TFrame") - sidebar_buttons_frame.grid(row=1, column=0, sticky="nsew") + sidebar_buttons_frame = ttk.Frame(sidebar_frame, style="Sidebar.TFrame"); sidebar_buttons_frame.grid(row=1, column=0, sticky="nsew") sidebar_buttons_config = [ - {'name': 'Computer', - 'icon': self.icons['computer_small'], 'path': '/'}, - {'name': 'Downloads', 'icon': self.icons['downloads_small'], 'path': get_xdg_user_dir( - "XDG_DOWNLOAD_DIR", "Downloads")}, - {'name': 'Dokumente', 'icon': self.icons['documents_small'], 'path': get_xdg_user_dir( - "XDG_DOCUMENTS_DIR", "Documents")}, - {'name': 'Bilder', 'icon': self.icons['pictures_small'], 'path': get_xdg_user_dir( - "XDG_PICTURES_DIR", "Pictures")}, - {'name': 'Musik', 'icon': self.icons['music_small'], 'path': get_xdg_user_dir( - "XDG_MUSIC_DIR", "Music")}, - {'name': 'Videos', 'icon': self.icons['video_small'], 'path': get_xdg_user_dir( - "XDG_VIDEO_DIR", "Videos")}, + {'name': 'Computer', 'icon': self.icons['computer_small'], 'path': '/'}, + {'name': 'Downloads', 'icon': self.icons['downloads_small'], 'path': get_xdg_user_dir("XDG_DOWNLOAD_DIR", "Downloads")}, + {'name': 'Dokumente', 'icon': self.icons['documents_small'], 'path': get_xdg_user_dir("XDG_DOCUMENTS_DIR", "Documents")}, + {'name': 'Bilder', 'icon': self.icons['pictures_small'], 'path': get_xdg_user_dir("XDG_PICTURES_DIR", "Pictures")}, + {'name': 'Musik', 'icon': self.icons['music_small'], 'path': get_xdg_user_dir("XDG_MUSIC_DIR", "Music")}, + {'name': 'Videos', 'icon': self.icons['video_small'], 'path': get_xdg_user_dir("XDG_VIDEO_DIR", "Videos")}, ] for config in sidebar_buttons_config: - btn = ttk.Button(sidebar_buttons_frame, text=f" {config['name']}", image=config['icon'], - compound="left", command=lambda p=config['path']: self.navigate_to(p), style="Sidebar.TButton") - btn.pack(fill="x", pady=1) + btn = ttk.Button(sidebar_buttons_frame, text=f" {config['name']}", image=config['icon'], compound="left", command=lambda p=config['path']: self.navigate_to(p), style="Sidebar.TButton"); btn.pack(fill="x", pady=1) - content_frame = ttk.Frame(paned_window, padding=(5, 0, 0, 0)) - paned_window.add(content_frame, weight=1) - content_frame.grid_rowconfigure(2, weight=1) - content_frame.grid_columnconfigure(0, weight=1) + content_frame = ttk.Frame(paned_window, padding=(5, 0, 0, 0)); paned_window.add(content_frame, weight=1) + content_frame.grid_rowconfigure(2, weight=1); content_frame.grid_columnconfigure(0, weight=1) - 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]) + 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