531 lines
26 KiB
Python
531 lines
26 KiB
Python
# FlightMonitor/gui/panels/function_notebook_panel.py
|
|
"""
|
|
Panel managing the function-related notebooks (Live, History)
|
|
and their associated controls like BBox input and monitoring buttons.
|
|
"""
|
|
import tkinter as tk
|
|
from tkinter import ttk
|
|
from typing import Dict, Any, Optional, Callable
|
|
|
|
from ...utils.logger import get_logger
|
|
from ...data import config as app_config
|
|
from ...map.map_utils import _is_valid_bbox_dict # For BBox validation
|
|
|
|
|
|
module_logger = get_logger(__name__)
|
|
|
|
# Constants for default values
|
|
DEFAULT_TRACK_LENGTH = getattr(app_config, "DEFAULT_TRACK_HISTORY_POINTS", 20)
|
|
|
|
|
|
class FunctionNotebookPanel:
|
|
"""
|
|
Manages the function-selection notebook and its associated controls
|
|
(Live/History mode, Bounding Box input, Start/Stop buttons, Track options).
|
|
"""
|
|
|
|
def __init__(self, parent_frame: ttk.Frame, controller: Any):
|
|
"""
|
|
Initializes the FunctionNotebookPanel.
|
|
|
|
Args:
|
|
parent_frame: The parent ttk.Frame where this notebook will be placed.
|
|
controller: The application controller instance to delegate actions and
|
|
receive status updates from (e.g., for start/stop monitoring).
|
|
"""
|
|
self.parent_frame = parent_frame
|
|
self.controller = controller
|
|
|
|
# Initialize Tkinter variables for controls
|
|
self.mode_var = tk.StringVar(value="Live")
|
|
self.lat_min_var = tk.StringVar(value=str(app_config.DEFAULT_BBOX_LAT_MIN))
|
|
self.lon_min_var = tk.StringVar(value=str(app_config.DEFAULT_BBOX_LON_MIN))
|
|
self.lat_max_var = tk.StringVar(value=str(app_config.DEFAULT_BBOX_LAT_MAX))
|
|
self.lon_max_var = tk.StringVar(value=str(app_config.DEFAULT_BBOX_LON_MAX))
|
|
self.track_length_var = tk.IntVar(value=DEFAULT_TRACK_LENGTH)
|
|
|
|
self._build_ui() # Call a helper method to build the UI elements
|
|
module_logger.debug("FunctionNotebookPanel initialized.")
|
|
|
|
def _build_ui(self):
|
|
"""
|
|
Builds the main notebook structure and all its tabs and controls.
|
|
"""
|
|
self.function_notebook = ttk.Notebook(self.parent_frame)
|
|
self.function_notebook.pack(fill=tk.BOTH, expand=True, padx=2, pady=2)
|
|
|
|
# --- Live: Area Monitor Tab ---
|
|
self.live_bbox_tab_frame = ttk.Frame(self.function_notebook, padding=5)
|
|
self.function_notebook.add(self.live_bbox_tab_frame, text="Live: Area Monitor")
|
|
|
|
self.live_controls_options_frame = ttk.Frame(self.live_bbox_tab_frame)
|
|
self.live_controls_options_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 5))
|
|
|
|
self.controls_frame_live_area = ttk.Frame(self.live_controls_options_frame)
|
|
self.controls_frame_live_area.pack(side=tk.TOP, fill=tk.X)
|
|
|
|
# Control Frame (Mode Radiobuttons, Start/Stop Buttons)
|
|
self.control_frame = ttk.LabelFrame(
|
|
self.controls_frame_live_area, text="Controls", padding=(10, 5)
|
|
)
|
|
self.control_frame.pack(side=tk.TOP, fill=tk.X)
|
|
|
|
self.live_radio = ttk.Radiobutton(
|
|
self.control_frame,
|
|
text="Live",
|
|
variable=self.mode_var,
|
|
value="Live",
|
|
command=self._on_mode_change,
|
|
)
|
|
self.live_radio.pack(side=tk.LEFT, padx=(0, 5))
|
|
|
|
self.history_radio = ttk.Radiobutton(
|
|
self.control_frame,
|
|
text="History",
|
|
variable=self.mode_var,
|
|
value="History",
|
|
command=self._on_mode_change,
|
|
)
|
|
self.history_radio.pack(side=tk.LEFT, padx=5)
|
|
|
|
self.start_button = ttk.Button(
|
|
self.control_frame, text="Start Monitoring", command=self._on_start_monitoring
|
|
)
|
|
self.start_button.pack(side=tk.LEFT, padx=5)
|
|
|
|
self.stop_button = ttk.Button(
|
|
self.control_frame,
|
|
text="Stop Monitoring",
|
|
command=self._on_stop_monitoring,
|
|
state=tk.DISABLED,
|
|
)
|
|
self.stop_button.pack(side=tk.LEFT, padx=5)
|
|
|
|
# Bounding Box Input Frame
|
|
self.bbox_frame = ttk.LabelFrame(
|
|
self.controls_frame_live_area,
|
|
text="Geographic Area (Bounding Box)",
|
|
padding=(10, 5),
|
|
)
|
|
self.bbox_frame.pack(side=tk.TOP, fill=tk.X, pady=5)
|
|
self.bbox_frame.columnconfigure(1, weight=1)
|
|
self.bbox_frame.columnconfigure(3, weight=1)
|
|
|
|
ttk.Label(self.bbox_frame, text="Lat Min:").grid(row=0, column=0, padx=(0, 2), pady=2, sticky=tk.W)
|
|
self.lat_min_entry = ttk.Entry(self.bbox_frame, textvariable=self.lat_min_var)
|
|
self.lat_min_entry.grid(row=0, column=1, padx=(0, 5), pady=2, sticky=tk.EW)
|
|
|
|
ttk.Label(self.bbox_frame, text="Lon Min:").grid(row=0, column=2, padx=(5, 2), pady=2, sticky=tk.W)
|
|
self.lon_min_entry = ttk.Entry(self.bbox_frame, textvariable=self.lon_min_var)
|
|
self.lon_min_entry.grid(row=0, column=3, padx=(0, 0), pady=2, sticky=tk.EW)
|
|
|
|
ttk.Label(self.bbox_frame, text="Lat Max:").grid(row=1, column=0, padx=(0, 2), pady=2, sticky=tk.W)
|
|
self.lat_max_entry = ttk.Entry(self.bbox_frame, textvariable=self.lat_max_var)
|
|
self.lat_max_entry.grid(row=1, column=1, padx=(0, 5), pady=2, sticky=tk.EW)
|
|
|
|
ttk.Label(self.bbox_frame, text="Lon Max:").grid(row=1, column=2, padx=(5, 2), pady=2, sticky=tk.W)
|
|
self.lon_max_entry = ttk.Entry(self.bbox_frame, textvariable=self.lon_max_var)
|
|
self.lon_max_entry.grid(row=1, column=3, padx=(0, 0), pady=2, sticky=tk.EW)
|
|
|
|
# Track Options Frame
|
|
self.track_options_frame = ttk.LabelFrame(
|
|
self.live_controls_options_frame, text="Track Options", padding=(10, 5)
|
|
)
|
|
self.track_options_frame.pack(side=tk.TOP, fill=tk.X, pady=(10, 0))
|
|
|
|
ttk.Label(self.track_options_frame, text="Track Length (points):").pack(side=tk.LEFT, padx=(0, 5))
|
|
self.track_length_spinbox = ttk.Spinbox(
|
|
self.track_options_frame,
|
|
from_=2,
|
|
to=100,
|
|
textvariable=self.track_length_var,
|
|
width=5,
|
|
command=self._on_track_length_change,
|
|
state="readonly",
|
|
)
|
|
self.track_length_spinbox.pack(side=tk.LEFT, padx=5)
|
|
|
|
# --- Live: Airport Tab (Placeholder) ---
|
|
self.live_airport_tab_frame = ttk.Frame(self.function_notebook, padding=5)
|
|
self.function_notebook.add(self.live_airport_tab_frame, text="Live: Airport")
|
|
ttk.Label(
|
|
self.live_airport_tab_frame,
|
|
text="Live from Airport - Coming Soon",
|
|
font=("Arial", 10),
|
|
).pack(expand=True)
|
|
|
|
# --- History Tab (Placeholder) ---
|
|
self.history_tab_frame = ttk.Frame(self.function_notebook, padding=5)
|
|
self.function_notebook.add(self.history_tab_frame, text="History")
|
|
ttk.Label(
|
|
self.history_tab_frame,
|
|
text="History Analysis - Coming Soon",
|
|
font=("Arial", 10),
|
|
).pack(expand=True)
|
|
|
|
# Bind event for tab changes
|
|
self.function_notebook.bind("<<NotebookTabChanged>>", self._on_function_tab_change)
|
|
|
|
def _on_mode_change(self):
|
|
"""
|
|
Handles changes in the selected mode (Live/History).
|
|
Updates tab states and internal control states.
|
|
Delegates mode change notification to the controller.
|
|
"""
|
|
selected_mode = self.mode_var.get()
|
|
module_logger.info(f"FunctionNotebookPanel: Mode changed to {selected_mode}")
|
|
|
|
try:
|
|
tab_indices = {}
|
|
for i in range(self.function_notebook.index("end")):
|
|
tab_text = self.function_notebook.tab(i, "text")
|
|
tab_indices[tab_text] = i
|
|
|
|
live_bbox_idx = tab_indices.get("Live: Area Monitor", -1)
|
|
history_idx = tab_indices.get("History", -1)
|
|
live_airport_idx = tab_indices.get("Live: Airport", -1)
|
|
|
|
if selected_mode == "Live":
|
|
if live_bbox_idx != -1:
|
|
self.function_notebook.tab(live_bbox_idx, state="normal")
|
|
if live_airport_idx != -1:
|
|
self.function_notebook.tab(live_airport_idx, state="normal")
|
|
if history_idx != -1:
|
|
self.function_notebook.tab(history_idx, state="disabled")
|
|
# Switch to the first live tab if history was selected
|
|
if self.function_notebook.index("current") == history_idx and live_bbox_idx != -1:
|
|
self.function_notebook.select(live_bbox_idx)
|
|
elif selected_mode == "History":
|
|
if live_bbox_idx != -1:
|
|
self.function_notebook.tab(live_bbox_idx, state="disabled")
|
|
if live_airport_idx != -1:
|
|
self.function_notebook.tab(live_airport_idx, state="disabled")
|
|
if history_idx != -1:
|
|
self.function_notebook.tab(history_idx, state="normal")
|
|
# Switch to history tab if not already selected
|
|
if self.function_notebook.index("current") != history_idx and history_idx != -1:
|
|
self.function_notebook.select(history_idx)
|
|
except tk.TclError as e:
|
|
module_logger.warning(f"FunctionNotebookPanel: TclError updating notebook tabs on mode change: {e}", exc_info=False)
|
|
except Exception as e:
|
|
module_logger.error(f"FunctionNotebookPanel: Error updating function notebook tabs: {e}", exc_info=True)
|
|
|
|
# Notify controller about mode change, which implies clearing views
|
|
if self.controller and hasattr(self.controller, "main_window") and self.controller.main_window:
|
|
main_window = self.controller.main_window
|
|
if hasattr(main_window, "clear_all_views_data"):
|
|
main_window.clear_all_views_data()
|
|
if hasattr(main_window, "_update_controls_state_based_on_mode_and_tab"):
|
|
main_window._update_controls_state_based_on_mode_and_tab()
|
|
|
|
main_window.update_semaphore_and_status(tk.NORMAL, f"Mode: {selected_mode}. Ready.")
|
|
|
|
def _on_function_tab_change(self, event: Optional[tk.Event] = None):
|
|
"""
|
|
Handles changes in the selected function tab (e.g., Live: Area Monitor, History).
|
|
Updates placeholder text on the map view and control states.
|
|
"""
|
|
try:
|
|
tab_text = self.function_notebook.tab(self.function_notebook.index("current"), "text")
|
|
module_logger.info(f"FunctionNotebookPanel: Switched function tab to: {tab_text}")
|
|
|
|
placeholder_text_map = "Map Area." # Default
|
|
if "Live: Area Monitor" in tab_text:
|
|
placeholder_text_map = "Map - Live Area. Define area and press Start."
|
|
elif "Live: Airport" in tab_text:
|
|
placeholder_text_map = "Map - Live Airport. (Coming Soon)"
|
|
elif "History" in tab_text:
|
|
placeholder_text_map = "Map - History Analysis. (Coming Soon)"
|
|
|
|
# Delegate map placeholder update and control state update to MainWindow
|
|
if self.controller and hasattr(self.controller, "main_window") and self.controller.main_window:
|
|
main_window = self.controller.main_window
|
|
if hasattr(main_window, "_update_map_placeholder"):
|
|
main_window._update_map_placeholder(placeholder_text_map)
|
|
if hasattr(main_window, "_update_controls_state_based_on_mode_and_tab"):
|
|
main_window._update_controls_state_based_on_mode_and_tab()
|
|
|
|
except tk.TclError as e:
|
|
module_logger.warning(f"FunctionNotebookPanel: TclError on function tab change: {e}", exc_info=False)
|
|
except Exception as e:
|
|
module_logger.error(f"FunctionNotebookPanel: Error processing function tab change: {e}", exc_info=True)
|
|
|
|
def _on_start_monitoring(self):
|
|
"""
|
|
Callback for the 'Start Monitoring' button.
|
|
Retrieves current settings and delegates to the controller to start monitoring.
|
|
Updates internal button states.
|
|
"""
|
|
selected_mode = self.mode_var.get()
|
|
active_func_tab_text = self.function_notebook.tab(self.function_notebook.index("current"), "text")
|
|
|
|
# Disable start button, enable stop button, disable mode radios
|
|
self.start_button.config(state=tk.DISABLED)
|
|
self.stop_button.config(state=tk.NORMAL)
|
|
self.live_radio.config(state=tk.DISABLED)
|
|
self.history_radio.config(state=tk.DISABLED)
|
|
|
|
# This will also disable BBox/Track entries if monitoring is active
|
|
self._update_internal_controls_state()
|
|
|
|
if not self.controller:
|
|
module_logger.error("FunctionNotebookPanel: Controller unavailable for start monitoring.")
|
|
self._show_error_message("Internal Error", "Application controller missing.")
|
|
self._on_monitoring_failure_reset() # Reset GUI state on internal error
|
|
return
|
|
|
|
if selected_mode == "Live" and "Live: Area Monitor" in active_func_tab_text:
|
|
bbox = self.get_bounding_box_input()
|
|
if bbox:
|
|
module_logger.info(f"FunctionNotebookPanel: Starting Live Area monitoring with BBox: {bbox}")
|
|
self.controller.start_live_monitoring(bbox)
|
|
if self.controller and hasattr(self.controller, "main_window") and self.controller.main_window:
|
|
self.controller.main_window.update_semaphore_and_status(
|
|
"FETCHING", "Live monitoring starting..."
|
|
)
|
|
else:
|
|
self._show_error_message("Input Error", "Bounding Box values are invalid or incomplete.")
|
|
self._on_monitoring_failure_reset()
|
|
elif selected_mode == "History" and "History" in active_func_tab_text:
|
|
module_logger.info("FunctionNotebookPanel: Starting History monitoring.")
|
|
self.controller.start_history_monitoring() # Placeholder
|
|
if self.controller and hasattr(self.controller, "main_window") and self.controller.main_window:
|
|
self.controller.main_window.update_semaphore_and_status(
|
|
"OK", "History mode (placeholder)."
|
|
)
|
|
else:
|
|
err_msg = f"Start monitoring not supported on tab '{active_func_tab_text}' for mode '{selected_mode}'."
|
|
module_logger.warning(f"FunctionNotebookPanel: {err_msg}")
|
|
self._show_error_message("Operation Not Supported", err_msg)
|
|
self._on_monitoring_failure_reset()
|
|
|
|
def _on_stop_monitoring(self):
|
|
"""
|
|
Callback for the 'Stop Monitoring' button.
|
|
Delegates to the controller to stop the currently active monitoring.
|
|
"""
|
|
selected_mode = self.mode_var.get()
|
|
module_logger.info(f"FunctionNotebookPanel: Stop monitoring requested for mode: {selected_mode}")
|
|
|
|
if not self.controller:
|
|
module_logger.error("FunctionNotebookPanel: Controller unavailable for stop monitoring.")
|
|
self._show_error_message("Internal Error", "Application controller missing.")
|
|
self._on_monitoring_failure_reset() # Reset GUI state on internal error
|
|
return
|
|
|
|
if selected_mode == "Live":
|
|
self.controller.stop_live_monitoring()
|
|
elif selected_mode == "History":
|
|
self.controller.stop_history_monitoring() # Placeholder
|
|
|
|
# The GUI state will be reset by `MainWindow._reset_gui_to_stopped_state`
|
|
# which is typically called by the controller after the monitoring thread fully stops.
|
|
# However, we can set an immediate visual feedback here if desired.
|
|
if self.controller and hasattr(self.controller, "main_window") and self.controller.main_window:
|
|
self.controller.main_window.update_semaphore_and_status(
|
|
"FETCHING", f"{selected_mode} monitoring stopping..."
|
|
)
|
|
|
|
def _on_monitoring_failure_reset(self):
|
|
"""
|
|
Resets the GUI state of the panel after an internal failure to start monitoring.
|
|
This is separate from `set_monitoring_active_state` as it's an internal reset due to error.
|
|
"""
|
|
self.start_button.config(state=tk.NORMAL)
|
|
self.stop_button.config(state=tk.DISABLED)
|
|
self.live_radio.config(state=tk.NORMAL)
|
|
self.history_radio.config(state=tk.NORMAL)
|
|
self._update_internal_controls_state() # Re-enable bbox/track based on mode
|
|
|
|
def _on_track_length_change(self):
|
|
"""
|
|
Callback for the track length spinbox.
|
|
Delegates the new track length to the application controller.
|
|
"""
|
|
if self.controller and hasattr(self.controller, "set_map_track_length"):
|
|
try:
|
|
new_length = self.track_length_var.get()
|
|
if isinstance(new_length, int) and new_length >= 2: # Basic validation
|
|
self.controller.set_map_track_length(new_length)
|
|
module_logger.info(f"FunctionNotebookPanel: Track length set to {new_length}")
|
|
else:
|
|
module_logger.warning(f"FunctionNotebookPanel: Invalid track length from spinbox: {new_length}")
|
|
except tk.TclError: # Spinbox might not be fully ready or being destroyed
|
|
module_logger.warning("FunctionNotebookPanel: TclError on track length change, spinbox might not be ready.", exc_info=False)
|
|
except Exception as e:
|
|
module_logger.error(f"FunctionNotebookPanel: Error processing track length change: {e}", exc_info=True)
|
|
else:
|
|
module_logger.warning("FunctionNotebookPanel: Controller or set_map_track_length method not available.")
|
|
|
|
def _update_internal_controls_state(self):
|
|
"""
|
|
Updates the state of internal controls (BBox entries, track spinbox)
|
|
based on current mode and monitoring status.
|
|
This is a common method called by _on_mode_change and start/stop handlers.
|
|
"""
|
|
is_live_mode = self.mode_var.get() == "Live"
|
|
is_monitoring_active = self.stop_button.cget("state") == tk.NORMAL # If stop button is enabled, monitoring is active
|
|
|
|
active_func_tab_text = self.function_notebook.tab(self.function_notebook.index("current"), "text")
|
|
|
|
# Enable BBox entries only if in Live Area mode AND not currently monitoring
|
|
enable_bbox_entries = (
|
|
is_live_mode
|
|
and "Live: Area Monitor" in active_func_tab_text
|
|
and not is_monitoring_active
|
|
)
|
|
self._set_bbox_entries_state(tk.NORMAL if enable_bbox_entries else tk.DISABLED)
|
|
|
|
# Enable Track Length spinbox if in Live Area mode AND not currently monitoring
|
|
# (or depending on desired behavior, could allow changes during monitoring for next refresh)
|
|
enable_track_spinbox = (
|
|
is_live_mode
|
|
and "Live: Area Monitor" in active_func_tab_text
|
|
and not is_monitoring_active # Current choice: disable if monitoring active
|
|
)
|
|
if self.track_length_spinbox.winfo_exists():
|
|
try:
|
|
new_state = "readonly" if enable_track_spinbox else tk.DISABLED
|
|
self.track_length_spinbox.config(state=new_state)
|
|
except tk.TclError:
|
|
module_logger.warning("FunctionNotebookPanel: TclError updating track length spinbox state.", exc_info=False)
|
|
|
|
def _set_bbox_entries_state(self, state: str):
|
|
"""
|
|
Sets the state (e.g., tk.NORMAL, tk.DISABLED) of the BBox input entries.
|
|
"""
|
|
for entry_widget in [self.lat_min_entry, self.lon_min_entry, self.lat_max_entry, self.lon_max_entry]:
|
|
if entry_widget.winfo_exists():
|
|
try:
|
|
entry_widget.config(state=state)
|
|
except tk.TclError:
|
|
module_logger.debug(f"FunctionNotebookPanel: TclError setting BBox entry state for {entry_widget}, widget likely destroyed.", exc_info=False)
|
|
|
|
def get_selected_mode(self) -> str:
|
|
"""
|
|
Returns the currently selected monitoring mode ('Live' or 'History').
|
|
"""
|
|
return self.mode_var.get()
|
|
|
|
def get_active_function_tab_text(self) -> str:
|
|
"""
|
|
Returns the text of the currently selected function notebook tab.
|
|
"""
|
|
try:
|
|
return self.function_notebook.tab(self.function_notebook.index("current"), "text")
|
|
except tk.TclError:
|
|
module_logger.warning("FunctionNotebookPanel: TclError getting active function tab text.", exc_info=False)
|
|
return ""
|
|
|
|
def get_bounding_box_input(self) -> Optional[Dict[str, float]]:
|
|
"""
|
|
Retrieves and validates the bounding box coordinates from the GUI input fields.
|
|
Returns a dictionary {lat_min, lon_min, lat_max, lon_max} or None if invalid.
|
|
"""
|
|
try:
|
|
str_values = [
|
|
self.lat_min_var.get(),
|
|
self.lon_min_var.get(),
|
|
self.lat_max_var.get(),
|
|
self.lon_max_var.get(),
|
|
]
|
|
|
|
if not all(s.strip() for s in str_values): # Check for empty or whitespace-only strings
|
|
module_logger.debug("FunctionNotebookPanel: One or more BBox GUI fields are empty.")
|
|
return None
|
|
|
|
# Convert to float and create dictionary
|
|
bbox_candidate = {
|
|
"lat_min": float(str_values[0]),
|
|
"lon_min": float(str_values[1]),
|
|
"lat_max": float(str_values[2]),
|
|
"lon_max": float(str_values[3]),
|
|
}
|
|
|
|
# Validate using the utility function
|
|
if _is_valid_bbox_dict(bbox_candidate):
|
|
return bbox_candidate
|
|
else:
|
|
module_logger.warning(f"FunctionNotebookPanel: BBox from GUI failed validation: {bbox_candidate}")
|
|
return None
|
|
except ValueError:
|
|
module_logger.warning("FunctionNotebookPanel: Invalid numeric format in BBox GUI fields.", exc_info=False)
|
|
return None # ValueError if float conversion fails
|
|
except Exception as e:
|
|
module_logger.error(f"FunctionNotebookPanel: Unexpected error getting BBox from GUI: {e}", exc_info=True)
|
|
return None
|
|
|
|
def get_track_length_input(self) -> int:
|
|
"""
|
|
Returns the configured track length (number of points) from the spinbox.
|
|
"""
|
|
return self.track_length_var.get()
|
|
|
|
def update_bbox_gui_fields(self, bbox_dict: Dict[str, float]):
|
|
"""
|
|
Updates the bounding box input fields in the GUI with provided values.
|
|
"""
|
|
if bbox_dict and _is_valid_bbox_dict(bbox_dict):
|
|
decimals = getattr(app_config, "COORDINATE_DECIMAL_PLACES", 5) # Use a config for decimals
|
|
try:
|
|
self.lat_min_var.set(f"{bbox_dict['lat_min']:.{decimals}f}")
|
|
self.lon_min_var.set(f"{bbox_dict['lon_min']:.{decimals}f}")
|
|
self.lat_max_var.set(f"{bbox_dict['lat_max']:.{decimals}f}")
|
|
self.lon_max_var.set(f"{bbox_dict['lon_max']:.{decimals}f}")
|
|
except tk.TclError: # Can happen if widget is destroyed
|
|
module_logger.debug("FunctionNotebookPanel: TclError setting BBox GUI fields, widgets likely destroyed.", exc_info=False)
|
|
except Exception as e:
|
|
module_logger.error(f"FunctionNotebookPanel: Error setting BBox GUI fields: {e}", exc_info=False)
|
|
else: # Clear fields if bbox_dict is invalid or None
|
|
try:
|
|
self.lat_min_var.set("")
|
|
self.lon_min_var.set("")
|
|
self.lat_max_var.set("")
|
|
self.lon_max_var.set("")
|
|
except tk.TclError:
|
|
module_logger.debug("FunctionNotebookPanel: TclError clearing BBox GUI fields, widgets likely destroyed.", exc_info=False)
|
|
|
|
def set_monitoring_button_states(self, is_monitoring_active: bool):
|
|
"""
|
|
Sets the state of the Start/Stop buttons and mode radio buttons.
|
|
|
|
Args:
|
|
is_monitoring_active: True if monitoring is active, False otherwise.
|
|
"""
|
|
if is_monitoring_active:
|
|
self.start_button.config(state=tk.DISABLED)
|
|
self.stop_button.config(state=tk.NORMAL)
|
|
self.live_radio.config(state=tk.DISABLED)
|
|
self.history_radio.config(state=tk.DISABLED)
|
|
else:
|
|
self.start_button.config(state=tk.NORMAL)
|
|
self.stop_button.config(state=tk.DISABLED)
|
|
self.live_radio.config(state=tk.NORMAL)
|
|
self.history_radio.config(state=tk.NORMAL)
|
|
self._update_internal_controls_state() # Re-evaluate state of BBox/Track inputs
|
|
|
|
def _show_error_message(self, title: str, message: str):
|
|
"""
|
|
Helper method to show an error message, delegating to MainWindow via controller.
|
|
"""
|
|
if self.controller and hasattr(self.controller, "main_window") and self.controller.main_window:
|
|
main_window = self.controller.main_window
|
|
if hasattr(main_window, "show_error_message"):
|
|
main_window.show_error_message(title, message)
|
|
else:
|
|
module_logger.error(f"MainWindow.show_error_message not found. Error: {title} - {message}")
|
|
else:
|
|
module_logger.error(f"Controller or MainWindow not available to show error: {title} - {message}")
|
|
|
|
def _show_info_message(self, title: str, message: str):
|
|
"""
|
|
Helper method to show an info message, delegating to MainWindow via controller.
|
|
"""
|
|
if self.controller and hasattr(self.controller, "main_window") and self.controller.main_window:
|
|
main_window = self.controller.main_window
|
|
if hasattr(main_window, "show_info_message"):
|
|
main_window.show_info_message(title, message)
|
|
else:
|
|
module_logger.warning(f"MainWindow.show_info_message not found. Info: {title} - {message}")
|
|
else:
|
|
module_logger.warning(f"Controller or MainWindow not available to show info: {title} - {message}") |