SXXXXXXX_RadarDataReader/radar_data_reader/gui/segment_processor_tab.py
2025-07-14 13:33:35 +02:00

230 lines
11 KiB
Python

# radar_data_reader/gui/segment_processor_tab.py
"""
Defines the GUI layout and widgets for the 'Segment Processor' tab.
This tab allows users to perform batch processing on previously exported
flight segments.
"""
import tkinter as tk
from tkinter import ttk
from typing import List, Dict, Any, Callable
class SegmentProcessorTab(ttk.Frame):
"""
The UI frame for the Segment Processor feature.
"""
def __init__(self, master: ttk.Notebook, controller: Any):
super().__init__(master, padding="10")
self.controller = controller
self.segments_data_store: List[Dict[str, Any]] = []
# --- Tkinter Variables ---
self.output_dir_var = tk.StringVar()
self.generate_csv_var = tk.BooleanVar(value=True)
self.generate_json_var = tk.BooleanVar(value=False)
self.csv_profile_var = tk.StringVar()
self.json_profile_var = tk.StringVar()
self.create_separate_folders_var = tk.BooleanVar(value=True)
self.csv_use_tab_var = tk.BooleanVar(value=False)
self.use_full_path_var = tk.BooleanVar(value=False)
self.progress_var = tk.DoubleVar(value=0)
self.progress_text_var = tk.StringVar(value="N/A")
self._create_widgets()
def _create_widgets(self):
"""Creates and lays out all widgets for the tab."""
self.columnconfigure(0, weight=1)
self.rowconfigure(1, weight=1)
action_frame = ttk.Frame(self)
action_frame.grid(row=0, column=0, sticky="ew", pady=(0, 10))
self.load_segments_button = ttk.Button(
action_frame,
text="Load Exported Segments from Current Flight",
command=self.controller.load_segments_for_processing
)
self.load_segments_button.pack(side=tk.LEFT)
segments_frame = ttk.LabelFrame(self, text="Available Segments")
segments_frame.grid(row=1, column=0, sticky="nsew", pady=10)
segments_frame.columnconfigure(0, weight=1)
segments_frame.rowconfigure(0, weight=1)
self.segments_tree = ttk.Treeview(
segments_frame,
columns=("status", "name", "start_batch", "end_batch", "duration", "start_file", "end_file"),
show="headings",
selectmode="extended"
)
self.segments_tree.heading("status", text="Status")
self.segments_tree.heading("name", text="Segment Name")
self.segments_tree.heading("start_batch", text="Start Batch")
self.segments_tree.heading("end_batch", text="End Batch")
self.segments_tree.heading("duration", text="Duration (s)")
self.segments_tree.heading("start_file", text="Start File")
self.segments_tree.heading("end_file", text="End File")
self.segments_tree.column("status", width=100, anchor="center", stretch=False)
self.segments_tree.column("name", width=250, stretch=True)
self.segments_tree.column("start_batch", width=100, anchor="center")
self.segments_tree.column("end_batch", width=100, anchor="center")
self.segments_tree.column("duration", width=100, anchor="center")
self.segments_tree.column("start_file", width=200, stretch=True)
self.segments_tree.column("end_file", width=200, stretch=True)
self.segments_tree.grid(row=0, column=0, sticky="nsew")
scrollbar = ttk.Scrollbar(segments_frame, orient="vertical", command=self.segments_tree.yview)
self.segments_tree.configure(yscrollcommand=scrollbar.set)
scrollbar.grid(row=0, column=1, sticky="ns")
self.segments_tree.tag_configure("ready", foreground="green")
self.segments_tree.tag_configure("not_exported", foreground="gray")
help_label = ttk.Label(
segments_frame,
text="Tip: Segments in gray are not exported and cannot be selected. Use the 'Flight Analyzer' tab to export them.",
wraplength=600, justify=tk.LEFT, style="Italic.TLabel"
)
help_label.grid(row=1, column=0, columnspan=2, sticky="w", padx=5, pady=5)
self.master.style = ttk.Style()
self.master.style.configure("Italic.TLabel", font=("", 9, "italic"))
selection_frame = ttk.Frame(self)
selection_frame.grid(row=2, column=0, sticky="ew", pady=(5, 10))
ttk.Button(selection_frame, text="Select All Ready", command=self._select_all_ready).pack(side=tk.LEFT)
ttk.Button(selection_frame, text="Select None", command=self._select_none).pack(side=tk.LEFT, padx=5)
output_config_frame = ttk.LabelFrame(self, text="Batch Processing Output Configuration")
output_config_frame.grid(row=3, column=0, sticky="ew", pady=10)
output_config_frame.columnconfigure(1, weight=1)
ttk.Label(output_config_frame, text="Output Directory:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
dir_entry = ttk.Entry(output_config_frame, textvariable=self.output_dir_var)
dir_entry.grid(row=0, column=1, sticky="ew", padx=5)
dir_buttons_frame = ttk.Frame(output_config_frame)
dir_buttons_frame.grid(row=0, column=2, padx=5)
ttk.Button(
dir_buttons_frame, text="Browse...",
command=lambda: self.controller.select_output_dir(self.output_dir_var)
).pack(side=tk.LEFT)
ttk.Button(
dir_buttons_frame, text="Open...",
command=lambda: self.controller.open_folder_from_path(self.output_dir_var.get())
).pack(side=tk.LEFT, padx=(5,0))
formats_frame = ttk.Frame(output_config_frame)
formats_frame.grid(row=1, column=0, columnspan=3, sticky="ew", padx=10, pady=5)
formats_frame.columnconfigure(2, weight=1)
self.csv_check = ttk.Checkbutton(formats_frame, text="Generate .csv file", variable=self.generate_csv_var, command=self._on_format_toggle)
self.csv_check.grid(row=0, column=0, sticky="w")
ttk.Label(formats_frame, text="CSV Profile:").grid(row=0, column=1, sticky="e", padx=(10, 2))
self.csv_profile_combobox = ttk.Combobox(formats_frame, textvariable=self.csv_profile_var, state="readonly", width=25)
self.csv_profile_combobox.grid(row=0, column=2, sticky="ew")
self.json_check = ttk.Checkbutton(formats_frame, text="Generate .json file", variable=self.generate_json_var, command=self._on_format_toggle)
self.json_check.grid(row=1, column=0, sticky="w")
ttk.Label(formats_frame, text="JSON Profile:").grid(row=1, column=1, sticky="e", padx=(10, 2))
self.json_profile_combobox = ttk.Combobox(formats_frame, textvariable=self.json_profile_var, state="readonly", width=25)
self.json_profile_combobox.grid(row=1, column=2, sticky="ew")
self._on_format_toggle()
other_options_frame = ttk.Frame(output_config_frame)
other_options_frame.grid(row=2, column=0, columnspan=3, sticky='w', padx=10, pady=5)
ttk.Checkbutton(
other_options_frame, text="Create a separate folder for each segment's analysis",
variable=self.create_separate_folders_var
).pack(side=tk.LEFT, anchor="w")
ttk.Checkbutton(
other_options_frame, text="Use Tab Separator (CSV)",
variable=self.csv_use_tab_var
).pack(side=tk.LEFT, anchor="w", padx=(20, 0))
ttk.Checkbutton(
other_options_frame, text="Use Full Path for Headers",
variable=self.use_full_path_var
).pack(side=tk.LEFT, anchor="w", padx=(20, 0))
progress_frame = ttk.LabelFrame(self, text="Processing Progress")
progress_frame.grid(row=4, column=0, sticky="ew", pady=(10,0))
progress_frame.columnconfigure(1, weight=1)
self.progress_bar = ttk.Progressbar(progress_frame, variable=self.progress_var)
self.progress_bar.grid(row=0, column=0, columnspan=2, sticky="ew", padx=10, pady=5)
self.progress_label = ttk.Label(progress_frame, textvariable=self.progress_text_var)
self.progress_label.grid(row=1, column=0, sticky="w", padx=10, pady=(0, 5))
self.process_button = ttk.Button(
progress_frame,
text="Process Selected Segments",
command=self.controller.start_segment_batch_processing,
state=tk.DISABLED
)
self.process_button.grid(row=1, column=1, sticky="e", padx=10, pady=(0, 5))
def _on_format_toggle(self):
self.csv_profile_combobox.config(state="readonly" if self.generate_csv_var.get() else tk.DISABLED)
self.json_profile_combobox.config(state="readonly" if self.generate_json_var.get() else tk.DISABLED)
def _select_all_ready(self):
self.segments_tree.selection_remove(self.segments_tree.selection())
ready_items = self.segments_tree.tag_has("ready")
if ready_items:
self.segments_tree.selection_set(ready_items)
def _select_none(self):
self.segments_tree.selection_remove(self.segments_tree.selection())
def populate_segments(self, segments_data: List[Dict[str, Any]]):
self.segments_data_store = segments_data
for i in self.segments_tree.get_children():
self.segments_tree.delete(i)
has_selectable_items = False
for i, segment in enumerate(self.segments_data_store):
values_tuple = (
"Ready" if segment.get('is_exported_and_valid') else "Not Exported",
segment.get('Segment (Mode | Scale | WF)', 'N/A'),
segment.get('Start Batch', 'N/A'),
segment.get('End Batch', 'N/A'),
f"{segment.get('Duration (s)', 0):.2f}",
segment.get('Start File', 'N/A'),
segment.get('End File', 'N/A')
)
tag = "ready" if segment.get('is_exported_and_valid') else "not_exported"
if tag == "ready": has_selectable_items = True
self.segments_tree.insert("", "end", iid=str(i), values=values_tuple, tags=(tag,))
self.process_button.config(state=tk.NORMAL if has_selectable_items else tk.DISABLED)
def get_selected_segments_data(self) -> List[Dict[str, Any]]:
selected_indices = [int(iid) for iid in self.segments_tree.selection()]
selected_data = []
for index in selected_indices:
segment = self.segments_data_store[index]
if segment.get('is_exported_and_valid'):
selected_data.append(segment)
return selected_data
def update_export_profiles(self, profile_names: List[str], active_profile_name: str):
for combo, var in [
(self.csv_profile_combobox, self.csv_profile_var),
(self.json_profile_combobox, self.json_profile_var)
]:
combo["values"] = profile_names
if active_profile_name in profile_names:
var.set(active_profile_name)
elif profile_names:
var.set(profile_names[0])
else:
var.set("")