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 including the main control panel area, status bar, map parameters, info display
with Google Maps/Earth links, and helper functions for window creation. with Google Maps/Earth links, and helper functions for window creation.
The main ControlPanel frame is designed to be placed within a container managed 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 # Standard library imports
@ -20,7 +21,8 @@ from typing import TYPE_CHECKING, Dict, Tuple, Optional
# Third-party imports # Third-party imports
import tkinter as tk import tkinter as tk
from tkinter import ttk 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 # from tkinter.scrolledtext import ScrolledText
# Local application imports # Local application imports
@ -32,12 +34,13 @@ if TYPE_CHECKING:
from ControlPanel import ControlPanelApp 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, Main control panel frame containing SAR, MFD, Map parameter widgets,
information displays, statistics labels, and interaction buttons. information displays, statistics labels, and interaction buttons.
This frame is typically placed in the main application window's container. This frame is typically placed in the main application window's container.
""" """
def __init__(self, parent: tk.Widget, app: "ControlPanelApp", *args, **kwargs): def __init__(self, parent: tk.Widget, app: "ControlPanelApp", *args, **kwargs):
"""Initializes the ControlPanel frame.""" """Initializes the ControlPanel frame."""
log_prefix = "[UI Setup]" 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( self.sar_lon_shift_var = tk.StringVar(
value=f"{self.app.state.sar_lon_shift_deg:.6f}" value=f"{self.app.state.sar_lon_shift_deg:.6f}"
) )
self.dropped_stats_var = tk.StringVar( self.dropped_stats_var = tk.StringVar(value="Drop (Q): S=0, M=0, Tk=0, Mo=0")
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.incomplete_stats_var = tk.StringVar( self.show_meta_var = tk.BooleanVar(value=self.app.state.display_sar_metadata)
value="Incmpl (RX): S=0, M=0"
)
# --- References to UI widgets --- # --- References to UI widgets ---
self.mfd_color_labels: Dict[str, tk.Label] = {} 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 --- # --- 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.") 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 = ttk.Labelframe(self, text="SAR Parameters", padding=5)
self.sar_params_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=(5, 2)) 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 # Test Image Checkbox
self.test_image_var = tk.IntVar(value=1 if config.ENABLE_TEST_MODE else 0) 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 # Record SAR Checkbox
self.record_sar_var = tk.BooleanVar( self.record_sar_var = tk.BooleanVar(value=config.DEFAULT_SAR_RECORDING_ENABLED)
value=config.DEFAULT_SAR_RECORDING_ENABLED
)
self.record_sar_check = ttk.Checkbutton( self.record_sar_check = ttk.Checkbutton(
self.sar_params_frame, self.sar_params_frame,
text="Record SAR", text="Record SAR",
@ -126,7 +125,7 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
state="readonly", state="readonly",
width=6, width=6,
) )
try: # Set initial value based on current state try: # Set initial value based on current state
factor = 1 factor = 1
if self.app.state.sar_display_width > 0: if self.app.state.sar_display_width > 0:
factor = max(1, config.SAR_WIDTH // self.app.state.sar_display_width) 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) self.sar_size_combo.set(sz_str)
else: else:
self.sar_size_combo.set(config.DEFAULT_SAR_SIZE) 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.set(config.DEFAULT_SAR_SIZE)
self.sar_size_combo.grid( self.sar_size_combo.grid(
row=sar_row, column=1, sticky=tk.EW, padx=(0, 10), pady=1 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 # SAR Palette Combobox
self.palette_label = ttk.Label(self.sar_params_frame, text="Palette:") self.palette_label = ttk.Label(self.sar_params_frame, text="Palette:")
self.palette_label.grid( self.palette_label.grid(row=sar_row, column=2, sticky=tk.W, padx=(0, 2), pady=1)
row=sar_row, column=2, sticky=tk.W, padx=(0, 2), pady=1
)
self.palette_combo = ttk.Combobox( self.palette_combo = ttk.Combobox(
self.sar_params_frame, self.sar_params_frame,
values=config.COLOR_PALETTES, values=config.COLOR_PALETTES,
state="readonly", state="readonly",
width=8, 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( self.palette_combo.grid(
row=sar_row, column=3, sticky=tk.EW, padx=(0, 5), pady=1 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, orient=tk.HORIZONTAL,
from_=0.1, from_=0.1,
to=3.0, 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, command=self.app.update_contrast,
) )
self.contrast_scale.grid( self.contrast_scale.grid(
@ -188,42 +185,48 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
orient=tk.HORIZONTAL, orient=tk.HORIZONTAL,
from_=-100, from_=-100,
to=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, command=self.app.update_brightness,
) )
self.brightness_scale.grid( self.brightness_scale.grid(
row=sar_row, column=3, sticky=tk.EW, padx=(0, 5), pady=1 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 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.show_meta_check = ttk.Checkbutton(
self.sar_params_frame, self.sar_params_frame,
text="Show SAR Metadata", text="Show SAR Metadata",
variable=self.show_meta_var, 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( self.show_meta_check.grid(
row=sar_row, column=0, columnspan=4, sticky=tk.W, padx=5, pady=(5, 2) row=sar_row, column=0, columnspan=4, sticky=tk.W, padx=5, pady=(5, 2)
) )
# Configure SAR frame column weights # 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) self.sar_params_frame.columnconfigure(3, weight=1)
# --- 2. MFD Parameters Frame --- # --- 2. MFD Parameters Frame ---
self.mfd_params_frame = ttk.Labelframe(self, text="MFD Parameters", padding=5) 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) self.mfd_params_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=2)
mfd_categories_ordered = [ # Order for display mfd_categories_ordered = [
"Occlusion", "Cat A", "Cat B", "Cat C", "Cat C1", "Cat C2", "Cat C3", "Occlusion",
"Cat A",
"Cat B",
"Cat C",
"Cat C1",
"Cat C2",
"Cat C3",
] ]
num_categories = len(mfd_categories_ordered) num_categories = len(mfd_categories_ordered)
for index, name in enumerate(mfd_categories_ordered): for index, name in enumerate(mfd_categories_ordered):
row = index // 2 # Two categories per row row = index // 2
col_offset = 0 if (index % 2 == 0) else 4 # Offset for second column col_offset = 0 if (index % 2 == 0) else 4
# Category Label # Category Label
cat_label = ttk.Label(self.mfd_params_frame, text=f"{name}:") 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 Slider Variable and Widget
intensity_var = tk.IntVar(value=config.DEFAULT_MFD_INTENSITY) intensity_var = tk.IntVar(value=config.DEFAULT_MFD_INTENSITY)
try: # Set initial value from state try:
intensity_var.set( intensity_var.set(
self.app.state.mfd_params["categories"][name]["intensity"] self.app.state.mfd_params["categories"][name]["intensity"]
) )
except Exception: except Exception:
pass # Ignore if state not ready or key missing pass
intensity_scale = ttk.Scale( intensity_scale = ttk.Scale(
self.mfd_params_frame, self.mfd_params_frame,
orient=tk.HORIZONTAL, orient=tk.HORIZONTAL,
@ -246,7 +249,6 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
from_=0, from_=0,
to=255, to=255,
variable=intensity_var, variable=intensity_var,
# Use lambda to pass name and var correctly to callback
command=lambda v, n=name, var=intensity_var: ( command=lambda v, n=name, var=intensity_var: (
self.app.update_mfd_category_intensity(n, var.get()) 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 Preview Label
color_label = tk.Label( color_label = tk.Label(
self.mfd_params_frame, self.mfd_params_frame, text="", width=3, relief=tk.SUNKEN, borderwidth=1
text="",
width=3,
relief=tk.SUNKEN,
borderwidth=1
) )
try: # Set initial color from state try:
bgr = self.app.state.mfd_params["categories"][name]["color"] bgr = self.app.state.mfd_params["categories"][name]["color"]
hex_color = f"#{bgr[2]:02x}{bgr[1]:02x}{bgr[0]:02x}" hex_color = f"#{bgr[2]:02x}{bgr[1]:02x}{bgr[0]:02x}"
color_label.config(background=hex_color) color_label.config(background=hex_color)
except Exception: except Exception:
color_label.config(background="grey") # Fallback color_label.config(background="grey")
color_label.grid( color_label.grid(
row=row, column=3 + col_offset, sticky=tk.W, padx=(1, 5), pady=1 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 # Raw Map Intensity Slider
last_cat_row = (num_categories - 1) // 2 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_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_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 = ttk.Label(self.mfd_params_frame, text="Raw Map:")
raw_map_label.grid( 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) raw_map_intensity_var = tk.IntVar(value=config.DEFAULT_MFD_RAW_MAP_INTENSITY)
try: # Set initial value from state try:
raw_map_intensity_var.set(self.app.state.mfd_params["raw_map_intensity"]) raw_map_intensity_var.set(self.app.state.mfd_params["raw_map_intensity"])
except Exception: except Exception:
pass pass
# Store var reference if needed elsewhere self.mfd_raw_map_intensity_var = raw_map_intensity_var # Keep reference
self.mfd_raw_map_intensity_var = raw_map_intensity_var
raw_map_scale = ttk.Scale( raw_map_scale = ttk.Scale(
self.mfd_params_frame, self.mfd_params_frame,
@ -318,20 +318,20 @@ class ControlPanel(ttk.Frame): # This is the main panel holding user controls
raw_map_scale.grid( raw_map_scale.grid(
row=raw_map_row, row=raw_map_row,
column=1 + raw_map_col_offset, column=1 + raw_map_col_offset,
columnspan=3, # Span 3 cols after label columnspan=3,
sticky=tk.EW, sticky=tk.EW,
padx=(1, 5), padx=(1, 5),
pady=1, pady=1,
) )
# Configure MFD frame column weights # Configure MFD frame column weights
self.mfd_params_frame.columnconfigure(1, weight=1) # Allow sliders to expand self.mfd_params_frame.columnconfigure(1, weight=1)
self.mfd_params_frame.columnconfigure(5, weight=1) # Allow sliders on right self.mfd_params_frame.columnconfigure(5, weight=1)
# --- 3. Map Parameters Frame --- # --- 3. Map Parameters Frame ---
self.map_params_frame = ttk.Labelframe(self, text="Map Parameters", padding=5) 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) 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 # Map Size Combobox
self.map_size_label = ttk.Label(self.map_params_frame, text="Map Display Size:") 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, self.map_params_frame,
values=config.MAP_SIZE_FACTORS, values=config.MAP_SIZE_FACTORS,
state="readonly", 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( self.map_size_combo.grid(
row=map_row, column=1, sticky=tk.EW, padx=(2, 10), pady=1 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.save_map_button = ttk.Button(
self.map_params_frame, self.map_params_frame,
text="Save Map View", text="Save Map View",
command=self.app.save_current_map_view command=self.app.save_current_map_view,
) )
self.save_map_button.grid( self.save_map_button.grid(
row=map_row, column=2, columnspan=4, sticky=tk.E, padx=5, pady=1 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 # SAR Overlay Alpha Slider
self.alpha_label = ttk.Label(self.map_params_frame, text="SAR Overlay Alpha:") self.alpha_label = ttk.Label(self.map_params_frame, text="SAR Overlay Alpha:")
self.alpha_label.grid( self.alpha_label.grid(row=map_row, column=0, sticky=tk.W, padx=(5, 2), pady=1)
row=map_row, column=0, sticky=tk.W, padx=(5, 2), pady=1
)
self.sar_overlay_alpha_var = tk.DoubleVar( self.sar_overlay_alpha_var = tk.DoubleVar(
value=self.app.state.map_sar_overlay_alpha 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, to=1.0,
variable=self.sar_overlay_alpha_var, 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.bind("<ButtonRelease-1>", self.app.on_alpha_slider_release)
self.alpha_scale.grid( self.alpha_scale.grid(
row=map_row, column=1, columnspan=5, sticky=tk.EW, padx=(0, 5), pady=1 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 # SAR Shift Inputs and Apply Button
shift_label = ttk.Label(self.map_params_frame, text="SAR Shift (deg):") shift_label = ttk.Label(self.map_params_frame, text="SAR Shift (deg):")
shift_label.grid( shift_label.grid(row=map_row, column=0, sticky=tk.W, padx=(5, 2), pady=1)
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 = ttk.Label(self.map_params_frame, text="Lat:")
lat_label.grid(row=map_row, column=1, sticky=tk.W, padx=(0, 0), pady=1) lat_label.grid(row=map_row, column=1, sticky=tk.W, padx=(0, 0), pady=1)
self.lat_shift_entry = ttk.Entry( self.lat_shift_entry = ttk.Entry(
self.map_params_frame, self.map_params_frame, textvariable=self.sar_lat_shift_var, width=10
textvariable=self.sar_lat_shift_var,
width=10
) )
self.lat_shift_entry.grid( self.lat_shift_entry.grid(
row=map_row, column=2, sticky=tk.W, padx=(0, 5), pady=1 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 = ttk.Label(self.map_params_frame, text="Lon:")
lon_label.grid(row=map_row, column=3, sticky=tk.W, padx=(5, 0), pady=1) lon_label.grid(row=map_row, column=3, sticky=tk.W, padx=(5, 0), pady=1)
self.lon_shift_entry = ttk.Entry( self.lon_shift_entry = ttk.Entry(
self.map_params_frame, self.map_params_frame, textvariable=self.sar_lon_shift_var, width=10
textvariable=self.sar_lon_shift_var,
width=10
) )
self.lon_shift_entry.grid( self.lon_shift_entry.grid(
row=map_row, column=4, sticky=tk.W, padx=(0, 5), pady=1 row=map_row, column=4, sticky=tk.W, padx=(0, 5), pady=1
) )
self.apply_shift_button = ttk.Button( self.apply_shift_button = ttk.Button(
self.map_params_frame, self.map_params_frame,
text="Apply Shift", text="Apply Shift",
command=self.app.apply_sar_overlay_shift command=self.app.apply_sar_overlay_shift,
) )
self.apply_shift_button.grid( self.apply_shift_button.grid(
row=map_row, column=5, sticky=tk.E, padx=(5, 5), pady=1 row=map_row, column=5, sticky=tk.E, padx=(5, 5), pady=1
) )
# Configure Map frame column weights # Configure Map frame column weights
self.map_params_frame.columnconfigure(2, weight=1) # Allow Lat entry expand self.map_params_frame.columnconfigure(2, weight=1)
self.map_params_frame.columnconfigure(4, weight=1) # Allow Lon entry expand self.map_params_frame.columnconfigure(4, weight=1)
# --- 4. Info Display Frame --- # --- 4. Info Display Frame ---
self.info_display_frame = ttk.Labelframe(self, text="Info Display", padding=5) 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) self.info_display_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=2)
info_row = 0 # Row counter for Info grid info_row = 0 # Row counter for Info grid
button_width = 3 # Standard width for Go/GE buttons button_width = 3 # Standard width for Go/GE buttons
# --- Row 0: SAR Center Coords --- # --- Row 0: SAR Center Coords ---
ref_label = ttk.Label(self.info_display_frame, text="SAR Center:") 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, self.info_display_frame,
textvariable=self.sar_center_coords_var, textvariable=self.sar_center_coords_var,
state="readonly", state="readonly",
width=35 width=35,
) )
self.sar_center_entry.grid( self.sar_center_entry.grid(
row=info_row, column=1, columnspan=3, sticky=tk.EW, padx=(0, 2), pady=1 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, self.info_display_frame,
text="Go", text="Go",
width=button_width, 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( self.ref_gmaps_button.grid(
row=info_row, column=4, sticky=tk.E, padx=(0, 1), pady=1 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, self.info_display_frame,
text="GE", text="GE",
width=button_width, 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( self.ref_gearth_button.grid(
row=info_row, column=5, sticky=tk.E, padx=(0, 5), pady=1 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, self.info_display_frame,
textvariable=self.sar_orientation_var, textvariable=self.sar_orientation_var,
state="readonly", state="readonly",
width=15 width=15,
) )
self.sar_orientation_entry.grid( self.sar_orientation_entry.grid(
row=info_row, column=1, sticky=tk.EW, padx=(0, 5), pady=1 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 = ttk.Label(self.info_display_frame, text="Image Size:")
size_label.grid( size_label.grid(row=info_row, column=2, sticky=tk.W, padx=(10, 2), pady=1)
row=info_row, column=2, sticky=tk.W, padx=(10, 2), pady=1
)
self.sar_size_entry = ttk.Entry( self.sar_size_entry = ttk.Entry(
self.info_display_frame, self.info_display_frame,
textvariable=self.sar_size_km_var, textvariable=self.sar_size_km_var,
state="readonly", state="readonly",
width=25 width=25,
) )
self.sar_size_entry.grid( self.sar_size_entry.grid(
row=info_row, column=3, columnspan=3, sticky=tk.EW, padx=(0, 5), pady=1 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, self.info_display_frame,
textvariable=self.mouse_coords_var, textvariable=self.mouse_coords_var,
state="readonly", state="readonly",
width=35 width=35,
) )
self.mouse_latlon_entry.grid( self.mouse_latlon_entry.grid(
row=info_row, column=1, columnspan=3, sticky=tk.EW, padx=(0, 2), pady=1 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, self.info_display_frame,
text="Go", text="Go",
width=button_width, 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( self.sar_mouse_gmaps_button.grid(
row=info_row, column=4, sticky=tk.E, padx=(0, 1), pady=1 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, self.info_display_frame,
text="GE", text="GE",
width=button_width, 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( self.sar_mouse_gearth_button.grid(
row=info_row, column=5, sticky=tk.E, padx=(0, 5), pady=1 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, self.info_display_frame,
textvariable=self.map_mouse_coords_var, textvariable=self.map_mouse_coords_var,
state="readonly", state="readonly",
width=35 width=35,
) )
self.map_mouse_latlon_entry.grid( self.map_mouse_latlon_entry.grid(
row=info_row, column=1, columnspan=3, sticky=tk.EW, padx=(0, 2), pady=1 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, self.info_display_frame,
text="Go", text="Go",
width=button_width, 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( self.map_mouse_gmaps_button.grid(
row=info_row, column=4, sticky=tk.E, padx=(0, 1), pady=1 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, self.info_display_frame,
text="GE", text="GE",
width=button_width, 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( self.map_mouse_gearth_button.grid(
row=info_row, column=5, sticky=tk.E, padx=(0, 5), pady=1 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 --- # --- Row 4: Drop & Incomplete Stats ---
dropped_label_text = ttk.Label(self.info_display_frame, text="Stats Drop:") dropped_label_text = ttk.Label(self.info_display_frame, text="Stats Drop:")
dropped_label_text.grid( dropped_label_text.grid(row=info_row, column=0, sticky=tk.W, padx=5, pady=1)
row=info_row, column=0, sticky=tk.W, padx=5, pady=1
)
self.dropped_entry = ttk.Entry( self.dropped_entry = ttk.Entry(
self.info_display_frame, self.info_display_frame,
textvariable=self.dropped_stats_var, textvariable=self.dropped_stats_var,
state="readonly", state="readonly",
width=30 width=30,
) )
self.dropped_entry.grid( self.dropped_entry.grid(
row=info_row, column=1, sticky=tk.EW, padx=(0, 5), pady=1 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, self.info_display_frame,
textvariable=self.incomplete_stats_var, textvariable=self.incomplete_stats_var,
state="readonly", state="readonly",
width=15 width=15,
) )
self.incomplete_entry.grid( self.incomplete_entry.grid(
row=info_row, column=3, columnspan=3, sticky=tk.EW, padx=(0, 5), pady=1 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 --- # --- Row 5: "GE All" Button ---
self.ge_all_button = ttk.Button( self.ge_all_button = ttk.Button(
self.info_display_frame, self.info_display_frame, text="GE All", command=self.app.go_to_all_gearth
text="GE All",
command=self.app.go_to_all_gearth # Link to app callback
) )
self.ge_all_button.grid( self.ge_all_button.grid(
row=info_row, row=info_row, column=0, columnspan=6, sticky=tk.EW, padx=5, pady=(5, 5)
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
) )
# Configure column weights for Info Display frame # Configure column weights for Info Display frame
self.info_display_frame.columnconfigure(1, weight=1) # Entry column 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(3, weight=1) # Entry column
self.info_display_frame.columnconfigure(4, weight=0) # Button Go 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(5, weight=0) # Button GE
logging.debug(f"{log_prefix} Info Display frame created.") logging.debug(f"{log_prefix} Info Display frame created.")
# --- 5. Metadata Display Frame (Creation REMOVED from here) --- # --- 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 --- # --- End of init_ui ---
logging.debug(f"{log_prefix} init_ui widget creation complete.") logging.debug(f"{log_prefix} init_ui widget creation complete.")
# --- UI Update Methods --- # --- UI Update Methods ---
def set_sar_center_coords(self, latitude_str: str, longitude_str: str): def set_sar_center_coords(self, latitude_str: str, longitude_str: str):
"""Updates the SAR Center coordinates display.""" """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}" f"[UI Update] Error updating MFD color for {category_name}: {e}"
) )
# --- StatusBar Class --- # --- StatusBar Class ---
class StatusBar(ttk.Label): class StatusBar(ttk.Label):
"""Represents the status bar at the bottom of the main window.""" """Represents the status bar at the bottom of the main window."""
@ -709,10 +686,10 @@ class StatusBar(ttk.Label):
parent, parent,
text=config.INITIAL_STATUS_MESSAGE, text=config.INITIAL_STATUS_MESSAGE,
relief=tk.SUNKEN, relief=tk.SUNKEN,
anchor=tk.W, # Anchor text to the West (left) anchor=tk.W, # Anchor text to the West (left)
padding=(5, 2), # Add some internal padding padding=(5, 2), # Add some internal padding
*args, *args,
**kwargs **kwargs,
) )
# Packed by the main application layout manager # Packed by the main application layout manager
@ -726,6 +703,7 @@ class StatusBar(ttk.Label):
# Ignore errors during status update, especially during shutdown # Ignore errors during status update, especially during shutdown
pass pass
# --- Window Creation Helper --- # --- Window Creation Helper ---
def create_main_window( def create_main_window(
title: str, min_width: int, min_height: int, x_pos: int, y_pos: int 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 # Set minimum size constraint
root.minsize(min_width, min_height) root.minsize(min_width, min_height)
# Set initial position (may be overridden by OS window manager) # 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}") root.geometry(f"+{x_pos}+{y_pos}")
logging.debug( 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 return root
except Exception as e: except Exception as e:
@ -752,4 +731,5 @@ def create_main_window(
# Re-raise the exception to halt execution if window creation fails # Re-raise the exception to halt execution if window creation fails
raise raise
# --- END OF FILE ui.py --- # --- END OF FILE ui.py ---

769
utils.py

File diff suppressed because it is too large Load Diff