From 272d23c6bebd73192dc4a9f69ca00479ce1d0dda Mon Sep 17 00:00:00 2001 From: noisedestroyers Date: Sat, 26 Jul 2025 16:09:49 -0400 Subject: [PATCH] bookmark --- analyzer/gui/dock_panels.py | 75 ++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 9 deletions(-) diff --git a/analyzer/gui/dock_panels.py b/analyzer/gui/dock_panels.py index 9d0c8a8..abe3cf3 100644 --- a/analyzer/gui/dock_panels.py +++ b/analyzer/gui/dock_panels.py @@ -376,6 +376,9 @@ class FlowListDockWidget(QWidget): # Auto-resize numeric columns to fit content for col in [2, 3, 4, 7]: # Packets, Bytes, Max σ, Outliers self.flows_table.resizeColumnToContents(col) + + # Update header with time range information if we have flows + self._update_signal_plot_header(flows_list) def populate_flows_table_quick(self): """Populate the flows table quickly without plots for immediate display""" @@ -448,6 +451,41 @@ class FlowListDockWidget(QWidget): for col in [2, 3, 4, 7]: # Packets, Bytes, Max σ, Outliers self.flows_table.resizeColumnToContents(col) + def _update_signal_plot_header(self, flows_list): + """Update the Signal Plot column header with time range information""" + if not flows_list or not self.analyzer: + return + + # Find earliest and latest timestamps across all flows + earliest_time = float('inf') + latest_time = float('-inf') + + for flow in flows_list: + if flow.timestamps: + flow_start = min(flow.timestamps) + flow_end = max(flow.timestamps) + earliest_time = min(earliest_time, flow_start) + latest_time = max(latest_time, flow_end) + + if earliest_time != float('inf') and latest_time != float('-inf'): + # Convert timestamps to time-of-day format (assuming they're epoch seconds) + import datetime + try: + start_time = datetime.datetime.fromtimestamp(earliest_time).strftime('%H:%M:%S') + end_time = datetime.datetime.fromtimestamp(latest_time).strftime('%H:%M:%S') + header_text = f"Signal Plot\n{start_time} → {end_time}" + except (ValueError, OSError): + # If timestamp conversion fails, show duration instead + duration = latest_time - earliest_time + header_text = f"Signal Plot\n{duration:.1f}s span" + else: + header_text = "Signal Plot" + + # Update the header + self.flows_table.setHorizontalHeaderLabels([ + "Source IP", "Dest IP", "Pkts", "Bytes", "Max σ", "Avg ΔT", "Std ΔT", "Outliers", "Protocols", header_text + ]) + def on_flow_selected(self): """Handle flow selection""" selected_rows = self.flows_table.selectionModel().selectedRows() @@ -552,27 +590,46 @@ class FlowListDockWidget(QWidget): # Modern color scheme - blue for primary data ax.plot(timestamps, data, color='#0078d4', linewidth=1.2, alpha=0.9) - # Dark theme styling - ax.set_xlabel('Time (s)', fontsize=7, color='#ffffff') - ax.set_ylabel('Amplitude', fontsize=7, color='#ffffff') - ax.tick_params(labelsize=6, colors='#cccccc') + # Dark theme styling with larger, more legible text + ax.set_xlabel('Time (s)', fontsize=9, color='#ffffff') + # ax.set_ylabel('Amplitude', fontsize=9, color='#ffffff') # Removed for cleaner embedded view + ax.tick_params(labelsize=8, colors='#cccccc', left=False, labelleft=False, bottom=False, labelbottom=False) # Hide all tick labels ax.grid(True, alpha=0.15, color='#404040') + # No legend needed for embedded plots - cleaner appearance + + # Add Y-axis min/max indicators for better readability + y_min, y_max = ax.get_ylim() + ax.text(0.02, 0.95, f'Max: {y_max:.2f}', transform=ax.transAxes, + fontsize=7, color='#00ff88', weight='bold', + bbox=dict(boxstyle='round,pad=0.2', facecolor='#2d2d2d', alpha=0.8)) + ax.text(0.02, 0.02, f'Min: {y_min:.2f}', transform=ax.transAxes, + fontsize=7, color='#ff6b6b', weight='bold', + bbox=dict(boxstyle='round,pad=0.2', facecolor='#2d2d2d', alpha=0.8)) + # Style the spines for spine in ax.spines.values(): spine.set_color('#404040') spine.set_linewidth(0.5) - # Add compact title with dark theme - title_text = f'{channel_name} (+{len(signal_data_list)-1} more)' if len(signal_data_list) > 1 else channel_name - ax.set_title(title_text, fontsize=7, color='#ffffff', pad=8) + # Add compact title with time range info + time_start = timestamps[0] if len(timestamps) > 0 else 0 + time_end = timestamps[-1] if len(timestamps) > 0 else 0 + duration = time_end - time_start + + title_text = f'{channel_name}' + if len(signal_data_list) > 1: + title_text += f' (+{len(signal_data_list)-1} more)' + title_text += f' | {duration:.2f}s span' + + ax.set_title(title_text, fontsize=8, color='#ffffff', pad=8) else: self._create_error_plot(figure, canvas, "No channel data") return - # Tight layout with minimal horizontal margins (fixed pixel-equivalent values) - figure.subplots_adjust(left=0.08, right=0.98, top=0.85, bottom=0.25) + # Maximum plot area with ultra-minimal margins on all sides + figure.subplots_adjust(left=0.01, right=0.99, top=0.92, bottom=0.15) canvas.draw_idle() # Use draw_idle to avoid window creation except Exception as e: