bookmark
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user