SXXXXXXX_ControlPanel/ui.py
VALLONGOL cd66dab8db add go to google map and google earth button
add "x" red for pointing mouse on sar and map images
2025-04-15 12:25:00 +02:00

648 lines
26 KiB
Python

# --- START OF FILE ui.py ---
# ui.py
"""
THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED.
Defines the user interface components for the Control Panel application,
including the main control panel area, status bar, map parameters, info display
with Google Maps/Earth links, and helper functions for window creation.
"""
# Standard library imports
import logging
from typing import TYPE_CHECKING, Dict, Tuple, Optional
# Third-party imports
import tkinter as tk
from tkinter import ttk
# Local application imports
import config
# Type hinting for App reference
if TYPE_CHECKING:
from ControlPanel import ControlPanelApp
class ControlPanel(ttk.Frame):
"""
Main control panel frame containing SAR, MFD, Map parameter widgets,
information displays, statistics labels, and interaction buttons.
"""
def __init__(self, parent: tk.Widget, app: "ControlPanelApp", *args, **kwargs):
"""Initializes the ControlPanel frame."""
log_prefix = "[UI Setup]"
logging.debug(f"{log_prefix} Initializing ControlPanel frame...")
super().__init__(parent, *args, **kwargs)
self.app: "ControlPanelApp" = app
# StringVars for Entry widgets
self.sar_center_coords_var = tk.StringVar(value="Lat=N/A, Lon=N/A")
self.sar_orientation_var = tk.StringVar(value="N/A")
self.sar_size_km_var = tk.StringVar(value="N/A")
self.mouse_coords_var = tk.StringVar(value="Lat=N/A, Lon=N/A")
self.map_mouse_coords_var = tk.StringVar(value="Lat=N/A, Lon=N/A")
self.sar_lat_shift_var = tk.StringVar(
value=f"{self.app.state.sar_lat_shift_deg:.6f}"
)
self.sar_lon_shift_var = tk.StringVar(
value=f"{self.app.state.sar_lon_shift_deg:.6f}"
)
self.dropped_stats_var = tk.StringVar(value="Drop (Q): S=0, M=0, Tk=0, Mo=0")
self.incomplete_stats_var = tk.StringVar(value="Incmpl (RX): S=0, M=0")
# MFD color preview labels
self.mfd_color_labels: Dict[str, tk.Label] = {}
# Initialize UI elements
self.init_ui()
logging.debug(f"{log_prefix} ControlPanel frame initialization complete.")
def init_ui(self):
"""Initializes and arranges the user interface widgets within the frame."""
log_prefix = "[UI Setup]"
logging.debug(f"{log_prefix} Starting init_ui widget creation...")
self.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=5, pady=5)
# --- 1. SAR Parameters Frame ---
# (Code Unchanged - Test, Record, Size, Palette, Contrast, Brightness)
self.sar_params_frame = ttk.Labelframe(self, text="SAR Parameters", padding=5)
self.sar_params_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=(5, 2))
sar_row = 0
self.test_image_var = tk.IntVar(value=1 if config.ENABLE_TEST_MODE else 0)
self.test_image_check = ttk.Checkbutton(
self.sar_params_frame,
text="Test Image",
variable=self.test_image_var,
command=self.app.update_image_mode,
)
self.test_image_check.grid(
row=sar_row, column=0, columnspan=2, sticky=tk.W, padx=5, pady=2
)
self.record_sar_var = tk.BooleanVar(value=config.DEFAULT_SAR_RECORDING_ENABLED)
self.record_sar_check = ttk.Checkbutton(
self.sar_params_frame,
text="Record SAR",
variable=self.record_sar_var,
command=self.app.toggle_sar_recording,
)
self.record_sar_check.grid(
row=sar_row, column=2, columnspan=2, sticky=tk.W, padx=5, pady=2
)
sar_row += 1
self.sar_size_label = ttk.Label(self.sar_params_frame, text="Size:")
self.sar_size_label.grid(
row=sar_row, column=0, sticky=tk.W, padx=(5, 2), pady=1
)
self.sar_size_combo = ttk.Combobox(
self.sar_params_frame,
values=config.SAR_SIZE_FACTORS,
state="readonly",
width=6,
)
try:
factor = (
max(1, config.SAR_WIDTH // self.app.state.sar_display_width)
if self.app.state.sar_display_width > 0
else 1
)
sz_str = f"1:{factor}"
self.sar_size_combo.set(
sz_str if sz_str in config.SAR_SIZE_FACTORS else config.DEFAULT_SAR_SIZE
)
except:
self.sar_size_combo.set(config.DEFAULT_SAR_SIZE)
self.sar_size_combo.grid(
row=sar_row, column=1, sticky=tk.EW, padx=(0, 10), pady=1
)
self.sar_size_combo.bind("<<ComboboxSelected>>", self.app.update_sar_size)
self.palette_label = ttk.Label(self.sar_params_frame, text="Palette:")
self.palette_label.grid(row=sar_row, column=2, sticky=tk.W, padx=(0, 2), pady=1)
self.palette_combo = ttk.Combobox(
self.sar_params_frame,
values=config.COLOR_PALETTES,
state="readonly",
width=8,
)
self.palette_combo.set(self.app.state.sar_palette)
self.palette_combo.grid(
row=sar_row, column=3, sticky=tk.EW, padx=(0, 5), pady=1
)
self.palette_combo.bind("<<ComboboxSelected>>", self.app.update_sar_palette)
sar_row += 1
self.contrast_label = ttk.Label(self.sar_params_frame, text="Contrast:")
self.contrast_label.grid(
row=sar_row, column=0, sticky=tk.W, padx=(5, 2), pady=1
)
self.contrast_scale = ttk.Scale(
self.sar_params_frame,
orient=tk.HORIZONTAL,
from_=0.1,
to=3.0,
value=self.app.state.sar_contrast,
command=self.app.update_contrast,
)
self.contrast_scale.grid(
row=sar_row, column=1, sticky=tk.EW, padx=(0, 10), pady=1
)
self.brightness_label = ttk.Label(self.sar_params_frame, text="Brightness:")
self.brightness_label.grid(
row=sar_row, column=2, sticky=tk.W, padx=(0, 2), pady=1
)
self.brightness_scale = ttk.Scale(
self.sar_params_frame,
orient=tk.HORIZONTAL,
from_=-100,
to=100,
value=self.app.state.sar_brightness,
command=self.app.update_brightness,
)
self.brightness_scale.grid(
row=sar_row, column=3, sticky=tk.EW, padx=(0, 5), pady=1
)
self.sar_params_frame.columnconfigure(1, weight=1)
self.sar_params_frame.columnconfigure(3, weight=1)
# --- 2. MFD Parameters Frame ---
# (Code Unchanged - Category controls, Raw Map slider)
self.mfd_params_frame = ttk.Labelframe(self, text="MFD Parameters", padding=5)
self.mfd_params_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=2)
mfd_categories_ordered = [
"Occlusion",
"Cat A",
"Cat B",
"Cat C",
"Cat C1",
"Cat C2",
"Cat C3",
]
num_categories = len(mfd_categories_ordered)
for index, name in enumerate(mfd_categories_ordered):
row = index // 2
col_offset = 0 if (index % 2 == 0) else 4
ttk.Label(self.mfd_params_frame, text=f"{name}:").grid(
row=row, column=0 + col_offset, sticky=tk.W, padx=(5, 1), pady=1
)
intensity_var = tk.IntVar(value=config.DEFAULT_MFD_INTENSITY)
try:
intensity_var.set(
self.app.state.mfd_params["categories"][name]["intensity"]
)
except:
pass
setattr(
self,
f"mfd_{name.replace(' ','_').lower()}_intensity_var",
intensity_var,
)
ttk.Scale(
self.mfd_params_frame,
orient=tk.HORIZONTAL,
length=100,
from_=0,
to=255,
variable=intensity_var,
command=lambda v, n=name, var=intensity_var: self.app.update_mfd_category_intensity(
n, var.get()
),
).grid(row=row, column=1 + col_offset, sticky=tk.EW, padx=1, pady=1)
ttk.Button(
self.mfd_params_frame,
text="Color",
width=5,
command=lambda n=name: self.app.choose_mfd_category_color(n),
).grid(row=row, column=2 + col_offset, sticky=tk.W, padx=1, pady=1)
cl = tk.Label(
self.mfd_params_frame, text="", width=3, relief=tk.SUNKEN, borderwidth=1
)
try:
bgr = self.app.state.mfd_params["categories"][name]["color"]
cl.config(background=f"#{bgr[2]:02x}{bgr[1]:02x}{bgr[0]:02x}")
except:
cl.config(background="grey")
cl.grid(
row=row, column=3 + col_offset, sticky=tk.W, padx=(1, 5), pady=1
)
self.mfd_color_labels[name] = cl
last_row = (num_categories - 1) // 2
raw_col = 4 if (num_categories % 2 != 0) else 0
raw_row = last_row if (num_categories % 2 != 0) else last_row + 1
ttk.Label(self.mfd_params_frame, text="Raw Map:").grid(
row=raw_row, column=0 + raw_col, sticky=tk.W, padx=(5, 1), pady=1
)
raw_var = tk.IntVar(value=config.DEFAULT_MFD_RAW_MAP_INTENSITY)
try:
raw_var.set(self.app.state.mfd_params["raw_map_intensity"])
except:
pass
self.mfd_raw_map_intensity_var = raw_var
ttk.Scale(
self.mfd_params_frame,
orient=tk.HORIZONTAL,
length=100,
from_=0,
to=255,
variable=raw_var,
command=lambda v: self.app.update_mfd_raw_map_intensity(raw_var.get()),
).grid(
row=raw_row,
column=1 + raw_col,
columnspan=3,
sticky=tk.EW,
padx=(1, 5),
pady=1,
)
self.mfd_params_frame.columnconfigure(1, weight=1)
self.mfd_params_frame.columnconfigure(5, weight=1)
# --- 3. Map Parameters Frame ---
# (Code Unchanged - Map Size, Save Button, Overlay Checkbox, Alpha Slider, Shift Inputs+Button)
self.map_params_frame = ttk.Labelframe(self, text="Map Parameters", padding=5)
self.map_params_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=2)
map_row = 0
self.map_size_label = ttk.Label(self.map_params_frame, text="Map Display Size:")
self.map_size_label.grid(
row=map_row, column=0, sticky=tk.W, padx=(5, 2), pady=1
)
self.map_size_combo = ttk.Combobox(
self.map_params_frame,
values=config.MAP_SIZE_FACTORS,
state="readonly",
width=6,
)
self.map_size_combo.set(config.DEFAULT_MAP_SIZE)
self.map_size_combo.grid(
row=map_row, column=1, sticky=tk.EW, padx=(2, 10), pady=1
)
self.map_size_combo.bind("<<ComboboxSelected>>", self.app.update_map_size)
self.save_map_button = ttk.Button(
self.map_params_frame,
text="Save Map View",
command=self.app.save_current_map_view,
)
self.save_map_button.grid(
row=map_row, column=2, columnspan=4, sticky=tk.E, padx=5, pady=1
) # Span more columns
map_row += 1
self.sar_overlay_var = tk.BooleanVar(
value=self.app.state.map_sar_overlay_enabled
)
self.sar_overlay_check = ttk.Checkbutton(
self.map_params_frame,
text="Show SAR Overlay on Map",
variable=self.sar_overlay_var,
command=self.app.toggle_sar_overlay,
)
self.sar_overlay_check.grid(
row=map_row, column=0, columnspan=2, sticky=tk.W, padx=5, pady=2
)
map_row += 1
self.alpha_label = ttk.Label(self.map_params_frame, text="SAR Overlay Alpha:")
self.alpha_label.grid(row=map_row, column=0, sticky=tk.W, padx=(5, 2), pady=1)
self.sar_overlay_alpha_var = tk.DoubleVar(
value=self.app.state.map_sar_overlay_alpha
)
self.alpha_scale = ttk.Scale(
self.map_params_frame,
orient=tk.HORIZONTAL,
from_=0.0,
to=1.0,
variable=self.sar_overlay_alpha_var,
)
self.alpha_scale.bind("<ButtonRelease-1>", self.app.on_alpha_slider_release)
self.alpha_scale.grid(
row=map_row, column=1, columnspan=5, sticky=tk.EW, padx=(0, 5), pady=1
) # Span more
map_row += 1
shift_label = ttk.Label(self.map_params_frame, text="SAR Shift (deg):")
shift_label.grid(row=map_row, column=0, sticky=tk.W, padx=(5, 2), pady=1)
lat_label = ttk.Label(self.map_params_frame, text="Lat:")
lat_label.grid(row=map_row, column=1, sticky=tk.W, padx=(0, 0), pady=1)
self.lat_shift_entry = ttk.Entry(
self.map_params_frame, textvariable=self.sar_lat_shift_var, width=10
)
self.lat_shift_entry.grid(
row=map_row, column=2, sticky=tk.W, padx=(0, 5), pady=1
)
lon_label = ttk.Label(self.map_params_frame, text="Lon:")
lon_label.grid(row=map_row, column=3, sticky=tk.W, padx=(5, 0), pady=1)
self.lon_shift_entry = ttk.Entry(
self.map_params_frame, textvariable=self.sar_lon_shift_var, width=10
)
self.lon_shift_entry.grid(
row=map_row, column=4, sticky=tk.W, padx=(0, 5), pady=1
)
self.apply_shift_button = ttk.Button(
self.map_params_frame,
text="Apply Shift",
command=self.app.apply_sar_overlay_shift,
)
self.apply_shift_button.grid(
row=map_row, column=5, sticky=tk.E, padx=(5, 5), pady=1
)
map_row += 1
self.map_params_frame.columnconfigure(2, weight=1)
self.map_params_frame.columnconfigure(
4, weight=1
) # Give weight to entry columns
# --- 4. Info Display Frame ---
logging.debug(f"{log_prefix} Creating Info Display frame...")
self.info_display_frame = ttk.Labelframe(self, text="Info Display", padding=5)
self.info_display_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=2)
info_row = 0
button_width = 3 # Define a standard width for Go/GE buttons
# --- Row 0: SAR Center Coords ---
ref_label = ttk.Label(self.info_display_frame, text="SAR Center:")
ref_label.grid(row=info_row, column=0, sticky=tk.W, padx=(5, 2), pady=1)
self.sar_center_entry = ttk.Entry(
self.info_display_frame,
textvariable=self.sar_center_coords_var,
state="readonly",
width=35,
)
self.sar_center_entry.grid(
row=info_row, column=1, columnspan=3, sticky=tk.EW, padx=(0, 2), pady=1
)
# --- >>> START OF MODIFIED BUTTONS <<< ---
self.ref_gmaps_button = ttk.Button(
self.info_display_frame,
text="Go",
width=button_width,
command=lambda: self.app.go_to_google_maps("sar_center"),
)
self.ref_gmaps_button.grid(
row=info_row, column=4, sticky=tk.E, padx=(0, 1), pady=1
)
self.ref_gearth_button = ttk.Button(
self.info_display_frame,
text="GE",
width=button_width,
command=lambda: self.app.go_to_google_earth("sar_center"),
) # New Button + Callback
self.ref_gearth_button.grid(
row=info_row, column=5, sticky=tk.E, padx=(0, 5), pady=1
)
# --- >>> END OF MODIFIED BUTTONS <<< ---
info_row += 1
# --- Row 1: Image Orient & Image Size ---
orient_label = ttk.Label(self.info_display_frame, text="Image Orient:")
orient_label.grid(row=info_row, column=0, sticky=tk.W, padx=5, pady=1)
self.sar_orientation_entry = ttk.Entry(
self.info_display_frame,
textvariable=self.sar_orientation_var,
state="readonly",
width=15,
)
self.sar_orientation_entry.grid(
row=info_row, column=1, sticky=tk.EW, padx=(0, 5), pady=1
)
size_label = ttk.Label(self.info_display_frame, text="Image Size:")
size_label.grid(row=info_row, column=2, sticky=tk.W, padx=(10, 2), pady=1)
self.sar_size_entry = ttk.Entry(
self.info_display_frame,
textvariable=self.sar_size_km_var,
state="readonly",
width=25,
)
self.sar_size_entry.grid(
row=info_row, column=3, columnspan=3, sticky=tk.EW, padx=(0, 5), pady=1
) # Span 3 cols
info_row += 1
# --- Row 2: SAR Mouse Coords ---
sar_mouse_label = ttk.Label(self.info_display_frame, text="SAR Mouse:")
sar_mouse_label.grid(row=info_row, column=0, sticky=tk.W, padx=5, pady=1)
self.mouse_latlon_entry = ttk.Entry(
self.info_display_frame,
textvariable=self.mouse_coords_var,
state="readonly",
width=35,
)
self.mouse_latlon_entry.grid(
row=info_row, column=1, columnspan=3, sticky=tk.EW, padx=(0, 2), pady=1
)
# --- >>> START OF MODIFIED BUTTONS <<< ---
self.sar_mouse_gmaps_button = ttk.Button(
self.info_display_frame,
text="Go",
width=button_width,
command=lambda: self.app.go_to_google_maps("sar_mouse"),
)
self.sar_mouse_gmaps_button.grid(
row=info_row, column=4, sticky=tk.E, padx=(0, 1), pady=1
)
self.sar_mouse_gearth_button = ttk.Button(
self.info_display_frame,
text="GE",
width=button_width,
command=lambda: self.app.go_to_google_earth("sar_mouse"),
) # New Button + Callback
self.sar_mouse_gearth_button.grid(
row=info_row, column=5, sticky=tk.E, padx=(0, 5), pady=1
)
# --- >>> END OF MODIFIED BUTTONS <<< ---
info_row += 1
# --- Row 3: Map Mouse Coords ---
map_mouse_label = ttk.Label(self.info_display_frame, text="Map Mouse:")
map_mouse_label.grid(row=info_row, column=0, sticky=tk.W, padx=5, pady=1)
self.map_mouse_latlon_entry = ttk.Entry(
self.info_display_frame,
textvariable=self.map_mouse_coords_var,
state="readonly",
width=35,
)
self.map_mouse_latlon_entry.grid(
row=info_row, column=1, columnspan=3, sticky=tk.EW, padx=(0, 2), pady=1
)
# --- >>> START OF MODIFIED BUTTONS <<< ---
self.map_mouse_gmaps_button = ttk.Button(
self.info_display_frame,
text="Go",
width=button_width,
command=lambda: self.app.go_to_google_maps("map_mouse"),
)
self.map_mouse_gmaps_button.grid(
row=info_row, column=4, sticky=tk.E, padx=(0, 1), pady=1
)
self.map_mouse_gearth_button = ttk.Button(
self.info_display_frame,
text="GE",
width=button_width,
command=lambda: self.app.go_to_google_earth("map_mouse"),
) # New Button + Callback
self.map_mouse_gearth_button.grid(
row=info_row, column=5, sticky=tk.E, padx=(0, 5), pady=1
)
# --- >>> END OF MODIFIED BUTTONS <<< ---
info_row += 1
# --- Row 4: Drop & Incomplete Stats ---
dropped_label_text = ttk.Label(self.info_display_frame, text="Stats Drop:")
dropped_label_text.grid(row=info_row, column=0, sticky=tk.W, padx=5, pady=1)
self.dropped_entry = ttk.Entry(
self.info_display_frame,
textvariable=self.dropped_stats_var,
state="readonly",
width=30,
)
self.dropped_entry.grid(
row=info_row, column=1, sticky=tk.EW, padx=(0, 5), pady=1
)
incomplete_label_text = ttk.Label(self.info_display_frame, text="Incomplete:")
incomplete_label_text.grid(
row=info_row, column=2, sticky=tk.W, padx=(10, 2), pady=1
)
self.incomplete_entry = ttk.Entry(
self.info_display_frame,
textvariable=self.incomplete_stats_var,
state="readonly",
width=15,
)
self.incomplete_entry.grid(
row=info_row, column=3, columnspan=3, sticky=tk.EW, padx=(0, 5), pady=1
) # Span 3
# --- Row 5: "GE All" Button ---
info_row += 1 # Incrementa la riga
self.ge_all_button = ttk.Button(
self.info_display_frame,
text="GE All",
command=self.app.go_to_all_gearth # Collega alla nuova funzione callback
)
# Posiziona il bottone, ad esempio centrato o allineato a destra
self.ge_all_button.grid(
row=info_row,
column=0, # Inizia dalla prima colonna
columnspan=6, # Fallo espandere su tutte le colonne per centrarlo (o allinea a destra)
sticky=tk.EW, # Espandi orizzontalmente
padx=5,
pady=(5, 5) # Aggiungi padding sotto
)
# Configure column weights for Info Display frame
self.info_display_frame.columnconfigure(
1, weight=1
) # Give weight to first entry column
self.info_display_frame.columnconfigure(
3, weight=1
) # Give weight to second entry column (col 3)
self.info_display_frame.columnconfigure(4, weight=0) # Button Go
self.info_display_frame.columnconfigure(5, weight=0) # Button GE
logging.debug(f"{log_prefix} Info Display frame created.")
# --- End of init_ui ---
logging.debug(f"{log_prefix} init_ui widget creation complete.")
# --- Methods to update UI displays ---
# (set_sar_center_coords, set_mouse_coordinates, set_map_mouse_coordinates)
# (set_sar_orientation, set_sar_size_km, set_statistics_display)
# (update_mfd_color_display - Unchanged logic, adapt if needed)
def set_sar_center_coords(self, latitude_str: str, longitude_str: str):
text = f"Lat={latitude_str}, Lon={longitude_str}"
try:
self.sar_center_coords_var.set(text)
except Exception as e:
logging.warning(f"[UI Update] Error setting SAR center coords: {e}")
def set_mouse_coordinates(self, latitude_str: str, longitude_str: str):
text = f"Lat={latitude_str}, Lon={longitude_str}"
try:
self.mouse_coords_var.set(text)
except Exception as e:
logging.warning(f"[UI Update] Error setting SAR mouse coords: {e}")
def set_map_mouse_coordinates(self, latitude_str: str, longitude_str: str):
text = f"Lat={latitude_str}, Lon={longitude_str}"
try:
self.map_mouse_coords_var.set(text)
except Exception as e:
logging.warning(f"[UI Update] Error setting Map mouse coords: {e}")
def set_sar_orientation(self, orientation_deg_str: str):
try:
self.sar_orientation_var.set(orientation_deg_str)
except Exception as e:
logging.warning(f"[UI Update] Error setting SAR orientation: {e}")
def set_sar_size_km(self, size_text: str):
try:
self.sar_size_km_var.set(size_text)
except Exception as e:
logging.warning(f"[UI Update] Error setting SAR size: {e}")
def set_statistics_display(self, dropped_text: str, incomplete_text: str):
try:
self.dropped_stats_var.set(dropped_text)
self.incomplete_stats_var.set(incomplete_text)
except Exception as e:
logging.warning(f"[UI Update] Error setting stats display: {e}")
def update_mfd_color_display(
self, category_name: str, color_bgr_tuple: Tuple[int, int, int]
):
if category_name in self.mfd_color_labels:
lbl = self.mfd_color_labels[category_name]
hex_color = f"#{color_bgr_tuple[2]:02x}{color_bgr_tuple[1]:02x}{color_bgr_tuple[0]:02x}"
try:
if lbl.winfo_exists():
lbl.config(background=hex_color)
except Exception as e:
logging.exception(
f"[UI Update] Error updating MFD color for {category_name}: {e}"
)
class StatusBar(ttk.Label):
"""Represents the status bar at the bottom of the main window."""
def __init__(self, parent: tk.Widget, *args, **kwargs):
log_prefix = "[UI Setup]"
logging.debug(f"{log_prefix} Initializing StatusBar...")
super().__init__(
parent,
text=config.INITIAL_STATUS_MESSAGE,
relief=tk.SUNKEN,
anchor=tk.W,
*args,
**kwargs,
)
self.pack(side=tk.BOTTOM, fill=tk.X)
def set_status_text(self, text: str):
"""Sets the text displayed in the status bar."""
try:
if self.winfo_exists():
self.config(text=text)
except Exception:
pass # Ignore errors during status update
def create_main_window(
title: str, min_width: int, min_height: int, x_pos: int, y_pos: int
) -> tk.Tk:
"""Creates and configures the main Tkinter root window."""
log_prefix = "[UI Setup]"
try:
root = tk.Tk()
root.title(title)
root.minsize(min_width, min_height)
root.geometry(f"+{x_pos}+{y_pos}")
logging.debug(
f"{log_prefix} Main Tkinter root window created at ({x_pos},{y_pos})."
)
return root
except Exception as e:
logging.critical(
f"{log_prefix} Failed to create main Tkinter window:", exc_info=True
)
raise
# --- END OF FILE ui.py ---