cleanup and files added
This commit is contained in:
437
qt_app_pyside1/ui/analytics_tables.py
Normal file
437
qt_app_pyside1/ui/analytics_tables.py
Normal file
@@ -0,0 +1,437 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QTableWidget, QTableWidgetItem,
|
||||
QTabWidget, QScrollArea, QFrame, QHeaderView, QPushButton
|
||||
)
|
||||
from PySide6.QtCore import Qt, QTimer
|
||||
from PySide6.QtGui import QFont, QColor
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class AnalyticsTablesWidget(QWidget):
|
||||
"""Analytics widget with structured tables for debug data"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.debug_data = {
|
||||
'detections': [],
|
||||
'traffic_lights': [],
|
||||
'violations': [],
|
||||
'performance': [],
|
||||
'tracking': []
|
||||
}
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
"""Initialize the UI with tabbed tables"""
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(5, 5, 5, 5)
|
||||
|
||||
# Header
|
||||
header = QLabel("📊 Analytics Tables")
|
||||
header.setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #03DAC5;
|
||||
padding: 8px;
|
||||
background: #1E1E1E;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
""")
|
||||
layout.addWidget(header)
|
||||
|
||||
# Create tab widget
|
||||
self.tabs = QTabWidget()
|
||||
self.tabs.setStyleSheet("""
|
||||
QTabWidget::pane {
|
||||
border: 1px solid #333;
|
||||
background: #1E1E1E;
|
||||
}
|
||||
QTabWidget::tab-bar {
|
||||
alignment: left;
|
||||
}
|
||||
QTabBar::tab {
|
||||
background: #2D2D2D;
|
||||
color: #fff;
|
||||
padding: 8px 16px;
|
||||
margin-right: 2px;
|
||||
border-top-left-radius: 6px;
|
||||
border-top-right-radius: 6px;
|
||||
}
|
||||
QTabBar::tab:selected {
|
||||
background: #03DAC5;
|
||||
color: #000;
|
||||
}
|
||||
QTabBar::tab:hover {
|
||||
background: #018786;
|
||||
}
|
||||
""")
|
||||
|
||||
# Create individual tables
|
||||
self._create_detection_table()
|
||||
self._create_traffic_light_table()
|
||||
self._create_violation_table()
|
||||
self._create_performance_table()
|
||||
self._create_tracking_table()
|
||||
|
||||
layout.addWidget(self.tabs)
|
||||
|
||||
# Control buttons
|
||||
controls = self._create_controls()
|
||||
layout.addWidget(controls)
|
||||
|
||||
def _create_controls(self):
|
||||
"""Create control buttons"""
|
||||
frame = QFrame()
|
||||
frame.setStyleSheet("""
|
||||
QFrame {
|
||||
background: #2D2D2D;
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
}
|
||||
""")
|
||||
layout = QHBoxLayout(frame)
|
||||
|
||||
clear_btn = QPushButton("🗑️ Clear All")
|
||||
clear_btn.setStyleSheet(self._button_style())
|
||||
clear_btn.clicked.connect(self.clear_all_tables)
|
||||
|
||||
export_btn = QPushButton("📤 Export CSV")
|
||||
export_btn.setStyleSheet(self._button_style())
|
||||
export_btn.clicked.connect(self.export_to_csv)
|
||||
|
||||
layout.addWidget(clear_btn)
|
||||
layout.addWidget(export_btn)
|
||||
layout.addStretch()
|
||||
|
||||
return frame
|
||||
|
||||
def _button_style(self):
|
||||
return """
|
||||
QPushButton {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background: #03DAC5;
|
||||
color: #000;
|
||||
}
|
||||
"""
|
||||
|
||||
def _create_detection_table(self):
|
||||
"""Create detection matching table"""
|
||||
table = QTableWidget()
|
||||
table.setColumnCount(5)
|
||||
table.setHorizontalHeaderLabels([
|
||||
"Detection (x,y)", "Track ID", "IoU", "Distance", "Match Status"
|
||||
])
|
||||
|
||||
# Style the table
|
||||
self._style_table(table)
|
||||
|
||||
# Add to tabs
|
||||
scroll = QScrollArea()
|
||||
scroll.setWidget(table)
|
||||
scroll.setWidgetResizable(True)
|
||||
self.tabs.addTab(scroll, "🎯 Detection Matching")
|
||||
|
||||
# Store reference
|
||||
self.detection_table = table
|
||||
|
||||
def _create_traffic_light_table(self):
|
||||
"""Create traffic light status table"""
|
||||
table = QTableWidget()
|
||||
table.setColumnCount(5)
|
||||
table.setHorizontalHeaderLabels([
|
||||
"Detection", "Red Ratio", "Yellow Ratio", "Green Ratio", "Status"
|
||||
])
|
||||
|
||||
self._style_table(table)
|
||||
|
||||
scroll = QScrollArea()
|
||||
scroll.setWidget(table)
|
||||
scroll.setWidgetResizable(True)
|
||||
self.tabs.addTab(scroll, "🚦 Traffic Lights")
|
||||
|
||||
self.traffic_light_table = table
|
||||
|
||||
def _create_violation_table(self):
|
||||
"""Create violation summary table"""
|
||||
table = QTableWidget()
|
||||
table.setColumnCount(4)
|
||||
table.setHorizontalHeaderLabels([
|
||||
"Track ID", "Violation Type", "Timestamp", "Details"
|
||||
])
|
||||
|
||||
self._style_table(table)
|
||||
|
||||
scroll = QScrollArea()
|
||||
scroll.setWidget(table)
|
||||
scroll.setWidgetResizable(True)
|
||||
self.tabs.addTab(scroll, "🚨 Violations")
|
||||
|
||||
self.violation_table = table
|
||||
|
||||
def _create_performance_table(self):
|
||||
"""Create performance metrics table"""
|
||||
table = QTableWidget()
|
||||
table.setColumnCount(6)
|
||||
table.setHorizontalHeaderLabels([
|
||||
"Frame", "FPS", "Inference (ms)", "Device", "Objects", "Timestamp"
|
||||
])
|
||||
|
||||
self._style_table(table)
|
||||
|
||||
scroll = QScrollArea()
|
||||
scroll.setWidget(table)
|
||||
scroll.setWidgetResizable(True)
|
||||
self.tabs.addTab(scroll, "⚡ Performance")
|
||||
|
||||
self.performance_table = table
|
||||
|
||||
def _create_tracking_table(self):
|
||||
"""Create vehicle tracking table"""
|
||||
table = QTableWidget()
|
||||
table.setColumnCount(6)
|
||||
table.setHorizontalHeaderLabels([
|
||||
"Track ID", "Position (x,y)", "Moving", "Violating", "Confidence", "Status"
|
||||
])
|
||||
|
||||
self._style_table(table)
|
||||
|
||||
scroll = QScrollArea()
|
||||
scroll.setWidget(table)
|
||||
scroll.setWidgetResizable(True)
|
||||
self.tabs.addTab(scroll, "🚗 Vehicle Tracking")
|
||||
|
||||
self.tracking_table = table
|
||||
|
||||
def _style_table(self, table):
|
||||
"""Apply consistent styling to tables"""
|
||||
table.setStyleSheet("""
|
||||
QTableWidget {
|
||||
background-color: #1E1E1E;
|
||||
color: #fff;
|
||||
gridline-color: #333;
|
||||
selection-background-color: #03DAC5;
|
||||
selection-color: #000;
|
||||
border: none;
|
||||
font-size: 12px;
|
||||
}
|
||||
QTableWidget::item {
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
QHeaderView::section {
|
||||
background-color: #2D2D2D;
|
||||
color: #03DAC5;
|
||||
padding: 8px;
|
||||
border: none;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
}
|
||||
""")
|
||||
|
||||
# Set header resize mode
|
||||
header = table.horizontalHeader()
|
||||
header.setStretchLastSection(True)
|
||||
header.setSectionResizeMode(QHeaderView.Interactive)
|
||||
|
||||
# Enable sorting
|
||||
table.setSortingEnabled(True)
|
||||
|
||||
def process_debug_logs(self, debug_text):
|
||||
"""Process debug logs and update tables"""
|
||||
if not debug_text:
|
||||
return
|
||||
|
||||
# Parse different types of debug data
|
||||
self._parse_detection_matches(debug_text)
|
||||
self._parse_traffic_lights(debug_text)
|
||||
self._parse_violations(debug_text)
|
||||
self._parse_performance(debug_text)
|
||||
self._parse_tracking(debug_text)
|
||||
|
||||
def _parse_detection_matches(self, text):
|
||||
"""Parse detection matching logs"""
|
||||
# Pattern for successful matches
|
||||
success_pattern = r'\[MATCH SUCCESS\] Detection at \(([\d.]+),([\d.]+)\) matched with track ID=(\d+).*IoU=([\d.]+), distance=([\d.]+)'
|
||||
# Pattern for failed matches
|
||||
fail_pattern = r'\[MATCH FAILED\] No suitable match found for car detection at \(([\d.]+), ([\d.]+)\)'
|
||||
|
||||
# Find successful matches
|
||||
for match in re.finditer(success_pattern, text):
|
||||
x, y, track_id, iou, distance = match.groups()
|
||||
self._add_detection_row(f"({x},{y})", track_id, iou, distance, "✅ Matched")
|
||||
|
||||
# Find failed matches
|
||||
for match in re.finditer(fail_pattern, text):
|
||||
x, y = match.groups()
|
||||
self._add_detection_row(f"({x},{y})", "—", "—", "—", "❌ Unmatched")
|
||||
|
||||
def _parse_traffic_lights(self, text):
|
||||
"""Parse traffic light status logs"""
|
||||
# Pattern for traffic light ratios
|
||||
ratio_pattern = r'\[DEBUG\] ratios: red=([\d.]+), yellow=([\d.]+), green=([\d.]+)'
|
||||
status_pattern = r'📝 Drawing traffic light status: (\w+) at bbox \[(\d+), (\d+), (\d+), (\d+)\]'
|
||||
|
||||
ratios = re.findall(ratio_pattern, text)
|
||||
statuses = re.findall(status_pattern, text)
|
||||
|
||||
# Combine ratios with statuses
|
||||
for i, ((red, yellow, green), (status, x1, y1, x2, y2)) in enumerate(zip(ratios, statuses)):
|
||||
detection_id = f"Traffic Light {i+1}"
|
||||
status_emoji = "🔴" if status == "red" else "🟡" if status == "yellow" else "🟢"
|
||||
self._add_traffic_light_row(detection_id, red, yellow, green, f"{status_emoji} {status.title()}")
|
||||
|
||||
def _parse_violations(self, text):
|
||||
"""Parse violation logs"""
|
||||
violation_pattern = r'🚨 Emitting RED LIGHT VIOLATION: Track ID (\d+)'
|
||||
improper_stop_pattern = r'\[VIOLATION\] Improper stop on crosswalk: Vehicle ID=(\d+) stopped on crosswalk during red light \(overlap=([\d.]+), speed=([\d.]+)\)'
|
||||
|
||||
# Red light violations
|
||||
for match in re.finditer(violation_pattern, text):
|
||||
track_id = match.group(1)
|
||||
timestamp = datetime.now().strftime("%H:%M:%S")
|
||||
self._add_violation_row(track_id, "🚨 Red Light Violation", timestamp, "Vehicle crossed during red light")
|
||||
|
||||
# Improper stops
|
||||
for match in re.finditer(improper_stop_pattern, text):
|
||||
track_id, overlap, speed = match.groups()
|
||||
timestamp = datetime.now().strftime("%H:%M:%S")
|
||||
details = f"Overlap: {overlap}, Speed: {speed}"
|
||||
self._add_violation_row(track_id, "🚧 Improper Stop", timestamp, details)
|
||||
|
||||
def _parse_performance(self, text):
|
||||
"""Parse performance metrics"""
|
||||
perf_pattern = r'\[PERF\] Emitting performance_stats_ready: {.*\'frame_idx\': (\d+).*\'fps\': ([\d.]+).*\'inference_time\': ([\d.]+).*\'device\': \'(\w+)\'.*}'
|
||||
stats_pattern = r'🟢 Stats Updated: FPS=([\d.]+), Inference=([\d.]+)ms, Traffic Light=(\w+)'
|
||||
|
||||
# Performance stats
|
||||
for match in re.finditer(perf_pattern, text):
|
||||
frame_idx, fps, inference_time, device = match.groups()
|
||||
timestamp = datetime.now().strftime("%H:%M:%S")
|
||||
self._add_performance_row(frame_idx, fps, inference_time, device, "—", timestamp)
|
||||
|
||||
# Basic stats
|
||||
for match in re.finditer(stats_pattern, text):
|
||||
fps, inference_time, traffic_light = match.groups()
|
||||
timestamp = datetime.now().strftime("%H:%M:%S")
|
||||
self._add_performance_row("—", fps, inference_time, "—", "—", timestamp)
|
||||
|
||||
def _parse_tracking(self, text):
|
||||
"""Parse vehicle tracking data"""
|
||||
track_pattern = r'Vehicle (\d+): ID=(\d+), center_y=([\d.]+), moving=(\w+), violating=(\w+)'
|
||||
bbox_pattern = r'ID=(\d+) bbox=\[([\d.]+), ([\d.]+), ([\d.]+), ([\d.]+)\] center_y=([\d.]+)'
|
||||
|
||||
# Track vehicles
|
||||
for match in re.finditer(track_pattern, text):
|
||||
vehicle_num, track_id, center_y, moving, violating = match.groups()
|
||||
position = f"(—, {center_y})"
|
||||
status = "🟢 Active"
|
||||
confidence = "—"
|
||||
self._add_tracking_row(track_id, position, moving, violating, confidence, status)
|
||||
|
||||
# Vehicle bboxes
|
||||
for match in re.finditer(bbox_pattern, text):
|
||||
track_id, x1, y1, x2, y2, center_y = match.groups()
|
||||
center_x = (float(x1) + float(x2)) / 2
|
||||
position = f"({center_x:.1f}, {center_y})"
|
||||
# Update existing row or add new one
|
||||
|
||||
def _add_detection_row(self, detection, track_id, iou, distance, status):
|
||||
"""Add row to detection table"""
|
||||
table = self.detection_table
|
||||
row = table.rowCount()
|
||||
table.insertRow(row)
|
||||
|
||||
items = [detection, str(track_id), str(iou), str(distance), status]
|
||||
for col, item in enumerate(items):
|
||||
table_item = QTableWidgetItem(item)
|
||||
if status == "✅ Matched":
|
||||
table_item.setBackground(QColor(3, 218, 197, 50))
|
||||
elif status == "❌ Unmatched":
|
||||
table_item.setBackground(QColor(255, 87, 87, 50))
|
||||
table.setItem(row, col, table_item)
|
||||
|
||||
def _add_traffic_light_row(self, detection, red, yellow, green, status):
|
||||
"""Add row to traffic light table"""
|
||||
table = self.traffic_light_table
|
||||
row = table.rowCount()
|
||||
table.insertRow(row)
|
||||
|
||||
items = [detection, red, yellow, green, status]
|
||||
for col, item in enumerate(items):
|
||||
table_item = QTableWidgetItem(item)
|
||||
if "🔴" in status:
|
||||
table_item.setBackground(QColor(255, 87, 87, 50))
|
||||
elif "🟡" in status:
|
||||
table_item.setBackground(QColor(255, 193, 7, 50))
|
||||
elif "🟢" in status:
|
||||
table_item.setBackground(QColor(76, 175, 80, 50))
|
||||
table.setItem(row, col, table_item)
|
||||
|
||||
def _add_violation_row(self, track_id, violation_type, timestamp, details):
|
||||
"""Add row to violation table"""
|
||||
table = self.violation_table
|
||||
row = table.rowCount()
|
||||
table.insertRow(row)
|
||||
|
||||
items = [track_id, violation_type, timestamp, details]
|
||||
for col, item in enumerate(items):
|
||||
table_item = QTableWidgetItem(item)
|
||||
table_item.setBackground(QColor(255, 87, 87, 30))
|
||||
table.setItem(row, col, table_item)
|
||||
|
||||
def _add_performance_row(self, frame, fps, inference_time, device, objects, timestamp):
|
||||
"""Add row to performance table"""
|
||||
table = self.performance_table
|
||||
row = table.rowCount()
|
||||
table.insertRow(row)
|
||||
|
||||
items = [str(frame), str(fps), str(inference_time), str(device), str(objects), timestamp]
|
||||
for col, item in enumerate(items):
|
||||
table_item = QTableWidgetItem(item)
|
||||
# Color code by device
|
||||
if device == "GPU":
|
||||
table_item.setBackground(QColor(3, 218, 197, 30))
|
||||
elif device == "CPU":
|
||||
table_item.setBackground(QColor(255, 193, 7, 30))
|
||||
table.setItem(row, col, table_item)
|
||||
|
||||
def _add_tracking_row(self, track_id, position, moving, violating, confidence, status):
|
||||
"""Add row to tracking table"""
|
||||
table = self.tracking_table
|
||||
row = table.rowCount()
|
||||
table.insertRow(row)
|
||||
|
||||
items = [track_id, position, moving, violating, confidence, status]
|
||||
for col, item in enumerate(items):
|
||||
table_item = QTableWidgetItem(item)
|
||||
if violating == "True":
|
||||
table_item.setBackground(QColor(255, 87, 87, 50))
|
||||
elif moving == "True":
|
||||
table_item.setBackground(QColor(3, 218, 197, 30))
|
||||
table.setItem(row, col, table_item)
|
||||
|
||||
def clear_all_tables(self):
|
||||
"""Clear all tables"""
|
||||
for table in [self.detection_table, self.traffic_light_table,
|
||||
self.violation_table, self.performance_table, self.tracking_table]:
|
||||
table.setRowCount(0)
|
||||
|
||||
def export_to_csv(self):
|
||||
"""Export tables to CSV files"""
|
||||
# This would implement CSV export functionality
|
||||
print("CSV export functionality would be implemented here")
|
||||
|
||||
def update_from_debug_text(self, debug_text):
|
||||
"""Main method to update tables from debug text"""
|
||||
if debug_text:
|
||||
self.process_debug_logs(debug_text)
|
||||
Reference in New Issue
Block a user