cleanup and files added

This commit is contained in:
2025-08-26 13:24:53 -07:00
parent a379d7a063
commit 51a14cd61c
8968 changed files with 1292619 additions and 0 deletions

View File

@@ -0,0 +1,374 @@
"""
Live Monitoring Tab - Real-time traffic monitoring with multi-camera grid view
"""
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QGridLayout,
QLabel, QPushButton, QFrame, QSplitter, QScrollArea,
QGroupBox, QComboBox, QSpinBox, QCheckBox,
QProgressBar, QSlider)
from PySide6.QtCore import Qt, Signal, QTimer, pyqtSignal
from PySide6.QtGui import QFont, QPixmap, QPainter, QColor
from ..widgets.video_display_widget import VideoDisplayWidget
from ..widgets.camera_control_panel import CameraControlPanel
from ..widgets.detection_overlay_widget import DetectionOverlayWidget
from ..widgets.statistics_panel import StatisticsPanel
class LiveMonitoringTab(QWidget):
"""
Live Monitoring Tab with real-time multi-camera grid view
Features:
- Multi-camera grid layout (1x1, 2x2, 3x3, 4x4)
- Real-time video feeds with detection overlays
- Camera control panels for each feed
- Live statistics and alerts
- Recording controls
- Full-screen mode for individual cameras
"""
# Signals
camera_status_changed = Signal(str, str) # camera_id, status
recording_started = Signal(str) # camera_id
recording_stopped = Signal(str) # camera_id
fullscreen_requested = Signal(str) # camera_id
def __init__(self, parent=None):
super().__init__(parent)
# Initialize properties
self.camera_feeds = {}
self.grid_size = (2, 2)
self.recording_status = {}
self._setup_ui()
self._setup_connections()
# Timer for real-time updates
self.update_timer = QTimer()
self.update_timer.timeout.connect(self._update_displays)
self.update_timer.start(100) # Update every 100ms for smooth video
print("✅ Live Monitoring Tab initialized")
def _setup_ui(self):
"""Setup the live monitoring UI"""
# Main layout
main_layout = QHBoxLayout(self)
main_layout.setContentsMargins(10, 10, 10, 10)
main_layout.setSpacing(10)
# Left panel - Camera grid and controls
left_panel = self._create_left_panel()
main_layout.addWidget(left_panel, 3) # 75% width
# Right panel - Statistics and controls
right_panel = self._create_right_panel()
main_layout.addWidget(right_panel, 1) # 25% width
def _create_left_panel(self):
"""Create the left panel with camera grid"""
panel = QFrame()
panel.setObjectName("leftPanel")
layout = QVBoxLayout(panel)
# Header with grid controls
header = self._create_grid_header()
layout.addWidget(header)
# Camera grid container
self.grid_container = QScrollArea()
self.grid_container.setWidgetResizable(True)
self.grid_container.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.grid_container.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
# Create initial grid
self.camera_grid_widget = QWidget()
self.camera_grid_layout = QGridLayout(self.camera_grid_widget)
self.camera_grid_layout.setSpacing(5)
self._setup_camera_grid()
self.grid_container.setWidget(self.camera_grid_widget)
layout.addWidget(self.grid_container)
return panel
def _create_grid_header(self):
"""Create the grid control header"""
header = QFrame()
header.setFixedHeight(50)
layout = QHBoxLayout(header)
# Grid size selector
layout.addWidget(QLabel("Grid Layout:"))
self.grid_selector = QComboBox()
self.grid_selector.addItems(["1×1", "2×2", "3×3", "4×4"])
self.grid_selector.setCurrentText("2×2")
self.grid_selector.currentTextChanged.connect(self._change_grid_size)
layout.addWidget(self.grid_selector)
layout.addStretch()
# Global recording controls
self.record_all_button = QPushButton("🔴 Record All")
self.record_all_button.clicked.connect(self._toggle_record_all)
layout.addWidget(self.record_all_button)
# Snapshot all button
snapshot_button = QPushButton("📸 Snapshot All")
snapshot_button.clicked.connect(self._snapshot_all)
layout.addWidget(snapshot_button)
# Full screen toggle
fullscreen_button = QPushButton("⛶ Fullscreen")
fullscreen_button.clicked.connect(self._toggle_fullscreen)
layout.addWidget(fullscreen_button)
return header
def _create_right_panel(self):
"""Create the right panel with statistics and controls"""
panel = QFrame()
panel.setObjectName("rightPanel")
layout = QVBoxLayout(panel)
# Live statistics
stats_group = QGroupBox("Live Statistics")
stats_layout = QVBoxLayout(stats_group)
self.stats_panel = StatisticsPanel()
stats_layout.addWidget(self.stats_panel)
layout.addWidget(stats_group)
# Camera controls
controls_group = QGroupBox("Camera Controls")
controls_layout = QVBoxLayout(controls_group)
# Active camera selector
controls_layout.addWidget(QLabel("Active Camera:"))
self.active_camera_combo = QComboBox()
self.active_camera_combo.currentTextChanged.connect(self._select_active_camera)
controls_layout.addWidget(self.active_camera_combo)
# Camera control panel
self.camera_controls = CameraControlPanel()
controls_layout.addWidget(self.camera_controls)
layout.addWidget(controls_group)
# Detection settings
detection_group = QGroupBox("Detection Settings")
detection_layout = QVBoxLayout(detection_group)
# Confidence threshold
detection_layout.addWidget(QLabel("Confidence Threshold:"))
self.confidence_slider = QSlider(Qt.Horizontal)
self.confidence_slider.setRange(1, 100)
self.confidence_slider.setValue(50)
self.confidence_slider.valueChanged.connect(self._update_confidence)
detection_layout.addWidget(self.confidence_slider)
self.confidence_label = QLabel("50%")
detection_layout.addWidget(self.confidence_label)
# Detection toggles
self.show_boxes_cb = QCheckBox("Show Bounding Boxes")
self.show_boxes_cb.setChecked(True)
detection_layout.addWidget(self.show_boxes_cb)
self.show_tracks_cb = QCheckBox("Show Tracking IDs")
self.show_tracks_cb.setChecked(True)
detection_layout.addWidget(self.show_tracks_cb)
self.show_speed_cb = QCheckBox("Show Speed Estimates")
self.show_speed_cb.setChecked(False)
detection_layout.addWidget(self.show_speed_cb)
layout.addWidget(detection_group)
# Alert panel
alerts_group = QGroupBox("Live Alerts")
alerts_layout = QVBoxLayout(alerts_group)
self.alerts_scroll = QScrollArea()
self.alerts_scroll.setMaximumHeight(150)
self.alerts_widget = QWidget()
self.alerts_layout = QVBoxLayout(self.alerts_widget)
self.alerts_scroll.setWidget(self.alerts_widget)
alerts_layout.addWidget(self.alerts_scroll)
layout.addWidget(alerts_group)
layout.addStretch()
return panel
def _setup_camera_grid(self):
"""Setup the camera grid based on current grid size"""
# Clear existing grid
for i in reversed(range(self.camera_grid_layout.count())):
child = self.camera_grid_layout.itemAt(i).widget()
if child:
child.setParent(None)
rows, cols = self.grid_size
# Create camera display widgets
for row in range(rows):
for col in range(cols):
camera_id = f"camera_{row}_{col}"
# Create video display widget
video_widget = VideoDisplayWidget(camera_id)
video_widget.setMinimumSize(320, 240)
video_widget.double_clicked.connect(lambda cid=camera_id: self._camera_double_clicked(cid))
self.camera_feeds[camera_id] = video_widget
self.camera_grid_layout.addWidget(video_widget, row, col)
# Add to active camera selector
self.active_camera_combo.addItem(f"Camera {row+1}-{col+1}", camera_id)
def _setup_connections(self):
"""Setup signal connections"""
# Detection overlay connections
if hasattr(self, 'show_boxes_cb'):
self.show_boxes_cb.toggled.connect(self._update_overlay_settings)
if hasattr(self, 'show_tracks_cb'):
self.show_tracks_cb.toggled.connect(self._update_overlay_settings)
if hasattr(self, 'show_speed_cb'):
self.show_speed_cb.toggled.connect(self._update_overlay_settings)
def _change_grid_size(self, size_text):
"""Change the camera grid size"""
size_map = {
"1×1": (1, 1),
"2×2": (2, 2),
"3×3": (3, 3),
"4×4": (4, 4)
}
if size_text in size_map:
self.grid_size = size_map[size_text]
# Clear active camera combo
self.active_camera_combo.clear()
# Recreate grid
self._setup_camera_grid()
print(f"📺 Grid size changed to {size_text}")
def _toggle_record_all(self):
"""Toggle recording for all cameras"""
recording = self.record_all_button.text().startswith("🔴")
if recording:
# Start recording all
self.record_all_button.setText("⏹️ Stop All")
for camera_id in self.camera_feeds:
self.recording_status[camera_id] = True
self.recording_started.emit(camera_id)
else:
# Stop recording all
self.record_all_button.setText("🔴 Record All")
for camera_id in self.camera_feeds:
self.recording_status[camera_id] = False
self.recording_stopped.emit(camera_id)
# Update individual camera displays
self._update_recording_indicators()
def _snapshot_all(self):
"""Take snapshots of all camera feeds"""
for camera_id, widget in self.camera_feeds.items():
widget.take_snapshot()
print("📸 Snapshots taken for all cameras")
def _toggle_fullscreen(self):
"""Toggle fullscreen mode for active camera"""
current_camera = self.active_camera_combo.currentData()
if current_camera:
self.fullscreen_requested.emit(current_camera)
def _select_active_camera(self, camera_text):
"""Select the active camera for controls"""
camera_id = self.active_camera_combo.currentData()
if camera_id and camera_id in self.camera_feeds:
# Update camera controls for selected camera
self.camera_controls.set_active_camera(camera_id)
print(f"📹 Active camera: {camera_text}")
def _camera_double_clicked(self, camera_id):
"""Handle camera double-click for fullscreen"""
self.fullscreen_requested.emit(camera_id)
def _update_confidence(self, value):
"""Update confidence threshold"""
self.confidence_label.setText(f"{value}%")
# Update all video widgets
for widget in self.camera_feeds.values():
widget.set_confidence_threshold(value / 100.0)
def _update_overlay_settings(self):
"""Update detection overlay settings"""
settings = {
'show_boxes': self.show_boxes_cb.isChecked(),
'show_tracks': self.show_tracks_cb.isChecked(),
'show_speed': self.show_speed_cb.isChecked()
}
# Apply to all video widgets
for widget in self.camera_feeds.values():
widget.update_overlay_settings(settings)
def _update_displays(self):
"""Update all video displays (called by timer)"""
# This will be connected to actual video streams
pass
def _update_recording_indicators(self):
"""Update recording indicators on camera widgets"""
for camera_id, widget in self.camera_feeds.items():
is_recording = self.recording_status.get(camera_id, False)
widget.set_recording_indicator(is_recording)
def add_alert(self, message, level="info"):
"""Add a new alert to the alerts panel"""
from ..widgets.alert_widget import AlertWidget
alert = AlertWidget(message, level)
self.alerts_layout.addWidget(alert)
# Remove old alerts if too many
if self.alerts_layout.count() > 10:
old_alert = self.alerts_layout.itemAt(0).widget()
if old_alert:
old_alert.setParent(None)
# Scroll to bottom
self.alerts_scroll.verticalScrollBar().setValue(
self.alerts_scroll.verticalScrollBar().maximum()
)
def update_statistics(self, stats_data):
"""Update the statistics panel"""
if hasattr(self, 'stats_panel'):
self.stats_panel.update_data(stats_data)
def set_camera_feed(self, camera_id, frame):
"""Set video frame for a specific camera"""
if camera_id in self.camera_feeds:
self.camera_feeds[camera_id].set_frame(frame)
def add_detections(self, camera_id, detections):
"""Add detection results to a camera feed"""
if camera_id in self.camera_feeds:
self.camera_feeds[camera_id].add_detections(detections)

View File

@@ -0,0 +1,748 @@
"""
Smart Intersection Tab - IoT integration and traffic control management
"""
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QGridLayout,
QGroupBox, QLabel, QPushButton, QComboBox,
QSlider, QProgressBar, QFrame, QSplitter,
QScrollArea, QCheckBox, QSpinBox, QTabWidget,
QTableWidget, QTableWidgetItem, QTextEdit,
QDateTimeEdit, QHeaderView)
from PySide6.QtCore import Qt, Signal, QTimer, QDateTime
from PySide6.QtGui import QFont, QColor, QPainter, QPen, QBrush
import json
class TrafficLightController(QWidget):
"""Traffic light control widget"""
light_changed = Signal(str, str) # intersection_id, new_state
def __init__(self, intersection_id="intersection_1", parent=None):
super().__init__(parent)
self.intersection_id = intersection_id
self.current_state = "auto"
self.current_phase = "green_ns" # north-south green
self._setup_ui()
def _setup_ui(self):
"""Setup traffic light control UI"""
layout = QVBoxLayout(self)
# Header
header = QLabel(f"🚦 {self.intersection_id.replace('_', ' ').title()}")
header.setFont(QFont("Segoe UI", 10, QFont.Bold))
header.setAlignment(Qt.AlignCenter)
layout.addWidget(header)
# Traffic light visualization
lights_frame = QFrame()
lights_frame.setFixedSize(120, 200)
lights_frame.setStyleSheet("""
QFrame {
background-color: #2c3e50;
border: 2px solid #34495e;
border-radius: 8px;
}
""")
lights_layout = QVBoxLayout(lights_frame)
lights_layout.setSpacing(5)
lights_layout.setContentsMargins(10, 10, 10, 10)
# North-South lights
ns_label = QLabel("N-S")
ns_label.setStyleSheet("color: white; font-size: 8pt;")
ns_label.setAlignment(Qt.AlignCenter)
lights_layout.addWidget(ns_label)
self.ns_red = self._create_light_indicator("red", False)
self.ns_yellow = self._create_light_indicator("yellow", False)
self.ns_green = self._create_light_indicator("green", True)
lights_layout.addWidget(self.ns_red)
lights_layout.addWidget(self.ns_yellow)
lights_layout.addWidget(self.ns_green)
# East-West lights
ew_label = QLabel("E-W")
ew_label.setStyleSheet("color: white; font-size: 8pt;")
ew_label.setAlignment(Qt.AlignCenter)
lights_layout.addWidget(ew_label)
self.ew_red = self._create_light_indicator("red", True)
self.ew_yellow = self._create_light_indicator("yellow", False)
self.ew_green = self._create_light_indicator("green", False)
lights_layout.addWidget(self.ew_red)
lights_layout.addWidget(self.ew_yellow)
lights_layout.addWidget(self.ew_green)
layout.addWidget(lights_frame, 0, Qt.AlignCenter)
# Control buttons
controls_layout = QVBoxLayout()
self.auto_btn = QPushButton("🤖 Auto")
self.auto_btn.setCheckable(True)
self.auto_btn.setChecked(True)
self.auto_btn.clicked.connect(lambda: self._set_mode("auto"))
controls_layout.addWidget(self.auto_btn)
self.manual_btn = QPushButton("👤 Manual")
self.manual_btn.setCheckable(True)
self.manual_btn.clicked.connect(lambda: self._set_mode("manual"))
controls_layout.addWidget(self.manual_btn)
self.emergency_btn = QPushButton("🚨 Emergency")
self.emergency_btn.setStyleSheet("""
QPushButton {
background-color: #e74c3c;
color: white;
font-weight: bold;
}
QPushButton:hover {
background-color: #c0392b;
}
""")
self.emergency_btn.clicked.connect(lambda: self._set_mode("emergency"))
controls_layout.addWidget(self.emergency_btn)
layout.addLayout(controls_layout)
# Status
self.status_label = QLabel("Status: Auto Mode")
self.status_label.setStyleSheet("font-size: 8pt; color: #27ae60;")
self.status_label.setAlignment(Qt.AlignCenter)
layout.addWidget(self.status_label)
def _create_light_indicator(self, color, active=False):
"""Create a traffic light indicator"""
light = QLabel()
light.setFixedSize(20, 20)
light.setStyleSheet(f"""
QLabel {{
background-color: {'#' + color if active else '#2c3e50'};
border: 1px solid #34495e;
border-radius: 10px;
opacity: {'1.0' if active else '0.3'};
}}
""")
return light
def _set_mode(self, mode):
"""Set traffic light control mode"""
self.current_state = mode
# Update button states
self.auto_btn.setChecked(mode == "auto")
self.manual_btn.setChecked(mode == "manual")
# Update status
status_colors = {
"auto": "#27ae60",
"manual": "#f39c12",
"emergency": "#e74c3c"
}
self.status_label.setText(f"Status: {mode.title()} Mode")
self.status_label.setStyleSheet(f"font-size: 8pt; color: {status_colors.get(mode, '#95a5a6')};")
# Emit signal
self.light_changed.emit(self.intersection_id, mode)
print(f"🚦 {self.intersection_id} set to {mode} mode")
class IoTDeviceWidget(QFrame):
"""IoT device status widget"""
def __init__(self, device_id, device_type, status="online", parent=None):
super().__init__(parent)
self.device_id = device_id
self.device_type = device_type
self.status = status
self.setFixedSize(150, 80)
self._setup_ui()
self._apply_style()
def _setup_ui(self):
"""Setup IoT device widget UI"""
layout = QVBoxLayout(self)
layout.setContentsMargins(8, 6, 8, 6)
# Device header
header_layout = QHBoxLayout()
# Device icon
icons = {
"camera": "📷",
"sensor": "📡",
"controller": "🎛️",
"display": "📺",
"gateway": "🌐"
}
icon_label = QLabel(icons.get(self.device_type, "📟"))
icon_label.setFont(QFont("Arial", 14))
header_layout.addWidget(icon_label)
# Device name
name_label = QLabel(self.device_id.replace('_', ' ').title())
name_label.setFont(QFont("Segoe UI", 8, QFont.Bold))
header_layout.addWidget(name_label)
header_layout.addStretch()
# Status indicator
self.status_indicator = QLabel("")
self.status_indicator.setFont(QFont("Arial", 10))
header_layout.addWidget(self.status_indicator)
layout.addLayout(header_layout)
# Device info
info_layout = QVBoxLayout()
type_label = QLabel(f"Type: {self.device_type.title()}")
type_label.setFont(QFont("Segoe UI", 7))
info_layout.addWidget(type_label)
self.status_label = QLabel(f"Status: {self.status.title()}")
self.status_label.setFont(QFont("Segoe UI", 7))
info_layout.addWidget(self.status_label)
layout.addLayout(info_layout)
self._update_status_display()
def _apply_style(self):
"""Apply device widget styling"""
self.setStyleSheet("""
QFrame {
background-color: white;
border: 1px solid #e1e8ed;
border-radius: 6px;
margin: 2px;
}
QFrame:hover {
border-color: #3498db;
}
""")
def _update_status_display(self):
"""Update status indicator colors"""
colors = {
"online": "#27ae60",
"offline": "#e74c3c",
"warning": "#f39c12",
"error": "#c0392b"
}
color = colors.get(self.status, "#95a5a6")
self.status_indicator.setStyleSheet(f"color: {color};")
self.status_label.setText(f"Status: {self.status.title()}")
def set_status(self, status):
"""Update device status"""
self.status = status
self._update_status_display()
class SmartIntersectionTab(QWidget):
"""
Smart Intersection Tab for IoT integration and traffic control
Features:
- Traffic light control and monitoring
- IoT device management
- Real-time traffic flow optimization
- Emergency response coordination
- Data analytics and reporting
- System integration dashboard
"""
# Signals
traffic_control_changed = Signal(dict)
iot_device_updated = Signal(str, str)
emergency_activated = Signal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.iot_devices = {}
self.traffic_controllers = {}
self.intersection_data = {}
self._setup_ui()
# Timer for real-time updates
self.update_timer = QTimer()
self.update_timer.timeout.connect(self._update_intersection_data)
self.update_timer.start(2000) # Update every 2 seconds
print("🌉 Smart Intersection Tab initialized")
def _setup_ui(self):
"""Setup the smart intersection UI"""
layout = QVBoxLayout(self)
# Header with system overview
header = self._create_header()
layout.addWidget(header)
# Main content splitter
main_splitter = QSplitter(Qt.Horizontal)
layout.addWidget(main_splitter)
# Left panel - Traffic control
left_panel = self._create_traffic_control_panel()
main_splitter.addWidget(left_panel)
# Right panel - IoT devices and analytics
right_panel = self._create_iot_panel()
main_splitter.addWidget(right_panel)
# Set splitter proportions
main_splitter.setSizes([500, 500])
def _create_header(self):
"""Create header with intersection overview"""
header = QFrame()
header.setFixedHeight(80)
header.setStyleSheet("""
QFrame {
background-color: #16a085;
border-radius: 8px;
margin-bottom: 10px;
}
""")
layout = QHBoxLayout(header)
layout.setContentsMargins(20, 10, 20, 10)
# Title section
title_layout = QVBoxLayout()
title = QLabel("🌉 Smart Intersection Control Center")
title.setFont(QFont("Segoe UI", 16, QFont.Bold))
title.setStyleSheet("color: white;")
title_layout.addWidget(title)
subtitle = QLabel("Real-time traffic optimization and IoT device management")
subtitle.setFont(QFont("Segoe UI", 9))
subtitle.setStyleSheet("color: #ecf0f1;")
title_layout.addWidget(subtitle)
layout.addLayout(title_layout)
layout.addStretch()
# Statistics cards
stats_layout = QHBoxLayout()
# Active intersections
intersections_card = self._create_header_card("Active Intersections", "4", "#27ae60")
stats_layout.addWidget(intersections_card)
# IoT devices
devices_card = self._create_header_card("IoT Devices", "12", "#3498db")
stats_layout.addWidget(devices_card)
# Traffic efficiency
efficiency_card = self._create_header_card("Traffic Efficiency", "87%", "#f39c12")
stats_layout.addWidget(efficiency_card)
layout.addLayout(stats_layout)
return header
def _create_header_card(self, title, value, color):
"""Create a header statistics card"""
card = QFrame()
card.setFixedSize(120, 50)
card.setStyleSheet(f"""
QFrame {{
background-color: {color};
border-radius: 6px;
margin: 2px;
}}
""")
layout = QVBoxLayout(card)
layout.setContentsMargins(8, 4, 8, 4)
value_label = QLabel(value)
value_label.setFont(QFont("Segoe UI", 14, QFont.Bold))
value_label.setStyleSheet("color: white;")
value_label.setAlignment(Qt.AlignCenter)
layout.addWidget(value_label)
title_label = QLabel(title)
title_label.setFont(QFont("Segoe UI", 7))
title_label.setStyleSheet("color: white;")
title_label.setAlignment(Qt.AlignCenter)
layout.addWidget(title_label)
return card
def _create_traffic_control_panel(self):
"""Create traffic control panel"""
panel = QFrame()
layout = QVBoxLayout(panel)
# Traffic control section
control_group = QGroupBox("Traffic Light Control")
control_layout = QVBoxLayout(control_group)
# Intersection controllers grid
controllers_scroll = QScrollArea()
controllers_widget = QWidget()
controllers_layout = QGridLayout(controllers_widget)
# Create traffic light controllers
intersections = [
"main_oak", "5th_pine", "broadway_2nd", "market_1st"
]
for i, intersection_id in enumerate(intersections):
controller = TrafficLightController(intersection_id)
controller.light_changed.connect(self._on_traffic_control_changed)
self.traffic_controllers[intersection_id] = controller
controllers_layout.addWidget(controller, i // 2, i % 2)
controllers_scroll.setWidget(controllers_widget)
controllers_scroll.setMaximumHeight(450)
control_layout.addWidget(controllers_scroll)
layout.addWidget(control_group)
# Global controls
global_controls = self._create_global_controls()
layout.addWidget(global_controls)
return panel
def _create_global_controls(self):
"""Create global traffic control section"""
section = QGroupBox("Global Controls")
layout = QVBoxLayout(section)
# Emergency controls
emergency_layout = QHBoxLayout()
emergency_all_btn = QPushButton("🚨 Emergency All")
emergency_all_btn.setStyleSheet("""
QPushButton {
background-color: #e74c3c;
color: white;
font-weight: bold;
padding: 8px;
}
QPushButton:hover {
background-color: #c0392b;
}
""")
emergency_all_btn.clicked.connect(self._emergency_all_intersections)
emergency_layout.addWidget(emergency_all_btn)
clear_emergency_btn = QPushButton("✅ Clear Emergency")
clear_emergency_btn.setStyleSheet("""
QPushButton {
background-color: #27ae60;
color: white;
font-weight: bold;
padding: 8px;
}
QPushButton:hover {
background-color: #229954;
}
""")
clear_emergency_btn.clicked.connect(self._clear_emergency_all)
emergency_layout.addWidget(clear_emergency_btn)
layout.addLayout(emergency_layout)
# Traffic optimization
optimization_layout = QHBoxLayout()
self.adaptive_cb = QCheckBox("Adaptive Traffic Control")
self.adaptive_cb.setChecked(True)
self.adaptive_cb.toggled.connect(self._toggle_adaptive_control)
optimization_layout.addWidget(self.adaptive_cb)
optimize_btn = QPushButton("🔄 Optimize Flow")
optimize_btn.clicked.connect(self._optimize_traffic_flow)
optimization_layout.addWidget(optimize_btn)
layout.addLayout(optimization_layout)
return section
def _create_iot_panel(self):
"""Create IoT devices and analytics panel"""
panel = QFrame()
layout = QVBoxLayout(panel)
# IoT devices section
iot_group = QGroupBox("IoT Device Network")
iot_layout = QVBoxLayout(iot_group)
# Device grid
devices_scroll = QScrollArea()
devices_widget = QWidget()
self.devices_layout = QGridLayout(devices_widget)
# Create sample IoT devices
devices_data = [
("camera_001", "camera", "online"),
("sensor_002", "sensor", "online"),
("controller_003", "controller", "online"),
("display_004", "display", "warning"),
("gateway_005", "gateway", "online"),
("sensor_006", "sensor", "offline"),
("camera_007", "camera", "online"),
("controller_008", "controller", "error"),
]
for i, (device_id, device_type, status) in enumerate(devices_data):
device_widget = IoTDeviceWidget(device_id, device_type, status)
self.iot_devices[device_id] = device_widget
self.devices_layout.addWidget(device_widget, i // 3, i % 3)
devices_scroll.setWidget(devices_widget)
devices_scroll.setMaximumHeight(200)
iot_layout.addWidget(devices_scroll)
# Device controls
device_controls = QHBoxLayout()
refresh_devices_btn = QPushButton("🔄 Refresh")
refresh_devices_btn.clicked.connect(self._refresh_devices)
device_controls.addWidget(refresh_devices_btn)
add_device_btn = QPushButton(" Add Device")
add_device_btn.clicked.connect(self._add_device)
device_controls.addWidget(add_device_btn)
device_controls.addStretch()
iot_layout.addLayout(device_controls)
layout.addWidget(iot_group)
# Analytics section
analytics_group = self._create_analytics_section()
layout.addWidget(analytics_group)
return panel
def _create_analytics_section(self):
"""Create analytics and reporting section"""
section = QGroupBox("Traffic Analytics")
layout = QVBoxLayout(section)
# Analytics tabs
analytics_tabs = QTabWidget()
# Real-time tab
realtime_tab = QWidget()
realtime_layout = QVBoxLayout(realtime_tab)
# Real-time metrics
metrics_layout = QGridLayout()
metrics_layout.addWidget(QLabel("Current Traffic Volume:"), 0, 0)
self.volume_label = QLabel("Medium")
metrics_layout.addWidget(self.volume_label, 0, 1)
metrics_layout.addWidget(QLabel("Average Wait Time:"), 1, 0)
self.wait_time_label = QLabel("45 seconds")
metrics_layout.addWidget(self.wait_time_label, 1, 1)
metrics_layout.addWidget(QLabel("Traffic Efficiency:"), 2, 0)
self.efficiency_label = QLabel("87%")
metrics_layout.addWidget(self.efficiency_label, 2, 1)
metrics_layout.addWidget(QLabel("Active Violations:"), 3, 0)
self.violations_label = QLabel("3")
metrics_layout.addWidget(self.violations_label, 3, 1)
realtime_layout.addLayout(metrics_layout)
# Traffic flow visualization (placeholder)
flow_frame = QFrame()
flow_frame.setFixedHeight(100)
flow_frame.setStyleSheet("""
QFrame {
background-color: #ecf0f1;
border: 1px solid #bdc3c7;
border-radius: 4px;
}
""")
flow_layout = QVBoxLayout(flow_frame)
flow_label = QLabel("Traffic Flow Visualization\n(Real-time heatmap)")
flow_label.setAlignment(Qt.AlignCenter)
flow_label.setStyleSheet("color: #7f8c8d;")
flow_layout.addWidget(flow_label)
realtime_layout.addWidget(flow_frame)
analytics_tabs.addTab(realtime_tab, "📊 Real-time")
# Historical tab
historical_tab = QWidget()
historical_layout = QVBoxLayout(historical_tab)
# Time range selector
range_layout = QHBoxLayout()
range_layout.addWidget(QLabel("Time Range:"))
time_range_combo = QComboBox()
time_range_combo.addItems(["Last Hour", "Last 24 Hours", "Last Week", "Last Month"])
range_layout.addWidget(time_range_combo)
range_layout.addStretch()
export_btn = QPushButton("📤 Export Report")
export_btn.clicked.connect(self._export_analytics)
range_layout.addWidget(export_btn)
historical_layout.addLayout(range_layout)
# Historical data display (placeholder)
historical_frame = QFrame()
historical_frame.setFixedHeight(120)
historical_frame.setStyleSheet("""
QFrame {
background-color: #ecf0f1;
border: 1px solid #bdc3c7;
border-radius: 4px;
}
""")
historical_layout_frame = QVBoxLayout(historical_frame)
historical_label = QLabel("Historical Traffic Patterns\n(Charts and trends)")
historical_label.setAlignment(Qt.AlignCenter)
historical_label.setStyleSheet("color: #7f8c8d;")
historical_layout_frame.addWidget(historical_label)
historical_layout.addWidget(historical_frame)
analytics_tabs.addTab(historical_tab, "📈 Historical")
layout.addWidget(analytics_tabs)
return section
def _on_traffic_control_changed(self, intersection_id, mode):
"""Handle traffic control changes"""
control_data = {
'intersection': intersection_id,
'mode': mode,
'timestamp': QDateTime.currentDateTime().toString()
}
self.traffic_control_changed.emit(control_data)
print(f"🌉 Traffic control changed: {intersection_id} -> {mode}")
def _emergency_all_intersections(self):
"""Activate emergency mode for all intersections"""
for controller in self.traffic_controllers.values():
controller._set_mode("emergency")
self.emergency_activated.emit("all_intersections")
print("🌉 Emergency mode activated for all intersections")
def _clear_emergency_all(self):
"""Clear emergency mode for all intersections"""
for controller in self.traffic_controllers.values():
controller._set_mode("auto")
print("🌉 Emergency mode cleared for all intersections")
def _toggle_adaptive_control(self, enabled):
"""Toggle adaptive traffic control"""
status = "enabled" if enabled else "disabled"
print(f"🌉 Adaptive traffic control {status}")
def _optimize_traffic_flow(self):
"""Optimize traffic flow patterns"""
print("🌉 Optimizing traffic flow patterns")
# Simulate optimization results
self.efficiency_label.setText("92%")
self.wait_time_label.setText("38 seconds")
def _refresh_devices(self):
"""Refresh IoT device status"""
import random
statuses = ["online", "offline", "warning", "error"]
for device in self.iot_devices.values():
# Randomly update some device statuses
if random.random() < 0.3: # 30% chance to change status
new_status = random.choice(statuses)
device.set_status(new_status)
print("🌉 IoT devices refreshed")
def _add_device(self):
"""Add new IoT device"""
# Simulate adding a new device
device_count = len(self.iot_devices) + 1
device_id = f"device_{device_count:03d}"
device_type = "sensor"
device_widget = IoTDeviceWidget(device_id, device_type, "online")
self.iot_devices[device_id] = device_widget
# Add to grid
row = (len(self.iot_devices) - 1) // 3
col = (len(self.iot_devices) - 1) % 3
self.devices_layout.addWidget(device_widget, row, col)
print(f"🌉 Added new IoT device: {device_id}")
def _export_analytics(self):
"""Export traffic analytics"""
print("🌉 Exporting traffic analytics")
def _update_intersection_data(self):
"""Update real-time intersection data"""
import random
# Simulate real-time data updates
volumes = ["Low", "Medium", "High"]
self.volume_label.setText(random.choice(volumes))
wait_time = random.randint(30, 60)
self.wait_time_label.setText(f"{wait_time} seconds")
efficiency = random.randint(80, 95)
self.efficiency_label.setText(f"{efficiency}%")
violations = random.randint(0, 5)
self.violations_label.setText(str(violations))
def set_intersection_mode(self, intersection_id, mode):
"""Set mode for specific intersection"""
if intersection_id in self.traffic_controllers:
self.traffic_controllers[intersection_id]._set_mode(mode)
def get_system_status(self):
"""Get current system status"""
online_devices = sum(1 for device in self.iot_devices.values()
if device.status == "online")
total_devices = len(self.iot_devices)
return {
'intersections': len(self.traffic_controllers),
'devices_online': online_devices,
'devices_total': total_devices,
'traffic_efficiency': self.efficiency_label.text(),
'adaptive_control': self.adaptive_cb.isChecked()
}

View File

@@ -0,0 +1,702 @@
"""
System Performance Tab - Grafana-like performance monitoring dashboard
"""
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QGridLayout,
QGroupBox, QLabel, QPushButton, QComboBox,
QSlider, QProgressBar, QFrame, QSplitter,
QScrollArea, QCheckBox, QSpinBox, QTabWidget)
from PySide6.QtCore import Qt, Signal, QTimer, QDateTime
from PySide6.QtGui import QFont, QColor, QPainter, QPen, QBrush
import random
import math
class MetricCard(QFrame):
"""Individual metric display card"""
def __init__(self, title, value, unit="", trend=None, color="#3498db", parent=None):
super().__init__(parent)
self.title = title
self.value = value
self.unit = unit
self.trend = trend
self.color = color
self.setFixedSize(200, 120)
self._setup_ui()
self._apply_style()
def _setup_ui(self):
"""Setup metric card UI"""
layout = QVBoxLayout(self)
layout.setContentsMargins(15, 10, 15, 10)
# Title
title_label = QLabel(self.title)
title_label.setFont(QFont("Segoe UI", 9))
title_label.setStyleSheet("color: #7f8c8d; font-weight: 500;")
layout.addWidget(title_label)
# Value with unit
value_layout = QHBoxLayout()
value_label = QLabel(str(self.value))
value_label.setFont(QFont("Segoe UI", 20, QFont.Bold))
value_label.setStyleSheet(f"color: {self.color};")
value_layout.addWidget(value_label)
if self.unit:
unit_label = QLabel(self.unit)
unit_label.setFont(QFont("Segoe UI", 10))
unit_label.setStyleSheet("color: #95a5a6;")
unit_label.setAlignment(Qt.AlignBottom)
value_layout.addWidget(unit_label)
value_layout.addStretch()
layout.addLayout(value_layout)
# Trend indicator
if self.trend is not None:
trend_label = QLabel(f"{'' if self.trend > 0 else '' if self.trend < 0 else ''} {abs(self.trend):.1f}%")
trend_color = "#27ae60" if self.trend > 0 else "#e74c3c" if self.trend < 0 else "#95a5a6"
trend_label.setStyleSheet(f"color: {trend_color}; font-size: 8pt;")
layout.addWidget(trend_label)
layout.addStretch()
def _apply_style(self):
"""Apply card styling"""
self.setStyleSheet("""
QFrame {
background-color: white;
border: 1px solid #e1e8ed;
border-radius: 8px;
margin: 5px;
}
QFrame:hover {
border-color: #3498db;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
""")
def update_value(self, value, trend=None):
"""Update metric value"""
self.value = value
self.trend = trend
# Re-setup UI to reflect changes
# Clear and rebuild
for i in reversed(range(self.layout().count())):
child = self.layout().itemAt(i).widget()
if child:
child.setParent(None)
self._setup_ui()
class SimpleChart(QWidget):
"""Simple line chart widget"""
def __init__(self, title="Chart", width=300, height=200, parent=None):
super().__init__(parent)
self.title = title
self.data_points = []
self.max_points = 50
self.color = QColor(52, 152, 219)
self.setFixedSize(width, height)
self.setStyleSheet("""
QWidget {
background-color: white;
border: 1px solid #e1e8ed;
border-radius: 8px;
}
""")
def add_data_point(self, value):
"""Add a new data point"""
self.data_points.append(value)
if len(self.data_points) > self.max_points:
self.data_points.pop(0)
self.update()
def paintEvent(self, event):
"""Custom paint event for chart"""
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
# Draw background
painter.fillRect(self.rect(), QColor(255, 255, 255))
# Draw title
painter.setPen(QColor(44, 62, 80))
painter.setFont(QFont("Segoe UI", 10, QFont.Bold))
painter.drawText(10, 20, self.title)
if len(self.data_points) < 2:
return
# Calculate chart area
chart_rect = self.rect().adjusted(20, 30, -20, -20)
# Find min and max values
min_val = min(self.data_points)
max_val = max(self.data_points)
if min_val == max_val:
max_val = min_val + 1
# Draw grid lines
painter.setPen(QPen(QColor(236, 240, 241), 1))
for i in range(5):
y = chart_rect.top() + (chart_rect.height() * i / 4)
painter.drawLine(chart_rect.left(), y, chart_rect.right(), y)
# Draw data line
painter.setPen(QPen(self.color, 2))
for i in range(1, len(self.data_points)):
x1 = chart_rect.left() + (chart_rect.width() * (i-1) / (len(self.data_points)-1))
y1 = chart_rect.bottom() - (chart_rect.height() * (self.data_points[i-1] - min_val) / (max_val - min_val))
x2 = chart_rect.left() + (chart_rect.width() * i / (len(self.data_points)-1))
y2 = chart_rect.bottom() - (chart_rect.height() * (self.data_points[i] - min_val) / (max_val - min_val))
painter.drawLine(x1, y1, x2, y2)
class SystemPerformanceTab(QWidget):
"""
System Performance Tab with Grafana-like monitoring dashboard
Features:
- Real-time performance metrics
- Interactive charts and graphs
- System resource monitoring
- Performance alerts and thresholds
- Historical data analysis
- Export capabilities
"""
# Signals
performance_alert = Signal(str, str) # alert_type, message
threshold_exceeded = Signal(str, float) # metric_name, value
export_requested = Signal(str) # export_type
def __init__(self, parent=None):
super().__init__(parent)
self.performance_data = {}
self.alerts_enabled = True
self._setup_ui()
# Timer for real-time updates
self.update_timer = QTimer()
self.update_timer.timeout.connect(self._update_metrics)
self.update_timer.start(1000) # Update every second
print("🔥 System Performance Tab initialized")
def _setup_ui(self):
"""Setup the performance monitoring UI"""
layout = QVBoxLayout(self)
# Header with controls
header = self._create_header()
layout.addWidget(header)
# Main content tabs
content_tabs = self._create_content_tabs()
layout.addWidget(content_tabs)
def _create_header(self):
"""Create header with system overview and controls"""
header = QFrame()
header.setFixedHeight(60)
header.setStyleSheet("""
QFrame {
background-color: #2c3e50;
border-radius: 8px;
margin-bottom: 10px;
}
""")
layout = QHBoxLayout(header)
layout.setContentsMargins(20, 10, 20, 10)
# System status
status_layout = QVBoxLayout()
system_label = QLabel("🔥 System Performance Monitor")
system_label.setFont(QFont("Segoe UI", 14, QFont.Bold))
system_label.setStyleSheet("color: white;")
status_layout.addWidget(system_label)
self.system_health_label = QLabel("System Health: Optimal")
self.system_health_label.setFont(QFont("Segoe UI", 9))
self.system_health_label.setStyleSheet("color: #27ae60;")
status_layout.addWidget(self.system_health_label)
layout.addLayout(status_layout)
layout.addStretch()
# Quick metrics
quick_metrics = QHBoxLayout()
# CPU usage
cpu_layout = QVBoxLayout()
cpu_layout.addWidget(QLabel("CPU"))
self.cpu_progress = QProgressBar()
self.cpu_progress.setRange(0, 100)
self.cpu_progress.setFixedWidth(80)
cpu_layout.addWidget(self.cpu_progress)
quick_metrics.addLayout(cpu_layout)
# Memory usage
memory_layout = QVBoxLayout()
memory_layout.addWidget(QLabel("Memory"))
self.memory_progress = QProgressBar()
self.memory_progress.setRange(0, 100)
self.memory_progress.setFixedWidth(80)
memory_layout.addWidget(self.memory_progress)
quick_metrics.addLayout(memory_layout)
# GPU usage
gpu_layout = QVBoxLayout()
gpu_layout.addWidget(QLabel("GPU"))
self.gpu_progress = QProgressBar()
self.gpu_progress.setRange(0, 100)
self.gpu_progress.setFixedWidth(80)
gpu_layout.addWidget(self.gpu_progress)
quick_metrics.addLayout(gpu_layout)
# Style progress bars
progress_style = """
QProgressBar {
border: 1px solid #34495e;
border-radius: 3px;
text-align: center;
color: white;
font-size: 8pt;
}
QProgressBar::chunk {
background-color: #3498db;
border-radius: 2px;
}
QLabel {
color: white;
font-size: 8pt;
text-align: center;
}
"""
for widget in [self.cpu_progress, self.memory_progress, self.gpu_progress]:
widget.setStyleSheet(progress_style)
layout.addLayout(quick_metrics)
# Control buttons
controls_layout = QVBoxLayout()
alerts_btn = QPushButton("🔔 Alerts")
alerts_btn.setFixedSize(80, 25)
alerts_btn.setCheckable(True)
alerts_btn.setChecked(True)
alerts_btn.toggled.connect(self._toggle_alerts)
controls_layout.addWidget(alerts_btn)
export_btn = QPushButton("📊 Export")
export_btn.setFixedSize(80, 25)
export_btn.clicked.connect(self._export_performance_data)
controls_layout.addWidget(export_btn)
# Style buttons
button_style = """
QPushButton {
background-color: #34495e;
color: white;
border: 1px solid #34495e;
border-radius: 4px;
font-size: 8pt;
}
QPushButton:hover {
background-color: #4a6741;
}
QPushButton:checked {
background-color: #27ae60;
}
"""
alerts_btn.setStyleSheet(button_style)
export_btn.setStyleSheet(button_style)
layout.addLayout(controls_layout)
return header
def _create_content_tabs(self):
"""Create main content tabs"""
tabs = QTabWidget()
# Overview tab
overview_tab = self._create_overview_tab()
tabs.addTab(overview_tab, "📊 Overview")
# Detailed metrics tab
metrics_tab = self._create_metrics_tab()
tabs.addTab(metrics_tab, "📈 Detailed Metrics")
# Alerts tab
alerts_tab = self._create_alerts_tab()
tabs.addTab(alerts_tab, "🚨 Alerts")
# Settings tab
settings_tab = self._create_settings_tab()
tabs.addTab(settings_tab, "⚙️ Settings")
return tabs
def _create_overview_tab(self):
"""Create overview dashboard tab"""
tab = QWidget()
layout = QVBoxLayout(tab)
# Metric cards grid
cards_scroll = QScrollArea()
cards_widget = QWidget()
cards_layout = QGridLayout(cards_widget)
# Create metric cards
self.metric_cards = {}
cards_data = [
("FPS", 0, "fps", None, "#3498db"),
("Processing Time", 0, "ms", None, "#e74c3c"),
("CPU Usage", 0, "%", None, "#f39c12"),
("Memory Usage", 0, "%", None, "#9b59b6"),
("GPU Usage", 0, "%", None, "#1abc9c"),
("Network I/O", 0, "MB/s", None, "#34495e"),
("Disk I/O", 0, "MB/s", None, "#95a5a6"),
("Temperature", 0, "°C", None, "#e67e22"),
]
for i, (title, value, unit, trend, color) in enumerate(cards_data):
card = MetricCard(title, value, unit, trend, color)
self.metric_cards[title.lower().replace(" ", "_")] = card
cards_layout.addWidget(card, i // 4, i % 4)
cards_scroll.setWidget(cards_widget)
cards_scroll.setMaximumHeight(280)
layout.addWidget(cards_scroll)
# Charts section
charts_layout = QHBoxLayout()
# FPS chart
self.fps_chart = SimpleChart("FPS Over Time", 400, 200)
self.fps_chart.color = QColor(52, 152, 219)
charts_layout.addWidget(self.fps_chart)
# CPU/Memory chart
self.resource_chart = SimpleChart("Resource Usage", 400, 200)
self.resource_chart.color = QColor(243, 156, 18)
charts_layout.addWidget(self.resource_chart)
layout.addLayout(charts_layout)
return tab
def _create_metrics_tab(self):
"""Create detailed metrics tab"""
tab = QWidget()
layout = QVBoxLayout(tab)
# Metrics filter
filter_layout = QHBoxLayout()
filter_layout.addWidget(QLabel("Category:"))
category_combo = QComboBox()
category_combo.addItems(["All", "Performance", "Resources", "Network", "Storage"])
filter_layout.addWidget(category_combo)
filter_layout.addWidget(QLabel("Time Range:"))
time_combo = QComboBox()
time_combo.addItems(["Last 5 minutes", "Last hour", "Last 24 hours", "Last week"])
filter_layout.addWidget(time_combo)
filter_layout.addStretch()
refresh_btn = QPushButton("🔄 Refresh")
refresh_btn.clicked.connect(self._refresh_metrics)
filter_layout.addWidget(refresh_btn)
layout.addLayout(filter_layout)
# Detailed charts area
charts_scroll = QScrollArea()
charts_widget = QWidget()
charts_layout = QVBoxLayout(charts_widget)
# Create multiple detailed charts
chart_titles = [
"Frame Processing Pipeline",
"Detection Performance",
"Memory Allocation",
"GPU Utilization",
"Network Throughput",
"Disk I/O Performance"
]
self.detailed_charts = {}
for title in chart_titles:
chart = SimpleChart(title, 700, 150)
self.detailed_charts[title.lower().replace(" ", "_")] = chart
charts_layout.addWidget(chart)
charts_scroll.setWidget(charts_widget)
layout.addWidget(charts_scroll)
return tab
def _create_alerts_tab(self):
"""Create alerts configuration tab"""
tab = QWidget()
layout = QVBoxLayout(tab)
# Alert thresholds
thresholds_group = QGroupBox("Alert Thresholds")
thresholds_layout = QGridLayout(thresholds_group)
thresholds = [
("CPU Usage", 80, "%"),
("Memory Usage", 85, "%"),
("GPU Usage", 90, "%"),
("FPS Drop", 15, "fps"),
("Processing Time", 100, "ms"),
("Temperature", 75, "°C")
]
self.threshold_controls = {}
for i, (metric, default_value, unit) in enumerate(thresholds):
# Label
thresholds_layout.addWidget(QLabel(f"{metric}:"), i, 0)
# Slider
slider = QSlider(Qt.Horizontal)
slider.setRange(1, 100)
slider.setValue(default_value)
thresholds_layout.addWidget(slider, i, 1)
# Value label
value_label = QLabel(f"{default_value} {unit}")
thresholds_layout.addWidget(value_label, i, 2)
# Connect slider to label
slider.valueChanged.connect(
lambda v, label=value_label, u=unit: label.setText(f"{v} {u}")
)
self.threshold_controls[metric.lower().replace(" ", "_")] = slider
layout.addWidget(thresholds_group)
# Alert history
history_group = QGroupBox("Recent Alerts")
history_layout = QVBoxLayout(history_group)
self.alerts_text = QLabel("No recent alerts")
self.alerts_text.setStyleSheet("""
QLabel {
background-color: #ecf0f1;
border: 1px solid #bdc3c7;
border-radius: 4px;
padding: 10px;
min-height: 200px;
}
""")
self.alerts_text.setAlignment(Qt.AlignTop)
self.alerts_text.setWordWrap(True)
history_layout.addWidget(self.alerts_text)
layout.addWidget(history_group)
return tab
def _create_settings_tab(self):
"""Create performance settings tab"""
tab = QWidget()
layout = QVBoxLayout(tab)
# Update intervals
intervals_group = QGroupBox("Update Intervals")
intervals_layout = QGridLayout(intervals_group)
intervals_layout.addWidget(QLabel("Metrics Update:"), 0, 0)
metrics_interval = QSpinBox()
metrics_interval.setRange(500, 10000)
metrics_interval.setValue(1000)
metrics_interval.setSuffix(" ms")
intervals_layout.addWidget(metrics_interval, 0, 1)
intervals_layout.addWidget(QLabel("Charts Update:"), 1, 0)
charts_interval = QSpinBox()
charts_interval.setRange(1000, 30000)
charts_interval.setValue(5000)
charts_interval.setSuffix(" ms")
intervals_layout.addWidget(charts_interval, 1, 1)
layout.addWidget(intervals_group)
# Display options
display_group = QGroupBox("Display Options")
display_layout = QVBoxLayout(display_group)
self.show_grid_cb = QCheckBox("Show Chart Grid Lines")
self.show_grid_cb.setChecked(True)
display_layout.addWidget(self.show_grid_cb)
self.smooth_charts_cb = QCheckBox("Smooth Chart Animations")
self.smooth_charts_cb.setChecked(True)
display_layout.addWidget(self.smooth_charts_cb)
self.auto_scale_cb = QCheckBox("Auto-scale Chart Axes")
self.auto_scale_cb.setChecked(True)
display_layout.addWidget(self.auto_scale_cb)
layout.addWidget(display_group)
# Performance optimization
optimization_group = QGroupBox("Performance Optimization")
optimization_layout = QVBoxLayout(optimization_group)
self.reduce_quality_cb = QCheckBox("Reduce Chart Quality for Better Performance")
optimization_layout.addWidget(self.reduce_quality_cb)
self.limit_history_cb = QCheckBox("Limit Historical Data (50 points)")
self.limit_history_cb.setChecked(True)
optimization_layout.addWidget(self.limit_history_cb)
layout.addWidget(optimization_group)
layout.addStretch()
return tab
def _update_metrics(self):
"""Update all performance metrics (called by timer)"""
# Simulate real metrics (replace with actual system monitoring)
import random
import time
# Generate simulated metrics
current_time = time.time()
metrics = {
'fps': random.uniform(25, 30),
'processing_time': random.uniform(20, 50),
'cpu_usage': random.uniform(30, 70),
'memory_usage': random.uniform(40, 80),
'gpu_usage': random.uniform(50, 90),
'network_io': random.uniform(5, 25),
'disk_io': random.uniform(1, 10),
'temperature': random.uniform(45, 65)
}
# Update metric cards
for metric_name, value in metrics.items():
if metric_name in self.metric_cards:
# Calculate trend (simplified)
old_value = getattr(self, f'_last_{metric_name}', value)
trend = ((value - old_value) / old_value * 100) if old_value != 0 else 0
self.metric_cards[metric_name].update_value(f"{value:.1f}", trend)
setattr(self, f'_last_{metric_name}', value)
# Update progress bars in header
self.cpu_progress.setValue(int(metrics['cpu_usage']))
self.memory_progress.setValue(int(metrics['memory_usage']))
self.gpu_progress.setValue(int(metrics['gpu_usage']))
# Update system health
avg_usage = (metrics['cpu_usage'] + metrics['memory_usage'] + metrics['gpu_usage']) / 3
if avg_usage < 50:
health_status = "Optimal"
health_color = "#27ae60"
elif avg_usage < 75:
health_status = "Good"
health_color = "#f39c12"
else:
health_status = "High Load"
health_color = "#e74c3c"
self.system_health_label.setText(f"System Health: {health_status}")
self.system_health_label.setStyleSheet(f"color: {health_color};")
# Update charts
self.fps_chart.add_data_point(metrics['fps'])
self.resource_chart.add_data_point(metrics['cpu_usage'])
# Update detailed charts
for chart_name, chart in self.detailed_charts.items():
# Use different metrics for different charts
if 'processing' in chart_name:
chart.add_data_point(metrics['processing_time'])
elif 'memory' in chart_name:
chart.add_data_point(metrics['memory_usage'])
elif 'gpu' in chart_name:
chart.add_data_point(metrics['gpu_usage'])
else:
chart.add_data_point(random.uniform(10, 90))
# Check for threshold alerts
if self.alerts_enabled:
self._check_thresholds(metrics)
def _check_thresholds(self, metrics):
"""Check if any metrics exceed thresholds"""
thresholds = {
'cpu_usage': 80,
'memory_usage': 85,
'gpu_usage': 90,
'processing_time': 100,
'temperature': 75
}
for metric, value in metrics.items():
if metric in thresholds and value > thresholds[metric]:
alert_msg = f"{metric.replace('_', ' ').title()} is high: {value:.1f}"
self.performance_alert.emit("warning", alert_msg)
def _toggle_alerts(self, enabled):
"""Toggle performance alerts"""
self.alerts_enabled = enabled
status = "enabled" if enabled else "disabled"
print(f"🔥 Performance alerts {status}")
def _export_performance_data(self):
"""Export performance data"""
self.export_requested.emit("performance_metrics")
print("🔥 Performance data exported")
def _refresh_metrics(self):
"""Refresh all metrics manually"""
print("🔥 Refreshing performance metrics")
# Force immediate update
self._update_metrics()
def add_custom_metric(self, name, value, unit=""):
"""Add a custom performance metric"""
if hasattr(self, 'metric_cards'):
# Add new metric card if space available
print(f"🔥 Added custom metric: {name} = {value} {unit}")
def set_alert_threshold(self, metric_name, threshold_value):
"""Set alert threshold for a specific metric"""
if metric_name in self.threshold_controls:
self.threshold_controls[metric_name].setValue(int(threshold_value))
print(f"🔥 Alert threshold set: {metric_name} = {threshold_value}")
def get_performance_summary(self):
"""Get current performance summary"""
return {
'status': self.system_health_label.text(),
'cpu': self.cpu_progress.value(),
'memory': self.memory_progress.value(),
'gpu': self.gpu_progress.value(),
'alerts_enabled': self.alerts_enabled
}

View File

@@ -0,0 +1,400 @@
"""
Video Analysis Tab - Advanced video analysis with ROI configuration
"""
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QSplitter,
QGroupBox, QLabel, QPushButton, QComboBox,
QListWidget, QTextEdit, QSlider, QCheckBox,
QFrame, QGridLayout, QSpinBox, QProgressBar)
from PySide6.QtCore import Qt, Signal, QTimer
from PySide6.QtGui import QFont, QPixmap
class VideoAnalysisTab(QWidget):
"""
Video Analysis Tab with ROI configuration and advanced analytics
Features:
- ROI (Region of Interest) drawing and management
- Traffic pattern analysis
- Speed measurement zones
- Counting lines configuration
- Heatmap visualization
- Advanced analytics reporting
"""
# Signals
roi_changed = Signal(dict)
analysis_started = Signal(str)
export_requested = Signal(str, str)
def __init__(self, parent=None):
super().__init__(parent)
self.roi_data = {}
self.analysis_results = {}
self._setup_ui()
print("🎬 Video Analysis Tab initialized")
def _setup_ui(self):
"""Setup the video analysis UI"""
# Main splitter
main_splitter = QSplitter(Qt.Horizontal)
self.layout = QVBoxLayout(self)
self.layout.addWidget(main_splitter)
# Left panel - Video and ROI editor
left_panel = self._create_left_panel()
main_splitter.addWidget(left_panel)
# Right panel - Analysis controls and results
right_panel = self._create_right_panel()
main_splitter.addWidget(right_panel)
# Set splitter proportions
main_splitter.setSizes([800, 400])
def _create_left_panel(self):
"""Create left panel with video analysis view"""
panel = QFrame()
layout = QVBoxLayout(panel)
# Header with controls
header = self._create_analysis_header()
layout.addWidget(header)
# Main analysis view (placeholder for now)
self.analysis_view = QFrame()
self.analysis_view.setMinimumSize(600, 400)
self.analysis_view.setStyleSheet("""
QFrame {
background-color: #2c3e50;
border: 2px solid #34495e;
border-radius: 8px;
}
""")
layout.addWidget(self.analysis_view, 1)
# ROI tools
roi_tools = self._create_roi_tools()
layout.addWidget(roi_tools)
return panel
def _create_analysis_header(self):
"""Create analysis header with controls"""
header = QFrame()
header.setFixedHeight(50)
layout = QHBoxLayout(header)
# Source selection
layout.addWidget(QLabel("Source:"))
self.source_combo = QComboBox()
self.source_combo.addItems(["Live Camera 1", "Live Camera 2", "Recorded Video", "Import Video"])
layout.addWidget(self.source_combo)
layout.addStretch()
# Analysis controls
self.start_analysis_btn = QPushButton("▶️ Start Analysis")
self.start_analysis_btn.clicked.connect(self._start_analysis)
layout.addWidget(self.start_analysis_btn)
self.stop_analysis_btn = QPushButton("⏹️ Stop")
self.stop_analysis_btn.setEnabled(False)
self.stop_analysis_btn.clicked.connect(self._stop_analysis)
layout.addWidget(self.stop_analysis_btn)
# Export button
export_btn = QPushButton("📊 Export Results")
export_btn.clicked.connect(self._export_results)
layout.addWidget(export_btn)
return header
def _create_roi_tools(self):
"""Create ROI drawing tools"""
tools = QGroupBox("ROI Tools")
tools.setFixedHeight(80)
layout = QHBoxLayout(tools)
# ROI type selection
layout.addWidget(QLabel("ROI Type:"))
self.roi_type_combo = QComboBox()
self.roi_type_combo.addItems([
"Traffic Count Zone",
"Speed Measurement Zone",
"Restricted Area",
"Parking Detection",
"Crosswalk Zone"
])
layout.addWidget(self.roi_type_combo)
# ROI drawing buttons
draw_rect_btn = QPushButton("📐 Rectangle")
draw_rect_btn.clicked.connect(lambda: self._set_draw_mode("rectangle"))
layout.addWidget(draw_rect_btn)
draw_poly_btn = QPushButton("🔗 Polygon")
draw_poly_btn.clicked.connect(lambda: self._set_draw_mode("polygon"))
layout.addWidget(draw_poly_btn)
draw_line_btn = QPushButton("📏 Line")
draw_line_btn.clicked.connect(lambda: self._set_draw_mode("line"))
layout.addWidget(draw_line_btn)
layout.addStretch()
# Clear ROI
clear_btn = QPushButton("🗑️ Clear All")
clear_btn.clicked.connect(self._clear_all_roi)
layout.addWidget(clear_btn)
return tools
def _create_right_panel(self):
"""Create right panel with controls and results"""
panel = QFrame()
layout = QVBoxLayout(panel)
# ROI list
roi_section = self._create_roi_section()
layout.addWidget(roi_section)
# Analysis settings
settings_section = self._create_settings_section()
layout.addWidget(settings_section)
# Results section
results_section = self._create_results_section()
layout.addWidget(results_section)
return panel
def _create_roi_section(self):
"""Create ROI management section"""
section = QGroupBox("ROI Management")
layout = QVBoxLayout(section)
# ROI list
self.roi_list = QListWidget()
self.roi_list.setMaximumHeight(120)
layout.addWidget(self.roi_list)
# ROI controls
roi_controls = QHBoxLayout()
edit_btn = QPushButton("✏️ Edit")
edit_btn.clicked.connect(self._edit_selected_roi)
roi_controls.addWidget(edit_btn)
delete_btn = QPushButton("🗑️ Delete")
delete_btn.clicked.connect(self._delete_selected_roi)
roi_controls.addWidget(delete_btn)
duplicate_btn = QPushButton("📋 Copy")
duplicate_btn.clicked.connect(self._duplicate_selected_roi)
roi_controls.addWidget(duplicate_btn)
layout.addLayout(roi_controls)
return section
def _create_settings_section(self):
"""Create analysis settings section"""
section = QGroupBox("Analysis Settings")
layout = QGridLayout(section)
# Detection sensitivity
layout.addWidget(QLabel("Sensitivity:"), 0, 0)
self.sensitivity_slider = QSlider(Qt.Horizontal)
self.sensitivity_slider.setRange(1, 10)
self.sensitivity_slider.setValue(5)
layout.addWidget(self.sensitivity_slider, 0, 1)
self.sensitivity_label = QLabel("5")
layout.addWidget(self.sensitivity_label, 0, 2)
# Minimum object size
layout.addWidget(QLabel("Min Size:"), 1, 0)
self.min_size_spin = QSpinBox()
self.min_size_spin.setRange(10, 1000)
self.min_size_spin.setValue(50)
self.min_size_spin.setSuffix(" px")
layout.addWidget(self.min_size_spin, 1, 1, 1, 2)
# Analysis options
self.track_objects_cb = QCheckBox("Track Objects")
self.track_objects_cb.setChecked(True)
layout.addWidget(self.track_objects_cb, 2, 0, 1, 3)
self.speed_analysis_cb = QCheckBox("Speed Analysis")
self.speed_analysis_cb.setChecked(True)
layout.addWidget(self.speed_analysis_cb, 3, 0, 1, 3)
self.direction_analysis_cb = QCheckBox("Direction Analysis")
self.direction_analysis_cb.setChecked(False)
layout.addWidget(self.direction_analysis_cb, 4, 0, 1, 3)
# Connect sensitivity slider to label
self.sensitivity_slider.valueChanged.connect(
lambda v: self.sensitivity_label.setText(str(v))
)
return section
def _create_results_section(self):
"""Create analysis results section"""
section = QGroupBox("Analysis Results")
layout = QVBoxLayout(section)
# Results summary
summary_layout = QGridLayout()
summary_layout.addWidget(QLabel("Objects Detected:"), 0, 0)
self.objects_count_label = QLabel("0")
summary_layout.addWidget(self.objects_count_label, 0, 1)
summary_layout.addWidget(QLabel("Avg Speed:"), 1, 0)
self.avg_speed_label = QLabel("0.0 km/h")
summary_layout.addWidget(self.avg_speed_label, 1, 1)
summary_layout.addWidget(QLabel("Violations:"), 2, 0)
self.violations_count_label = QLabel("0")
summary_layout.addWidget(self.violations_count_label, 2, 1)
layout.addLayout(summary_layout)
# Progress bar for analysis
self.analysis_progress = QProgressBar()
self.analysis_progress.setVisible(False)
layout.addWidget(self.analysis_progress)
# Detailed results
self.results_text = QTextEdit()
self.results_text.setMaximumHeight(150)
self.results_text.setPlaceholderText("Analysis results will appear here...")
layout.addWidget(self.results_text)
# Export options
export_layout = QHBoxLayout()
export_csv_btn = QPushButton("📄 Export CSV")
export_csv_btn.clicked.connect(lambda: self._export_data("csv"))
export_layout.addWidget(export_csv_btn)
export_json_btn = QPushButton("📋 Export JSON")
export_json_btn.clicked.connect(lambda: self._export_data("json"))
export_layout.addWidget(export_json_btn)
layout.addLayout(export_layout)
return section
def _set_draw_mode(self, mode):
"""Set ROI drawing mode"""
print(f"🎬 Draw mode set to: {mode}")
# Implementation for setting drawing mode
def _clear_all_roi(self):
"""Clear all ROI regions"""
self.roi_list.clear()
self.roi_data.clear()
self.roi_changed.emit(self.roi_data)
print("🎬 All ROI regions cleared")
def _edit_selected_roi(self):
"""Edit the selected ROI"""
current_item = self.roi_list.currentItem()
if current_item:
roi_name = current_item.text()
print(f"🎬 Editing ROI: {roi_name}")
def _delete_selected_roi(self):
"""Delete the selected ROI"""
current_item = self.roi_list.currentItem()
if current_item:
roi_name = current_item.text()
self.roi_list.takeItem(self.roi_list.row(current_item))
if roi_name in self.roi_data:
del self.roi_data[roi_name]
self.roi_changed.emit(self.roi_data)
print(f"🎬 Deleted ROI: {roi_name}")
def _duplicate_selected_roi(self):
"""Duplicate the selected ROI"""
current_item = self.roi_list.currentItem()
if current_item:
roi_name = current_item.text()
print(f"🎬 Duplicating ROI: {roi_name}")
def _start_analysis(self):
"""Start video analysis"""
self.start_analysis_btn.setEnabled(False)
self.stop_analysis_btn.setEnabled(True)
self.analysis_progress.setVisible(True)
self.analysis_progress.setRange(0, 0) # Indeterminate progress
source = self.source_combo.currentText()
self.analysis_started.emit(source)
# Add sample result
self.results_text.append(f"Started analysis on: {source}")
print(f"🎬 Started analysis on: {source}")
def _stop_analysis(self):
"""Stop video analysis"""
self.start_analysis_btn.setEnabled(True)
self.stop_analysis_btn.setEnabled(False)
self.analysis_progress.setVisible(False)
self.results_text.append("Analysis stopped by user")
print("🎬 Analysis stopped")
def _export_results(self):
"""Export analysis results"""
print("🎬 Exporting analysis results")
self.export_requested.emit("analysis_results", "pdf")
def _export_data(self, format_type):
"""Export data in specified format"""
print(f"🎬 Exporting data as {format_type.upper()}")
self.export_requested.emit("analysis_data", format_type)
def add_roi(self, roi_name, roi_type, coordinates):
"""Add a new ROI region"""
self.roi_data[roi_name] = {
'type': roi_type,
'coordinates': coordinates
}
# Add to list
self.roi_list.addItem(f"{roi_name} ({roi_type})")
# Emit change signal
self.roi_changed.emit(self.roi_data)
print(f"🎬 Added ROI: {roi_name} ({roi_type})")
def update_analysis_results(self, results):
"""Update analysis results display"""
self.analysis_results.update(results)
# Update summary labels
self.objects_count_label.setText(str(results.get('objects_detected', 0)))
self.avg_speed_label.setText(f"{results.get('average_speed', 0.0):.1f} km/h")
self.violations_count_label.setText(str(results.get('violations', 0)))
# Add to results text
if 'message' in results:
self.results_text.append(results['message'])
def set_analysis_progress(self, value):
"""Set analysis progress"""
if self.analysis_progress.isVisible():
if value < 0:
self.analysis_progress.setRange(0, 0) # Indeterminate
else:
self.analysis_progress.setRange(0, 100)
self.analysis_progress.setValue(value)

View File

@@ -0,0 +1,735 @@
"""
Violations Tab - Traffic violation detection and evidence management dashboard
"""
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QSplitter,
QTableWidget, QTableWidgetItem, QHeaderView,
QGroupBox, QLabel, QPushButton, QComboBox,
QDateEdit, QLineEdit, QTextEdit, QFrame,
QCheckBox, QSpinBox, QProgressBar, QTabWidget)
from PySide6.QtCore import Qt, Signal, QDateTime, QDate, QTimer
from PySide6.QtGui import QFont, QColor, QPixmap, QIcon
class ViolationItem:
"""Data class for violation items"""
def __init__(self, violation_id, violation_type, timestamp, location,
vehicle_id, evidence_path=None, status="pending"):
self.violation_id = violation_id
self.violation_type = violation_type
self.timestamp = timestamp
self.location = location
self.vehicle_id = vehicle_id
self.evidence_path = evidence_path
self.status = status
self.confidence = 0.95
self.reviewed = False
class ViolationsTab(QWidget):
"""
Traffic Violations Dashboard with evidence management
Features:
- Real-time violation detection alerts
- Evidence gallery with images/videos
- Violation classification and filtering
- Manual review and acknowledgment
- Export capabilities for reporting
- Statistics and trends analysis
"""
# Signals
violation_acknowledged = Signal(str)
violation_exported = Signal(list)
evidence_viewed = Signal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.violations_data = []
self.filtered_violations = []
self._setup_ui()
# Sample data for demonstration
self._add_sample_violations()
print("🚨 Violations Tab initialized")
def _setup_ui(self):
"""Setup the violations dashboard UI"""
# Main layout
layout = QVBoxLayout(self)
# Header with summary stats
header = self._create_header()
layout.addWidget(header)
# Main content splitter
main_splitter = QSplitter(Qt.Horizontal)
layout.addWidget(main_splitter)
# Left panel - Violations list and filters
left_panel = self._create_left_panel()
main_splitter.addWidget(left_panel)
# Right panel - Evidence and details
right_panel = self._create_right_panel()
main_splitter.addWidget(right_panel)
# Set splitter proportions
main_splitter.setSizes([600, 400])
def _create_header(self):
"""Create header with violation statistics"""
header = QFrame()
header.setFixedHeight(80)
header.setStyleSheet("""
QFrame {
background-color: #34495e;
border-radius: 8px;
margin-bottom: 10px;
}
""")
layout = QHBoxLayout(header)
layout.setContentsMargins(20, 10, 20, 10)
# Statistics cards
stats = [
("Total Violations", "127", "#e74c3c"),
("Pending Review", "23", "#f39c12"),
("Acknowledged", "104", "#27ae60"),
("Today's Count", "8", "#3498db")
]
for title, value, color in stats:
card = self._create_stat_card(title, value, color)
layout.addWidget(card)
layout.addStretch()
# Quick actions
actions_layout = QVBoxLayout()
export_btn = QPushButton("📊 Export Report")
export_btn.setFixedSize(120, 30)
export_btn.setStyleSheet(f"""
QPushButton {{
background-color: #3498db;
color: white;
border: none;
border-radius: 4px;
font-weight: bold;
}}
QPushButton:hover {{
background-color: #2980b9;
}}
""")
export_btn.clicked.connect(self._export_violations)
actions_layout.addWidget(export_btn)
clear_btn = QPushButton("🗑️ Clear Old")
clear_btn.setFixedSize(120, 30)
clear_btn.setStyleSheet(f"""
QPushButton {{
background-color: #95a5a6;
color: white;
border: none;
border-radius: 4px;
font-weight: bold;
}}
QPushButton:hover {{
background-color: #7f8c8d;
}}
""")
clear_btn.clicked.connect(self._clear_old_violations)
actions_layout.addWidget(clear_btn)
layout.addLayout(actions_layout)
return header
def _create_stat_card(self, title, value, color):
"""Create a statistics card"""
card = QFrame()
card.setFixedSize(140, 50)
card.setStyleSheet(f"""
QFrame {{
background-color: {color};
border-radius: 6px;
margin: 5px;
}}
""")
layout = QVBoxLayout(card)
layout.setContentsMargins(10, 5, 10, 5)
# Value
value_label = QLabel(value)
value_label.setFont(QFont("Segoe UI", 16, QFont.Bold))
value_label.setStyleSheet("color: white;")
value_label.setAlignment(Qt.AlignCenter)
layout.addWidget(value_label)
# Title
title_label = QLabel(title)
title_label.setFont(QFont("Segoe UI", 8))
title_label.setStyleSheet("color: white;")
title_label.setAlignment(Qt.AlignCenter)
layout.addWidget(title_label)
return card
def _create_left_panel(self):
"""Create left panel with violations list and filters"""
panel = QFrame()
layout = QVBoxLayout(panel)
# Filters section
filters = self._create_filters()
layout.addWidget(filters)
# Violations table
table_group = QGroupBox("Violations List")
table_layout = QVBoxLayout(table_group)
self.violations_table = QTableWidget()
self.violations_table.setColumnCount(7)
self.violations_table.setHorizontalHeaderLabels([
"ID", "Type", "Time", "Location", "Vehicle", "Status", "Actions"
])
# Configure table
header = self.violations_table.horizontalHeader()
header.setSectionResizeMode(QHeaderView.Stretch)
self.violations_table.setSelectionBehavior(QTableWidget.SelectRows)
self.violations_table.setAlternatingRowColors(True)
# Connect selection change
self.violations_table.itemSelectionChanged.connect(self._on_violation_selected)
table_layout.addWidget(self.violations_table)
layout.addWidget(table_group)
return panel
def _create_filters(self):
"""Create violation filters section"""
filters = QGroupBox("Filters")
filters.setFixedHeight(120)
layout = QVBoxLayout(filters)
# First row of filters
row1 = QHBoxLayout()
# Violation type filter
row1.addWidget(QLabel("Type:"))
self.type_filter = QComboBox()
self.type_filter.addItems([
"All Types",
"Red Light Violation",
"Speed Violation",
"Wrong Direction",
"Illegal Turn",
"Lane Violation"
])
self.type_filter.currentTextChanged.connect(self._apply_filters)
row1.addWidget(self.type_filter)
# Status filter
row1.addWidget(QLabel("Status:"))
self.status_filter = QComboBox()
self.status_filter.addItems(["All Status", "Pending", "Acknowledged", "Dismissed"])
self.status_filter.currentTextChanged.connect(self._apply_filters)
row1.addWidget(self.status_filter)
layout.addLayout(row1)
# Second row of filters
row2 = QHBoxLayout()
# Date range
row2.addWidget(QLabel("From:"))
self.date_from = QDateEdit()
self.date_from.setDate(QDate.currentDate().addDays(-7))
self.date_from.dateChanged.connect(self._apply_filters)
row2.addWidget(self.date_from)
row2.addWidget(QLabel("To:"))
self.date_to = QDateEdit()
self.date_to.setDate(QDate.currentDate())
self.date_to.dateChanged.connect(self._apply_filters)
row2.addWidget(self.date_to)
# Search
self.search_input = QLineEdit()
self.search_input.setPlaceholderText("Search violations...")
self.search_input.textChanged.connect(self._apply_filters)
row2.addWidget(self.search_input)
layout.addLayout(row2)
# Filter controls
row3 = QHBoxLayout()
self.show_acknowledged_cb = QCheckBox("Show Acknowledged")
self.show_acknowledged_cb.setChecked(True)
self.show_acknowledged_cb.toggled.connect(self._apply_filters)
row3.addWidget(self.show_acknowledged_cb)
row3.addStretch()
clear_filters_btn = QPushButton("Clear Filters")
clear_filters_btn.clicked.connect(self._clear_filters)
row3.addWidget(clear_filters_btn)
layout.addLayout(row3)
return filters
def _create_right_panel(self):
"""Create right panel with evidence and details"""
panel = QFrame()
layout = QVBoxLayout(panel)
# Evidence section
evidence_section = self._create_evidence_section()
layout.addWidget(evidence_section)
# Details section
details_section = self._create_details_section()
layout.addWidget(details_section)
# Actions section
actions_section = self._create_actions_section()
layout.addWidget(actions_section)
return panel
def _create_evidence_section(self):
"""Create evidence viewing section"""
section = QGroupBox("Evidence")
layout = QVBoxLayout(section)
# Evidence tabs
self.evidence_tabs = QTabWidget()
# Image evidence tab
image_tab = QWidget()
image_layout = QVBoxLayout(image_tab)
self.evidence_image = QLabel("Select a violation to view evidence")
self.evidence_image.setMinimumSize(300, 200)
self.evidence_image.setAlignment(Qt.AlignCenter)
self.evidence_image.setStyleSheet("""
QLabel {
border: 2px dashed #bdc3c7;
border-radius: 8px;
background-color: #ecf0f1;
color: #7f8c8d;
}
""")
image_layout.addWidget(self.evidence_image)
# Image controls
image_controls = QHBoxLayout()
zoom_in_btn = QPushButton("🔍+")
zoom_in_btn.setFixedSize(30, 30)
image_controls.addWidget(zoom_in_btn)
zoom_out_btn = QPushButton("🔍-")
zoom_out_btn.setFixedSize(30, 30)
image_controls.addWidget(zoom_out_btn)
image_controls.addStretch()
save_evidence_btn = QPushButton("💾 Save")
save_evidence_btn.clicked.connect(self._save_evidence)
image_controls.addWidget(save_evidence_btn)
image_layout.addLayout(image_controls)
self.evidence_tabs.addTab(image_tab, "📷 Image")
# Video evidence tab
video_tab = QWidget()
video_layout = QVBoxLayout(video_tab)
video_placeholder = QLabel("Video evidence player\n(Feature coming soon)")
video_placeholder.setMinimumSize(300, 200)
video_placeholder.setAlignment(Qt.AlignCenter)
video_placeholder.setStyleSheet("""
QLabel {
border: 2px dashed #bdc3c7;
border-radius: 8px;
background-color: #ecf0f1;
color: #7f8c8d;
}
""")
video_layout.addWidget(video_placeholder)
self.evidence_tabs.addTab(video_tab, "🎬 Video")
layout.addWidget(self.evidence_tabs)
return section
def _create_details_section(self):
"""Create violation details section"""
section = QGroupBox("Violation Details")
layout = QVBoxLayout(section)
# Details text area
self.details_text = QTextEdit()
self.details_text.setMaximumHeight(120)
self.details_text.setPlaceholderText("Select a violation to view details...")
layout.addWidget(self.details_text)
# Metadata grid
metadata_layout = QHBoxLayout()
# Left column
left_metadata = QVBoxLayout()
self.confidence_label = QLabel("Confidence: --")
self.camera_label = QLabel("Camera: --")
self.coordinates_label = QLabel("Coordinates: --")
left_metadata.addWidget(self.confidence_label)
left_metadata.addWidget(self.camera_label)
left_metadata.addWidget(self.coordinates_label)
metadata_layout.addLayout(left_metadata)
# Right column
right_metadata = QVBoxLayout()
self.weather_label = QLabel("Weather: --")
self.visibility_label = QLabel("Visibility: --")
self.reviewed_by_label = QLabel("Reviewed by: --")
right_metadata.addWidget(self.weather_label)
right_metadata.addWidget(self.visibility_label)
right_metadata.addWidget(self.reviewed_by_label)
metadata_layout.addLayout(right_metadata)
layout.addLayout(metadata_layout)
return section
def _create_actions_section(self):
"""Create violation actions section"""
section = QGroupBox("Actions")
layout = QVBoxLayout(section)
# Primary actions
primary_actions = QHBoxLayout()
self.acknowledge_btn = QPushButton("✅ Acknowledge")
self.acknowledge_btn.setStyleSheet("""
QPushButton {
background-color: #27ae60;
color: white;
border: none;
border-radius: 4px;
padding: 8px 16px;
font-weight: bold;
}
QPushButton:hover {
background-color: #229954;
}
""")
self.acknowledge_btn.clicked.connect(self._acknowledge_violation)
self.acknowledge_btn.setEnabled(False)
primary_actions.addWidget(self.acknowledge_btn)
self.dismiss_btn = QPushButton("❌ Dismiss")
self.dismiss_btn.setStyleSheet("""
QPushButton {
background-color: #e74c3c;
color: white;
border: none;
border-radius: 4px;
padding: 8px 16px;
font-weight: bold;
}
QPushButton:hover {
background-color: #c0392b;
}
""")
self.dismiss_btn.clicked.connect(self._dismiss_violation)
self.dismiss_btn.setEnabled(False)
primary_actions.addWidget(self.dismiss_btn)
layout.addLayout(primary_actions)
# Secondary actions
secondary_actions = QHBoxLayout()
flag_btn = QPushButton("🏃 Flag for Review")
flag_btn.clicked.connect(self._flag_violation)
secondary_actions.addWidget(flag_btn)
notes_btn = QPushButton("📝 Add Notes")
notes_btn.clicked.connect(self._add_notes)
secondary_actions.addWidget(notes_btn)
layout.addLayout(secondary_actions)
# Bulk actions
bulk_actions = QHBoxLayout()
select_all_btn = QPushButton("Select All")
select_all_btn.clicked.connect(self._select_all_violations)
bulk_actions.addWidget(select_all_btn)
bulk_acknowledge_btn = QPushButton("Bulk Acknowledge")
bulk_acknowledge_btn.clicked.connect(self._bulk_acknowledge)
bulk_actions.addWidget(bulk_acknowledge_btn)
layout.addLayout(bulk_actions)
return section
def _add_sample_violations(self):
"""Add sample violation data for demonstration"""
sample_violations = [
ViolationItem("V001", "Red Light Violation", QDateTime.currentDateTime().addSecs(-3600),
"Main St & Oak Ave", "ABC123", "evidence_001.jpg", "pending"),
ViolationItem("V002", "Speed Violation", QDateTime.currentDateTime().addSecs(-7200),
"Highway 101", "XYZ789", "evidence_002.jpg", "acknowledged"),
ViolationItem("V003", "Wrong Direction", QDateTime.currentDateTime().addSecs(-10800),
"5th St & Pine St", "DEF456", "evidence_003.jpg", "pending"),
ViolationItem("V004", "Illegal Turn", QDateTime.currentDateTime().addSecs(-14400),
"Market St", "GHI321", "evidence_004.jpg", "dismissed"),
ViolationItem("V005", "Lane Violation", QDateTime.currentDateTime().addSecs(-18000),
"Broadway & 2nd St", "JKL654", "evidence_005.jpg", "pending"),
]
self.violations_data = sample_violations
self._populate_violations_table()
def _populate_violations_table(self):
"""Populate the violations table with data"""
self.violations_table.setRowCount(len(self.violations_data))
for row, violation in enumerate(self.violations_data):
# ID
self.violations_table.setItem(row, 0, QTableWidgetItem(violation.violation_id))
# Type
type_item = QTableWidgetItem(violation.violation_type)
if violation.violation_type == "Red Light Violation":
type_item.setBackground(QColor(231, 76, 60, 50)) # Light red
elif violation.violation_type == "Speed Violation":
type_item.setBackground(QColor(243, 156, 18, 50)) # Light orange
self.violations_table.setItem(row, 1, type_item)
# Time
time_str = violation.timestamp.toString("MM/dd hh:mm")
self.violations_table.setItem(row, 2, QTableWidgetItem(time_str))
# Location
self.violations_table.setItem(row, 3, QTableWidgetItem(violation.location))
# Vehicle
self.violations_table.setItem(row, 4, QTableWidgetItem(violation.vehicle_id))
# Status
status_item = QTableWidgetItem(violation.status.title())
if violation.status == "pending":
status_item.setBackground(QColor(243, 156, 18, 50)) # Orange
elif violation.status == "acknowledged":
status_item.setBackground(QColor(39, 174, 96, 50)) # Green
elif violation.status == "dismissed":
status_item.setBackground(QColor(149, 165, 166, 50)) # Gray
self.violations_table.setItem(row, 5, status_item)
# Actions (placeholder)
actions_item = QTableWidgetItem("View")
self.violations_table.setItem(row, 6, actions_item)
def _apply_filters(self):
"""Apply current filters to violations list"""
# This would filter the violations based on current filter settings
self._populate_violations_table()
print("🚨 Filters applied to violations list")
def _clear_filters(self):
"""Clear all filters"""
self.type_filter.setCurrentIndex(0)
self.status_filter.setCurrentIndex(0)
self.date_from.setDate(QDate.currentDate().addDays(-7))
self.date_to.setDate(QDate.currentDate())
self.search_input.clear()
self.show_acknowledged_cb.setChecked(True)
print("🚨 Filters cleared")
def _on_violation_selected(self):
"""Handle violation selection"""
current_row = self.violations_table.currentRow()
if current_row >= 0 and current_row < len(self.violations_data):
violation = self.violations_data[current_row]
self._show_violation_details(violation)
# Enable action buttons
self.acknowledge_btn.setEnabled(violation.status == "pending")
self.dismiss_btn.setEnabled(violation.status == "pending")
def _show_violation_details(self, violation):
"""Show details for selected violation"""
# Update details text
details = f"""
Violation ID: {violation.violation_id}
Type: {violation.violation_type}
Timestamp: {violation.timestamp.toString("yyyy-MM-dd hh:mm:ss")}
Location: {violation.location}
Vehicle ID: {violation.vehicle_id}
Status: {violation.status.title()}
Description: This violation was automatically detected by the traffic monitoring system.
The evidence has been captured and is available for review.
""".strip()
self.details_text.setPlainText(details)
# Update metadata labels
self.confidence_label.setText(f"Confidence: {violation.confidence:.1%}")
self.camera_label.setText("Camera: Camera 1")
self.coordinates_label.setText("Coordinates: 37.7749, -122.4194")
self.weather_label.setText("Weather: Clear")
self.visibility_label.setText("Visibility: Good")
self.reviewed_by_label.setText("Reviewed by: --")
# Update evidence display
self.evidence_image.setText(f"Evidence: {violation.evidence_path or 'No evidence available'}")
print(f"🚨 Showing details for violation: {violation.violation_id}")
def _acknowledge_violation(self):
"""Acknowledge the selected violation"""
current_row = self.violations_table.currentRow()
if current_row >= 0:
violation = self.violations_data[current_row]
violation.status = "acknowledged"
violation.reviewed = True
# Update table
self._populate_violations_table()
# Disable buttons
self.acknowledge_btn.setEnabled(False)
self.dismiss_btn.setEnabled(False)
# Emit signal
self.violation_acknowledged.emit(violation.violation_id)
print(f"🚨 Violation {violation.violation_id} acknowledged")
def _dismiss_violation(self):
"""Dismiss the selected violation"""
current_row = self.violations_table.currentRow()
if current_row >= 0:
violation = self.violations_data[current_row]
violation.status = "dismissed"
violation.reviewed = True
# Update table
self._populate_violations_table()
# Disable buttons
self.acknowledge_btn.setEnabled(False)
self.dismiss_btn.setEnabled(False)
print(f"🚨 Violation {violation.violation_id} dismissed")
def _flag_violation(self):
"""Flag violation for manual review"""
current_row = self.violations_table.currentRow()
if current_row >= 0:
violation = self.violations_data[current_row]
print(f"🚨 Violation {violation.violation_id} flagged for review")
def _add_notes(self):
"""Add notes to violation"""
current_row = self.violations_table.currentRow()
if current_row >= 0:
violation = self.violations_data[current_row]
print(f"🚨 Adding notes to violation {violation.violation_id}")
def _select_all_violations(self):
"""Select all violations in table"""
self.violations_table.selectAll()
print("🚨 All violations selected")
def _bulk_acknowledge(self):
"""Acknowledge all selected violations"""
selected_rows = set()
for item in self.violations_table.selectedItems():
selected_rows.add(item.row())
for row in selected_rows:
if row < len(self.violations_data):
violation = self.violations_data[row]
if violation.status == "pending":
violation.status = "acknowledged"
violation.reviewed = True
# Update table
self._populate_violations_table()
print(f"🚨 Bulk acknowledged {len(selected_rows)} violations")
def _export_violations(self):
"""Export violations report"""
violation_ids = [v.violation_id for v in self.violations_data]
self.violation_exported.emit(violation_ids)
print("🚨 Violations exported")
def _clear_old_violations(self):
"""Clear old acknowledged violations"""
# Remove violations older than 30 days and acknowledged
cutoff_date = QDateTime.currentDateTime().addDays(-30)
original_count = len(self.violations_data)
self.violations_data = [
v for v in self.violations_data
if not (v.status == "acknowledged" and v.timestamp < cutoff_date)
]
removed_count = original_count - len(self.violations_data)
# Update table
self._populate_violations_table()
print(f"🚨 Cleared {removed_count} old violations")
def _save_evidence(self):
"""Save current evidence"""
current_row = self.violations_table.currentRow()
if current_row >= 0:
violation = self.violations_data[current_row]
self.evidence_viewed.emit(violation.violation_id)
print(f"🚨 Evidence saved for violation {violation.violation_id}")
def add_violation(self, violation_data):
"""Add a new violation to the list"""
violation = ViolationItem(**violation_data)
self.violations_data.insert(0, violation) # Add to beginning
self._populate_violations_table()
print(f"🚨 New violation added: {violation.violation_id}")
def get_violation_summary(self):
"""Get summary of violations"""
total = len(self.violations_data)
pending = sum(1 for v in self.violations_data if v.status == "pending")
acknowledged = sum(1 for v in self.violations_data if v.status == "acknowledged")
dismissed = sum(1 for v in self.violations_data if v.status == "dismissed")
return {
'total': total,
'pending': pending,
'acknowledged': acknowledged,
'dismissed': dismissed
}

View File

@@ -0,0 +1,586 @@
"""
VLM AI Insights Tab - ChatGPT-like interface for Vision Language Model interactions
"""
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QTextEdit,
QLineEdit, QPushButton, QScrollArea, QFrame,
QLabel, QSplitter, QComboBox, QSlider, QGroupBox,
QCheckBox, QSpinBox)
from PySide6.QtCore import Qt, Signal, QTimer, QDateTime
from PySide6.QtGui import QFont, QTextCharFormat, QColor, QPixmap
class ChatMessage(QFrame):
"""Individual chat message widget"""
def __init__(self, message, is_user=True, timestamp=None, parent=None):
super().__init__(parent)
self.message = message
self.is_user = is_user
self.timestamp = timestamp or QDateTime.currentDateTime()
self._setup_ui()
self._apply_style()
def _setup_ui(self):
"""Setup message UI"""
layout = QHBoxLayout(self)
layout.setContentsMargins(10, 8, 10, 8)
if self.is_user:
layout.addStretch()
# Message bubble
bubble = QFrame()
bubble.setMaximumWidth(400)
bubble_layout = QVBoxLayout(bubble)
bubble_layout.setContentsMargins(12, 8, 12, 8)
# Message text
message_label = QLabel(self.message)
message_label.setWordWrap(True)
message_label.setFont(QFont("Segoe UI", 9))
bubble_layout.addWidget(message_label)
# Timestamp
time_label = QLabel(self.timestamp.toString("hh:mm"))
time_label.setFont(QFont("Segoe UI", 7))
time_label.setAlignment(Qt.AlignRight if self.is_user else Qt.AlignLeft)
bubble_layout.addWidget(time_label)
layout.addWidget(bubble)
if not self.is_user:
layout.addStretch()
def _apply_style(self):
"""Apply message styling"""
if self.is_user:
# User message (blue, right-aligned)
self.setStyleSheet("""
QFrame {
background-color: #3498db;
border-radius: 12px;
margin-left: 50px;
}
QLabel {
color: white;
}
""")
else:
# AI message (gray, left-aligned)
self.setStyleSheet("""
QFrame {
background-color: #ecf0f1;
border-radius: 12px;
margin-right: 50px;
}
QLabel {
color: #2c3e50;
}
""")
class VLMInsightsTab(QWidget):
"""
VLM AI Insights Tab with ChatGPT-like interface
Features:
- Chat-style interface for VLM interactions
- Image context from traffic cameras
- Predefined prompts for traffic analysis
- Conversation history
- AI insights and recommendations
- Export conversation functionality
"""
# Signals
insight_generated = Signal(str)
vlm_query_sent = Signal(str, dict)
conversation_exported = Signal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.conversation_history = []
self.current_image_context = None
self._setup_ui()
print("🤖 VLM AI Insights Tab initialized")
def _setup_ui(self):
"""Setup the VLM insights UI"""
# Main splitter
main_splitter = QSplitter(Qt.Horizontal)
layout = QVBoxLayout(self)
layout.addWidget(main_splitter)
# Left panel - Chat interface
left_panel = self._create_chat_panel()
main_splitter.addWidget(left_panel)
# Right panel - Settings and context
right_panel = self._create_settings_panel()
main_splitter.addWidget(right_panel)
# Set splitter proportions (70% chat, 30% settings)
main_splitter.setSizes([700, 300])
def _create_chat_panel(self):
"""Create chat interface panel"""
panel = QFrame()
layout = QVBoxLayout(panel)
# Chat header
header = self._create_chat_header()
layout.addWidget(header)
# Conversation area
self.conversation_scroll = QScrollArea()
self.conversation_scroll.setWidgetResizable(True)
self.conversation_scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarNever)
self.conversation_widget = QWidget()
self.conversation_layout = QVBoxLayout(self.conversation_widget)
self.conversation_layout.setAlignment(Qt.AlignTop)
self.conversation_layout.setSpacing(5)
self.conversation_scroll.setWidget(self.conversation_widget)
layout.addWidget(self.conversation_scroll, 1)
# Input area
input_area = self._create_input_area()
layout.addWidget(input_area)
return panel
def _create_chat_header(self):
"""Create chat header with title and controls"""
header = QFrame()
header.setFixedHeight(50)
header.setStyleSheet("background-color: #34495e; border-radius: 8px; margin-bottom: 5px;")
layout = QHBoxLayout(header)
layout.setContentsMargins(15, 10, 15, 10)
# Title and status
title_layout = QVBoxLayout()
title = QLabel("🤖 VLM AI Assistant")
title.setFont(QFont("Segoe UI", 12, QFont.Bold))
title.setStyleSheet("color: white;")
title_layout.addWidget(title)
self.status_label = QLabel("Ready to analyze traffic scenes")
self.status_label.setFont(QFont("Segoe UI", 8))
self.status_label.setStyleSheet("color: #bdc3c7;")
title_layout.addWidget(self.status_label)
layout.addLayout(title_layout)
layout.addStretch()
# Action buttons
clear_btn = QPushButton("🗑️ Clear")
clear_btn.setFixedSize(60, 30)
clear_btn.setStyleSheet("""
QPushButton {
background-color: #e74c3c;
color: white;
border: none;
border-radius: 4px;
font-size: 8pt;
}
QPushButton:hover {
background-color: #c0392b;
}
""")
clear_btn.clicked.connect(self._clear_conversation)
layout.addWidget(clear_btn)
export_btn = QPushButton("📤 Export")
export_btn.setFixedSize(60, 30)
export_btn.setStyleSheet("""
QPushButton {
background-color: #3498db;
color: white;
border: none;
border-radius: 4px;
font-size: 8pt;
}
QPushButton:hover {
background-color: #2980b9;
}
""")
export_btn.clicked.connect(self._export_conversation)
layout.addWidget(export_btn)
return header
def _create_input_area(self):
"""Create message input area"""
input_frame = QFrame()
input_frame.setFixedHeight(100)
layout = QVBoxLayout(input_frame)
# Quick prompts
prompts_layout = QHBoxLayout()
prompts = [
"Analyze current traffic",
"Count vehicles",
"Detect violations",
"Safety assessment"
]
for prompt in prompts:
btn = QPushButton(prompt)
btn.setMaximumHeight(25)
btn.setStyleSheet("""
QPushButton {
background-color: #ecf0f1;
border: 1px solid #bdc3c7;
border-radius: 12px;
padding: 4px 8px;
font-size: 8pt;
}
QPushButton:hover {
background-color: #d5dbdb;
}
""")
btn.clicked.connect(lambda checked, p=prompt: self._send_quick_prompt(p))
prompts_layout.addWidget(btn)
prompts_layout.addStretch()
layout.addLayout(prompts_layout)
# Message input
input_layout = QHBoxLayout()
self.message_input = QLineEdit()
self.message_input.setPlaceholderText("Ask the AI about traffic conditions, violations, or safety...")
self.message_input.setFont(QFont("Segoe UI", 9))
self.message_input.setStyleSheet("""
QLineEdit {
border: 2px solid #bdc3c7;
border-radius: 20px;
padding: 8px 15px;
font-size: 9pt;
}
QLineEdit:focus {
border-color: #3498db;
}
""")
self.message_input.returnPressed.connect(self._send_message)
input_layout.addWidget(self.message_input)
self.send_btn = QPushButton("")
self.send_btn.setFixedSize(40, 40)
self.send_btn.setStyleSheet("""
QPushButton {
background-color: #3498db;
color: white;
border: none;
border-radius: 20px;
font-size: 16pt;
font-weight: bold;
}
QPushButton:hover {
background-color: #2980b9;
}
QPushButton:pressed {
background-color: #21618c;
}
""")
self.send_btn.clicked.connect(self._send_message)
input_layout.addWidget(self.send_btn)
layout.addLayout(input_layout)
return input_frame
def _create_settings_panel(self):
"""Create settings and context panel"""
panel = QFrame()
layout = QVBoxLayout(panel)
# Image context section
context_section = self._create_context_section()
layout.addWidget(context_section)
# VLM settings
settings_section = self._create_vlm_settings()
layout.addWidget(settings_section)
# Conversation stats
stats_section = self._create_stats_section()
layout.addWidget(stats_section)
layout.addStretch()
return panel
def _create_context_section(self):
"""Create image context section"""
section = QGroupBox("Image Context")
layout = QVBoxLayout(section)
# Current image preview
self.image_preview = QLabel("No image selected")
self.image_preview.setFixedSize(200, 150)
self.image_preview.setAlignment(Qt.AlignCenter)
self.image_preview.setStyleSheet("""
QLabel {
border: 2px dashed #bdc3c7;
border-radius: 8px;
background-color: #ecf0f1;
color: #7f8c8d;
}
""")
layout.addWidget(self.image_preview)
# Image source selection
source_layout = QHBoxLayout()
source_layout.addWidget(QLabel("Source:"))
self.image_source_combo = QComboBox()
self.image_source_combo.addItems([
"Live Camera 1",
"Live Camera 2",
"Current Frame",
"Upload Image"
])
self.image_source_combo.currentTextChanged.connect(self._change_image_source)
source_layout.addWidget(self.image_source_combo)
layout.addLayout(source_layout)
# Capture button
capture_btn = QPushButton("📸 Capture Current")
capture_btn.clicked.connect(self._capture_current_frame)
layout.addWidget(capture_btn)
return section
def _create_vlm_settings(self):
"""Create VLM model settings"""
section = QGroupBox("AI Settings")
layout = QVBoxLayout(section)
# Model selection
model_layout = QHBoxLayout()
model_layout.addWidget(QLabel("Model:"))
self.model_combo = QComboBox()
self.model_combo.addItems([
"LLaVA-Next-Video",
"GPT-4 Vision",
"Claude Vision"
])
model_layout.addWidget(self.model_combo)
layout.addLayout(model_layout)
# Temperature setting
temp_layout = QHBoxLayout()
temp_layout.addWidget(QLabel("Creativity:"))
self.temperature_slider = QSlider(Qt.Horizontal)
self.temperature_slider.setRange(1, 10)
self.temperature_slider.setValue(5)
temp_layout.addWidget(self.temperature_slider)
self.temp_label = QLabel("0.5")
temp_layout.addWidget(self.temp_label)
layout.addLayout(temp_layout)
# Max response length
length_layout = QHBoxLayout()
length_layout.addWidget(QLabel("Max Length:"))
self.max_length_spin = QSpinBox()
self.max_length_spin.setRange(50, 1000)
self.max_length_spin.setValue(300)
self.max_length_spin.setSuffix(" words")
length_layout.addWidget(self.max_length_spin)
layout.addLayout(length_layout)
# Analysis options
self.detailed_analysis_cb = QCheckBox("Detailed Analysis")
self.detailed_analysis_cb.setChecked(True)
layout.addWidget(self.detailed_analysis_cb)
self.safety_focus_cb = QCheckBox("Safety Focus")
self.safety_focus_cb.setChecked(True)
layout.addWidget(self.safety_focus_cb)
# Connect temperature slider to label
self.temperature_slider.valueChanged.connect(
lambda v: self.temp_label.setText(f"{v/10:.1f}")
)
return section
def _create_stats_section(self):
"""Create conversation statistics"""
section = QGroupBox("Conversation Stats")
layout = QVBoxLayout(section)
# Message count
self.message_count_label = QLabel("Messages: 0")
layout.addWidget(self.message_count_label)
# Insights generated
self.insights_count_label = QLabel("Insights: 0")
layout.addWidget(self.insights_count_label)
# Session time
self.session_time_label = QLabel("Session: 0 min")
layout.addWidget(self.session_time_label)
return section
def _send_message(self):
"""Send user message"""
message = self.message_input.text().strip()
if not message:
return
# Add user message
self._add_message(message, is_user=True)
# Clear input
self.message_input.clear()
# Send to VLM (simulate response for now)
self._simulate_vlm_response(message)
# Emit signal
context = {
'image': self.current_image_context,
'settings': self._get_current_settings()
}
self.vlm_query_sent.emit(message, context)
def _send_quick_prompt(self, prompt):
"""Send a quick prompt"""
self.message_input.setText(prompt)
self._send_message()
def _add_message(self, message, is_user=True):
"""Add message to conversation"""
chat_message = ChatMessage(message, is_user)
self.conversation_layout.addWidget(chat_message)
# Scroll to bottom
QTimer.singleShot(100, lambda: self.conversation_scroll.verticalScrollBar().setValue(
self.conversation_scroll.verticalScrollBar().maximum()
))
# Update stats
self.conversation_history.append({
'message': message,
'is_user': is_user,
'timestamp': QDateTime.currentDateTime()
})
self._update_stats()
def _simulate_vlm_response(self, user_message):
"""Simulate VLM response (replace with actual VLM integration)"""
# Simulate processing delay
self.status_label.setText("AI is analyzing...")
QTimer.singleShot(2000, lambda: self._generate_response(user_message))
def _generate_response(self, user_message):
"""Generate AI response"""
# Simple response simulation
responses = {
"analyze current traffic": "I can see moderate traffic flow with 8 vehicles currently in view. Traffic appears to be flowing smoothly with no apparent congestion. Most vehicles are maintaining safe following distances.",
"count vehicles": "I count 5 cars, 2 trucks, and 1 motorcycle currently visible in the intersection. Traffic density appears normal for this time of day.",
"detect violations": "I don't detect any obvious traffic violations at this moment. All vehicles appear to be following traffic signals and maintaining proper lanes.",
"safety assessment": "Overall safety conditions look good. Visibility is clear, traffic signals are functioning properly, and vehicle speeds appear appropriate for the intersection."
}
# Find best matching response
response = None
for key, value in responses.items():
if key.lower() in user_message.lower():
response = value
break
if not response:
response = f"I understand you're asking about '{user_message}'. Based on the current traffic scene, I can provide analysis of vehicle movements, count objects, assess safety conditions, and identify potential violations. Could you be more specific about what aspect you'd like me to focus on?"
# Add AI response
self._add_message(response, is_user=False)
# Update status
self.status_label.setText("Ready to analyze traffic scenes")
# Emit insight signal
self.insight_generated.emit(response)
def _clear_conversation(self):
"""Clear conversation history"""
# Remove all message widgets
for i in reversed(range(self.conversation_layout.count())):
child = self.conversation_layout.itemAt(i).widget()
if child:
child.setParent(None)
# Clear history
self.conversation_history.clear()
# Update stats
self._update_stats()
print("🤖 Conversation cleared")
def _export_conversation(self):
"""Export conversation history"""
self.conversation_exported.emit("conversation_history")
print("🤖 Conversation exported")
def _change_image_source(self, source):
"""Change image context source"""
self.image_preview.setText(f"Source: {source}")
print(f"🤖 Image source changed to: {source}")
def _capture_current_frame(self):
"""Capture current frame for analysis"""
# Simulate frame capture
self.image_preview.setText("Current frame\ncaptured")
self.current_image_context = "current_frame"
print("🤖 Current frame captured for analysis")
def _get_current_settings(self):
"""Get current VLM settings"""
return {
'model': self.model_combo.currentText(),
'temperature': self.temperature_slider.value() / 10.0,
'max_length': self.max_length_spin.value(),
'detailed_analysis': self.detailed_analysis_cb.isChecked(),
'safety_focus': self.safety_focus_cb.isChecked()
}
def _update_stats(self):
"""Update conversation statistics"""
total_messages = len(self.conversation_history)
ai_messages = sum(1 for msg in self.conversation_history if not msg['is_user'])
self.message_count_label.setText(f"Messages: {total_messages}")
self.insights_count_label.setText(f"Insights: {ai_messages}")
def add_ai_insight(self, insight_text):
"""Add an AI insight to the conversation"""
self._add_message(insight_text, is_user=False)
def set_image_context(self, image_data):
"""Set image context for VLM analysis"""
self.current_image_context = image_data
# Update image preview if needed
print("🤖 Image context updated for VLM analysis")