Files
I-SecretUpdate/ui/main_window.py
T
2025-12-19 12:58:58 +01:00

286 lines
9.5 KiB
Python

"""
Main Window
Main GUI window that integrates all frames.
"""
import customtkinter as ctk
from ui.login_frame import LoginFrame
from ui.subscription_selection_frame import SubscriptionSelectionFrame
from ui.app_selection_frame import AppSelectionFrame
from ui.secret_generation_frame import SecretGenerationFrame
from ui.result_frame import ResultFrame
from typing import Callable, Dict, List
class MainWindow(ctk.CTk):
"""Main application window."""
def __init__(
self,
on_connect: Callable = None,
on_subscription_selected: Callable = None,
on_app_selected: Callable = None,
on_generate_secret: Callable = None,
on_generate_another: Callable = None
):
"""
Initialize the main window.
Args:
on_connect: Callback for authentication
on_subscription_selected: Callback when subscription is selected
on_app_selected: Callback when app is selected
on_generate_secret: Callback when generate secret is clicked
on_generate_another: Callback when generate another is clicked
"""
super().__init__()
# Configure window
self.title("Azure Key Vault Secret Manager")
self.geometry("950x850")
# Set application icon (if available)
self._set_icon()
# Configure grid
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(1, weight=1)
# Title
self.title_label = ctk.CTkLabel(
self,
text="Azure Key Vault Secret Manager",
font=ctk.CTkFont(size=28, weight="bold")
)
self.title_label.grid(row=0, column=0, padx=20, pady=20, sticky="n")
# Scrollable frame for content with optimized scrolling
self.scroll_frame = ctk.CTkScrollableFrame(
self,
scrollbar_button_color=("gray75", "gray25"),
scrollbar_button_hover_color=("gray65", "gray35")
)
self.scroll_frame.grid(row=1, column=0, padx=20, pady=(0, 20), sticky="nsew")
self.scroll_frame.grid_columnconfigure(0, weight=1)
# Optimize scroll step for smoother scrolling (40px = 2x faster than default 20px)
self.scroll_frame._parent_canvas.configure(yscrollincrement=40)
# Add smooth mouse wheel scrolling
def smooth_scroll(event):
"""Handle smooth mouse wheel scrolling."""
# Platform-specific delta handling
if event.delta > 0:
delta = -1 # Scroll up
else:
delta = 1 # Scroll down
self.scroll_frame._parent_canvas.yview_scroll(delta, "units")
return "break" # Prevent event propagation
# Bind mouse wheel event (Windows/Mac)
self.scroll_frame._parent_canvas.bind_all("<MouseWheel>", smooth_scroll, add="+")
# Login Frame
self.login_frame = LoginFrame(self.scroll_frame, on_connect=on_connect)
self.login_frame.grid(row=0, column=0, padx=0, pady=(0, 15), sticky="ew")
# Subscription Selection Frame
self.subscription_selection_frame = SubscriptionSelectionFrame(
self.scroll_frame,
on_subscription_selected=on_subscription_selected
)
self.subscription_selection_frame.grid(row=1, column=0, padx=0, pady=(0, 15), sticky="ew")
self.subscription_selection_frame.set_enabled(False)
# App Selection Frame
self.app_selection_frame = AppSelectionFrame(
self.scroll_frame,
on_app_selected=on_app_selected
)
self.app_selection_frame.grid(row=2, column=0, padx=0, pady=(0, 15), sticky="ew")
self.app_selection_frame.set_enabled(False)
# Secret Generation Frame
self.secret_generation_frame = SecretGenerationFrame(
self.scroll_frame,
on_generate=on_generate_secret
)
self.secret_generation_frame.grid(row=3, column=0, padx=0, pady=(0, 15), sticky="ew")
self.secret_generation_frame.set_enabled(False)
# Result Frame (initially hidden)
self.result_frame = ResultFrame(
self.scroll_frame,
on_generate_another=on_generate_another
)
self.result_frame.grid(row=4, column=0, padx=0, pady=(0, 15), sticky="ew")
self.result_frame.hide()
def set_authenticated(self, authenticated: bool):
"""
Update UI after authentication.
Args:
authenticated: Whether authentication succeeded
"""
self.login_frame.set_authenticated(authenticated)
if authenticated:
self.subscription_selection_frame.set_enabled(True)
def set_connecting(self):
"""Set UI to connecting state."""
self.login_frame.set_connecting()
def enable_connect_button(self):
"""Enable connect button for retry."""
self.login_frame.enable_button()
def set_subscriptions(self, subscriptions: List[Dict[str, str]]):
"""
Set the list of subscriptions.
Args:
subscriptions: List of subscription dictionaries
"""
self.subscription_selection_frame.set_subscriptions(subscriptions)
def set_subscription_selected(self):
"""Enable UI after subscription is selected."""
self.app_selection_frame.set_enabled(True)
self.secret_generation_frame.set_enabled(True)
def set_apps(self, apps: List[Dict[str, str]]):
"""
Set the list of applications.
Args:
apps: List of app dictionaries
"""
self.app_selection_frame.set_apps(apps)
def set_vaults(self, vaults: List[Dict[str, str]]):
"""
Set the list of Key Vaults.
Args:
vaults: List of vault dictionaries
"""
self.secret_generation_frame.set_vaults(vaults)
def set_loading_subscriptions(self, loading: bool):
"""
Set loading subscriptions state.
Args:
loading: Whether currently loading
"""
self.subscription_selection_frame.set_loading(loading)
def set_loading_apps(self, loading: bool):
"""
Set loading apps state.
Args:
loading: Whether currently loading
"""
self.app_selection_frame.set_loading(loading)
def set_loading_vaults(self, loading: bool):
"""
Set loading vaults state.
Args:
loading: Whether currently loading
"""
self.secret_generation_frame.set_loading_vaults(loading)
def set_generating(self, generating: bool):
"""
Set generating secret state.
Args:
generating: Whether currently generating
"""
self.secret_generation_frame.set_generating(generating)
def show_result(
self,
secret_name: str,
vault_name: str,
secret_value: str,
removed_count: int = 0
):
"""
Show secret generation result.
Args:
secret_name: The sanitized secret name
vault_name: The Key Vault name
secret_value: The secret value
removed_count: Number of old secrets removed
"""
self.result_frame.show_result(secret_name, vault_name, secret_value, removed_count)
def reset_form(self):
"""Reset the secret generation form."""
self.secret_generation_frame.reset()
self.result_frame.hide()
def get_selected_app(self) -> Dict[str, str]:
"""Get the currently selected app."""
return self.app_selection_frame.get_selected_app()
def get_description(self) -> str:
"""Get the secret description."""
return self.secret_generation_frame.get_description()
def get_selected_vault(self) -> Dict[str, str]:
"""Get the currently selected vault."""
return self.secret_generation_frame.get_selected_vault()
def get_remove_old_secrets(self) -> bool:
"""Get whether to remove old secrets."""
return self.secret_generation_frame.get_remove_old_secrets()
def _set_icon(self):
"""Set the application icon if available."""
import os
from pathlib import Path
# Get the directory where the script is located
script_dir = Path(__file__).parent.parent
# Try common icon file names and locations
icon_paths = [
script_dir / "icon.ico",
script_dir / "assets" / "icon.ico",
script_dir / "icon.png",
script_dir / "assets" / "icon.png",
]
for icon_path in icon_paths:
if icon_path.exists():
try:
if icon_path.suffix == '.ico':
# Use .ico file directly (Windows)
self.iconbitmap(str(icon_path))
print(f"Icon loaded: {icon_path}")
return
elif icon_path.suffix == '.png':
# Use .png file with iconphoto (cross-platform)
import tkinter as tk
from PIL import Image, ImageTk
img = Image.open(icon_path)
photo = ImageTk.PhotoImage(img)
self.iconphoto(True, photo)
print(f"Icon loaded: {icon_path}")
return
except Exception as e:
print(f"Failed to load icon from {icon_path}: {str(e)}")
# No icon found - use default
print("No custom icon found. Using default application icon.")