584 lines
23 KiB
Python
584 lines
23 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,
|
|
and helper functions for window creation. Uses standardized logging prefixes.
|
|
"""
|
|
|
|
# Standard library imports
|
|
import logging
|
|
from typing import TYPE_CHECKING, Dict, Tuple # Added Type Hinting imports
|
|
|
|
# Third-party imports
|
|
import tkinter as tk
|
|
from tkinter import ttk
|
|
from tkinter import colorchooser
|
|
|
|
# Local application imports
|
|
import config # Import config for defaults
|
|
|
|
# Type hinting for App reference
|
|
if TYPE_CHECKING:
|
|
# from app import App # Original before renaming main script
|
|
from ControlPanel import ControlPanelApp # Use the new main app class name
|
|
|
|
|
|
class ControlPanel(ttk.Frame):
|
|
"""
|
|
Represents the main control panel frame containing SAR, MFD, and Map
|
|
parameter widgets, information displays, and statistics labels.
|
|
Initializes UI elements and provides methods for updating specific labels.
|
|
"""
|
|
|
|
# def __init__(self, parent: tk.Widget, app: 'App', *args, **kwargs): # Original before renaming main script
|
|
def __init__(
|
|
self, parent: tk.Widget, app: "ControlPanelApp", *args, **kwargs
|
|
): # Use new main app class name
|
|
"""
|
|
Initializes the ControlPanel frame.
|
|
|
|
Args:
|
|
parent (tk.Widget): The parent widget (usually the main window).
|
|
app (ControlPanelApp): Reference to the main application instance for callbacks.
|
|
"""
|
|
log_prefix = "[UI Setup]"
|
|
logging.debug(f"{log_prefix} Initializing ControlPanel frame...")
|
|
super().__init__(parent, *args, **kwargs)
|
|
|
|
self.app = app # Store reference to the App instance
|
|
# Type hint for the dictionary holding MFD color labels
|
|
self.mfd_color_labels: Dict[str, tk.Label] = {}
|
|
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.
|
|
Order: SAR Params, MFD Params, Map Params, SAR Info.
|
|
Compacted MFD Params and rearranged SAR Info."""
|
|
log_prefix = "[UI Setup]"
|
|
logging.debug(f"{log_prefix} Starting init_ui widget creation...")
|
|
|
|
# Configure main frame packing
|
|
self.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=5, pady=5)
|
|
|
|
# --- 1. SAR Parameters Frame ---
|
|
logging.debug(f"{log_prefix} Creating SAR Parameters frame...")
|
|
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)
|
|
) # PACK ORDER 1
|
|
|
|
sar_row = 0 # Row counter for SAR frame grid
|
|
|
|
# --- >>> START OF MODIFIED SECTION IN SAR FRAME <<< ---
|
|
# Row 0: Test Image and Record SAR Checkboxes
|
|
# Test Image Checkbox
|
|
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, # Callback in App
|
|
)
|
|
# Place Test Image checkbox in column 0, spanning 2 columns initially
|
|
self.test_image_check.grid(
|
|
row=sar_row, column=0, columnspan=2, sticky=tk.W, padx=5, pady=2
|
|
)
|
|
|
|
# Record SAR Checkbox
|
|
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, # New callback in App/ControlPanel
|
|
)
|
|
# Place Record SAR checkbox in column 2, spanning 2 columns initially
|
|
self.record_sar_check.grid(
|
|
row=sar_row, column=2, columnspan=2, sticky=tk.W, padx=5, pady=2
|
|
)
|
|
sar_row += 1 # -> 1
|
|
# --- >>> END OF MODIFIED SECTION IN SAR FRAME <<< ---
|
|
|
|
# Row 1 (Now Row 1): Size and Palette
|
|
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: # Set initial SAR size from AppState
|
|
initial_factor = 1
|
|
if self.app.state.sar_display_width > 0:
|
|
initial_factor = config.SAR_WIDTH // self.app.state.sar_display_width
|
|
initial_size_str = f"1:{initial_factor}"
|
|
if initial_size_str not in config.SAR_SIZE_FACTORS:
|
|
initial_size_str = config.DEFAULT_SAR_SIZE
|
|
self.sar_size_combo.set(initial_size_str)
|
|
except Exception as e:
|
|
logging.warning(f"{log_prefix} Error setting initial SAR size: {e}")
|
|
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
|
|
) # Callback in App
|
|
|
|
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
|
|
) # Initial value from AppState
|
|
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
|
|
) # Callback in App
|
|
sar_row += 1 # -> 2
|
|
|
|
# Row 2 (Now Row 2): Contrast and Brightness
|
|
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, # Initial value from AppState
|
|
command=self.app.update_contrast, # Callback in App
|
|
)
|
|
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, # Initial value from AppState
|
|
command=self.app.update_brightness, # Callback in App
|
|
)
|
|
self.brightness_scale.grid(
|
|
row=sar_row, column=3, sticky=tk.EW, padx=(0, 5), pady=1
|
|
)
|
|
|
|
# Configure column weights for expansion in SAR frame
|
|
self.sar_params_frame.columnconfigure(1, weight=1)
|
|
self.sar_params_frame.columnconfigure(3, weight=1)
|
|
logging.debug(f"{log_prefix} SAR Parameters frame created and packed.")
|
|
|
|
# --- 2. MFD Parameters Frame ---
|
|
logging.debug(f"{log_prefix} Creating 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
|
|
) # PACK ORDER 2
|
|
|
|
# Define categories in the desired display order for pairing
|
|
mfd_categories_ordered = [
|
|
"Occlusion",
|
|
"Cat A",
|
|
"Cat B",
|
|
"Cat C",
|
|
"Cat C1",
|
|
"Cat C2",
|
|
"Cat C3",
|
|
]
|
|
num_categories = len(mfd_categories_ordered)
|
|
|
|
logging.debug(f"{log_prefix} Creating compacted MFD category controls...")
|
|
for index, internal_name in enumerate(mfd_categories_ordered):
|
|
current_row = index // 2 # Integer division determines the row
|
|
col_offset = 0 if (index % 2 == 0) else 4 # Column offset for right pair
|
|
|
|
# Label for category
|
|
label_text = f"{internal_name}:"
|
|
label = ttk.Label(self.mfd_params_frame, text=label_text)
|
|
label.grid(
|
|
row=current_row, column=0 + col_offset, sticky=tk.W, padx=(5, 1), pady=1
|
|
)
|
|
|
|
# Intensity Slider
|
|
intensity_var_name = (
|
|
f"mfd_{internal_name.replace(' ', '_').lower()}_intensity_var"
|
|
)
|
|
initial_intensity = config.DEFAULT_MFD_INTENSITY # Default
|
|
try: # Get initial from AppState
|
|
initial_intensity = self.app.state.mfd_params["categories"][
|
|
internal_name
|
|
]["intensity"]
|
|
except Exception as e:
|
|
logging.warning(
|
|
f"{log_prefix} Error getting MFD intensity for {internal_name}: {e}"
|
|
)
|
|
intensity_var = tk.IntVar(value=initial_intensity)
|
|
setattr(self, intensity_var_name, intensity_var) # Store variable reference
|
|
slider = ttk.Scale(
|
|
self.mfd_params_frame,
|
|
orient=tk.HORIZONTAL,
|
|
length=100,
|
|
from_=0,
|
|
to=255,
|
|
variable=intensity_var,
|
|
# Use lambda to pass current value and category name to App callback
|
|
command=lambda v, name=internal_name, var=intensity_var: self.app.update_mfd_category_intensity(
|
|
name, var.get()
|
|
),
|
|
)
|
|
slider.grid(
|
|
row=current_row, column=1 + col_offset, sticky=tk.EW, padx=1, pady=1
|
|
)
|
|
|
|
# Color Chooser Button
|
|
color_button = ttk.Button(
|
|
self.mfd_params_frame,
|
|
text="Color",
|
|
width=5,
|
|
# Use lambda to pass category name to App callback
|
|
command=lambda name=internal_name: self.app.choose_mfd_category_color(
|
|
name
|
|
),
|
|
)
|
|
color_button.grid(
|
|
row=current_row, column=2 + col_offset, sticky=tk.W, padx=1, pady=1
|
|
)
|
|
|
|
# Color Preview Label
|
|
color_label = tk.Label(
|
|
self.mfd_params_frame, text="", width=3, relief=tk.SUNKEN, borderwidth=1
|
|
)
|
|
try: # Set initial color from AppState
|
|
initial_bgr = self.app.state.mfd_params["categories"][internal_name][
|
|
"color"
|
|
]
|
|
initial_hex = (
|
|
f"#{initial_bgr[2]:02x}{initial_bgr[1]:02x}{initial_bgr[0]:02x}"
|
|
)
|
|
color_label.config(background=initial_hex)
|
|
except Exception as e:
|
|
logging.warning(
|
|
f"{log_prefix} Error setting MFD color for {internal_name}: {e}"
|
|
)
|
|
color_label.config(background="grey") # Fallback color
|
|
color_label.grid(
|
|
row=current_row, column=3 + col_offset, sticky=tk.W, padx=(1, 5), pady=1
|
|
)
|
|
self.mfd_color_labels[internal_name] = color_label # Store label reference
|
|
|
|
# Place Raw Map slider
|
|
last_cat_row = (num_categories - 1) // 2
|
|
raw_map_start_col = 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_start_col,
|
|
sticky=tk.W,
|
|
padx=(5, 1),
|
|
pady=1,
|
|
)
|
|
initial_raw_intensity = config.DEFAULT_MFD_RAW_MAP_INTENSITY # Default
|
|
try:
|
|
# Get initial from AppState
|
|
initial_raw_intensity = self.app.state.mfd_params["raw_map_intensity"]
|
|
except Exception as e:
|
|
logging.warning(f"{log_prefix} Error getting raw map intensity: {e}")
|
|
self.mfd_raw_map_intensity_var = tk.IntVar(value=initial_raw_intensity)
|
|
raw_map_slider = ttk.Scale(
|
|
self.mfd_params_frame,
|
|
orient=tk.HORIZONTAL,
|
|
length=100,
|
|
from_=0,
|
|
to=255,
|
|
variable=self.mfd_raw_map_intensity_var,
|
|
# Use lambda to pass current value to App callback
|
|
command=lambda v: self.app.update_mfd_raw_map_intensity(
|
|
self.mfd_raw_map_intensity_var.get()
|
|
),
|
|
)
|
|
# Span the slider across remaining columns in its pair
|
|
raw_map_slider.grid(
|
|
row=raw_map_row,
|
|
column=1 + raw_map_start_col,
|
|
columnspan=3,
|
|
sticky=tk.EW,
|
|
padx=(1, 5),
|
|
pady=1,
|
|
)
|
|
|
|
# Configure column weights for sliders in MFD frame
|
|
self.mfd_params_frame.columnconfigure(1, weight=1)
|
|
self.mfd_params_frame.columnconfigure(
|
|
5, weight=1
|
|
) # Corresponds to the second slider column
|
|
logging.debug(f"{log_prefix} MFD Parameters frame created and packed.")
|
|
|
|
# --- 3. Map Parameters Frame ---
|
|
logging.debug(f"{log_prefix} Creating 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
|
|
) # PACK ORDER 3
|
|
|
|
map_row = 0 # Row counter for Map frame grid
|
|
|
|
# --- ROW 0: Map Display Size ---
|
|
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) # Set initial value
|
|
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)
|
|
map_row += 1 # -> 1
|
|
|
|
# --- ROW 1: SAR Overlay Checkbox ---
|
|
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, # Callback in App/ControlPanel
|
|
)
|
|
self.sar_overlay_check.grid(
|
|
row=map_row, column=0, columnspan=2, sticky=tk.W, padx=5, pady=2
|
|
)
|
|
map_row += 1 # -> 2
|
|
|
|
# --- ROW 2: 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.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, # Min alpha
|
|
to=1.0, # Max alpha
|
|
variable=self.sar_overlay_alpha_var,
|
|
command=self.app.update_sar_overlay_alpha, # Callback in App/ControlPanel
|
|
)
|
|
self.alpha_scale.grid(
|
|
row=map_row, column=1, columnspan=3, sticky=tk.EW, padx=(0, 5), pady=1
|
|
)
|
|
map_row += 1 # -> 3
|
|
|
|
# Configure column weights for expansion
|
|
self.map_params_frame.columnconfigure(1, weight=1)
|
|
self.map_params_frame.columnconfigure(2, weight=1)
|
|
self.map_params_frame.columnconfigure(3, weight=1)
|
|
logging.debug(f"{log_prefix} Map Parameters frame created and packed.")
|
|
|
|
# --- 4. SAR Info Frame ---
|
|
logging.debug(f"{log_prefix} Creating SAR Info frame...")
|
|
self.sar_info_frame = ttk.Labelframe(self, text="SAR Info", padding=5)
|
|
self.sar_info_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=2) # PACK ORDER 4
|
|
|
|
info_row = 0
|
|
# Row 0: Image Ref
|
|
self.sar_center_label = ttk.Label(
|
|
self.sar_info_frame, text="Image Ref: Lat=N/A, Lon=N/A"
|
|
)
|
|
self.sar_center_label.grid(
|
|
row=info_row, column=0, columnspan=4, sticky=tk.W, padx=5, pady=1
|
|
)
|
|
info_row += 1
|
|
|
|
# Row 1: Image Orient & Image Size
|
|
self.sar_orientation_label = ttk.Label(
|
|
self.sar_info_frame, text="Image Orient: N/A"
|
|
)
|
|
self.sar_orientation_label.grid(
|
|
row=info_row, column=0, columnspan=2, sticky=tk.W, padx=5, pady=1
|
|
)
|
|
self.sar_size_label = ttk.Label(self.sar_info_frame, text="Image Size: N/A")
|
|
self.sar_size_label.grid(
|
|
row=info_row, column=2, columnspan=2, sticky=tk.W, padx=5, pady=1
|
|
)
|
|
info_row += 1
|
|
|
|
# Row 2: Mouse Coordinates
|
|
self.mouse_latlon_label = ttk.Label(
|
|
self.sar_info_frame, text="Mouse : Lat=N/A, Lon=N/A"
|
|
)
|
|
self.mouse_latlon_label.grid(
|
|
row=info_row, column=0, columnspan=4, sticky=tk.W, padx=5, pady=1
|
|
)
|
|
info_row += 1
|
|
|
|
# Row 3: Drop & Incomplete Stats
|
|
self.dropped_label = ttk.Label(
|
|
self.sar_info_frame, text="Drop (Q): S=0, M=0, Tk=0, Mo=0"
|
|
)
|
|
self.dropped_label.grid(
|
|
row=info_row, column=0, columnspan=2, sticky=tk.W, padx=5, pady=1
|
|
)
|
|
self.incomplete_label = ttk.Label(
|
|
self.sar_info_frame, text="Incmpl (RX): S=0, M=0"
|
|
)
|
|
self.incomplete_label.grid(
|
|
row=info_row, column=2, columnspan=2, sticky=tk.W, padx=5, pady=1
|
|
)
|
|
|
|
# Configure column weights for SAR Info frame for better spacing
|
|
self.sar_info_frame.columnconfigure(
|
|
1, weight=1
|
|
) # Space between cols 0/1 and 2/3
|
|
self.sar_info_frame.columnconfigure(3, weight=1) # Space after col 2/3
|
|
logging.debug(f"{log_prefix} SAR Info frame created and packed.")
|
|
|
|
# --- End of init_ui ---
|
|
logging.debug(f"{log_prefix} init_ui widget creation complete.")
|
|
|
|
# --- Methods to update UI labels ---
|
|
def set_mouse_coordinates(self, latitude_str: str, longitude_str: str):
|
|
"""Updates the mouse coordinates label with pre-formatted DMS strings."""
|
|
log_prefix = "[UI Update]"
|
|
text_to_display = f"Mouse : Lat={latitude_str}, Lon={longitude_str}"
|
|
try:
|
|
# Check attribute exists and widget exists
|
|
label = getattr(self, "mouse_latlon_label", None)
|
|
if label and label.winfo_exists():
|
|
label.config(text=text_to_display)
|
|
except Exception as e:
|
|
logging.warning(f"{log_prefix} Error updating mouse coords UI: {e}")
|
|
|
|
def set_sar_orientation(self, orientation_deg_str: str):
|
|
"""Updates the SAR orientation label."""
|
|
log_prefix = "[UI Update]"
|
|
text_to_display = f"Image Orient: {orientation_deg_str}"
|
|
try:
|
|
label = getattr(self, "sar_orientation_label", None)
|
|
if label and label.winfo_exists():
|
|
label.config(text=text_to_display)
|
|
except Exception as e:
|
|
logging.warning(f"{log_prefix} Error updating SAR orientation UI: {e}")
|
|
|
|
def set_sar_size_km(self, size_text: str):
|
|
"""Updates the SAR image size label."""
|
|
log_prefix = "[UI Update]"
|
|
text_to_display = f"Image Size: {size_text}"
|
|
try:
|
|
label = getattr(self, "sar_size_label", None)
|
|
if label and label.winfo_exists():
|
|
label.config(text=text_to_display)
|
|
except Exception as e:
|
|
logging.warning(f"{log_prefix} Error updating SAR size UI: {e}")
|
|
|
|
# Type hint the color tuple argument
|
|
def update_mfd_color_display(
|
|
self, category_name: str, color_bgr_tuple: Tuple[int, int, int]
|
|
):
|
|
"""Updates the background color of the specified MFD category's display label."""
|
|
log_prefix = "[UI Update]"
|
|
if category_name in self.mfd_color_labels:
|
|
target_label = self.mfd_color_labels[category_name]
|
|
try:
|
|
if target_label.winfo_exists():
|
|
# Format BGR tuple to HEX color string
|
|
hex_color = (
|
|
f"#{int(color_bgr_tuple[2]):02x}"
|
|
f"{int(color_bgr_tuple[1]):02x}"
|
|
f"{int(color_bgr_tuple[0]):02x}"
|
|
)
|
|
target_label.config(background=hex_color)
|
|
except Exception as e:
|
|
logging.error(
|
|
f"{log_prefix} Error updating color display for {category_name}: {e}",
|
|
exc_info=True,
|
|
)
|
|
else:
|
|
logging.warning(
|
|
f"{log_prefix} Unknown category key for MFD color display: '{category_name}'"
|
|
)
|
|
|
|
|
|
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)
|
|
logging.debug(f"{log_prefix} StatusBar initialization complete.")
|
|
|
|
def set_status_text(self, text: str):
|
|
"""Sets the text displayed in the status bar."""
|
|
log_prefix = "[UI Status]"
|
|
try:
|
|
# Update config only if widget still exists
|
|
if self.winfo_exists():
|
|
self.config(text=text)
|
|
except Exception as e:
|
|
logging.warning(f"{log_prefix} Error setting status bar text: {e}")
|
|
|
|
|
|
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]"
|
|
logging.debug(f"{log_prefix} Creating main Tkinter root window...")
|
|
try:
|
|
root = tk.Tk()
|
|
root.title(title)
|
|
root.minsize(min_width, min_height)
|
|
# Set initial position using geometry string
|
|
root.geometry(f"+{x_pos}+{y_pos}")
|
|
logging.debug(
|
|
f"{log_prefix} Main Tkinter root window created and positioned "
|
|
f"at ({x_pos},{y_pos})."
|
|
)
|
|
return root
|
|
except Exception as e:
|
|
logging.critical(
|
|
f"{log_prefix} Failed to create main Tkinter window: {e}", exc_info=True
|
|
)
|
|
raise # Re-raise the exception after logging
|
|
|
|
|
|
# --- END OF FILE ui.py ---
|