Clean push: Removed heavy files & added only latest snapshot

This commit is contained in:
2025-07-26 05:16:12 +05:30
commit acf84e8767
250 changed files with 58564 additions and 0 deletions

View File

@@ -0,0 +1,558 @@
"""
Finale UI - Modern Main Window
Advanced traffic monitoring interface with Material Design and dark theme.
Connects to existing detection/violation logic from qt_app_pyside.
"""
from PySide6.QtWidgets import (
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QTabWidget,
QDockWidget, QSplitter, QFrame, QMessageBox, QApplication,
QFileDialog, QStatusBar, QMenuBar, QMenu, QToolBar
)
from PySide6.QtCore import Qt, QTimer, QSettings, QSize, Signal, Slot, QPropertyAnimation, QEasingCurve
from PySide6.QtGui import QIcon, QPixmap, QAction, QPainter, QBrush, QColor
import os
import sys
import json
import time
import traceback
from pathlib import Path
# Import finale UI components
try:
# Try relative imports first (when running as a package)
from .styles import FinaleStyles, MaterialColors
from .icons import FinaleIcons
from .toolbar import FinaleToolbar
from .components.stats_widgets import StatsWidget, MetricsWidget, SystemResourceWidget
from .views import LiveView, AnalyticsView, ViolationsView, SettingsView
except ImportError:
# Fallback to direct imports (when running as script)
try:
from styles import FinaleStyles, MaterialColors
from icons import FinaleIcons
from toolbar import FinaleToolbar
from components.stats_widgets import StatsWidget, MetricsWidget, SystemResourceWidget
from views import LiveView, AnalyticsView, ViolationsView, SettingsView
except ImportError:
print('Error importing main window components')
# Import existing detection/violation logic from qt_app_pyside
sys.path.append(str(Path(__file__).parent.parent))
try:
from controllers.model_manager import ModelManager
from controllers.video_controller_new import VideoController
from controllers.analytics_controller import AnalyticsController
from controllers.performance_overlay import PerformanceOverlay
# Import detection_openvino for advanced detection logic
from detection_openvino import OpenVINOVehicleDetector
from red_light_violation_pipeline import RedLightViolationPipeline
from utils.helpers import load_configuration, save_configuration
from utils.annotation_utils import draw_detections, convert_cv_to_pixmap
from utils.enhanced_annotation_utils import enhanced_draw_detections
from utils.traffic_light_utils import detect_traffic_light_color
except ImportError as e:
print(f"Warning: Could not import some dependencies: {e}")
# Fallback imports
from controllers.model_manager import ModelManager
VideoController = None
def load_configuration(path): return {}
def save_configuration(config, path): pass
class FinaleMainWindow(QMainWindow):
"""
Modern main window for traffic monitoring with advanced UI.
Connects to existing detection/violation logic without modifying it.
"""
# Signals for UI updates
theme_changed = Signal(bool) # dark_mode
view_changed = Signal(str) # view_name
fullscreen_toggled = Signal(bool)
def __init__(self):
super().__init__()
# Initialize settings and configuration
self.settings = QSettings("Finale", "TrafficMonitoring")
self.config_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), "qt_app_pyside", "config.json")
self.config = load_configuration(self.config_file)
# UI state
self.dark_mode = True
self.current_view = "live"
self.is_fullscreen = False
# Animation system
self.animations = {}
# Initialize UI
self.setup_ui()
# Initialize backend controllers (existing logic)
self.setup_controllers()
# Connect signals
self.connect_signals()
# Apply theme and restore settings
self.apply_theme()
self.restore_settings()
# Show ready message
self.statusBar().showMessage("Finale UI Ready", 3000)
def setup_ui(self):
"""Set up the modern user interface"""
# Window properties with advanced styling
self.setWindowTitle("Finale Traffic Monitoring System")
self.setMinimumSize(1400, 900)
self.resize(1600, 1000)
# Set window icon
self.setWindowIcon(FinaleIcons.get_icon("traffic_monitoring"))
# Create central widget with modern layout
self.setup_central_widget()
# Create modern toolbar
self.setup_toolbar()
# Create docked widgets
self.setup_dock_widgets()
# Create status bar
self.setup_status_bar()
# Create menu bar
self.setup_menu_bar()
# Apply initial styling
self.setStyleSheet(FinaleStyles.get_main_window_style())
def setup_central_widget(self):
"""Create the central widget with modern tabbed interface"""
# Create main splitter for flexible layout
self.main_splitter = QSplitter(Qt.Horizontal)
# Create left panel for main content
self.content_widget = QWidget()
self.content_layout = QVBoxLayout(self.content_widget)
self.content_layout.setContentsMargins(0, 0, 0, 0)
self.content_layout.setSpacing(0)
# Create modern tab widget
self.tabs = QTabWidget()
self.tabs.setTabPosition(QTabWidget.North)
self.tabs.setMovable(True)
self.tabs.setTabsClosable(False)
# Create views (these will be implemented next)
self.live_view = LiveView()
self.analytics_view = AnalyticsView()
self.violations_view = ViolationsView()
self.settings_view = SettingsView()
# Add tabs with icons
self.tabs.addTab(self.live_view, FinaleIcons.get_icon("live"), "Live Detection")
self.tabs.addTab(self.analytics_view, FinaleIcons.get_icon("analytics"), "Analytics")
self.tabs.addTab(self.violations_view, FinaleIcons.get_icon("warning"), "Violations")
self.tabs.addTab(self.settings_view, FinaleIcons.get_icon("settings"), "Settings")
# Style the tab widget
self.tabs.setStyleSheet(FinaleStyles.get_tab_widget_style())
# Add to layout
self.content_layout.addWidget(self.tabs)
self.main_splitter.addWidget(self.content_widget)
# Set as central widget
self.setCentralWidget(self.main_splitter)
def setup_toolbar(self):
"""Create the modern toolbar"""
self.toolbar = FinaleToolbar(self)
self.addToolBar(Qt.TopToolBarArea, self.toolbar)
# Connect toolbar signals
self.toolbar.play_clicked.connect(self.on_play_clicked)
self.toolbar.pause_clicked.connect(self.on_pause_clicked)
self.toolbar.stop_clicked.connect(self.on_stop_clicked)
self.toolbar.record_clicked.connect(self.on_record_clicked)
self.toolbar.snapshot_clicked.connect(self.on_snapshot_clicked)
self.toolbar.settings_clicked.connect(self.show_settings)
self.toolbar.fullscreen_clicked.connect(self.toggle_fullscreen)
self.toolbar.theme_changed.connect(self.set_dark_mode)
def setup_dock_widgets(self):
"""Create docked widgets for statistics and controls"""
# Stats dock widget
self.stats_dock = QDockWidget("Statistics", self)
self.stats_dock.setObjectName("StatsDock")
self.stats_widget = StatsWidget()
self.stats_dock.setWidget(self.stats_widget)
self.stats_dock.setFeatures(
QDockWidget.DockWidgetMovable |
QDockWidget.DockWidgetClosable |
QDockWidget.DockWidgetFloatable
)
self.addDockWidget(Qt.RightDockWidgetArea, self.stats_dock)
# Metrics dock widget
self.metrics_dock = QDockWidget("Performance", self)
self.metrics_dock.setObjectName("MetricsDock")
self.metrics_widget = MetricsWidget()
self.metrics_dock.setWidget(self.metrics_widget)
self.metrics_dock.setFeatures(
QDockWidget.DockWidgetMovable |
QDockWidget.DockWidgetClosable |
QDockWidget.DockWidgetFloatable
)
self.addDockWidget(Qt.RightDockWidgetArea, self.metrics_dock)
# System resources dock widget
self.system_dock = QDockWidget("System", self)
self.system_dock.setObjectName("SystemDock")
self.system_widget = SystemResourceWidget()
self.system_dock.setWidget(self.system_widget)
self.system_dock.setFeatures(
QDockWidget.DockWidgetMovable |
QDockWidget.DockWidgetClosable |
QDockWidget.DockWidgetFloatable
)
self.addDockWidget(Qt.RightDockWidgetArea, self.system_dock)
# Tabify dock widgets for space efficiency
self.tabifyDockWidget(self.stats_dock, self.metrics_dock)
self.tabifyDockWidget(self.metrics_dock, self.system_dock)
# Show stats dock by default
self.stats_dock.raise_()
# Apply dock widget styling
for dock in [self.stats_dock, self.metrics_dock, self.system_dock]:
dock.setStyleSheet(FinaleStyles.get_dock_widget_style())
def setup_status_bar(self):
"""Create modern status bar"""
self.status_bar = QStatusBar()
self.setStatusBar(self.status_bar)
# Add permanent widgets to status bar
self.fps_label = QWidget()
self.connection_label = QWidget()
self.model_label = QWidget()
self.status_bar.addPermanentWidget(self.fps_label)
self.status_bar.addPermanentWidget(self.connection_label)
self.status_bar.addPermanentWidget(self.model_label)
# Style status bar
self.status_bar.setStyleSheet(FinaleStyles.get_status_bar_style())
def setup_menu_bar(self):
"""Create modern menu bar"""
self.menu_bar = self.menuBar()
# File menu
file_menu = self.menu_bar.addMenu("&File")
open_action = QAction(FinaleIcons.get_icon("folder"), "&Open Video", self)
open_action.setShortcut("Ctrl+O")
open_action.triggered.connect(self.open_file)
file_menu.addAction(open_action)
save_action = QAction(FinaleIcons.get_icon("save"), "&Save Config", self)
save_action.setShortcut("Ctrl+S")
save_action.triggered.connect(self.save_config)
file_menu.addAction(save_action)
file_menu.addSeparator()
exit_action = QAction(FinaleIcons.get_icon("exit"), "E&xit", self)
exit_action.setShortcut("Ctrl+Q")
exit_action.triggered.connect(self.close)
file_menu.addAction(exit_action)
# View menu
view_menu = self.menu_bar.addMenu("&View")
fullscreen_action = QAction(FinaleIcons.get_icon("fullscreen"), "&Fullscreen", self)
fullscreen_action.setShortcut("F11")
fullscreen_action.setCheckable(True)
fullscreen_action.triggered.connect(self.toggle_fullscreen)
view_menu.addAction(fullscreen_action)
theme_action = QAction(FinaleIcons.get_icon("theme"), "&Dark Theme", self)
theme_action.setCheckable(True)
theme_action.setChecked(self.dark_mode)
theme_action.triggered.connect(self.toggle_theme)
view_menu.addAction(theme_action)
# Tools menu
tools_menu = self.menu_bar.addMenu("&Tools")
settings_action = QAction(FinaleIcons.get_icon("settings"), "&Settings", self)
settings_action.setShortcut("Ctrl+,")
settings_action.triggered.connect(self.show_settings)
tools_menu.addAction(settings_action)
# Help menu
help_menu = self.menu_bar.addMenu("&Help")
about_action = QAction(FinaleIcons.get_icon("info"), "&About", self)
about_action.triggered.connect(self.show_about)
help_menu.addAction(about_action)
# Style menu bar
self.menu_bar.setStyleSheet(FinaleStyles.get_menu_bar_style())
def setup_controllers(self):
"""Initialize backend controllers (existing logic)"""
try:
# Initialize model manager (existing from qt_app_pyside)
self.model_manager = ModelManager(self.config_file)
# Initialize video controller (existing from qt_app_pyside)
self.video_controller = VideoController(self.model_manager)
# Initialize analytics controller (existing from qt_app_pyside)
self.analytics_controller = AnalyticsController()
# Initialize performance overlay (existing from qt_app_pyside)
self.performance_overlay = PerformanceOverlay()
print("✅ Backend controllers initialized successfully")
except Exception as e:
print(f"❌ Error initializing controllers: {e}")
QMessageBox.critical(self, "Initialization Error",
f"Failed to initialize backend controllers:\n{str(e)}")
def connect_signals(self):
"""Connect signals between UI and backend"""
try:
# Connect video controller signals to UI updates
if hasattr(self.video_controller, 'frame_ready'):
self.video_controller.frame_ready.connect(self.on_frame_ready)
if hasattr(self.video_controller, 'stats_ready'):
self.video_controller.stats_ready.connect(self.on_stats_ready)
if hasattr(self.video_controller, 'violation_detected'):
self.video_controller.violation_detected.connect(self.on_violation_detected)
# Connect tab change signal
self.tabs.currentChanged.connect(self.on_tab_changed)
# Connect view signals to backend
self.live_view.source_changed.connect(self.on_source_changed)
print("✅ Signals connected successfully")
except Exception as e:
print(f"❌ Error connecting signals: {e}")
# Event handlers for UI interactions
@Slot()
def on_play_clicked(self):
"""Handle play button click"""
if hasattr(self.video_controller, 'start'):
self.video_controller.start()
self.toolbar.set_playback_state("playing")
@Slot()
def on_pause_clicked(self):
"""Handle pause button click"""
if hasattr(self.video_controller, 'pause'):
self.video_controller.pause()
self.toolbar.set_playback_state("paused")
@Slot()
def on_stop_clicked(self):
"""Handle stop button click"""
if hasattr(self.video_controller, 'stop'):
self.video_controller.stop()
self.toolbar.set_playback_state("stopped")
@Slot()
def on_record_clicked(self):
"""Handle record button click"""
# Implementation depends on existing recording logic
pass
@Slot()
def on_snapshot_clicked(self):
"""Handle snapshot button click"""
# Implementation depends on existing snapshot logic
pass
# Backend signal handlers
@Slot(object, object, dict)
def on_frame_ready(self, pixmap, detections, metrics):
"""Handle frame ready signal from video controller"""
# Update live view
if self.current_view == "live":
self.live_view.update_frame(pixmap, detections)
# Update toolbar status
self.toolbar.update_status("processing", True)
@Slot(dict)
def on_stats_ready(self, stats):
"""Handle stats ready signal from video controller"""
# Update stats widgets
self.stats_widget.update_stats(stats)
self.metrics_widget.update_metrics(stats)
# Update toolbar FPS
if 'fps' in stats:
self.toolbar.update_fps(stats['fps'])
@Slot(dict)
def on_violation_detected(self, violation_data):
"""Handle violation detected signal"""
# Update violations view
self.violations_view.add_violation(violation_data)
# Update toolbar status
self.toolbar.update_status("violation", True)
# Play notification sound/animation if enabled
self.play_violation_notification()
@Slot(str)
def on_source_changed(self, source_path):
"""Handle source change from live view"""
if hasattr(self.video_controller, 'set_source'):
self.video_controller.set_source(source_path)
@Slot(int)
def on_tab_changed(self, index):
"""Handle tab change"""
tab_names = ["live", "analytics", "violations", "settings"]
if 0 <= index < len(tab_names):
self.current_view = tab_names[index]
self.view_changed.emit(self.current_view)
# UI control methods
def toggle_fullscreen(self):
"""Toggle fullscreen mode"""
if self.isFullScreen():
self.showNormal()
self.is_fullscreen = False
else:
self.showFullScreen()
self.is_fullscreen = True
self.fullscreen_toggled.emit(self.is_fullscreen)
def toggle_theme(self):
"""Toggle between dark and light theme"""
self.set_dark_mode(not self.dark_mode)
def set_dark_mode(self, dark_mode):
"""Set theme mode"""
self.dark_mode = dark_mode
self.apply_theme()
self.theme_changed.emit(self.dark_mode)
def apply_theme(self):
"""Apply current theme to all UI elements"""
# Apply main styles
self.setStyleSheet(FinaleStyles.get_main_window_style(self.dark_mode))
# Update all child widgets
for child in self.findChildren(QWidget):
if hasattr(child, 'apply_theme'):
child.apply_theme(self.dark_mode)
# Update color scheme
if self.dark_mode:
MaterialColors.apply_dark_theme()
else:
MaterialColors.apply_light_theme()
def show_settings(self):
"""Show settings view"""
self.tabs.setCurrentWidget(self.settings_view)
def show_about(self):
"""Show about dialog"""
QMessageBox.about(self, "About Finale UI",
"Finale Traffic Monitoring System\n"
"Modern UI for OpenVINO-based traffic detection\n"
"Built with PySide6 and Material Design")
def open_file(self):
"""Open file dialog for video source"""
file_path, _ = QFileDialog.getOpenFileName(
self, "Open Video File", "",
"Video Files (*.mp4 *.avi *.mov *.mkv);;All Files (*)"
)
if file_path:
self.on_source_changed(file_path)
def save_config(self):
"""Save current configuration"""
try:
save_configuration(self.config, self.config_file)
self.statusBar().showMessage("Configuration saved", 3000)
except Exception as e:
QMessageBox.warning(self, "Save Error", f"Failed to save configuration:\n{str(e)}")
def play_violation_notification(self):
"""Play violation notification (visual/audio)"""
# Create a brief red flash animation
self.create_violation_flash()
def create_violation_flash(self):
"""Create a red flash effect for violations"""
# Create a semi-transparent red overlay
overlay = QWidget(self)
overlay.setStyleSheet("background-color: rgba(244, 67, 54, 0.3);")
overlay.resize(self.size())
overlay.show()
# Animate the overlay
self.flash_animation = QPropertyAnimation(overlay, b"windowOpacity")
self.flash_animation.setDuration(500)
self.flash_animation.setStartValue(0.3)
self.flash_animation.setEndValue(0.0)
self.flash_animation.setEasingCurve(QEasingCurve.OutCubic)
self.flash_animation.finished.connect(overlay.deleteLater)
self.flash_animation.start()
# Settings persistence
def save_settings(self):
"""Save window settings"""
self.settings.setValue("geometry", self.saveGeometry())
self.settings.setValue("windowState", self.saveState())
self.settings.setValue("dark_mode", self.dark_mode)
self.settings.setValue("current_view", self.current_view)
def restore_settings(self):
"""Restore window settings"""
if self.settings.contains("geometry"):
self.restoreGeometry(self.settings.value("geometry"))
if self.settings.contains("windowState"):
self.restoreState(self.settings.value("windowState"))
if self.settings.contains("dark_mode"):
self.dark_mode = self.settings.value("dark_mode", True, bool)
if self.settings.contains("current_view"):
view_name = self.settings.value("current_view", "live")
view_index = {"live": 0, "analytics": 1, "violations": 2, "settings": 3}.get(view_name, 0)
self.tabs.setCurrentIndex(view_index)
def closeEvent(self, event):
"""Handle window close event"""
# Save settings
self.save_settings()
# Stop video controller
if hasattr(self.video_controller, 'stop'):
self.video_controller.stop()
# Accept close event
event.accept()