fix tab processor segment

This commit is contained in:
VALLONGOL 2025-07-14 13:23:06 +02:00
parent c814943739
commit b049d80bd3
4 changed files with 107 additions and 83 deletions

View File

@ -446,7 +446,7 @@
}, },
"segment_processor_config": { "segment_processor_config": {
"last_output_dir": "C:/src/____GitProjects/radar_data_reader/out_analisys", "last_output_dir": "C:/src/____GitProjects/radar_data_reader/out_analisys",
"create_separate_folders": false "create_separate_folders": true
}, },
"segment_export_g_reconvert_config": { "segment_export_g_reconvert_config": {
"cpp_executable_path": "C:/src/GRIFO-E/REP/Projects/Tools/wsLuna/g_reconvert/Debug/g_reconvert.exe", "cpp_executable_path": "C:/src/GRIFO-E/REP/Projects/Tools/wsLuna/g_reconvert/Debug/g_reconvert.exe",

View File

@ -197,18 +197,28 @@ class AppController:
try: try:
output_dir, basename, profiles = Path(self.view.out_output_dir_var.get()), self.view.out_basename_var.get(), self.config_manager.get_export_profiles() output_dir, basename, profiles = Path(self.view.out_output_dir_var.get()), self.view.out_basename_var.get(), self.config_manager.get_export_profiles()
use_full_path = self.view.out_use_full_path_var.get() use_full_path = self.view.out_use_full_path_var.get()
if self.view.out_output_csv_var.get(): if self.view.out_output_csv_var.get():
profile = next((p for p in profiles if p.name == self.view.out_csv_profile_var.get()), None) profile = next((p for p in profiles if p.name == self.view.out_csv_profile_var.get()), None)
if not profile: raise ValueError(f"CSV profile '{self.view.out_csv_profile_var.get()}' not found.") if not profile: raise ValueError(f"CSV profile '{self.view.out_csv_profile_var.get()}' not found.")
self.active_export_profiles["csv"] = profile self.active_export_profiles["csv"] = profile
path, delimiter = (output_dir / basename).with_suffix(".csv"), "\t" if self.view.out_csv_use_tab_var.get() else ","
csv_filename = f"{basename}_{profile.name}.csv"
path = output_dir / csv_filename
delimiter = "\t" if self.view.out_csv_use_tab_var.get() else ","
fh = open(path, "w", encoding="utf-8", newline="") fh = open(path, "w", encoding="utf-8", newline="")
self.csv_writers["csv"].writerow([field.column_name for field in profile.fields]) header = [field.data_path if use_full_path else field.column_name for field in profile.fields]
csv_writer = csv.writer(fh, delimiter=delimiter)
csv_writer.writerow(header)
self.output_file_handles["csv"] = fh self.output_file_handles["csv"] = fh
self.csv_writers["csv"] = csv_writer
if self.view.out_output_json_var.get(): if self.view.out_output_json_var.get():
profile = next((p for p in profiles if p.name == self.view.out_json_profile_var.get()), None) profile = next((p for p in profiles if p.name == self.view.out_json_profile_var.get()), None)
if not profile: raise ValueError(f"JSON profile '{self.view.out_json_profile_var.get()}' not found.") if not profile: raise ValueError(f"JSON profile '{self.view.out_json_profile_var.get()}' not found.")
self.active_export_profiles["json"] = profile self.active_export_profiles["json"] = profile
return True return True
except (IOError, ValueError) as e: except (IOError, ValueError) as e:
log.error(f"Failed to prepare output files: {e}") log.error(f"Failed to prepare output files: {e}")
@ -216,17 +226,12 @@ class AppController:
return False return False
def start_out_processing(self): def start_out_processing(self):
if self.is_processing: if self.is_processing: log.warning("Processing already in progress."); return
log.warning("Processing already in progress.")
return
if not all([self.view.out_filepath_var.get(), self.view.out_output_dir_var.get(), self.view.out_basename_var.get()]): if not all([self.view.out_filepath_var.get(), self.view.out_output_dir_var.get(), self.view.out_basename_var.get()]):
log.error("Please set input file, output directory, and base filename.") log.error("Please set input file, output directory, and base filename."); return
return
if not any([self.view.out_output_csv_var.get(), self.view.out_output_json_var.get()]): if not any([self.view.out_output_csv_var.get(), self.view.out_output_json_var.get()]):
log.error("Please select at least one output format (CSV or JSON).") log.error("Please select at least one output format (CSV or JSON)."); return
return if not self._prepare_out_processor_files(): return
if not self._prepare_out_processor_files():
return
self.is_processing = True self.is_processing = True
self.view.start_processing_ui() self.view.start_processing_ui()
@ -240,8 +245,7 @@ class AppController:
active_profile = self.active_export_profiles.get("csv") or self.active_export_profiles.get("json") active_profile = self.active_export_profiles.get("csv") or self.active_export_profiles.get("json")
if not active_profile: if not active_profile:
log.error("No active export profile found for processing.") log.error("No active export profile found for processing.")
self.is_processing = False self.is_processing = False; self.view.update_ui_for_processing_state(False)
self.view.update_ui_for_processing_state(False)
return return
self.out_processor.start_processing(Path(filepath_str), active_profile) self.out_processor.start_processing(Path(filepath_str), active_profile)
@ -321,8 +325,10 @@ class AppController:
if "json" in self.active_export_profiles: log.info("JSON export enabled, but no data. Skipping file creation.") if "json" in self.active_export_profiles: log.info("JSON export enabled, but no data. Skipping file creation.")
return return
try: try:
profile = self.active_export_profiles["json"]
output_dir, basename = Path(self.view.out_output_dir_var.get()), self.view.out_basename_var.get() output_dir, basename = Path(self.view.out_output_dir_var.get()), self.view.out_basename_var.get()
path = (output_dir / basename).with_suffix(".json") json_filename = f"{basename}_{profile.name}.json"
path = output_dir / json_filename
log.info(f"Writing {len(self.json_data_buffer)} records to JSON file: {path}") log.info(f"Writing {len(self.json_data_buffer)} records to JSON file: {path}")
with open(path, "w", encoding="utf-8") as f: json.dump(self.json_data_buffer, f, indent=4) with open(path, "w", encoding="utf-8") as f: json.dump(self.json_data_buffer, f, indent=4)
log.info("JSON file written successfully.") log.info("JSON file written successfully.")
@ -531,13 +537,11 @@ class AppController:
def start_segment_export(self): def start_segment_export(self):
if self.is_processing: if self.is_processing:
messagebox.showwarning("Process Busy", "Cannot start export while another process is running.", parent=self.view) messagebox.showwarning("Process Busy", "Cannot start export while another process is running.", parent=self.view); return
return
selected_item_ids = self.view.flight_timeline_tree.selection() selected_item_ids = self.view.flight_timeline_tree.selection()
if not selected_item_ids: if not selected_item_ids:
messagebox.showinfo("No Selection", "Please select one or more segments to export.", parent=self.view) messagebox.showinfo("No Selection", "Please select one or more segments to export.", parent=self.view); return
return
if self.last_flight_summary_df is None: if self.last_flight_summary_df is None:
messagebox.showerror("Error", "Flight summary data is not available. Cannot proceed with export.") messagebox.showerror("Error", "Flight summary data is not available. Cannot proceed with export.")
@ -552,7 +556,7 @@ class AppController:
rec_folder = Path(self.view.analyzer_rec_folder_var.get()) rec_folder = Path(self.view.analyzer_rec_folder_var.get())
export_config = self.config_manager.get("segment_export_config") export_config = self.config_manager.get("segment_export_config")
folder_template = export_config.get("naming_options", {}).get("folder_name_template", "{Segment}") folder_template = export_config.get("naming_options", {}).get("folder_name_template", "{Segment}_{StartBatch}-{EndBatch}")
current_flight_path = self.flight_analyzer.current_flight_folder_path current_flight_path = self.flight_analyzer.current_flight_folder_path
if not current_flight_path: if not current_flight_path:
log.error("Cannot start export, flight analysis folder path is unknown.") log.error("Cannot start export, flight analysis folder path is unknown.")

View File

@ -25,10 +25,7 @@ from ..core.export_profiles import ExportProfile
log = logger.get_logger(__name__) log = logger.get_logger(__name__)
def _get_value_from_path(batch: Any, path: str) -> Any: def _get_value_from_path(batch: Any, path: str) -> Any:
# Questa è una versione semplificata per il processor.
# In una versione futura, si potrebbe unificare con quella in AppController.
try: try:
if path == "batch_id": return getattr(batch, 'batch_id', 'N/A') if path == "batch_id": return getattr(batch, 'batch_id', 'N/A')
parts = re.split(r"\.|\[", path) parts = re.split(r"\.|\[", path)
@ -47,19 +44,16 @@ def _get_value_from_path(batch: Any, path: str) -> Any:
except Exception: except Exception:
return "N/A" return "N/A"
def _convert_ctypes_for_json(obj: Any) -> Any: def _convert_ctypes_for_json(obj: Any) -> Any:
# Funzione di supporto per la serializzazione JSON
if isinstance(obj, (int, float, str, bool)) or obj is None: return obj if isinstance(obj, (int, float, str, bool)) or obj is None: return obj
if hasattr(obj, '_length_'): # È un array ctypes if hasattr(obj, '_length_'):
return [_convert_ctypes_for_json(item) for item in obj] return [_convert_ctypes_for_json(item) for item in obj]
if hasattr(obj, '_fields_'): # È una struttura ctypes if hasattr(obj, '_fields_'):
return {field: _convert_ctypes_for_json(getattr(obj, field)) for field, _ in obj._fields_} return {field: _convert_ctypes_for_json(getattr(obj, field)) for field, _ in obj._fields_}
if isinstance(obj, bytes): if isinstance(obj, bytes):
return obj.hex() return obj.hex()
return obj return obj
class SegmentProcessor: class SegmentProcessor:
"""Manages the batch processing of exported flight segments.""" """Manages the batch processing of exported flight segments."""
def __init__(self, config: Dict[str, Any], result_queue: queue.Queue, command_queue: queue.Queue): def __init__(self, config: Dict[str, Any], result_queue: queue.Queue, command_queue: queue.Queue):
@ -84,13 +78,10 @@ class SegmentProcessor:
segments_to_process = self.config.get("segments_to_process", []) segments_to_process = self.config.get("segments_to_process", [])
output_dir = Path(self.config.get("output_dir")) output_dir = Path(self.config.get("output_dir"))
create_folders = self.config.get("create_separate_folders", True) create_folders = self.config.get("create_separate_folders", True)
profiles = self.config.get("profiles", []) profiles = self.config.get("profiles", [])
csv_profile = next((p for p in profiles if p.name == self.config.get("csv_profile_name")), None)
csv_profile_name = self.config.get("csv_profile_name") json_profile = next((p for p in profiles if p.name == self.config.get("json_profile_name")), None)
json_profile_name = self.config.get("json_profile_name")
csv_profile = next((p for p in profiles if p.name == csv_profile_name), None)
json_profile = next((p for p in profiles if p.name == json_profile_name), None)
log.info(f"Starting batch processing for {len(segments_to_process)} segments.") log.info(f"Starting batch processing for {len(segments_to_process)} segments.")
@ -110,28 +101,20 @@ class SegmentProcessor:
"segment_name": segment_name "segment_name": segment_name
}) })
# Determina la cartella di output per questo segmento current_output_dir = output_dir / segment_name if create_folders else output_dir
current_output_dir = output_dir
if create_folders:
current_output_dir = output_dir / segment_name
current_output_dir.mkdir(parents=True, exist_ok=True) current_output_dir.mkdir(parents=True, exist_ok=True)
# Esegui il worker e gestisci l'output
self._process_single_segment(out_file_path, current_output_dir, segment_name, csv_profile, json_profile) self._process_single_segment(out_file_path, current_output_dir, segment_name, csv_profile, json_profile)
log.info("Batch processing finished.") log.info("Batch processing finished.")
self.result_queue.put({"type": "complete", "message": "Segment batch processing complete."}) self.result_queue.put({"type": "complete", "message": "Segment batch processing complete."})
def _process_single_segment(self, in_path: Path, out_dir: Path, base_name: str, csv_p: ExportProfile, json_p: ExportProfile): def _process_single_segment(self, in_path: Path, out_dir: Path, base_name: str, csv_p: ExportProfile, json_p: ExportProfile):
"""Processes a single .out file and generates CSV/JSON outputs."""
# Creiamo code locali per questo worker specifico
worker_cmd_q = mp.Queue() worker_cmd_q = mp.Queue()
worker_res_q = mp.Queue() worker_res_q = mp.Queue()
# Il profilo passato al worker serve solo a guidare il parsing, active_profile = csv_p if self.config.get("generate_csv") else json_p
# l'esportazione vera la gestiamo qui. if not active_profile: return
active_profile = csv_p or json_p
if not active_profile: return # Skip if no profiles are active
worker_args = (in_path, worker_cmd_q, worker_res_q, active_profile) worker_args = (in_path, worker_cmd_q, worker_res_q, active_profile)
process = mp.Process(target=run_worker_process, args=worker_args, daemon=True) process = mp.Process(target=run_worker_process, args=worker_args, daemon=True)
@ -140,22 +123,32 @@ class SegmentProcessor:
csv_writer, json_data, csv_fh = None, [], None csv_writer, json_data, csv_fh = None, [], None
try: try:
if csv_p: if self.config.get("generate_csv") and csv_p:
csv_path = (out_dir / base_name).with_suffix(".csv") csv_filename = f"{base_name}_{csv_p.name}.csv"
csv_path = out_dir / csv_filename
csv_fh = open(csv_path, "w", encoding="utf-8", newline="") csv_fh = open(csv_path, "w", encoding="utf-8", newline="")
csv_writer = csv.writer(csv_fh) delimiter = "\t" if self.config.get("csv_use_tab") else ","
csv_writer.writerow([field.column_name for field in csv_p.fields]) csv_writer = csv.writer(csv_fh, delimiter=delimiter)
header = [field.data_path if self.config.get("use_full_path_headers") else field.column_name for field in csv_p.fields]
csv_writer.writerow(header)
if self.config.get("generate_json") and json_p:
json_data = [] # Assicurati che sia vuota per ogni segmento
while True: while True:
msg = worker_res_q.get() msg = worker_res_q.get()
if msg['type'] == 'data_batch': if msg['type'] == 'data_batch':
batch = msg['data'] batch = msg['data']
if csv_writer: if csv_writer and csv_p:
row = [_get_value_from_path(batch, field.data_path) for field in csv_p.fields] row = [_get_value_from_path(batch, field.data_path) for field in csv_p.fields]
csv_writer.writerow(row) csv_writer.writerow(row)
if json_p: if json_data is not None and json_p:
json_row = {field.column_name: _get_value_from_path(batch, field.data_path) for field in json_p.fields} row_dict = {}
json_data.append(_convert_ctypes_for_json(json_row)) for field in json_p.fields:
key = field.data_path if self.config.get("use_full_path_headers") else field.column_name
raw_value = _get_value_from_path(batch, field.data_path)
row_dict[key] = _convert_ctypes_for_json(raw_value)
json_data.append(row_dict)
elif msg['type'] == 'complete': elif msg['type'] == 'complete':
break break
elif msg['type'] == 'error': elif msg['type'] == 'error':
@ -164,8 +157,9 @@ class SegmentProcessor:
finally: finally:
if csv_fh: if csv_fh:
csv_fh.close() csv_fh.close()
if json_p and json_data: if self.config.get("generate_json") and json_p and json_data:
json_path = (out_dir / base_name).with_suffix(".json") json_filename = f"{base_name}_{json_p.name}.json"
json_path = out_dir / json_filename
with open(json_path, "w", encoding="utf-8") as f: with open(json_path, "w", encoding="utf-8") as f:
json.dump(json_data, f, indent=4) json.dump(json_data, f, indent=4)
process.join(timeout=2) process.join(timeout=2)
@ -174,7 +168,6 @@ class SegmentProcessor:
@staticmethod @staticmethod
def scan_for_segments(flight_path: Path, flight_summary_df: pd.DataFrame, naming_template: str) -> List[Dict[str, Any]]: def scan_for_segments(flight_path: Path, flight_summary_df: pd.DataFrame, naming_template: str) -> List[Dict[str, Any]]:
"""Scans a flight workspace to find and verify exported segments."""
if flight_summary_df is None or flight_summary_df.empty: if flight_summary_df is None or flight_summary_df.empty:
return [] return []

View File

@ -31,6 +31,9 @@ class SegmentProcessorTab(ttk.Frame):
self.csv_use_tab_var = tk.BooleanVar(value=False) self.csv_use_tab_var = tk.BooleanVar(value=False)
self.use_full_path_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() self._create_widgets()
def _create_widgets(self): def _create_widgets(self):
@ -53,16 +56,24 @@ class SegmentProcessorTab(ttk.Frame):
segments_frame.columnconfigure(0, weight=1) segments_frame.columnconfigure(0, weight=1)
segments_frame.rowconfigure(0, weight=1) segments_frame.rowconfigure(0, weight=1)
# --- MODIFICA: Nuova struttura della Treeview ---
self.segments_tree = ttk.Treeview( self.segments_tree = ttk.Treeview(
segments_frame, segments_frame,
columns=("status", "details"), columns=("status", "name", "start_time", "end_time", "duration"),
show="headings", show="headings",
selectmode="extended" selectmode="extended"
) )
self.segments_tree.heading("status", text="Status") self.segments_tree.heading("status", text="Status")
self.segments_tree.heading("details", text="Segment Information") self.segments_tree.heading("name", text="Segment Name")
self.segments_tree.column("status", width=120, anchor="center", stretch=False) self.segments_tree.heading("start_time", text="Start Time")
self.segments_tree.column("details", width=500, stretch=True) self.segments_tree.heading("end_time", text="End Time")
self.segments_tree.heading("duration", text="Duration (s)")
self.segments_tree.column("status", width=100, anchor="center", stretch=False)
self.segments_tree.column("name", width=300, stretch=True)
self.segments_tree.column("start_time", width=100, anchor="center", stretch=False)
self.segments_tree.column("end_time", width=100, anchor="center", stretch=False)
self.segments_tree.column("duration", width=100, anchor="center", stretch=False)
self.segments_tree.grid(row=0, column=0, sticky="nsew") self.segments_tree.grid(row=0, column=0, sticky="nsew")
scrollbar = ttk.Scrollbar(segments_frame, orient="vertical", command=self.segments_tree.yview) scrollbar = ttk.Scrollbar(segments_frame, orient="vertical", command=self.segments_tree.yview)
@ -91,7 +102,6 @@ class SegmentProcessorTab(ttk.Frame):
output_config_frame.grid(row=3, column=0, sticky="ew", pady=10) output_config_frame.grid(row=3, column=0, sticky="ew", pady=10)
output_config_frame.columnconfigure(1, weight=1) output_config_frame.columnconfigure(1, weight=1)
# Row 0: Output Directory
ttk.Label(output_config_frame, text="Output Directory:").grid(row=0, column=0, padx=5, pady=5, sticky="w") 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 = ttk.Entry(output_config_frame, textvariable=self.output_dir_var)
dir_entry.grid(row=0, column=1, sticky="ew", padx=5) dir_entry.grid(row=0, column=1, sticky="ew", padx=5)
@ -100,20 +110,23 @@ class SegmentProcessorTab(ttk.Frame):
command=lambda: self.controller.select_output_dir(self.output_dir_var) command=lambda: self.controller.select_output_dir(self.output_dir_var)
).grid(row=0, column=2, padx=5) ).grid(row=0, column=2, padx=5)
# Row 1 & 2: Formats and Profiles
formats_frame = ttk.Frame(output_config_frame) formats_frame = ttk.Frame(output_config_frame)
formats_frame.grid(row=1, column=0, columnspan=3, sticky="ew", padx=10, pady=5) formats_frame.grid(row=1, column=0, columnspan=3, sticky="ew", padx=10, pady=5)
formats_frame.columnconfigure(1, weight=1) formats_frame.columnconfigure(2, weight=1)
ttk.Checkbutton(formats_frame, text="Generate .csv file", variable=self.generate_csv_var).grid(row=0, column=0, sticky="w") 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=30) self.csv_profile_combobox = ttk.Combobox(formats_frame, textvariable=self.csv_profile_var, state="readonly", width=30)
self.csv_profile_combobox.grid(row=0, column=1, sticky="w") self.csv_profile_combobox.grid(row=0, column=2, sticky="ew")
ttk.Checkbutton(formats_frame, text="Generate .json file", variable=self.generate_json_var).grid(row=1, column=0, sticky="w") 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=30) self.json_profile_combobox = ttk.Combobox(formats_frame, textvariable=self.json_profile_var, state="readonly", width=30)
self.json_profile_combobox.grid(row=1, column=1, sticky="w") self.json_profile_combobox.grid(row=1, column=2, sticky="ew")
self._on_format_toggle()
# Row 3: Other Checkbox Options
other_options_frame = ttk.Frame(output_config_frame) other_options_frame = ttk.Frame(output_config_frame)
other_options_frame.grid(row=2, column=0, columnspan=3, sticky='w', padx=10, pady=5) other_options_frame.grid(row=2, column=0, columnspan=3, sticky='w', padx=10, pady=5)
ttk.Checkbutton( ttk.Checkbutton(
@ -129,13 +142,29 @@ class SegmentProcessorTab(ttk.Frame):
variable=self.use_full_path_var variable=self.use_full_path_var
).pack(side=tk.LEFT, anchor="w", padx=(20, 0)) ).pack(side=tk.LEFT, anchor="w", padx=(20, 0))
# --- NUOVO: Progress Frame ---
progress_frame = ttk.LabelFrame(self, text="Processing Progress")
progress_frame.grid(row=4, column=0, sticky="ew", pady=(10,0))
progress_frame.columnconfigure(0, weight=1)
self.progress_bar = ttk.Progressbar(progress_frame, variable=self.progress_var)
self.progress_bar.grid(row=0, column=0, sticky="ew", padx=5, 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=5, pady=(0, 5))
self.process_button = ttk.Button( self.process_button = ttk.Button(
self, progress_frame,
text="Process Selected Segments", text="Process Selected Segments",
command=self.controller.start_segment_batch_processing, command=self.controller.start_segment_batch_processing,
state=tk.DISABLED state=tk.DISABLED
) )
self.process_button.grid(row=4, column=0, sticky="e", pady=(10, 0)) self.process_button.grid(row=0, column=1, rowspan=2, padx=10)
def _on_format_toggle(self):
"""Enable/disable profile comboboxes based on checkbox state."""
self.csv_profile_combobox.config(state=tk.NORMAL if self.generate_csv_var.get() else tk.DISABLED)
self.json_profile_combobox.config(state=tk.NORMAL if self.generate_json_var.get() else tk.DISABLED)
def _select_all_ready(self): def _select_all_ready(self):
self.segments_tree.selection_remove(self.segments_tree.selection()) self.segments_tree.selection_remove(self.segments_tree.selection())
@ -152,13 +181,11 @@ class SegmentProcessorTab(ttk.Frame):
self.segments_tree.delete(i) self.segments_tree.delete(i)
has_selectable_items = False has_selectable_items = False
for segment in self.segments_data_store: for i, segment in enumerate(self.segments_data_store):
# Costruisci la stringa dettagliata name = segment.get('Segment (Mode | Scale | WF)', 'Unknown Segment')
details = segment.get('Segment (Mode | Scale | WF)', 'Unknown Segment')
start_time = segment.get('start_time_str', 'N/A') start_time = segment.get('start_time_str', 'N/A')
end_time = segment.get('end_time_str', 'N/A') end_time = segment.get('end_time_str', 'N/A')
duration = segment.get('Duration (s)', 0) duration = segment.get('Duration (s)', 0)
details_str = f"{details} | Start: {start_time}, End: {end_time}, Duration: {duration:.2f}s"
if segment.get('is_exported_and_valid'): if segment.get('is_exported_and_valid'):
status_text, tag = "Ready", "ready" status_text, tag = "Ready", "ready"
@ -166,16 +193,16 @@ class SegmentProcessorTab(ttk.Frame):
else: else:
status_text, tag = "Not Exported", "not_exported" status_text, tag = "Not Exported", "not_exported"
iid = segment.get('folder_name') self.segments_tree.insert("", "end", iid=str(i), values=(status_text, name, start_time, end_time, f"{duration:.2f}"), tags=(tag,))
self.segments_tree.insert("", "end", iid=iid, values=(status_text, details_str), tags=(tag,))
self.process_button.config(state=tk.NORMAL if has_selectable_items else tk.DISABLED) self.process_button.config(state=tk.NORMAL if has_selectable_items else tk.DISABLED)
def get_selected_segments_data(self) -> List[Dict[str, Any]]: def get_selected_segments_data(self) -> List[Dict[str, Any]]:
selected_iids = self.segments_tree.selection() selected_indices = [int(iid) for iid in self.segments_tree.selection()]
selected_data = [] selected_data = []
for segment in self.segments_data_store: for index in selected_indices:
if segment.get('folder_name') in selected_iids and segment.get('is_exported_and_valid'): segment = self.segments_data_store[index]
if segment.get('is_exported_and_valid'):
selected_data.append(segment) selected_data.append(segment)
return selected_data return selected_data