1
0
Files
Programm-Shortcut/gui/dialogs.py
2025-09-08 17:19:22 +02:00

351 lines
14 KiB
Python

import tkinter as tk
from tkinter import ttk, filedialog, messagebox, simpledialog
import os
import webbrowser
from typing import Callable, Optional, Dict, Any, List
from models.program import Program
from config import Config
from utils.icon_utils import IconManager
from gui.modern_widgets import ModernStyle, ModernCard, ModernButton
class BaseDialog:
def __init__(self, parent: tk.Widget, title: str, width: int = 500, height: int = 400):
self.parent = parent
self.result = None
self.modern_style = ModernStyle()
self.icon_manager = IconManager()
theme = Config.get_theme()
self.window = tk.Toplevel(parent)
self.window.title(title)
self.window.geometry(f"{width}x{height}")
self.window.minsize(width, height) # Mindestgröße setzen
self.window.resizable(True, True) # Größe änderbar machen
self.window.grab_set()
self.window.configure(bg=theme["bg_primary"])
# Set icon
try:
self.window.iconbitmap(self.icon_manager.resource_path(Config.ICON_FILE))
except:
pass
# Configure modern styling
style = ttk.Style()
self.modern_style.configure_styles(style)
self._center_window()
self._create_widgets()
def _center_window(self):
self.window.update_idletasks()
width = self.window.winfo_width()
height = self.window.winfo_height()
x = (self.window.winfo_screenwidth() // 2) - (width // 2)
y = (self.window.winfo_screenheight() // 2) - (height // 2)
self.window.geometry(f"{width}x{height}+{x}+{y}")
def _create_widgets(self):
pass
def show(self):
self.window.wait_window()
return self.result
class ProgramDialog(BaseDialog):
def __init__(self, parent: tk.Widget, title: str, categories: List[str],
program: Optional[Program] = None):
self.categories = categories
self.program = program
self.is_edit = program is not None
super().__init__(parent, title, 450, 280)
def _create_widgets(self):
# Simple container
container = ttk.Frame(self.window, padding="20")
container.pack(fill=tk.BOTH, expand=True)
# Form fields using grid
self._create_form_fields(container)
# Buttons
self._create_buttons(container)
def _create_form_fields(self, parent):
# Name field
ttk.Label(parent, text="Programmname:").grid(row=0, column=0, sticky="w", pady=5)
self.name_entry = ttk.Entry(parent, width=30)
self.name_entry.grid(row=0, column=1, columnspan=2, sticky="we", pady=5)
# Path field
ttk.Label(parent, text="Programmpfad:").grid(row=1, column=0, sticky="w", pady=5)
self.path_entry = ttk.Entry(parent, width=30)
self.path_entry.grid(row=1, column=1, sticky="we", pady=5)
browse_button = ttk.Button(parent, text="Durchsuchen", command=self._browse_file)
browse_button.grid(row=1, column=2, padx=5, pady=5)
# Category field
ttk.Label(parent, text="Kategorie:").grid(row=2, column=0, sticky="w", pady=5)
self.category_combobox = ttk.Combobox(parent, width=28, values=sorted(self.categories))
self.category_combobox.grid(row=2, column=1, columnspan=2, sticky="we", pady=5)
# Admin rights checkbox
self.admin_var = tk.BooleanVar(value=False)
admin_check = ttk.Checkbutton(parent, text="Benötigt Administratorrechte",
variable=self.admin_var)
admin_check.grid(row=3, column=0, columnspan=3, sticky="w", pady=5)
# Fill fields if editing
if self.program:
self.name_entry.insert(0, self.program.name)
self.path_entry.insert(0, self.program.path)
if self.program.category:
self.category_combobox.set(self.program.category)
elif self.categories:
self.category_combobox.current(0)
self.admin_var.set(self.program.requires_admin)
elif self.categories:
self.category_combobox.current(0)
self.name_entry.focus_set()
# Make column 1 expandable
parent.columnconfigure(1, weight=1)
def _create_buttons(self, parent):
# Button frame
button_frame = ttk.Frame(parent)
button_frame.grid(row=4, column=0, columnspan=3, pady=15)
ttk.Button(button_frame, text="Speichern", command=self._save).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="Abbrechen", command=self.window.destroy).pack(side=tk.LEFT, padx=5)
def _browse_file(self):
if not self.is_edit:
self.path_entry.delete(0, tk.END)
filename = filedialog.askopenfilename(
filetypes=[
("Ausführbare Dateien", "*.exe"),
("Batch-Dateien", "*.bat;*.cmd"),
("Alle ausführbaren Dateien", "*.exe;*.bat;*.cmd"),
("Alle Dateien", "*.*")
]
)
if filename:
if not self.is_edit:
self.path_entry.insert(0, filename)
else:
self.path_entry.delete(0, tk.END)
self.path_entry.insert(0, filename)
def _save(self):
name = self.name_entry.get().strip()
path = self.path_entry.get().strip()
category = self.category_combobox.get().strip()
requires_admin = self.admin_var.get()
if not name or not path:
messagebox.showwarning("Warnung", "Bitte füllen Sie alle Felder aus.")
return
if not os.path.exists(path):
messagebox.showwarning("Warnung", "Der angegebene Pfad existiert nicht.")
return
self.result = Program(
name=name,
path=path,
category=category if category else None,
requires_admin=requires_admin
)
self.window.destroy()
class OptionsDialog(BaseDialog):
def __init__(self, parent: tk.Widget, current_options: Dict[str, Any]):
self.current_options = current_options.copy()
super().__init__(parent, "Programmoptionen", 400, 320)
def _create_widgets(self):
# Simple container with normal padding
container = ttk.Frame(self.window, padding="20")
container.pack(fill=tk.BOTH, expand=True)
# Title
title_label = ttk.Label(container, text="Programmoptionen",
font=("Segoe UI", 12, "bold"))
title_label.grid(row=0, column=0, columnspan=2, sticky="w", pady=(0, 15))
# Sort option
ttk.Label(container, text="Standardsortierung:").grid(row=1, column=0, sticky="w", pady=5)
self.sort_var = tk.StringVar(value=self._get_sort_option_text())
sort_combobox = ttk.Combobox(container, textvariable=self.sort_var,
values=Config.SORT_OPTIONS,
width=20)
sort_combobox.grid(row=1, column=1, sticky="we", pady=5)
sort_combobox.configure(state="readonly")
# Remember category option
self.remember_category_var = tk.BooleanVar(
value=self.current_options.get("remember_last_category", False)
)
remember_category_check = ttk.Checkbutton(
container,
text="Letzte ausgewählte Kategorie merken",
variable=self.remember_category_var
)
remember_category_check.grid(row=2, column=0, columnspan=2, sticky="w", pady=10)
# Separator
separator = ttk.Separator(container, orient="horizontal")
separator.grid(row=3, column=0, columnspan=2, sticky="ew", pady=10)
# Info section
info_frame = ttk.Frame(container)
info_frame.grid(row=4, column=0, columnspan=2, sticky="ew", pady=5)
ttk.Label(info_frame, text=f"Version: {Config.APP_VERSION}",
font=("Segoe UI", 9)).pack(anchor="w")
ttk.Label(info_frame, text=f"{Config.APP_AUTHOR}",
font=("Segoe UI", 9)).pack(anchor="w")
ttk.Label(info_frame, text=f"{Config.APP_DESCRIPTION}",
font=("Segoe UI", 9)).pack(anchor="w")
# Link
link_label = ttk.Label(info_frame, text="Quellcode auf git.ponywave.de",
font=("Segoe UI", 9, "underline"),
foreground="blue", cursor="hand2")
link_label.pack(anchor="w", pady=(5, 0))
link_label.bind("<Button-1>", lambda e: self._open_url(Config.SOURCE_URL))
# Buttons
button_frame = ttk.Frame(container)
button_frame.grid(row=5, column=0, columnspan=2, pady=20)
ttk.Button(button_frame, text="Speichern", command=self._save).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="Abbrechen", command=self.window.destroy).pack(side=tk.LEFT, padx=5)
# Make column 1 expandable
container.columnconfigure(1, weight=1)
def _get_sort_option_text(self) -> str:
column = self.current_options.get("default_sort_column")
reverse = self.current_options.get("default_sort_reverse", False)
if column == "name":
return "Programm (Z-A)" if reverse else "Programm (A-Z)"
elif column == "category":
return "Kategorie (Z-A)" if reverse else "Kategorie (A-Z)"
else:
return "Keine"
def _save(self):
sort_option = self.sort_var.get()
remember_category = self.remember_category_var.get()
# Translate sort options
if sort_option == "Programm (A-Z)":
sort_column, sort_reverse = "name", False
elif sort_option == "Programm (Z-A)":
sort_column, sort_reverse = "name", True
elif sort_option == "Kategorie (A-Z)":
sort_column, sort_reverse = "category", False
elif sort_option == "Kategorie (Z-A)":
sort_column, sort_reverse = "category", True
else: # "Keine"
sort_column, sort_reverse = None, False
self.result = {
"default_sort_column": sort_column,
"default_sort_reverse": sort_reverse,
"remember_last_category": remember_category
}
self.window.destroy()
def _open_url(self, url: str):
try:
webbrowser.open(url)
except Exception as e:
messagebox.showerror("Fehler", f"Fehler beim Öffnen der URL: {str(e)}")
class CategoryDialog(BaseDialog):
def __init__(self, parent: tk.Widget, dialog_type: str = "add", categories: List[str] = None):
self.dialog_type = dialog_type
self.categories = categories or []
if dialog_type == "add":
title = "Neue Kategorie"
width, height = 400, 150
else: # delete
title = "Kategorie löschen"
width, height = 450, 200
super().__init__(parent, title, width, height)
def _create_widgets(self):
container = ttk.Frame(self.window, padding="20")
container.pack(fill=tk.BOTH, expand=True)
if self.dialog_type == "add":
# Label
ttk.Label(container, text="Geben Sie den Namen der neuen Kategorie ein:").pack(pady=(0, 10))
# Entry
self.name_entry = ttk.Entry(container, width=30)
self.name_entry.pack(pady=(0, 15))
self.name_entry.focus_set()
# Bind Enter key
self.name_entry.bind("<Return>", lambda e: self._ok())
else: # delete
# Label
label_text = f"Verfügbare Kategorien: {', '.join(sorted(self.categories))}\n\nWelche Kategorie soll gelöscht werden?"
ttk.Label(container, text=label_text, wraplength=400).pack(pady=(0, 10))
# Combobox for category selection
self.category_combobox = ttk.Combobox(container, values=sorted(self.categories), width=30)
self.category_combobox.pack(pady=(0, 15))
self.category_combobox.focus_set()
if self.categories:
self.category_combobox.current(0)
# Buttons
button_frame = ttk.Frame(container)
button_frame.pack()
ttk.Button(button_frame, text="OK", command=self._ok).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="Abbrechen", command=self.window.destroy).pack(side=tk.LEFT, padx=5)
def _ok(self):
if self.dialog_type == "add":
self.result = self.name_entry.get().strip()
else: # delete
self.result = self.category_combobox.get().strip()
self.window.destroy()
@staticmethod
def add_category(parent: tk.Widget) -> Optional[str]:
dialog = CategoryDialog(parent, "add")
return dialog.show()
@staticmethod
def delete_category(parent: tk.Widget, categories: List[str]) -> Optional[str]:
dialog = CategoryDialog(parent, "delete", categories)
return dialog.show()
@staticmethod
def confirm_remove_category(parent: tk.Widget, category: str) -> bool:
return messagebox.askyesno("Bestätigung",
Config.MESSAGES["confirm_remove_category"].format(category),
parent=parent)
@staticmethod
def confirm_remove_program(parent: tk.Widget, program_name: str) -> bool:
return messagebox.askyesno("Bestätigung",
f"Möchten Sie '{program_name}' wirklich entfernen?",
parent=parent)