add war sar metadata view

This commit is contained in:
VALLONGOL 2025-04-15 14:10:43 +02:00
parent 93833289fd
commit c0b40803cf
3 changed files with 951 additions and 672 deletions

File diff suppressed because it is too large Load Diff

210
ui.py
View File

@ -10,7 +10,8 @@ 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.
The main ControlPanel frame is designed to be placed within a container managed
by the main application.
by the main application. The metadata display components are now created and
managed directly by the main application (ControlPanelApp).
"""
# Standard library imports
@ -20,7 +21,8 @@ from typing import TYPE_CHECKING, Dict, Tuple, Optional
# Third-party imports
import tkinter as tk
from tkinter import ttk
# Import ScrolledText only if needed within this module (currently not)
# Removed ScrolledText import as it's no longer created here
# from tkinter.scrolledtext import ScrolledText
# Local application imports
@ -32,12 +34,13 @@ if TYPE_CHECKING:
from ControlPanel import ControlPanelApp
class ControlPanel(ttk.Frame): # This is the main panel holding user controls
class ControlPanel(ttk.Frame): # This is the main panel holding user controls
"""
Main control panel frame containing SAR, MFD, Map parameter widgets,
information displays, statistics labels, and interaction buttons.
This frame is typically placed in the main application window's container.
"""
def __init__(self, parent: tk.Widget, app: "ControlPanelApp", *args, **kwargs):
"""Initializes the ControlPanel frame."""
log_prefix = "[UI Setup]"
@ -57,19 +60,17 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
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"
)
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")
# Checkbox variable for metadata toggle (still needed here)
self.show_meta_var = tk.BooleanVar(value=self.app.state.display_sar_metadata)
# --- References to UI widgets ---
self.mfd_color_labels: Dict[str, tk.Label] = {}
# References to metadata widgets are REMOVED from here
# References to metadata widgets are REMOVED from this class
# --- Initialize UI structure ---
self.init_ui() # Call the UI building method
self.init_ui() # Call the UI building method
logging.debug(f"{log_prefix} ControlPanel frame initialization complete.")
@ -85,7 +86,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
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 # Row counter for SAR grid
sar_row = 0 # Row counter for SAR grid
# Test Image Checkbox
self.test_image_var = tk.IntVar(value=1 if config.ENABLE_TEST_MODE else 0)
@ -100,9 +101,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
)
# Record SAR Checkbox
self.record_sar_var = tk.BooleanVar(
value=config.DEFAULT_SAR_RECORDING_ENABLED
)
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",
@ -126,7 +125,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
state="readonly",
width=6,
)
try: # Set initial value based on current state
try: # Set initial value based on current state
factor = 1
if self.app.state.sar_display_width > 0:
factor = max(1, config.SAR_WIDTH // self.app.state.sar_display_width)
@ -135,7 +134,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
self.sar_size_combo.set(sz_str)
else:
self.sar_size_combo.set(config.DEFAULT_SAR_SIZE)
except Exception: # Fallback to default if error
except Exception: # Fallback to default if error
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
@ -144,16 +143,14 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
# SAR Palette Combobox
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_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) # Set from state
self.palette_combo.set(self.app.state.sar_palette) # Set from state
self.palette_combo.grid(
row=sar_row, column=3, sticky=tk.EW, padx=(0, 5), pady=1
)
@ -171,7 +168,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
orient=tk.HORIZONTAL,
from_=0.1,
to=3.0,
value=self.app.state.sar_contrast, # Set from state
value=self.app.state.sar_contrast, # Set from state
command=self.app.update_contrast,
)
self.contrast_scale.grid(
@ -188,42 +185,48 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
orient=tk.HORIZONTAL,
from_=-100,
to=100,
value=self.app.state.sar_brightness, # Set from state
value=self.app.state.sar_brightness, # Set from state
command=self.app.update_brightness,
)
self.brightness_scale.grid(
row=sar_row, column=3, sticky=tk.EW, padx=(0, 5), pady=1
)
# SAR Metadata Checkbox
# SAR Metadata Checkbox (Still created here as it belongs logically with SAR params)
sar_row += 1
self.show_meta_var = tk.BooleanVar(value=self.app.state.display_sar_metadata)
# self.show_meta_var is already created in __init__
self.show_meta_check = ttk.Checkbutton(
self.sar_params_frame,
text="Show SAR Metadata",
variable=self.show_meta_var,
command=self.app.toggle_sar_metadata_display # Link to app callback
command=self.app.toggle_sar_metadata_display, # Link to app callback
)
self.show_meta_check.grid(
row=sar_row, column=0, columnspan=4, sticky=tk.W, padx=5, pady=(5, 2)
)
# Configure SAR frame column weights
self.sar_params_frame.columnconfigure(1, weight=1) # Allow sliders to expand
self.sar_params_frame.columnconfigure(1, weight=1) # Allow sliders to expand
self.sar_params_frame.columnconfigure(3, weight=1)
# --- 2. MFD Parameters Frame ---
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 = [ # Order for display
"Occlusion", "Cat A", "Cat B", "Cat C", "Cat C1", "Cat C2", "Cat C3",
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 # Two categories per row
col_offset = 0 if (index % 2 == 0) else 4 # Offset for second column
row = index // 2
col_offset = 0 if (index % 2 == 0) else 4
# Category Label
cat_label = ttk.Label(self.mfd_params_frame, text=f"{name}:")
@ -233,12 +236,12 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
# Intensity Slider Variable and Widget
intensity_var = tk.IntVar(value=config.DEFAULT_MFD_INTENSITY)
try: # Set initial value from state
try:
intensity_var.set(
self.app.state.mfd_params["categories"][name]["intensity"]
)
except Exception:
pass # Ignore if state not ready or key missing
pass
intensity_scale = ttk.Scale(
self.mfd_params_frame,
orient=tk.HORIZONTAL,
@ -246,7 +249,6 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
from_=0,
to=255,
variable=intensity_var,
# Use lambda to pass name and var correctly to callback
command=lambda v, n=name, var=intensity_var: (
self.app.update_mfd_category_intensity(n, var.get())
),
@ -268,41 +270,39 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
# Color Preview Label
color_label = tk.Label(
self.mfd_params_frame,
text="",
width=3,
relief=tk.SUNKEN,
borderwidth=1
self.mfd_params_frame, text="", width=3, relief=tk.SUNKEN, borderwidth=1
)
try: # Set initial color from state
try:
bgr = self.app.state.mfd_params["categories"][name]["color"]
hex_color = f"#{bgr[2]:02x}{bgr[1]:02x}{bgr[0]:02x}"
color_label.config(background=hex_color)
except Exception:
color_label.config(background="grey") # Fallback
color_label.config(background="grey")
color_label.grid(
row=row, column=3 + col_offset, sticky=tk.W, padx=(1, 5), pady=1
)
self.mfd_color_labels[name] = color_label # Store label reference
self.mfd_color_labels[name] = color_label
# Raw Map Intensity Slider
last_cat_row = (num_categories - 1) // 2
# Determine position based on number of categories
raw_map_col_offset = 4 if (num_categories % 2 != 0) else 0
raw_map_row = last_cat_row if (num_categories % 2 != 0) else last_cat_row + 1
raw_map_label = ttk.Label(self.mfd_params_frame, text="Raw Map:")
raw_map_label.grid(
row=raw_map_row, column=0 + raw_map_col_offset, sticky=tk.W, padx=(5, 1), pady=1
row=raw_map_row,
column=0 + raw_map_col_offset,
sticky=tk.W,
padx=(5, 1),
pady=1,
)
raw_map_intensity_var = tk.IntVar(value=config.DEFAULT_MFD_RAW_MAP_INTENSITY)
try: # Set initial value from state
raw_map_intensity_var.set(self.app.state.mfd_params["raw_map_intensity"])
try:
raw_map_intensity_var.set(self.app.state.mfd_params["raw_map_intensity"])
except Exception:
pass
# Store var reference if needed elsewhere
self.mfd_raw_map_intensity_var = raw_map_intensity_var
pass
self.mfd_raw_map_intensity_var = raw_map_intensity_var # Keep reference
raw_map_scale = ttk.Scale(
self.mfd_params_frame,
@ -318,20 +318,20 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
raw_map_scale.grid(
row=raw_map_row,
column=1 + raw_map_col_offset,
columnspan=3, # Span 3 cols after label
columnspan=3,
sticky=tk.EW,
padx=(1, 5),
pady=1,
)
# Configure MFD frame column weights
self.mfd_params_frame.columnconfigure(1, weight=1) # Allow sliders to expand
self.mfd_params_frame.columnconfigure(5, weight=1) # Allow sliders on right
self.mfd_params_frame.columnconfigure(1, weight=1)
self.mfd_params_frame.columnconfigure(5, weight=1)
# --- 3. Map Parameters Frame ---
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 # Row counter for Map grid
map_row = 0 # Row counter for Map grid
# Map Size Combobox
self.map_size_label = ttk.Label(self.map_params_frame, text="Map Display Size:")
@ -342,9 +342,9 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
self.map_params_frame,
values=config.MAP_SIZE_FACTORS,
state="readonly",
width=6
width=6,
)
self.map_size_combo.set(config.DEFAULT_MAP_SIZE) # Set default initially
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
)
@ -354,7 +354,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
self.save_map_button = ttk.Button(
self.map_params_frame,
text="Save Map View",
command=self.app.save_current_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
@ -380,9 +380,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
# SAR Overlay Alpha Slider
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.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
)
@ -393,7 +391,6 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
to=1.0,
variable=self.sar_overlay_alpha_var,
)
# Trigger update only on release for performance
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
@ -403,51 +400,42 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
# SAR Shift Inputs and Apply Button
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
)
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.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.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
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
)
# Configure Map frame column weights
self.map_params_frame.columnconfigure(2, weight=1) # Allow Lat entry expand
self.map_params_frame.columnconfigure(4, weight=1) # Allow Lon entry expand
self.map_params_frame.columnconfigure(2, weight=1)
self.map_params_frame.columnconfigure(4, weight=1)
# --- 4. 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 # Row counter for Info grid
button_width = 3 # Standard width for Go/GE buttons
info_row = 0 # Row counter for Info grid
button_width = 3 # Standard width for Go/GE buttons
# --- Row 0: SAR Center Coords ---
ref_label = ttk.Label(self.info_display_frame, text="SAR Center:")
@ -456,7 +444,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
self.info_display_frame,
textvariable=self.sar_center_coords_var,
state="readonly",
width=35
width=35,
)
self.sar_center_entry.grid(
row=info_row, column=1, columnspan=3, sticky=tk.EW, padx=(0, 2), pady=1
@ -465,7 +453,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
self.info_display_frame,
text="Go",
width=button_width,
command=lambda: self.app.go_to_google_maps("sar_center")
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
@ -474,7 +462,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
self.info_display_frame,
text="GE",
width=button_width,
command=lambda: self.app.go_to_google_earth("sar_center")
command=lambda: self.app.go_to_google_earth("sar_center"),
)
self.ref_gearth_button.grid(
row=info_row, column=5, sticky=tk.E, padx=(0, 5), pady=1
@ -488,20 +476,18 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
self.info_display_frame,
textvariable=self.sar_orientation_var,
state="readonly",
width=15
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
)
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
width=25,
)
self.sar_size_entry.grid(
row=info_row, column=3, columnspan=3, sticky=tk.EW, padx=(0, 5), pady=1
@ -515,7 +501,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
self.info_display_frame,
textvariable=self.mouse_coords_var,
state="readonly",
width=35
width=35,
)
self.mouse_latlon_entry.grid(
row=info_row, column=1, columnspan=3, sticky=tk.EW, padx=(0, 2), pady=1
@ -524,7 +510,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
self.info_display_frame,
text="Go",
width=button_width,
command=lambda: self.app.go_to_google_maps("sar_mouse")
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
@ -533,7 +519,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
self.info_display_frame,
text="GE",
width=button_width,
command=lambda: self.app.go_to_google_earth("sar_mouse")
command=lambda: self.app.go_to_google_earth("sar_mouse"),
)
self.sar_mouse_gearth_button.grid(
row=info_row, column=5, sticky=tk.E, padx=(0, 5), pady=1
@ -547,7 +533,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
self.info_display_frame,
textvariable=self.map_mouse_coords_var,
state="readonly",
width=35
width=35,
)
self.map_mouse_latlon_entry.grid(
row=info_row, column=1, columnspan=3, sticky=tk.EW, padx=(0, 2), pady=1
@ -556,7 +542,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
self.info_display_frame,
text="Go",
width=button_width,
command=lambda: self.app.go_to_google_maps("map_mouse")
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
@ -565,7 +551,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
self.info_display_frame,
text="GE",
width=button_width,
command=lambda: self.app.go_to_google_earth("map_mouse")
command=lambda: self.app.go_to_google_earth("map_mouse"),
)
self.map_mouse_gearth_button.grid(
row=info_row, column=5, sticky=tk.E, padx=(0, 5), pady=1
@ -574,14 +560,12 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
# --- 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
)
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
width=30,
)
self.dropped_entry.grid(
row=info_row, column=1, sticky=tk.EW, padx=(0, 5), pady=1
@ -594,7 +578,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
self.info_display_frame,
textvariable=self.incomplete_stats_var,
state="readonly",
width=15
width=15,
)
self.incomplete_entry.grid(
row=info_row, column=3, columnspan=3, sticky=tk.EW, padx=(0, 5), pady=1
@ -603,33 +587,25 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
# --- Row 5: "GE All" Button ---
self.ge_all_button = ttk.Button(
self.info_display_frame,
text="GE All",
command=self.app.go_to_all_gearth # Link to app callback
self.info_display_frame, text="GE All", command=self.app.go_to_all_gearth
)
self.ge_all_button.grid(
row=info_row,
column=0, # Start column 0
columnspan=6, # Span all 6 columns
sticky=tk.EW, # Expand horizontally
padx=5,
pady=(5, 5) # Padding top and bottom
row=info_row, column=0, columnspan=6, sticky=tk.EW, padx=5, pady=(5, 5)
)
# Configure column weights for Info Display frame
self.info_display_frame.columnconfigure(1, weight=1) # Entry column
self.info_display_frame.columnconfigure(3, weight=1) # Entry column
self.info_display_frame.columnconfigure(4, weight=0) # Button Go
self.info_display_frame.columnconfigure(5, weight=0) # Button GE
self.info_display_frame.columnconfigure(1, weight=1) # Entry column
self.info_display_frame.columnconfigure(3, weight=1) # Entry column
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.")
# --- 5. Metadata Display Frame (Creation REMOVED from here) ---
# The structure is now created in ControlPanelApp.__init__
# This is now created and managed in ControlPanelApp
# --- End of init_ui ---
logging.debug(f"{log_prefix} init_ui widget creation complete.")
# --- UI Update Methods ---
def set_sar_center_coords(self, latitude_str: str, longitude_str: str):
"""Updates the SAR Center coordinates display."""
@ -698,6 +674,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
f"[UI Update] Error updating MFD color for {category_name}: {e}"
)
# --- StatusBar Class ---
class StatusBar(ttk.Label):
"""Represents the status bar at the bottom of the main window."""
@ -709,10 +686,10 @@ class StatusBar(ttk.Label):
parent,
text=config.INITIAL_STATUS_MESSAGE,
relief=tk.SUNKEN,
anchor=tk.W, # Anchor text to the West (left)
padding=(5, 2), # Add some internal padding
anchor=tk.W, # Anchor text to the West (left)
padding=(5, 2), # Add some internal padding
*args,
**kwargs
**kwargs,
)
# Packed by the main application layout manager
@ -726,6 +703,7 @@ class StatusBar(ttk.Label):
# Ignore errors during status update, especially during shutdown
pass
# --- Window Creation Helper ---
def create_main_window(
title: str, min_width: int, min_height: int, x_pos: int, y_pos: int
@ -740,9 +718,10 @@ def create_main_window(
# Set minimum size constraint
root.minsize(min_width, min_height)
# Set initial position (may be overridden by OS window manager)
# This is less critical now as the app constructor sets the final position
root.geometry(f"+{x_pos}+{y_pos}")
logging.debug(
f"{log_prefix} Main Tkinter root window created (requested pos: {x_pos},{y_pos})."
f"{log_prefix} Main Tkinter root window created (initial requested pos: {x_pos},{y_pos})."
)
return root
except Exception as e:
@ -752,4 +731,5 @@ def create_main_window(
# Re-raise the exception to halt execution if window creation fails
raise
# --- END OF FILE ui.py ---

769
utils.py

File diff suppressed because it is too large Load Diff