diff --git a/__pycache__/custom_file_dialog.cpython-312.pyc b/__pycache__/custom_file_dialog.cpython-312.pyc index 50f3c02..2723f90 100644 Binary files a/__pycache__/custom_file_dialog.cpython-312.pyc and b/__pycache__/custom_file_dialog.cpython-312.pyc differ diff --git a/audio-32.png b/audio-32.png deleted file mode 100644 index 0058d3d..0000000 Binary files a/audio-32.png and /dev/null differ diff --git a/audio-64.png b/audio-64.png deleted file mode 100644 index 98a91ec..0000000 Binary files a/audio-64.png and /dev/null differ diff --git a/computer-32.png b/computer-32.png deleted file mode 100644 index 4fa0929..0000000 Binary files a/computer-32.png and /dev/null differ diff --git a/computer-64.png b/computer-64.png deleted file mode 100644 index f15e0e6..0000000 Binary files a/computer-64.png and /dev/null differ diff --git a/custom_file_dialog.py b/custom_file_dialog.py index 5cbabfe..2a311e6 100644 --- a/custom_file_dialog.py +++ b/custom_file_dialog.py @@ -8,9 +8,11 @@ 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) @@ -33,6 +35,7 @@ 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 @@ -46,11 +49,15 @@ 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") @@ -66,7 +73,9 @@ 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): @@ -79,7 +88,8 @@ 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 = [] @@ -97,22 +107,28 @@ class CustomFileDialog(tk.Toplevel): def load_icons(self): self.icons = {} icon_files = { - 'computer_small': 'computer-32.png', - 'downloads_small': 'folder-water-download-32.png', - 'documents_small': 'folder-water-documents-32.png', - 'pictures_small': 'folder-water-pictures-32.png', - 'music_small': 'folder-water-music-32.png', - 'video_small': 'folder-water-video-32.png', - 'warning_small': 'warning.png', 'warning_large': 'warning.png', - 'folder_large': 'folder-water-64.png', 'file_large': 'document-64.png', - 'python_large': 'file-python-64.png', 'pdf_large': 'pdf-64.png', - 'archive_large': 'tar-64.png', 'audio_large': 'audio-64.png', - 'video_large': 'video-64.png', 'picture_large': 'picture-64.png', - 'iso_large': 'media-optical-64.png', 'folder_small': 'folder-water-32.png', - 'file_small': 'document-32.png', 'python_small': 'file-python-32.png', - 'pdf_small': 'pdf-32.png', 'archive_small': 'tar-32.png', - 'audio_small': 'audio-32.png', 'video_small_file': 'video-32.png', - 'picture_small': 'picture-32.png', 'iso_small': 'media-optical-32.png' + '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', + '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', + 'pictures_large': '/usr/share/icons/lx-icons/48/folder-water-pictures-48.png', + 'music_small': '/usr/share/icons/lx-icons/32/folder-water-music-32.png', + 'music_large': '/usr/share/icons/lx-icons/48/folder-water-music-48.png', + 'video_small': '/usr/share/icons/lx-icons/32/folder-water-video-32.png', + 'video_large_folder': '/usr/share/icons/lx-icons/48/folder-water-video-48.png', + 'warning_small': '/usr/share/icons/lx-icons/32/warning.png', 'warning_large': '/usr/share/icons/lx-icons/64/warning.png', + 'folder_large': '/usr/share/icons/lx-icons/64/folder-water-64.png', 'file_large': '/usr/share/icons/lx-icons/64/document-64.png', + 'python_large': '/usr/share/icons/lx-icons/64/file-python-64.png', 'pdf_large': '/usr/share/icons/lx-icons/64/pdf-64.png', + 'archive_large': '/usr/share/icons/lx-icons/64/tar-64.png', 'audio_large': '/usr/share/icons/lx-icons/64/audio-64.png', + 'video_large': '/usr/share/icons/lx-icons/64/video-64.png', 'picture_large': '/usr/share/icons/lx-icons/64/picture-64.png', + 'iso_large': '/usr/share/icons/lx-icons/64/media-optical-64.png', 'folder_small': '/usr/share/icons/lx-icons/32/folder-water-32.png', + 'file_small': '/usr/share/icons/lx-icons/32/document-32.png', 'python_small': '/usr/share/icons/lx-icons/32/file-python-32.png', + 'pdf_small': '/usr/share/icons/lx-icons/32/pdf-32.png', 'archive_small': '/usr/share/icons/lx-icons/32/tar-32.png', + 'audio_small': '/usr/share/icons/lx-icons/32/audio-32.png', 'video_small_file': '/usr/share/icons/lx-icons/32/video-32.png', + 'picture_small': '/usr/share/icons/lx-icons/32/picture-32.png', 'iso_small': '/usr/share/icons/lx-icons/32/media-optical-32.png' } for key, filename in icon_files.items(): try: @@ -123,92 +139,175 @@ 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): style = ttk.Style(self) - self.selection_color = "#D5E5F5"; self.selection_fg_color = "black" - self.icon_bg_color = style.lookup('TFrame', 'background') - style.configure("Item.TFrame", background=self.icon_bg_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', self.selection_fg_color)]) - 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) + base_bg = self.cget('background') + is_dark = sum(self.winfo_rgb(base_bg)) / 3 < 32768 + + if is_dark: + self.selection_color = "#4a6984" # Darker blue for selection + self.sidebar_color = "#2c2c2c" # Slightly lighter dark grey for sidebar + self.icon_bg_color = "#333333" # Main background for content + else: + self.selection_color = "#cce5ff" # Light blue for selection + self.sidebar_color = "#f0f0f0" # Light grey for sidebar + 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("Content.TFrame", background=self.icon_bg_color) - style.map("Treeview", background=[('selected', self.selection_color)], foreground=[('selected', self.selection_fg_color)]) - ttk.Style().configure("Sidebar.TButton", anchor="w", padding=5) + style.configure("Item.TFrame", background=self.icon_bg_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.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")]) 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); paned_window.pack(fill="both", expand=True) + 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); 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); 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); 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(1, 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, 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]) + 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]) - 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) + ttk.Separator(content_frame, orient='horizontal').grid( + row=1, column=0, sticky="ew", pady=5) - 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) + 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) 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 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) + 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 - # 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() + 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: @@ -218,96 +317,146 @@ 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): - 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) + delta = -1 if event.num == 4 else 1 + canvas.yview_scroll(delta, "units") + + 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 = 115, 100 + 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, 15), 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("", _on_mouse_wheel); widget.bind("", _on_mouse_wheel); widget.bind("", _on_mouse_wheel) + 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) 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) @@ -315,92 +464,131 @@ 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): diff --git a/document-32.png b/document-32.png deleted file mode 100644 index a9df045..0000000 Binary files a/document-32.png and /dev/null differ diff --git a/document-64.png b/document-64.png deleted file mode 100644 index f6cb0c8..0000000 Binary files a/document-64.png and /dev/null differ diff --git a/file-python-32.png b/file-python-32.png deleted file mode 100644 index 6d9b8d4..0000000 Binary files a/file-python-32.png and /dev/null differ diff --git a/file-python-64.png b/file-python-64.png deleted file mode 100644 index 9bbed59..0000000 Binary files a/file-python-64.png and /dev/null differ diff --git a/folder-water-32.png b/folder-water-32.png deleted file mode 100644 index 3d2d986..0000000 Binary files a/folder-water-32.png and /dev/null differ diff --git a/folder-water-64.png b/folder-water-64.png deleted file mode 100644 index 771c805..0000000 Binary files a/folder-water-64.png and /dev/null differ diff --git a/folder-water-documents-32.png b/folder-water-documents-32.png deleted file mode 100644 index 2e53a0b..0000000 Binary files a/folder-water-documents-32.png and /dev/null differ diff --git a/folder-water-documents-64.png b/folder-water-documents-64.png deleted file mode 100644 index a01c4dc..0000000 Binary files a/folder-water-documents-64.png and /dev/null differ diff --git a/folder-water-download-32.png b/folder-water-download-32.png deleted file mode 100644 index 2aa8dcb..0000000 Binary files a/folder-water-download-32.png and /dev/null differ diff --git a/folder-water-download-64.png b/folder-water-download-64.png deleted file mode 100644 index 60c4790..0000000 Binary files a/folder-water-download-64.png and /dev/null differ diff --git a/folder-water-music-32.png b/folder-water-music-32.png deleted file mode 100644 index 6d12074..0000000 Binary files a/folder-water-music-32.png and /dev/null differ diff --git a/folder-water-music-64.png b/folder-water-music-64.png deleted file mode 100644 index 45b9e9d..0000000 Binary files a/folder-water-music-64.png and /dev/null differ diff --git a/folder-water-pictures-32.png b/folder-water-pictures-32.png deleted file mode 100644 index 48743dd..0000000 Binary files a/folder-water-pictures-32.png and /dev/null differ diff --git a/folder-water-pictures-64.png b/folder-water-pictures-64.png deleted file mode 100644 index 7e04320..0000000 Binary files a/folder-water-pictures-64.png and /dev/null differ diff --git a/folder-water-video-32.png b/folder-water-video-32.png deleted file mode 100644 index 654f67c..0000000 Binary files a/folder-water-video-32.png and /dev/null differ diff --git a/folder-water-video-64.png b/folder-water-video-64.png deleted file mode 100644 index 3fffff1..0000000 Binary files a/folder-water-video-64.png and /dev/null differ diff --git a/mainwindow.py b/mainwindow.py index 3d809be..00ce153 100644 --- a/mainwindow.py +++ b/mainwindow.py @@ -58,7 +58,7 @@ if __name__ == "__main__": style = ttk.Style(root) root.tk.call('source', f"{theme_path}/water.tcl") try: - root.tk.call('set_theme', 'light') + root.tk.call('set_theme', 'dark') except tk.TclError: pass root.mainloop() diff --git a/media-optical-32.png b/media-optical-32.png deleted file mode 100644 index 449ab36..0000000 Binary files a/media-optical-32.png and /dev/null differ diff --git a/media-optical-64.png b/media-optical-64.png deleted file mode 100644 index 80d3d2f..0000000 Binary files a/media-optical-64.png and /dev/null differ diff --git a/pdf-32.png b/pdf-32.png deleted file mode 100644 index 34fc0e6..0000000 Binary files a/pdf-32.png and /dev/null differ diff --git a/pdf-64.png b/pdf-64.png deleted file mode 100644 index 6b41605..0000000 Binary files a/pdf-64.png and /dev/null differ diff --git a/picture-32.png b/picture-32.png deleted file mode 100644 index 9b8c448..0000000 Binary files a/picture-32.png and /dev/null differ diff --git a/picture-64.png b/picture-64.png deleted file mode 100644 index 61c337e..0000000 Binary files a/picture-64.png and /dev/null differ diff --git a/tar-32.png b/tar-32.png deleted file mode 100644 index 5043933..0000000 Binary files a/tar-32.png and /dev/null differ diff --git a/tar-64.png b/tar-64.png deleted file mode 100644 index e2887ce..0000000 Binary files a/tar-64.png and /dev/null differ diff --git a/video-32.png b/video-32.png deleted file mode 100644 index cb5de25..0000000 Binary files a/video-32.png and /dev/null differ diff --git a/video-64.png b/video-64.png deleted file mode 100644 index a087590..0000000 Binary files a/video-64.png and /dev/null differ diff --git a/warning.png b/warning.png deleted file mode 100644 index 8b015d3..0000000 Binary files a/warning.png and /dev/null differ