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 # Style einmalig konfigurieren (wird beim ersten Import ausgeführt) _style_configured = False def create_macos_button(parent, text, command=None, width=None, height=None, padx=10, pady=5, default=False): """Erstellt einen Button im nativen macOS-Stil (Aqua Theme)""" global _style_configured # Style nur einmal konfigurieren if not _style_configured: style = ttk.Style() # Verwende das native macOS Aqua Theme try: style.theme_use('aqua') # macOS natives Theme except: pass # Falls aqua nicht verfügbar ist, verwende Standard # Konfiguriere Button-Style für natives macOS-Aussehen style.configure('TButton', font=('Helvetica', 13), padding=(10, 6)) _style_configured = True # Verwende ttk.Button für natives macOS-Aussehen # Das Aqua Theme stellt primäre Buttons automatisch blau dar btn = ttk.Button(parent, text=text, command=command) if width: btn.config(width=width) # Auf macOS werden Buttons automatisch im nativen Stil dargestellt # Primäre Buttons erscheinen blau, sekundäre grau return btn 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 (macOS-Style) - primärer Button (blau) save_btn = create_macos_button(self.top, text="Speichern", command=self.save_config, width=16, default=True) save_btn.grid(row=4, column=1, pady=12, padx=5) 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 = 420 window_height = 180 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=12) # Durchsuchen Button (macOS-Style) - primärer Button (blau) self.browse_button = create_macos_button(self.button_frame, text="Durchsuchen", command=self.browse, width=14, default=True) self.browse_button.pack(side=tk.LEFT, padx=12, expand=True, pady=4) # Schließen Button (macOS-Style) - sekundärer Button (grau) self.close_button = create_macos_button(self.button_frame, text="Abbruch", command=self.on_app_close, width=14) self.close_button.pack(side=tk.RIGHT, padx=12, expand=True, pady=4) 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("950x520") # Größeres Fenster für bessere Lesbarkeit und passende Proportionen self.minsize(900, 500) # Minimale Größe angepasst # 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=800, 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=800, 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=800) question_label.pack(pady=(0, 25)) # Button-Frame mit besserem Layout - Standard macOS Buttons btn_frame = tk.Frame(main_frame) btn_frame.pack(pady=18) # Standard macOS Buttons (ttk.Button für besseres macOS-Aussehen) # Primärer Button (blau) - empfohlene Aktion yes_btn = create_macos_button(btn_frame, text="Tomedo-Daten übernehmen", command=self.on_yes, width=28, default=True) yes_btn.pack(side=tk.LEFT, padx=10, pady=6) # Sekundärer Button (grau) no_btn = create_macos_button(btn_frame, text="Original-Daten behalten", command=self.on_no, width=28) no_btn.pack(side=tk.LEFT, padx=10, pady=6) # Sekundärer Button (grau) cancel_btn = create_macos_button(btn_frame, text="Abbruch", command=self.on_cancel, width=20) cancel_btn.pack(side=tk.LEFT, padx=10, pady=6) 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("1000x680") # Größeres Fenster für vollständige Datenanzeige und bessere Lesbarkeit self.minsize(950, 650) # 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=800, 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=800, 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=800, 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=800, justify=tk.CENTER, fg='#666666') info_label.pack(pady=(0, 20)) # Button-Frame btn_frame = tk.Frame(main_frame) btn_frame.pack(pady=12) # Buttons (macOS-Style) - breiter für bessere Textlesbarkeit # Primärer Button (blau) - empfohlene Aktion tomedo_btn = create_macos_button(btn_frame, text="Tomedo-Daten verwenden", command=self.on_use_tomedo, width=32, default=True) tomedo_btn.pack(side=tk.LEFT, padx=8, pady=6) # Sekundärer Button (grau) keep_btn = create_macos_button(btn_frame, text="Original behalten", command=self.on_keep_original, width=26) keep_btn.pack(side=tk.LEFT, padx=8, pady=6) # Sekundärer Button (grau) cancel_btn = create_macos_button(btn_frame, text="Abbruch", command=self.on_cancel, width=20) cancel_btn.pack(side=tk.LEFT, padx=8, pady=6) 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("420x180") # Angepasste Fenstergröße tk.Label(self, text=self.message, font=('Helvetica', 13)).pack(pady=20) btn_frame = tk.Frame(self) btn_frame.pack(pady=12) # Buttons (macOS-Style) # Primärer Button (blau) - Ja ist die primäre Aktion ja_btn = create_macos_button(btn_frame, text="Ja", command=self.on_ja, width=16, default=True) ja_btn.pack(side=tk.LEFT, padx=10, pady=6) # Sekundärer Button (grau) nein_btn = create_macos_button(btn_frame, text="Nein", command=self.on_nein, width=16) nein_btn.pack(side=tk.LEFT, padx=10, pady=6) # Sekundärer Button (grau) abbruch_btn = create_macos_button(btn_frame, text="Abbruch", command=self.on_abbruch, width=16) abbruch_btn.pack(side=tk.LEFT, padx=10, pady=6) 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