import tkinter as tk import shutil from tkinter import filedialog, messagebox, font, ttk, simpledialog, Toplevel, Label, Entry, Button, Checkbutton, IntVar import time import os from queue import Queue import subprocess class SettingsDialog: def __init__(self, parent, config): self.config = config self.top = Toplevel(parent) self.top.title("Einstellungen") # Erhöhe die Breite des Fensters self.top.geometry('550x200') # Beispielgröße, anpassbar Label(self.top, text="Server URL:").grid(row=0, column=0) self.server_url_entry = Entry(self.top, width=40) # Erhöhe die Breite des Eingabefelds self.server_url_entry.grid(row=0, column=1, padx=10, pady=5) Label(self.top, text="Username:").grid(row=1, column=0) self.username_entry = Entry(self.top, width=40) # Erhöhe die Breite des Eingabefelds self.username_entry.grid(row=1, column=1, padx=10, pady=5) Label(self.top, text="Password:").grid(row=2, column=0) self.password_entry = Entry(self.top, show="*", width=40) # Erhöhe die Breite des Eingabefelds self.password_entry.grid(row=2, column=1, padx=10, pady=5) self.show_password_var = IntVar() self.show_password_check = Checkbutton(self.top, text='Zeigen', variable=self.show_password_var, command=self.toggle_password_visibility) self.show_password_check.grid(row=2, column=2) # Checkbox für CD-Überprüfung hinzufügen self.check_for_cd_var = IntVar(value=int(self.config.get('check_for_cd', '0'))) self.check_for_cd_check = Checkbutton(self.top, text="Bei Start nach CDs suchen", variable=self.check_for_cd_var) self.check_for_cd_check.grid(row=3, column=0, columnspan=2, sticky="w", padx=10, pady=5) self.load_config() # Laden der aktuellen Konfiguration # Speichern-Button Button(self.top, text="Speichern", command=self.save_config).grid(row=4, column=1, pady=10) def toggle_password_visibility(self): """Wechselt die Sichtbarkeit des Passworteingabefelds.""" if self.show_password_var.get() == 1: self.password_entry.config(show="") else: self.password_entry.config(show="*") def load_config(self): config_path = os.path.join(os.path.expanduser('~'), '.dicom2pacs.conf') try: with open(config_path, 'r') as config_file: for line in config_file: key, value = line.strip().split('=', 1) if key == 'server_url': self.server_url_entry.insert(0, value) elif key == 'server_username': self.username_entry.insert(0, value) elif key == 'server_pw': self.password_entry.insert(0, value) except FileNotFoundError: print("Konfigurationsdatei nicht gefunden. Verwende Standardwerte.") def save_config(self): config = { 'server_url': self.server_url_entry.get(), 'server_username': self.username_entry.get(), 'server_pw': self.password_entry.get(), 'check_for_cd' : str(self.check_for_cd_var.get()) } config_path = os.path.join(os.path.expanduser('~'), '.dicom2pacs.conf') with open(config_path, 'w') as config_file: for key, value in config.items(): config_file.write(f'{key}={value}\n') self.top.destroy() class GUI: def __init__(self, on_folder_selected_callback, transfer_folder, on_close_callback, config): self.config = config self.on_folder_selected = on_folder_selected_callback self.transfer_folder = transfer_folder self.on_close = on_close_callback self.root = tk.Tk() self.root.title("Bilder einlesen") # Erstellen der Menüleiste menubar = tk.Menu(self.root) self.root.config(menu=menubar) # Diese Zeile weist das 'menubar' Objekt dem 'menu' Attribut des Hauptfensters zu # Erstellen des Einstellungsmenüs settings_menu = tk.Menu(menubar, tearoff=0) settings_menu.add_command(label="Einstellungen... \u2318,", command=self.open_settings_dialog) menubar.add_cascade(label="Einstellungen", menu=settings_menu) self.bind_shortcuts() # Fenstergröße und -position festlegen window_width = 350 window_height = 150 screen_width = self.root.winfo_screenwidth() screen_height = self.root.winfo_screenheight() center_x = int(screen_width / 2 - window_width / 2) center_y = int(screen_height / 2 - window_height / 2) self.root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") # Erstelle die Progressbar self.progress = ttk.Progressbar(self.root, orient="horizontal", length=300, mode='determinate') self.progress.pack(pady=20) # Frame für die Buttons am unteren Rand des Fensters self.button_frame = tk.Frame(self.root) self.button_frame.pack(side=tk.BOTTOM, fill=tk.X, pady=10) # Durchsuchen Button self.browse_button = tk.Button(self.button_frame, text="Durchsuchen", command=self.browse) self.browse_button.pack(side=tk.LEFT, padx=10, expand=True) # Schließen Button self.close_button = tk.Button(self.button_frame, text="Abbruch", command=self.on_app_close) self.close_button.pack(side=tk.RIGHT, padx=10, expand=True) self.hint_label = tk.Label(self.root, text="Bitte wählen Sie den Ordner mit den Bilddaten aus!") self.hint_label.pack(pady=5) # Pady hinzugefügt für etwas Abstand nach oben und unten self.root.protocol("WM_DELETE_WINDOW", self.on_close) self.searching = False self.searching_label = None self.labels_frame = tk.Frame(self.root) # Ein Frame für die animierten Labels self.labels_frame.pack(fill=tk.BOTH, expand=True) self.label_widgets = [] self.text_queue = Queue() self.currently_animating = False def save_cd_check_setting(self): # Speichert den Zustand der Checkbox in der Konfigurationsdatei self.config['check_for_cd'] = str(self.check_for_cd_var.get()) config_path = os.path.join(os.path.expanduser('~'), '.dicom2pacs.conf') with open(config_path, 'w') as config_file: for key, value in self.config.items(): config_file.write(f'{key}={value}\n') def load_cd_check_setting(self): # Lädt den Zustand der Checkbox aus der Konfigurationsdatei self.check_for_cd_var.set(int(self.config.get('check_for_cd', 0))) def start_animation(self, text): self.text_queue.put(text) if not self.currently_animating: self.currently_animating = True self.process_next_animation() def process_next_animation(self): if not self.text_queue.empty(): text = self.text_queue.get() label = tk.Label(self.labels_frame, text=text) label.pack() self.label_widgets.append(label) self.blink(label) else: self.currently_animating = False def blink(self, label, is_visible=True): if label in self.label_widgets and self.currently_animating: label.config(foreground=self.root.cget('bg') if is_visible else 'black') self.root.after(1000, lambda: self.blink(label, not is_visible)) def stop_animation(self): self.currently_animating = False self.clear_labels() def clear_labels(self): for label in self.label_widgets: label.destroy() self.label_widgets.clear() def update_searching_label(self, text): # Aktualisiere das Suchlabel if hasattr(self, 'searching_label'): self.searching_label.config(text=text) self.root.update_idletasks() def reset_progress(self, total_files): self.progress['value'] = 0 self.progress['maximum'] = total_files print("Fortschrittsanzeige wurde zurückgesetzt.") self.root.update_idletasks() def update_progress(self, incremental_progress, total_files): if self.progress['value'] == 0 and incremental_progress == 0: # Initialisierung bei Beginn des Upload-Prozesses print("Initialisiere Fortschrittsanzeige auf 0") else: self.progress['value'] += incremental_progress new_value = self.progress['value'] print(f"Aktualisiere Fortschritt: {new_value}/{total_files}") print(f"Aktueller Wert der Progressbar: {new_value - incremental_progress}") print(f"Neuer Wert der Progressbar: {new_value}") self.progress['maximum'] = total_files self.root.update_idletasks() def start(self): self.root.mainloop() def browse(self): # Öffne das Browse-Fenster self.hint_label.pack_forget() # Öffne standardmäßig den Downloads-Ordner downloads_folder = os.path.join(os.path.expanduser('~'), 'Downloads') if not os.path.exists(downloads_folder): downloads_folder = os.path.expanduser('~') # Fallback auf Home-Verzeichnis source_folder = filedialog.askdirectory(initialdir=downloads_folder, title="Wählen Sie den Ordner mit den DICOM-Dateien") if source_folder: # Prüfe, ob ein Ordner ausgewählt wurde print(f"Ausgewähltes Verzeichnis: {source_folder}") print("Vor dem Aufruf von on_folder_selected") # Debugging-Ausgabe self.on_folder_selected(source_folder) # Ruf den Callback mit dem ausgewählten Ordner auf print("Nach dem Aufruf von on_folder_selected") # Debugging-Ausgabe else: print("Kein Ordner ausgewählt.") def close(self): self.root.destroy() def show_discrepancy_dialog(self, message): dialog = JaAbbruchDialog(self.root, "Unstimmigkeiten gefunden", message, self.on_app_close, self.transfer_folder) response = dialog.show() return response def show_confirm_keep_original_dialog(self, tomedo_data, original_data): """Zeigt einen Bestätigungsdialog, wenn Original-Daten behalten werden sollen""" dialog = ConfirmKeepOriginalDialog(self.root, self.on_app_close, self.transfer_folder, tomedo_data, original_data) response = dialog.show() return response def ask_for_more(self, message): dialog = JaNeinAbbruchDialog(self.root, "Weitere DICOM Dateien einlesen?", message, self.on_app_close, self.transfer_folder) response = dialog.show() if response: # Wenn die Antwort True ist, also der Benutzer mit Ja antwortet print(response) # Öffne standardmäßig den Downloads-Ordner downloads_folder = os.path.join(os.path.expanduser('~'), 'Downloads') if not os.path.exists(downloads_folder): downloads_folder = os.path.expanduser('~') # Fallback auf Home-Verzeichnis source_folder = filedialog.askdirectory(initialdir=downloads_folder, title="Wählen Sie den Ordner mit den DICOM-Dateien") if not source_folder: # Prüfen, ob der Benutzer den Vorgang abgebrochen hat, ohne einen Ordner auszuwählen return False # Sie können hier entscheiden, ob False oder eine andere Kennzeichnung zurückgegeben wird return response, source_folder else: print(response) return response, None def on_app_close(self): print("App wird beendet...") # Beende den Tkinter Event-Loop und schließe alle Fenster try: self.root.quit() except Exception as e: print(f"Fehler beim Beenden des Tkinter Event-Loops: {e}") # Lösche den Transferordner try: shutil.rmtree(self.transfer_folder) print(f"Transferordner {self.transfer_folder} wurde gelöscht.") except Exception as e: print(f"Fehler beim Löschen des Transferordners: {e}") # Datei existiert nicht, keine Aktion erforderlich def upload_progress(self, incremental_progress, total_files): if incremental_progress == 0: self.progress['value'] = 0 # Initialisierung bei Beginn des Upload-Prozesses print("Initialisiere Fortschrittsanzeige auf 0") else: self.progress['value'] += incremental_progress new_value = self.progress['value'] print(f"Aktualisiere Fortschritt: {new_value}/{total_files}") print(f"Aktueller Wert der Progressbar: {new_value - incremental_progress}") print(f"Neuer Wert der Progressbar: {new_value}") self.progress['maximum'] = total_files self.root.update_idletasks() async def upload_success(self, message): try: # Angenommen, wir führen eine Aktion aus, die fehlschlagen könnte print("Upload erfolgreich:", message) # Beispiel: Drucke die Erfolgsmeldung in der Konsole # Planen Sie GUI-Updates im Hauptthread #self.root.after(0, self.show_success_message, message) except Exception as e: print(f"Fehler in upload_success: {e}") # Planen Sie die Fehlermeldung im Hauptthread, um Thread-Sicherheit zu gewährleisten #self.root.after(0, self.show_error_message, str(e)) def show_success_message(self, message): messagebox.showinfo("Upload Erfolg", message) def show_error_message(self, message): messagebox.showerror("Fehler", message) def disable_browse_button(self): """Deaktiviert den Durchsuchen Button.""" self.browse_button.config(state=tk.DISABLED) def enable_browse_button(self): """Aktiviert den Durchsuchen Button.""" self.browse_button.config(state=tk.NORMAL) def bind_shortcuts(self): self.root.bind('', lambda event: self.open_settings_dialog()) def open_settings_dialog(self): # Erstellt eine Instanz der SettingsDialog Klasse SettingsDialog(self.root, self.config) class JaAbbruchDialog(tk.Toplevel): def __init__(self, parent, title, message, on_app_close, transfer_folder=None): super().__init__(parent) self.result = None self.on_app_close = on_app_close self.transfer_folder = transfer_folder self.title(title) self.geometry("750x480") # Noch größeres Fenster self.minsize(700, 450) # Minimale Größe setzen # Hauptframe für besseres Layout main_frame = tk.Frame(self) main_frame.pack(fill=tk.BOTH, expand=True, padx=40, pady=30) italic_font = font.Font(family="Helvetica", size=14, slant="italic") bold_font = font.Font(family="Helvetica", size=15, weight="bold") normal_font = font.Font(family="Helvetica", size=14) header_font = font.Font(family="Helvetica", size=17, weight="bold") # Titel title_label = tk.Label(main_frame, text=title, font=header_font) title_label.pack(pady=(0, 30)) # Erwartete Daten - in einem Frame mit Hintergrund expected_frame = tk.Frame(main_frame, bg='#ffffff', relief=tk.SUNKEN, bd=1) expected_frame.pack(fill=tk.X, pady=(0, 20)) expected_inner = tk.Frame(expected_frame, bg='#ffffff') expected_inner.pack(fill=tk.BOTH, expand=True, padx=20, pady=15) tk.Label(expected_inner, text="Erwartete Daten (Tomedo):", font=italic_font, bg='#ffffff', fg='#0066cc', anchor="w").pack(pady=(0, 8), fill=tk.X) expected_label = tk.Label(expected_inner, text=message.split('\n\n')[0], font=normal_font, justify=tk.LEFT, wraplength=650, bg='#ffffff', fg='#000000', anchor="w") expected_label.pack(pady=(0, 0), fill=tk.X) # Gefundene Daten - in einem Frame mit Hintergrund found_frame = tk.Frame(main_frame, bg='#ffffff', relief=tk.SUNKEN, bd=1) found_frame.pack(fill=tk.X, pady=(0, 30)) found_inner = tk.Frame(found_frame, bg='#ffffff') found_inner.pack(fill=tk.BOTH, expand=True, padx=20, pady=15) tk.Label(found_inner, text="Gefundene Daten (DICOM):", font=italic_font, bg='#ffffff', fg='#cc6600', anchor="w").pack(pady=(0, 8), fill=tk.X) found_label = tk.Label(found_inner, text=message.split('\n\n')[1], font=normal_font, justify=tk.LEFT, wraplength=650, bg='#ffffff', fg='#000000', anchor="w") found_label.pack(pady=(0, 0), fill=tk.X) # Frage question_label = tk.Label(main_frame, text="Welche Daten sollen verwendet werden?", font=bold_font, wraplength=650) question_label.pack(pady=(0, 25)) # Button-Frame mit besserem Layout - Standard macOS Buttons btn_frame = tk.Frame(main_frame) btn_frame.pack(pady=15) # Standard macOS Buttons (ohne spezielle Farben) - größer yes_btn = tk.Button(btn_frame, text="Tomedo-Daten übernehmen", command=self.on_yes, width=25, height=2) yes_btn.pack(side=tk.LEFT, padx=10) no_btn = tk.Button(btn_frame, text="Original-Daten behalten", command=self.on_no, width=25, height=2) no_btn.pack(side=tk.LEFT, padx=10) cancel_btn = tk.Button(btn_frame, text="Abbruch", command=self.on_cancel, width=18, height=2) cancel_btn.pack(side=tk.LEFT, padx=10) self.center_window() def center_window(self): self.update_idletasks() # Aktualisiere das Layout, um die Fenstergröße zu erhalten dialog_width = self.winfo_width() dialog_height = self.winfo_height() # Position des Hauptfensters parent_x = self.master.winfo_x() parent_y = self.master.winfo_y() parent_width = self.master.winfo_width() parent_height = self.master.winfo_height() # Berechne die Position für das zentrierte Dialogfenster center_x = int(parent_x + (parent_width - dialog_width) / 2) center_y = int(parent_y + (parent_height - dialog_height) / 2) self.geometry(f"{dialog_width}x{dialog_height}+{center_x}+{center_y}") def on_yes(self): self.result = True # Tomedo-Daten übernehmen self.destroy() def on_no(self): self.result = False # Original-Daten behalten self.destroy() def on_cancel(self): self.result = None # Abbruch # Lösche Transfer-Ordner bei Abbruch if self.transfer_folder: self._delete_transfer_folder() self.on_app_close() # Rufe on_app_close auf self.destroy() def on_close(self): self.result = None # Abbruch # Lösche Transfer-Ordner bei Abbruch if self.transfer_folder: self._delete_transfer_folder() self.on_app_close() # Rufe on_app_close auf self.destroy() def _delete_transfer_folder(self): """Löscht den Transfer-Ordner""" import shutil try: if os.path.exists(self.transfer_folder): shutil.rmtree(self.transfer_folder) print(f"Transferordner {self.transfer_folder} wurde gelöscht.") except Exception as e: print(f"Fehler beim Löschen des Transferordners: {e}") def show(self): # Binden des Schließ-Events des Fensters an on_close self.protocol("WM_DELETE_WINDOW", self.on_close) self.wait_window(self) return self.result class ConfirmKeepOriginalDialog(tk.Toplevel): """Bestätigungsdialog, wenn Original-Daten behalten werden sollen""" def __init__(self, parent, on_app_close, transfer_folder=None, tomedo_data="", original_data=""): super().__init__(parent) self.result = None self.on_app_close = on_app_close self.transfer_folder = transfer_folder self.title("Bestätigung") self.geometry("750x600") # Größeres Fenster für vollständige Datenanzeige self.minsize(750, 600) # Minimale Größe erhöht main_frame = tk.Frame(self) main_frame.pack(fill=tk.BOTH, expand=True, padx=40, pady=30) bold_font = font.Font(family="Helvetica", size=15, weight="bold") normal_font = font.Font(family="Helvetica", size=14) italic_font = font.Font(family="Helvetica", size=14, slant="italic") header_font = font.Font(family="Helvetica", size=17, weight="bold") # Titel title_label = tk.Label(main_frame, text="Bestätigung", font=header_font) title_label.pack(pady=(0, 20)) # Warnung warning_label = tk.Label(main_frame, text="⚠️ Warnung", font=bold_font, fg='#cc6600') warning_label.pack(pady=(0, 20)) # Erwartete Daten (Tomedo) - in einem Frame mit Hintergrund (wie im ersten Dialog) tomedo_frame = tk.Frame(main_frame, bg='#ffffff', relief=tk.SUNKEN, bd=1) tomedo_frame.pack(fill=tk.X, pady=(0, 20)) tomedo_inner = tk.Frame(tomedo_frame, bg='#ffffff') tomedo_inner.pack(fill=tk.BOTH, expand=True, padx=20, pady=15) tk.Label(tomedo_inner, text="Erwartete Daten (Tomedo):", font=italic_font, bg='#ffffff', fg='#0066cc', anchor="w").pack(pady=(0, 8), fill=tk.X) tomedo_label = tk.Label(tomedo_inner, text=tomedo_data, font=normal_font, justify=tk.LEFT, wraplength=650, bg='#ffffff', fg='#000000', anchor="w") tomedo_label.pack(pady=(0, 0), fill=tk.X) # Gefundene Daten (DICOM) - in einem Frame mit Hintergrund (wie im ersten Dialog) original_frame = tk.Frame(main_frame, bg='#ffffff', relief=tk.SUNKEN, bd=1) original_frame.pack(fill=tk.X, pady=(0, 25)) original_inner = tk.Frame(original_frame, bg='#ffffff') original_inner.pack(fill=tk.BOTH, expand=True, padx=20, pady=15) tk.Label(original_inner, text="Gefundene Daten (DICOM):", font=italic_font, bg='#ffffff', fg='#cc6600', anchor="w").pack(pady=(0, 8), fill=tk.X) original_label = tk.Label(original_inner, text=original_data, font=normal_font, justify=tk.LEFT, wraplength=650, bg='#ffffff', fg='#000000', anchor="w") original_label.pack(pady=(0, 0), fill=tk.X) # Frage question_label = tk.Label(main_frame, text="Sind Sie sicher, dass Sie die Original-Daten behalten möchten?", font=bold_font, wraplength=650, justify=tk.CENTER) question_label.pack(pady=(0, 10)) info_label = tk.Label(main_frame, text="Die Tomedo-Daten sind der Standard und sollten normalerweise verwendet werden.", font=normal_font, wraplength=650, justify=tk.CENTER, fg='#666666') info_label.pack(pady=(0, 20)) # Button-Frame btn_frame = tk.Frame(main_frame) btn_frame.pack(pady=10) # Buttons tomedo_btn = tk.Button(btn_frame, text="Tomedo-Daten verwenden", command=self.on_use_tomedo, width=22, height=2) tomedo_btn.pack(side=tk.LEFT, padx=8) keep_btn = tk.Button(btn_frame, text="Original behalten", command=self.on_keep_original, width=18, height=2) keep_btn.pack(side=tk.LEFT, padx=8) cancel_btn = tk.Button(btn_frame, text="Abbruch", command=self.on_cancel, width=15, height=2) cancel_btn.pack(side=tk.LEFT, padx=8) self.center_window() def center_window(self): self.update_idletasks() dialog_width = self.winfo_width() dialog_height = self.winfo_height() parent_x = self.master.winfo_x() parent_y = self.master.winfo_y() parent_width = self.master.winfo_width() parent_height = self.master.winfo_height() center_x = int(parent_x + (parent_width - dialog_width) / 2) center_y = int(parent_y + (parent_height - dialog_height) / 2) self.geometry(f"{dialog_width}x{dialog_height}+{center_x}+{center_y}") def on_use_tomedo(self): self.result = True # Tomedo-Daten verwenden self.destroy() def on_keep_original(self): self.result = False # Original-Daten behalten self.destroy() def on_cancel(self): self.result = None # Abbruch # Lösche Transfer-Ordner bei Abbruch if self.transfer_folder: self._delete_transfer_folder() self.on_app_close() self.destroy() def on_close(self): self.result = None # Abbruch # Lösche Transfer-Ordner bei Abbruch if self.transfer_folder: self._delete_transfer_folder() self.on_app_close() self.destroy() def _delete_transfer_folder(self): """Löscht den Transfer-Ordner""" try: if os.path.exists(self.transfer_folder): shutil.rmtree(self.transfer_folder) print(f"Transferordner {self.transfer_folder} wurde gelöscht.") except Exception as e: print(f"Fehler beim Löschen des Transferordners: {e}") def show(self): self.protocol("WM_DELETE_WINDOW", self.on_close) self.wait_window(self) return self.result class JaNeinAbbruchDialog(tk.Toplevel): def __init__(self, parent, title, message, on_app_close, transfer_folder=None): super().__init__(parent) self.result = None self.on_app_close = on_app_close self.transfer_folder = transfer_folder self.message = message # Speichere die Nachricht self.title(title) self.geometry("400x175") # Größe des Dialogs tk.Label(self, text=self.message).pack(pady=20) btn_frame = tk.Frame(self) btn_frame.pack(pady=10) ja_btn = tk.Button(btn_frame, text="Ja", command=self.on_ja) ja_btn.pack(side=tk.LEFT, padx=10, pady=20) nein_btn = tk.Button(btn_frame, text="Nein", command=self.on_nein) nein_btn.pack(side=tk.LEFT, padx=10, pady=20) abbruch_btn = tk.Button(btn_frame, text="Abbruch", command=self.on_abbruch) abbruch_btn.pack(side=tk.LEFT, padx=10, pady=20) self.center_window() def center_window(self): self.update_idletasks() # Aktualisiere das Layout, um die Fenstergröße zu erhalten dialog_width = self.winfo_width() dialog_height = self.winfo_height() # Position des Hauptfensters parent_x = self.master.winfo_x() parent_y = self.master.winfo_y() parent_width = self.master.winfo_width() parent_height = self.master.winfo_height() # Berechne die Position für das zentrierte Dialogfenster center_x = int(parent_x + (parent_width - dialog_width) / 2) center_y = int(parent_y + (parent_height - dialog_height) / 2) self.geometry(f"{dialog_width}x{dialog_height}+{center_x}+{center_y}") def on_ja(self): self.result = True self.destroy() def on_nein(self): self.result = False self.destroy() def on_abbruch(self): self.result = False # Lösche Transfer-Ordner bei Abbruch if self.transfer_folder: self._delete_transfer_folder() self.on_app_close() # Rufe on_app_close auf self.destroy() def on_close(self): self.result = False # Lösche Transfer-Ordner bei Abbruch if self.transfer_folder: self._delete_transfer_folder() self.on_app_close() # Rufe on_app_close auf self.destroy() def _delete_transfer_folder(self): """Löscht den Transfer-Ordner""" try: if os.path.exists(self.transfer_folder): shutil.rmtree(self.transfer_folder) print(f"Transferordner {self.transfer_folder} wurde gelöscht.") except Exception as e: print(f"Fehler beim Löschen des Transferordners: {e}") def show(self): # Binden des Schließ-Events des Fensters an on_close self.protocol("WM_DELETE_WINDOW", self.on_close) self.wait_window(self) return self.result