Files
dicom2pacs/gui.py

733 lines
31 KiB
Python
Executable File

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 Standard-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)
# Hinweis-Label mit angepasster Schriftgröße (wie Buttons: 13pt)
self.hint_label = tk.Label(self.root, text="Bitte wählen Sie den Ordner mit den Bilddaten aus!", font=('Helvetica', 13))
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('<Command-comma>', 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("600x220") # Größeres Fenster für passende Button-Größen
self.minsize(550, 200) # Minimale Größe
tk.Label(self, text=self.message, font=('Helvetica', 13), wraplength=550).pack(pady=25)
btn_frame = tk.Frame(self)
btn_frame.pack(pady=15)
# Buttons (macOS-Style) - angepasste Größen für bessere Proportionen
# Primärer Button (blau) - Ja ist die primäre Aktion
ja_btn = create_macos_button(btn_frame, text="Ja", command=self.on_ja, width=18, default=True)
ja_btn.pack(side=tk.LEFT, padx=12, pady=6)
# Sekundärer Button (grau)
nein_btn = create_macos_button(btn_frame, text="Nein", command=self.on_nein, width=18)
nein_btn.pack(side=tk.LEFT, padx=12, pady=6)
# Sekundärer Button (grau)
abbruch_btn = create_macos_button(btn_frame, text="Abbruch", command=self.on_abbruch, width=18)
abbruch_btn.pack(side=tk.LEFT, padx=12, 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