313 lines
12 KiB
Python
313 lines
12 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.flight_analysis_dir_var = tk.StringVar()
|
|
|
|
self._create_widgets()
|
|
|
|
def _create_widgets(self):
|
|
"""Creates and lays out all widgets for the tab."""
|
|
self.columnconfigure(0, weight=1)
|
|
self.rowconfigure(2, weight=1)
|
|
|
|
source_frame = ttk.LabelFrame(self, text="Flight Analysis Source Directory")
|
|
source_frame.grid(row=0, column=0, sticky="ew", pady=(0, 10))
|
|
source_frame.columnconfigure(1, weight=1)
|
|
|
|
ttk.Label(source_frame, text="Path:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
|
|
dir_entry = ttk.Entry(source_frame, textvariable=self.flight_analysis_dir_var)
|
|
dir_entry.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
|
|
|
|
browse_button = ttk.Button(
|
|
source_frame,
|
|
text="Browse...",
|
|
command=self.controller.select_flight_analysis_dir_for_segment_processor
|
|
)
|
|
browse_button.grid(row=0, column=2, padx=5, pady=5)
|
|
|
|
action_frame = ttk.Frame(self)
|
|
action_frame.grid(row=1, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
self.load_segments_button = ttk.Button(
|
|
action_frame,
|
|
text="Load Segments from Selected Directory",
|
|
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=2, 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")
|
|
|
|
selection_frame = ttk.Frame(segments_frame)
|
|
selection_frame.grid(row=1, column=0, columnspan=2, sticky="ew", pady=(5, 0))
|
|
|
|
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
|
|
)
|
|
|
|
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=2, column=0, columnspan=2, sticky="w", padx=5, pady=5)
|
|
self.master.style = ttk.Style()
|
|
self.master.style.configure("Italic.TLabel", font=("", 9, "italic"))
|
|
|
|
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("")
|