first commit

This commit is contained in:
2025-07-25 23:42:43 +02:00
commit ff970973e2
6 changed files with 296 additions and 0 deletions

Binary file not shown.

257
custom_file_dialog.py Normal file
View File

@@ -0,0 +1,257 @@
import tkinter as tk
from tkinter import ttk
import os
from functools import partial
class CustomFileDialog(tk.Toplevel):
def __init__(self, parent, initial_dir=None, filetypes=None):
super().__init__(parent)
self.parent = parent
self.title("Datei auswählen")
self.geometry("800x600") # Standardgröße, kann angepasst werden
# Macht das Dialogfenster modal zum Hauptfenster
self.transient(parent)
self.grab_set() # Fängt alle Ereignisse ab, bis der Dialog geschlossen wird
self.selected_file = None
self.current_dir = os.path.abspath(
initial_dir) if initial_dir else os.getcwd()
self.filetypes = filetypes if filetypes else [("All files", "*.*")]
self.current_filter_pattern = "*.*" # Standardfilter
# Icons (Platzhalter, müssten geladen werden)
# Sie müssen hier Ihre eigenen Icons laden oder generieren
# Beispiel: self.folder_icon = tk.PhotoImage(file="path/to/your/folder_icon.png")
# Für den Anfang verwenden wir leere PhotoImages
self.folder_icon = tk.PhotoImage(
file="./water-folder.png")
self.file_icon = tk.PhotoImage(
file="./text-x-generic.png")
self.iso_icon = tk.PhotoImage(
file="./media-optical.png")
self._create_widgets()
# Filter-Combobox initialisieren
filter_descriptions = [desc for desc, pattern in self.filetypes]
self.filter_combobox["values"] = filter_descriptions
# Ersten Filter als Standard setzen
self.filter_combobox.set(filter_descriptions[0])
# Den initialen Filter explizit setzen, da <<ComboboxSelected>> nicht immer ausgelöst wird
self.current_filter_pattern = self.filetypes[0][1]
self._populate_files()
# Behandelt das Schließen des Fensters
self.protocol("WM_DELETE_WINDOW", self._on_closing)
self.wait_window(self) # Wartet, bis das Fenster geschlossen wird
def _create_widgets(self):
# Hauptframe
main_frame = ttk.Frame(self, padding="10")
main_frame.pack(fill="both", expand=True)
# Canvas-Bereich soll sich ausdehnen
main_frame.grid_rowconfigure(1, weight=1)
main_frame.grid_columnconfigure(0, weight=1)
# Pfad-Navigation
path_frame = ttk.Frame(main_frame)
path_frame.grid(row=0, column=0, sticky="ew", pady=(0, 5))
path_frame.grid_columnconfigure(1, weight=1)
ttk.Button(path_frame, text="Up", command=self._go_up_dir).grid(
row=0, column=0, padx=(0, 5))
self.path_entry = ttk.Entry(path_frame, state="readonly")
self.path_entry.grid(row=0, column=1, sticky="ew")
ttk.Label(path_frame, text="Dateityp:").grid(
row=0, column=2, padx=(10, 5))
self.filter_combobox = ttk.Combobox(path_frame, state="readonly")
self.filter_combobox.grid(row=0, column=3, sticky="ew")
self.filter_combobox.bind(
"<<ComboboxSelected>>", self._on_filter_select)
# Optional: Pfad im System-Explorer öffnen
# Canvas für Dateianzeige (Kachelansicht)
self.canvas_frame = ttk.Frame(main_frame)
self.canvas_frame.grid(row=1, column=0, sticky="nsew")
self.canvas_frame.grid_rowconfigure(0, weight=1)
self.canvas_frame.grid_columnconfigure(0, weight=1)
self.canvas = tk.Canvas(
self.canvas_frame, bg="white", highlightthickness=0)
self.canvas.grid(row=0, column=0, sticky="nsew")
self.v_scrollbar = ttk.Scrollbar(
self.canvas_frame, orient="vertical", command=self.canvas.yview)
self.v_scrollbar.grid(row=0, column=1, sticky="ns")
self.canvas.configure(yscrollcommand=self.v_scrollbar.set)
self.h_scrollbar = ttk.Scrollbar(
self.canvas_frame, orient="horizontal", command=self.canvas.xview)
self.h_scrollbar.grid(row=1, column=0, sticky="ew")
self.canvas.configure(xscrollcommand=self.h_scrollbar.set)
# Frame zum Halten der Kacheln
self.inner_frame = ttk.Frame(self.canvas)
self.canvas.create_window((0, 0), window=self.inner_frame, anchor="nw")
self.inner_frame.bind("<Configure>", lambda e: self.canvas.configure(
scrollregion=self.canvas.bbox("all")))
# Für Klicks auf leeren Bereich
self.canvas.bind("<Button-1>", self._on_canvas_click)
# Buttons
button_frame = ttk.Frame(main_frame)
button_frame.grid(row=2, column=0, sticky="ew", pady=(5, 0))
button_frame.grid_columnconfigure(0, weight=1) # Leerraum links
ttk.Button(button_frame, text="Öffnen", command=self._on_open).grid(
row=0, column=1, padx=(0, 5))
ttk.Button(button_frame, text="Abbrechen",
command=self._on_cancel).grid(row=0, column=2)
def _populate_files(self):
# Lösche alle vorhandenen Kacheln
for widget in self.inner_frame.winfo_children():
widget.destroy()
self.path_entry.config(state="normal")
self.path_entry.delete(0, tk.END)
self.path_entry.insert(0, self.current_dir)
self.path_entry.config(state="readonly")
files_and_dirs = []
try:
for item in os.listdir(self.current_dir):
full_path = os.path.join(self.current_dir, item)
if os.path.isdir(full_path):
files_and_dirs.append((item, "dir"))
elif os.path.isfile(full_path):
# Dateien nur hinzufügen, wenn sie dem aktuellen Filter entsprechen
if self._matches_filetype(item):
files_and_dirs.append((item, "file"))
except PermissionError:
# Fehlerbehandlung für nicht zugängliche Verzeichnisse
ttk.Label(self.inner_frame,
text="Zugriff verweigert.").pack(pady=20)
return
except Exception as e:
ttk.Label(self.inner_frame, text=f"Fehler: {e}").pack(pady=20)
return
# Sortieren: Ordner zuerst, dann Dateien, alphabetisch
files_and_dirs.sort(key=lambda x: (x[1] == "file", x[0].lower()))
# Kachel-Layout (Beispiel: 4 Spalten)
col_count = 4
for i, (name, item_type) in enumerate(files_and_dirs):
row = i // col_count
col = i % col_count
icon = self.folder_icon if item_type == "dir" else (
self.iso_icon if name.lower().endswith(".iso") else self.file_icon)
# Kachel-Frame
item_frame = ttk.Frame(
self.inner_frame, relief="solid", borderwidth=1, padding=5)
item_frame.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
# Text soll sich ausdehnen
item_frame.grid_rowconfigure(1, weight=1)
item_frame.grid_columnconfigure(
0, weight=1) # Icon/Text zentrieren
# Daten an den Frame anhängen
item_frame.file_path = full_path
item_frame.item_type = item_type
# Icon
icon_label = ttk.Label(item_frame, image=icon)
icon_label.grid(row=0, column=0, pady=(0, 5))
# Daten an das Icon-Label anhängen
icon_label.file_path = full_path
icon_label.item_type = item_type
# Dateiname
# wraplength für Zeilenumbruch
name_label = ttk.Label(item_frame, text=name,
wraplength=100, anchor="n")
name_label.grid(row=1, column=0, sticky="ew")
# Daten an das Name-Label anhängen
name_label.file_path = full_path
name_label.item_type = item_type
# Bindungen für Klicks (mit functools.partial)
item_frame.bind(
"<Button-1>", partial(self._on_item_click, full_path, item_type))
icon_label.bind(
"<Button-1>", partial(self._on_item_click, full_path, item_type))
name_label.bind(
"<Button-1>", partial(self._on_item_click, full_path, item_type))
item_frame.bind(
"<Double-Button-1>", partial(self._on_item_double_click, full_path, item_type))
icon_label.bind(
"<Double-Button-1>", partial(self._on_item_double_click, full_path, item_type))
name_label.bind(
"<Double-Button-1>", partial(self._on_item_double_click, full_path, item_type))
def _go_up_dir(self):
parent_dir = os.path.dirname(self.current_dir)
if parent_dir != self.current_dir: # Verhindert, dass man über das Root-Verzeichnis hinausgeht
self.current_dir = parent_dir
self._populate_files()
def _on_item_click(self, path, item_type, event):
print(f"DEBUG: _on_item_click - Path: {path}, Type: {item_type}")
# Hier können Sie eine visuelle Auswahl hervorheben
self.selected_file = path # Temporär speichern
def _on_item_double_click(self, path, item_type, event):
print(
f"DEBUG: _on_item_double_click - Path: {path}, Type: {item_type}")
if item_type == "dir":
self.current_dir = path
self._populate_files()
else:
self.selected_file = path
self.destroy() # Dialog schließen und ausgewählte Datei zurückgeben
def _on_open(self):
if self.selected_file and os.path.isfile(self.selected_file):
self.destroy()
else:
# Optional: Fehlermeldung, wenn keine Datei ausgewählt oder Ordner doppelt geklickt wurde
print("Bitte eine Datei auswählen oder einen Ordner doppelt klicken.")
def _on_cancel(self):
self.selected_file = None # Nichts ausgewählt
self.destroy()
def _on_closing(self):
self.selected_file = None # Nichts ausgewählt, wenn Fenster geschlossen wird
self.destroy()
def _matches_filetype(self, filename):
# Wenn der aktuelle Filter "*.*" ist, passen alle Dateien
if self.current_filter_pattern == "*.*":
return True
# Ansonsten prüfen, ob die Datei zum aktuellen spezifischen Filter passt
ext = self.current_filter_pattern[2:].lower()
return filename.lower().endswith("." + ext)
def _on_filter_select(self, event):
selected_desc = self.filter_combobox.get()
for desc, pattern in self.filetypes:
if desc == selected_desc:
self.current_filter_pattern = pattern
break
self._populate_files() # Dateiliste neu laden mit neuem Filter
def _on_canvas_click(self, event):
# Wenn auf den leeren Bereich des Canvas geklickt wird, Auswahl aufheben
self.selected_file = None
# Optional: Visuelle Hervorhebung entfernen
def get_selected_file(self):
return self.selected_file

39
mainwindow.py Normal file
View File

@@ -0,0 +1,39 @@
#!/usr/bin/python3
import tkinter as tk
from tkinter import ttk
from custom_file_dialog import CustomFileDialog
class GlotzMol(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.geometry('800x400')
ttk.Label(text="Custodialog-teschdfeschda").grid(row=0, column=0)
self.columnconfigure(1, weight=1)
self.iso_path_entry = ttk.Entry(self)
self.iso_path_entry.grid(
row=1, column=0, columnspan=2, padx=15, pady=5, sticky="ew")
ttk.Button(self, text="Öffnen", command=self.customtest).grid(
row=2, column=0, padx=5, pady=5)
def customtest(self):
dialog = CustomFileDialog(self, initial_dir="/home/punix/Downloads",
filetypes=[("ISO files", "*.iso")])
path = dialog.get_selected_file()
if path:
self.iso_path_entry.delete(0, tk.END)
self.iso_path_entry.insert(0, path)
if __name__ == "__main__":
root = GlotzMol()
theme_path = '/usr/share/TK-Themes'
style = ttk.Style(root)
root.tk.call('source', f"{theme_path}/water.tcl")
try:
root.tk.call('set_theme', 'dark')
except tk.TclError:
pass
root.mainloop()

BIN
media-optical.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
text-x-generic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

BIN
water-folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB