Files

1201 lines
44 KiB
Python

from PySide6.QtWidgets import (
QMainWindow, QTabWidget, QDockWidget, QMessageBox,
QApplication, QFileDialog, QSplashScreen
)
from PySide6.QtCore import Qt, QTimer, QSettings, QSize, Slot
from PySide6.QtGui import QIcon, QPixmap, QAction
import os
import sys
import json
import time
import traceback
from datetime import datetime
from pathlib import Path
print("✅ Basic PySide6 imports successful")
print("🚀 LOADING MODERN UI - main_window1.py")
print("=" * 50)
# Custom exception handler for Qt
# Ensure Qt is imported before using Qt.qInstallMessageHandler
try:
from PySide6.QtCore import Qt as QtCoreQt
if hasattr(QtCoreQt, 'qInstallMessageHandler'):
def qt_message_handler(mode, context, message):
print(f"Qt Message: {message} (Mode: {mode})")
QtCoreQt.qInstallMessageHandler(qt_message_handler)
except Exception as e:
print(f"⚠️ Could not install Qt message handler: {e}")
# Import UI components with fallback handling
try:
from ui.fixed_live_tab import LiveTab
print("✅ Imported LiveTab")
except ImportError as e:
print(f"⚠️ Could not import LiveTab: {e}")
# Create a basic fallback LiveTab
from PySide6.QtWidgets import QWidget, QLabel, QVBoxLayout
from PySide6.QtCore import Signal
class LiveTab(QWidget):
source_changed = Signal(object)
video_dropped = Signal(object)
snapshot_requested = Signal()
run_requested = Signal(bool)
def __init__(self):
super().__init__()
self.current_source = None
layout = QVBoxLayout(self)
label = QLabel("Live Tab (Fallback Mode)")
layout.addWidget(label)
def update_display(self, *args):
pass
def update_display_np(self, *args):
pass
def update_stats(self, *args):
pass
try:
from ui.analytics_tab import AnalyticsTab
print("✅ Imported AnalyticsTab")
except ImportError as e:
print(f"⚠️ Could not import AnalyticsTab: {e}")
from PySide6.QtWidgets import QWidget, QLabel, QVBoxLayout
class AnalyticsTab(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout(self)
label = QLabel("Analytics Tab (Fallback Mode)")
layout.addWidget(label)
def update_analytics(self, *args):
pass
try:
from ui.violations_tab import ViolationsTab
print("✅ Imported ViolationsTab")
except ImportError as e:
print(f"⚠️ Could not import ViolationsTab: {e}")
from PySide6.QtWidgets import QWidget, QLabel, QVBoxLayout, QPushButton
class ViolationsTab(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout(self)
label = QLabel("Violations Tab (Fallback Mode)")
self.clear_btn = QPushButton("Clear")
layout.addWidget(label)
layout.addWidget(self.clear_btn)
def add_violation(self, *args):
pass
try:
from ui.export_tab import ExportTab
print("✅ Imported ExportTab")
except ImportError as e:
print(f"⚠️ Could not import ExportTab: {e}")
from PySide6.QtWidgets import QWidget, QLabel, QVBoxLayout, QPushButton, QComboBox
class ExportTab(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout(self)
label = QLabel("Export Tab (Fallback Mode)")
self.export_format_combo = QComboBox()
self.export_data_combo = QComboBox()
self.reset_btn = QPushButton("Reset")
self.save_config_btn = QPushButton("Save Config")
self.reload_config_btn = QPushButton("Reload Config")
self.export_btn = QPushButton("Export")
layout.addWidget(label)
layout.addWidget(self.export_format_combo)
layout.addWidget(self.export_data_combo)
layout.addWidget(self.reset_btn)
layout.addWidget(self.save_config_btn)
layout.addWidget(self.reload_config_btn)
layout.addWidget(self.export_btn)
def update_config_display(self, *args):
pass
def update_export_preview(self, *args):
pass
def get_config_from_ui(self):
return {}
try:
from ui.config_panel import ConfigPanel
print("✅ Imported ConfigPanel")
except ImportError as e:
print(f"⚠️ Could not import ConfigPanel: {e}")
from PySide6.QtWidgets import QWidget, QLabel, QVBoxLayout
from PySide6.QtCore import Signal
class ConfigPanel(QWidget):
config_changed = Signal(dict)
theme_toggled = Signal(bool)
def __init__(self):
super().__init__()
layout = QVBoxLayout(self)
label = QLabel("Config Panel (Fallback Mode)")
layout.addWidget(label)
def set_config(self, *args):
pass
def reset_config(self):
pass
# Import controllers with fallback handling
try:
from controllers.video_controller_new import VideoController
print("✅ Imported VideoController")
except ImportError as e:
print(f"⚠️ Could not import VideoController: {e}")
from PySide6.QtCore import QObject, Signal
class VideoController(QObject):
frame_ready = Signal(object, object, dict)
frame_np_ready = Signal(object)
stats_ready = Signal(dict)
raw_frame_ready = Signal(object, list, float)
violation_detected = Signal(dict)
def __init__(self, model_manager=None):
super().__init__()
self._running = False
def set_source(self, source):
print(f"VideoController (fallback): set_source called with {source}")
return True
def start(self):
print("VideoController (fallback): start called")
self._running = True
def stop(self):
print("VideoController (fallback): stop called")
self._running = False
def capture_snapshot(self):
print("VideoController (fallback): capture_snapshot called")
return None
try:
from controllers.analytics_controller import AnalyticsController
print("✅ Imported AnalyticsController")
except ImportError as e:
print(f"⚠️ Could not import AnalyticsController: {e}")
from PySide6.QtCore import QObject, Signal
class AnalyticsController(QObject):
analytics_updated = Signal(dict)
def __init__(self):
super().__init__()
def process_frame_data(self, *args):
pass
def clear_statistics(self):
pass
def register_violation(self, *args):
pass
def get_analytics(self):
return {'detection_counts': {}}
try:
from controllers.performance_overlay import PerformanceOverlay
print("✅ Imported PerformanceOverlay")
except ImportError as e:
print(f"⚠️ Could not import PerformanceOverlay: {e}")
from PySide6.QtWidgets import QWidget
class PerformanceOverlay(QWidget):
def __init__(self):
super().__init__()
self.setVisible(False)
def update_stats(self):
pass
try:
from controllers.model_manager import ModelManager
print("✅ Imported ModelManager")
except ImportError as e:
print(f"⚠️ Could not import ModelManager: {e}")
class ModelManager:
def __init__(self, config_file=None):
print("ModelManager (fallback): initialized")
def update_config(self, config):
pass
# Import utilities with fallback handling
try:
from utils.helpers import load_configuration, save_configuration, save_snapshot
print("✅ Imported utilities")
except ImportError as e:
print(f"⚠️ Could not import utilities: {e}")
import json
import os
def load_configuration(config_file):
try:
if os.path.exists(config_file):
with open(config_file, 'r') as f:
return json.load(f)
else:
return {}
except Exception as e:
print(f"Error loading config: {e}")
return {}
def save_configuration(config, config_file):
try:
with open(config_file, 'w') as f:
json.dump(config, f, indent=2)
return True
except Exception as e:
print(f"Error saving config: {e}")
return False
def save_snapshot(frame, file_path):
try:
# Try using PySide6's QPixmap to save
from PySide6.QtGui import QPixmap
if hasattr(frame, 'shape'): # NumPy array
try:
import cv2
cv2.imwrite(file_path, frame)
except ImportError:
print("OpenCV not available for saving")
return None
else: # QPixmap or similar
if hasattr(frame, 'save'):
frame.save(file_path)
else:
print("Unknown frame format for saving")
return None
return file_path
except Exception as e:
print(f"Error saving snapshot: {e}")
return None
class MainWindow(QMainWindow):
"""Main application window."""
def __init__(self):
super().__init__()
print("🚀 INITIALIZING MODERN UI - MainWindow1")
print("=" * 50)
# Initialize settings and configuration
self.settings = QSettings("OpenVINO", "TrafficMonitoring")
self.config_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), "config.json")
self.config = load_configuration(self.config_file)
# Set up UI
self.setupUI()
# Initialize controllers
self.setupControllers()
# Connect signals and slots
self.connectSignals()
# Restore settings
self.restoreSettings()
# Apply theme - Start with distinctive dark theme
self.applyTheme(True) # Start with dark theme
# Show ready message with modern styling
self.statusBar().showMessage("🚀 Modern UI Ready - All Systems Go!")
print("✅ MODERN UI (MainWindow1) FULLY LOADED!")
print("=" * 50)
def setupUI(self):
"""Set up the user interface"""
# Window properties with modern styling
self.setWindowTitle("🚀 Traffic Monitoring System - MODERN UI (OpenVINO PySide6)")
self.setMinimumSize(1200, 800)
self.resize(1400, 900)
# Add a distinctive window icon or styling
print("🎨 Setting up MODERN UI interface...")
# Set up central widget with tabs
self.tabs = QTabWidget()
# Create tabs with enhanced styling
self.live_tab = LiveTab()
self.analytics_tab = AnalyticsTab()
self.violations_tab = ViolationsTab()
self.export_tab = ExportTab()
# Add tabs to tab widget with modern icons/styling
self.tabs.addTab(self.live_tab, "🎥 Live Detection")
self.tabs.addTab(self.analytics_tab, "📊 Analytics")
self.tabs.addTab(self.violations_tab, "🚨 Violations")
self.tabs.addTab(self.export_tab, "💾 Export & Config")
# Set central widget
self.setCentralWidget(self.tabs)
# Create config panel in dock widget with modern styling
self.config_panel = ConfigPanel()
dock = QDockWidget("⚙️ Settings Panel", self)
dock.setObjectName("SettingsDock") # Set object name to avoid warning
dock.setWidget(self.config_panel)
dock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable)
dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
self.addDockWidget(Qt.RightDockWidgetArea, dock)
# Create status bar with modern styling
self.statusBar().showMessage("🚀 Modern UI Initialized - Ready for Action!")
# Create menu bar
self.setupMenus()
# Create performance overlay
self.performance_overlay = PerformanceOverlay()
print("✅ MODERN UI setup completed!")
def setupControllers(self):
"""Set up controllers and models"""
# Load config from file
try:
# Initialize model manager
self.model_manager = ModelManager(self.config_file)
print("✅ Model manager initialized")
# Create video controller
self.video_controller = VideoController(self.model_manager)
print("✅ Video controller initialized")
# Create analytics controller
self.analytics_controller = AnalyticsController()
print("✅ Analytics controller initialized")
# Setup update timer for performance overlay
if hasattr(self, 'performance_overlay'):
self.perf_timer = QTimer()
self.perf_timer.timeout.connect(self.performance_overlay.update_stats)
self.perf_timer.start(1000) # Update every second
print("✅ Performance overlay timer started")
else:
print("⚠️ Performance overlay not available")
except Exception as e:
QMessageBox.critical(
self,
"Initialization Error",
f"Error initializing controllers: {str(e)}\n\nPlease check if all required modules are available."
)
print(f"❌ Controller initialization error: {e}")
traceback.print_exc()
def connectSignals(self):
"""Connect signals and slots between components"""
# Video controller connections - With extra debug
print("🔌 Connecting video controller signals...")
try:
# Connect for UI frame updates (QPixmap-based)
if hasattr(self.live_tab, 'update_display'):
self.video_controller.frame_ready.connect(self.live_tab.update_display, Qt.QueuedConnection)
print("✅ Connected frame_ready signal")
else:
print("⚠️ live_tab.update_display method not found")
# Connect for direct NumPy frame display (critical for live video)
if hasattr(self.live_tab, 'update_display_np'):
self.video_controller.frame_np_ready.connect(self.live_tab.update_display_np, Qt.QueuedConnection)
print("✅ Connected frame_np_ready signal")
else:
print("⚠️ live_tab.update_display_np method not found")
# Connect stats signal
if hasattr(self.live_tab, 'update_stats'):
self.video_controller.stats_ready.connect(self.live_tab.update_stats, Qt.QueuedConnection)
print("✅ Connected stats_ready to live_tab")
else:
print("⚠️ live_tab.update_stats method not found")
# Also connect stats signal to update traffic light status in main window
self.video_controller.stats_ready.connect(self.update_traffic_light_status, Qt.QueuedConnection)
print("✅ Connected stats_ready signals")
# Connect raw frame data for analytics
if hasattr(self.analytics_controller, 'process_frame_data'):
self.video_controller.raw_frame_ready.connect(self.analytics_controller.process_frame_data)
print("✅ Connected raw_frame_ready signal")
else:
print("⚠️ analytics_controller.process_frame_data method not found")
# Connect violation detection signal
try:
self.video_controller.violation_detected.connect(self.handle_violation_detected, Qt.QueuedConnection)
print("✅ Connected violation_detected signal")
except Exception as e:
print(f"⚠️ Could not connect violation signal: {e}")
except Exception as e:
print(f"❌ Error connecting video controller signals: {e}")
traceback.print_exc()
# Live tab connections - with safety checks
try:
if hasattr(self.live_tab, 'source_changed'):
self.live_tab.source_changed.connect(self.video_controller.set_source)
print("✅ Connected live_tab.source_changed")
if hasattr(self.live_tab, 'video_dropped'):
self.live_tab.video_dropped.connect(self.video_controller.set_source)
print("✅ Connected live_tab.video_dropped")
if hasattr(self.live_tab, 'snapshot_requested'):
self.live_tab.snapshot_requested.connect(self.take_snapshot)
print("✅ Connected live_tab.snapshot_requested")
if hasattr(self.live_tab, 'run_requested'):
self.live_tab.run_requested.connect(self.toggle_video_processing)
print("✅ Connected live_tab.run_requested")
except Exception as e:
print(f"⚠️ Error connecting live_tab signals: {e}")
# Config panel connections - with safety checks
try:
if hasattr(self.config_panel, 'config_changed'):
self.config_panel.config_changed.connect(self.apply_config)
print("✅ Connected config_panel.config_changed")
if hasattr(self.config_panel, 'theme_toggled'):
self.config_panel.theme_toggled.connect(self.applyTheme)
print("✅ Connected config_panel.theme_toggled")
except Exception as e:
print(f"⚠️ Error connecting config_panel signals: {e}")
# Analytics controller connections - with safety checks
try:
if hasattr(self.analytics_controller, 'analytics_updated'):
if hasattr(self.analytics_tab, 'update_analytics'):
self.analytics_controller.analytics_updated.connect(self.analytics_tab.update_analytics)
print("✅ Connected analytics_controller to analytics_tab")
if hasattr(self.export_tab, 'update_export_preview'):
self.analytics_controller.analytics_updated.connect(self.export_tab.update_export_preview)
print("✅ Connected analytics_controller to export_tab")
except Exception as e:
print(f"⚠️ Error connecting analytics_controller signals: {e}")
# Tab-specific connections - with safety checks
try:
if hasattr(self.violations_tab, 'clear_btn') and hasattr(self.analytics_controller, 'clear_statistics'):
self.violations_tab.clear_btn.clicked.connect(self.analytics_controller.clear_statistics)
print("✅ Connected violations_tab.clear_btn")
if hasattr(self.export_tab, 'reset_btn') and hasattr(self.config_panel, 'reset_config'):
self.export_tab.reset_btn.clicked.connect(self.config_panel.reset_config)
print("✅ Connected export_tab.reset_btn")
if hasattr(self.export_tab, 'save_config_btn'):
self.export_tab.save_config_btn.clicked.connect(self.save_config)
print("✅ Connected export_tab.save_config_btn")
if hasattr(self.export_tab, 'reload_config_btn'):
self.export_tab.reload_config_btn.clicked.connect(self.load_config)
print("✅ Connected export_tab.reload_config_btn")
if hasattr(self.export_tab, 'export_btn'):
self.export_tab.export_btn.clicked.connect(self.export_data)
print("✅ Connected export_tab.export_btn")
except Exception as e:
print(f"⚠️ Error connecting tab-specific signals: {e}")
print("🔌 Signal connection process completed")
def setupMenus(self):
"""Set up application menus"""
# File menu
file_menu = self.menuBar().addMenu("&File")
open_action = QAction("&Open Video...", self)
open_action.setShortcut("Ctrl+O")
open_action.triggered.connect(self.open_video_file)
file_menu.addAction(open_action)
file_menu.addSeparator()
snapshot_action = QAction("Take &Snapshot", self)
snapshot_action.setShortcut("Ctrl+S")
snapshot_action.triggered.connect(self.take_snapshot)
file_menu.addAction(snapshot_action)
file_menu.addSeparator()
exit_action = QAction("E&xit", self)
exit_action.setShortcut("Alt+F4")
exit_action.triggered.connect(self.close)
file_menu.addAction(exit_action)
# View menu
view_menu = self.menuBar().addMenu("&View")
toggle_config_action = QAction("Show/Hide &Settings Panel", self)
toggle_config_action.setShortcut("F4")
toggle_config_action.triggered.connect(self.toggle_config_panel)
view_menu.addAction(toggle_config_action)
toggle_perf_action = QAction("Show/Hide &Performance Overlay", self)
toggle_perf_action.setShortcut("F5")
toggle_perf_action.triggered.connect(self.toggle_performance_overlay)
view_menu.addAction(toggle_perf_action)
# Help menu
help_menu = self.menuBar().addMenu("&Help")
about_action = QAction("&About", self)
about_action.triggered.connect(self.show_about_dialog)
help_menu.addAction(about_action)
@Slot(dict)
def apply_config(self, config):
"""
Apply configuration changes.
Args:
config: Configuration dictionary
"""
# Update configuration
if not config:
return
# Update config
for section in config:
if section in self.config:
self.config[section].update(config[section])
else:
self.config[section] = config[section]
# Update model manager
if self.model_manager:
self.model_manager.update_config(self.config)
# Save config to file
save_configuration(self.config, self.config_file)
# Update export tab
self.export_tab.update_config_display(self.config)
# Update status
self.statusBar().showMessage("Configuration applied", 2000)
@Slot()
def load_config(self):
"""Load configuration from file"""
# Ask for confirmation if needed
if self.video_controller and self.video_controller._running:
reply = QMessageBox.question(
self,
"Reload Configuration",
"Reloading configuration will stop current processing. Continue?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No
)
if reply == QMessageBox.No:
return
# Stop processing
self.video_controller.stop()
# Load config
self.config = load_configuration(self.config_file)
# Update UI
self.config_panel.set_config(self.config)
self.export_tab.update_config_display(self.config)
# Update model manager
if self.model_manager:
self.model_manager.update_config(self.config)
# Update status
self.statusBar().showMessage("Configuration loaded", 2000)
@Slot()
def save_config(self):
"""Save configuration to file"""
# Get config from UI
ui_config = self.export_tab.get_config_from_ui()
for section in ui_config:
if section in self.config:
self.config[section].update(ui_config[section])
else:
self.config[section] = ui_config[section]
# Save to file
if save_configuration(self.config, self.config_file):
self.statusBar().showMessage("Configuration saved", 2000)
else:
self.statusBar().showMessage("Error saving configuration", 2000)
# Update model manager
if self.model_manager:
self.model_manager.update_config(self.config)
@Slot()
def open_video_file(self):
"""Open video file dialog"""
file_path, _ = QFileDialog.getOpenFileName(
self,
"Open Video File",
"",
"Video Files (*.mp4 *.avi *.mov *.mkv *.webm);;All Files (*)"
)
if file_path:
# Update live tab
self.live_tab.source_changed.emit(file_path)
# Update status
self.statusBar().showMessage(f"Loaded video: {os.path.basename(file_path)}")
@Slot()
def take_snapshot(self):
"""Take snapshot of current frame"""
if self.video_controller:
# Get current frame
frame = self.video_controller.capture_snapshot()
if frame is not None:
# Save frame to file
save_dir = self.settings.value("snapshot_dir", ".")
file_path = os.path.join(save_dir, "snapshot_" +
str(int(time.time())) + ".jpg")
saved_path = save_snapshot(frame, file_path)
if saved_path:
self.statusBar().showMessage(f"Snapshot saved: {saved_path}", 3000)
else:
self.statusBar().showMessage("Error saving snapshot", 3000)
else:
self.statusBar().showMessage("No frame to capture", 3000)
@Slot()
def toggle_config_panel(self):
"""Toggle configuration panel visibility"""
dock_widgets = self.findChildren(QDockWidget)
for dock in dock_widgets:
dock.setVisible(not dock.isVisible())
@Slot()
def toggle_performance_overlay(self):
"""Toggle performance overlay visibility"""
if self.performance_overlay.isVisible():
self.performance_overlay.hide()
else:
# Position in the corner
self.performance_overlay.move(self.pos().x() + 10, self.pos().y() + 30)
self.performance_overlay.show()
@Slot(bool)
def applyTheme(self, dark_theme):
"""
Apply light or dark theme.
Args:
dark_theme: True for dark theme, False for light theme
"""
if dark_theme:
# Apply a modern dark theme with distinctive styling
dark_stylesheet = """
QMainWindow {
background-color: #1e1e1e;
color: #ffffff;
border: none;
}
QTabWidget::pane {
border: 1px solid #404040;
background-color: #2d2d2d;
border-radius: 8px;
}
QTabBar::tab {
background-color: #404040;
color: #ffffff;
padding: 12px 20px;
margin-right: 2px;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
font-weight: bold;
font-size: 11px;
}
QTabBar::tab:selected {
background-color: #0078d4;
color: #ffffff;
}
QTabBar::tab:hover {
background-color: #555555;
}
QStatusBar {
background-color: #333333;
color: #ffffff;
border-top: 1px solid #555555;
font-weight: bold;
}
QMenuBar {
background-color: #2d2d2d;
color: #ffffff;
border-bottom: 1px solid #555555;
padding: 4px;
}
QMenuBar::item {
background-color: transparent;
padding: 8px 16px;
border-radius: 4px;
}
QMenuBar::item:selected {
background-color: #0078d4;
}
QMenu {
background-color: #2d2d2d;
color: #ffffff;
border: 1px solid #555555;
border-radius: 4px;
}
QMenu::item {
padding: 8px 24px;
}
QMenu::item:selected {
background-color: #0078d4;
}
QDockWidget {
background-color: #2d2d2d;
color: #ffffff;
titlebar-close-icon: none;
titlebar-normal-icon: none;
border: 1px solid #555555;
border-radius: 4px;
}
QDockWidget::title {
background-color: #404040;
color: #ffffff;
padding: 8px;
text-align: center;
font-weight: bold;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
QWidget {
background-color: #2d2d2d;
color: #ffffff;
}
QLabel {
color: #ffffff;
font-size: 11px;
}
QPushButton {
background-color: #0078d4;
color: #ffffff;
border: none;
padding: 10px 20px;
border-radius: 6px;
font-weight: bold;
font-size: 10px;
}
QPushButton:hover {
background-color: #106ebe;
}
QPushButton:pressed {
background-color: #005a9e;
}
QComboBox {
background-color: #404040;
color: #ffffff;
border: 1px solid #555555;
padding: 8px;
border-radius: 4px;
font-size: 10px;
}
QComboBox::drop-down {
border: none;
background-color: #0078d4;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
width: 20px;
}
QComboBox::down-arrow {
image: none;
border: 2px solid #ffffff;
border-top: none;
border-left: none;
width: 6px;
height: 6px;
margin-right: 4px;
transform: rotate(45deg);
}
QComboBox QAbstractItemView {
background-color: #404040;
color: #ffffff;
selection-background-color: #0078d4;
border: 1px solid #555555;
}
/* Make the title bar more distinctive */
QMainWindow::title {
background-color: #1e1e1e;
color: #0078d4;
font-weight: bold;
font-size: 14px;
}
"""
self.setStyleSheet(dark_stylesheet)
# Also update window title to show it's the modern UI
self.setWindowTitle("🚀 Traffic Monitoring System - MODERN UI (OpenVINO PySide6)")
else:
# Light theme with modern styling
light_stylesheet = """
QMainWindow {
background-color: #f5f5f5;
color: #333333;
}
QTabWidget::pane {
border: 1px solid #cccccc;
background-color: #ffffff;
border-radius: 8px;
}
QTabBar::tab {
background-color: #e0e0e0;
color: #333333;
padding: 12px 20px;
margin-right: 2px;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
font-weight: bold;
}
QTabBar::tab:selected {
background-color: #0078d4;
color: #ffffff;
}
QTabBar::tab:hover {
background-color: #d0d0d0;
}
QStatusBar {
background-color: #e0e0e0;
color: #333333;
border-top: 1px solid #cccccc;
font-weight: bold;
}
QPushButton {
background-color: #0078d4;
color: #ffffff;
border: none;
padding: 10px 20px;
border-radius: 6px;
font-weight: bold;
}
QPushButton:hover {
background-color: #106ebe;
}
"""
self.setStyleSheet(light_stylesheet)
self.setWindowTitle("☀️ Traffic Monitoring System - LIGHT UI (OpenVINO PySide6)")
# Update status bar to show theme change
theme_name = "🌙 DARK MODERN" if dark_theme else "☀️ LIGHT MODERN"
self.statusBar().showMessage(f"Theme applied: {theme_name} UI", 3000)
@Slot()
def export_data(self):
"""Export data to file"""
export_format = self.export_tab.export_format_combo.currentText()
export_data = self.export_tab.export_data_combo.currentText()
# Get file type filter based on format
if export_format == "CSV":
file_filter = "CSV Files (*.csv)"
default_ext = ".csv"
elif export_format == "JSON":
file_filter = "JSON Files (*.json)"
default_ext = ".json"
elif export_format == "Excel":
file_filter = "Excel Files (*.xlsx)"
default_ext = ".xlsx"
elif export_format == "PDF Report":
file_filter = "PDF Files (*.pdf)"
default_ext = ".pdf"
else:
file_filter = "All Files (*)"
default_ext = ".txt"
# Get save path
file_path, _ = QFileDialog.getSaveFileName(
self,
"Export Data",
f"traffic_data{default_ext}",
file_filter
)
if not file_path:
return
try:
# Get analytics data
analytics = self.analytics_controller.get_analytics()
# Export based on format
if export_format == "CSV":
try:
from utils.helpers import create_export_csv
result = create_export_csv(analytics['detection_counts'], file_path)
except ImportError:
print("CSV export not available - utils.helpers not found")
result = False
elif export_format == "JSON":
try:
from utils.helpers import create_export_json
result = create_export_json(analytics, file_path)
except ImportError:
# Fallback JSON export
try:
with open(file_path, 'w') as f:
json.dump(analytics, f, indent=2, default=str)
result = True
except Exception as e:
print(f"JSON export error: {e}")
result = False
elif export_format == "Excel":
# Requires openpyxl
try:
import pandas as pd
df = pd.DataFrame({
'Class': list(analytics['detection_counts'].keys()),
'Count': list(analytics['detection_counts'].values())
})
df.to_excel(file_path, index=False)
result = True
except Exception as e:
print(f"Excel export error: {e}")
result = False
else:
# Not implemented
QMessageBox.information(
self,
"Not Implemented",
f"Export to {export_format} is not yet implemented."
)
return
if result:
self.statusBar().showMessage(f"Data exported to {file_path}", 3000)
else:
self.statusBar().showMessage("Error exporting data", 3000)
except Exception as e:
QMessageBox.critical(
self,
"Export Error",
f"Error exporting data: {str(e)}"
)
@Slot()
def show_about_dialog(self):
"""Show about dialog"""
QMessageBox.about(
self,
"About Traffic Monitoring System",
"<h3>Traffic Monitoring System</h3>"
"<p>Based on OpenVINO™ and PySide6</p>"
"<p>Version 1.0.0</p>"
"<p>© 2025 GSOC Project</p>"
)
@Slot(bool)
def toggle_video_processing(self, start):
"""
Start or stop video processing.
Args:
start: True to start processing, False to stop
"""
if self.video_controller:
if start:
try:
# Make sure the source is correctly set to what the LiveTab has
current_source = self.live_tab.current_source
print(f"DEBUG: MainWindow toggle_processing with source: {current_source} (type: {type(current_source)})")
# Validate source
if current_source is None:
self.statusBar().showMessage("Error: No valid source selected")
return
# For file sources, verify file exists
if isinstance(current_source, str) and not current_source.isdigit():
if not os.path.exists(current_source):
self.statusBar().showMessage(f"Error: File not found: {current_source}")
return
# Ensure the source is set before starting
print(f"🎥 Setting video controller source to: {current_source}")
self.video_controller.set_source(current_source)
# Now start processing after a short delay to ensure source is set
print("⏱️ Scheduling video processing start after 200ms delay...")
QTimer.singleShot(200, lambda: self._start_video_processing())
source_desc = f"file: {os.path.basename(current_source)}" if isinstance(current_source, str) and os.path.exists(current_source) else f"camera: {current_source}"
self.statusBar().showMessage(f"Video processing started with {source_desc}")
except Exception as e:
print(f"❌ Error starting video: {e}")
traceback.print_exc()
self.statusBar().showMessage(f"Error: {str(e)}")
else:
try:
print("🛑 Stopping video processing...")
self.video_controller.stop()
print("✅ Video controller stopped")
self.statusBar().showMessage("Video processing stopped")
except Exception as e:
print(f"❌ Error stopping video: {e}")
traceback.print_exc()
def _start_video_processing(self):
"""Actual video processing start with extra error handling"""
try:
print("🚀 Starting video controller...")
self.video_controller.start()
print("✅ Video controller started successfully")
except Exception as e:
print(f"❌ Error in video processing start: {e}")
traceback.print_exc()
self.statusBar().showMessage(f"Video processing error: {str(e)}")
def closeEvent(self, event):
"""Handle window close event"""
# Stop processing
if self.video_controller and self.video_controller._running:
self.video_controller.stop()
# Save settings
self.saveSettings()
# Accept close event
event.accept()
def restoreSettings(self):
"""Restore application settings"""
# Restore window geometry
geometry = self.settings.value("geometry")
if geometry:
self.restoreGeometry(geometry)
# Restore window state
state = self.settings.value("windowState")
if state:
self.restoreState(state)
def saveSettings(self):
"""Save application settings"""
# Save window geometry
self.settings.setValue("geometry", self.saveGeometry())
# Save window state
self.settings.setValue("windowState", self.saveState())
# Save current directory as snapshot directory
self.settings.setValue("snapshot_dir", os.getcwd())
@Slot(dict)
def update_traffic_light_status(self, stats):
"""Update status bar with traffic light information if detected"""
traffic_light_info = stats.get('traffic_light_color', 'unknown')
# Handle both string and dictionary return formats
if isinstance(traffic_light_info, dict):
traffic_light_color = traffic_light_info.get('color', 'unknown')
confidence = traffic_light_info.get('confidence', 0.0)
confidence_str = f" (Confidence: {confidence:.2f})" if confidence > 0 else ""
else:
traffic_light_color = traffic_light_info
confidence_str = ""
if traffic_light_color != 'unknown':
current_message = self.statusBar().currentMessage()
if not current_message or "Traffic Light" not in current_message:
# Handle both dictionary and string formats
if isinstance(traffic_light_color, dict):
color_text = traffic_light_color.get("color", "unknown").upper()
else:
color_text = str(traffic_light_color).upper()
self.statusBar().showMessage(f"Traffic Light: {color_text}{confidence_str}")
@Slot(dict)
def handle_violation_detected(self, violation):
"""Handle a detected traffic violation"""
try:
# Get track ID safely
track_id = violation.get('track_id', violation.get('id', 'Unknown'))
timestamp = violation.get('timestamp', datetime.now())
# Flash red status message
self.statusBar().showMessage(f"🚨 RED LIGHT VIOLATION DETECTED - Vehicle ID: {track_id}", 5000)
# Add to violations tab
if hasattr(self.violations_tab, 'add_violation'):
self.violations_tab.add_violation(violation)
else:
print("⚠️ violations_tab.add_violation method not found")
# Update analytics
if self.analytics_controller and hasattr(self.analytics_controller, 'register_violation'):
self.analytics_controller.register_violation(violation)
else:
print("⚠️ analytics_controller.register_violation method not found")
print(f"🚨 Violation processed: Track ID={track_id} at {timestamp}")
except Exception as e:
print(f"❌ Error handling violation: {e}")
traceback.print_exc()