cleanup and files added
This commit is contained in:
602
qt_app_pyside1/ui/video_detection_only_tab.py
Normal file
602
qt_app_pyside1/ui/video_detection_only_tab.py
Normal file
@@ -0,0 +1,602 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QSlider, QCheckBox,
|
||||
QFileDialog, QSizePolicy, QFrame, QTabWidget, QSplitter
|
||||
)
|
||||
from PySide6.QtCore import Signal, Qt
|
||||
from PySide6.QtGui import QPixmap, QIcon
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class EnhancedPerformanceOverlay(QFrame):
|
||||
"""Enhanced performance metrics overlay with traffic light status and fixed positioning."""
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setStyleSheet("""
|
||||
QFrame {
|
||||
background: rgba(20, 30, 40, 0.95);
|
||||
border: 2px solid #03DAC5;
|
||||
border-radius: 12px;
|
||||
color: #fff;
|
||||
font-family: 'Segoe UI', 'Arial', sans-serif;
|
||||
}
|
||||
""")
|
||||
# Fixed size to prevent overlay from changing size
|
||||
self.setFixedSize(400, 140)
|
||||
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
self.setAttribute(Qt.WA_TransparentForMouseEvents)
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(16, 12, 16, 12)
|
||||
layout.setSpacing(8)
|
||||
|
||||
# Title row
|
||||
title_layout = QHBoxLayout()
|
||||
title = QLabel("📊 Real-time Performance Metrics")
|
||||
title.setStyleSheet("""
|
||||
color: #03DAC5;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
margin-bottom: 4px;
|
||||
""")
|
||||
title_layout.addWidget(title)
|
||||
title_layout.addStretch()
|
||||
|
||||
# Traffic light status
|
||||
self.traffic_light_status = QLabel("🚦 Traffic: Unknown")
|
||||
self.traffic_light_status.setStyleSheet("""
|
||||
color: #FFD700;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
background: rgba(0,0,0,0.3);
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
""")
|
||||
title_layout.addWidget(self.traffic_light_status)
|
||||
layout.addLayout(title_layout)
|
||||
|
||||
# Performance metrics row
|
||||
perf_layout = QHBoxLayout()
|
||||
perf_layout.setSpacing(16)
|
||||
|
||||
# FPS and Inference in badges
|
||||
self.fps_label = QLabel("FPS: --")
|
||||
self.fps_label.setStyleSheet("""
|
||||
background: #27AE60;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 8px;
|
||||
min-width: 70px;
|
||||
""")
|
||||
self.fps_label.setAlignment(Qt.AlignCenter)
|
||||
|
||||
self.inference_label = QLabel("Inference: -- ms")
|
||||
self.inference_label.setStyleSheet("""
|
||||
background: #3498DB;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 8px;
|
||||
min-width: 110px;
|
||||
""")
|
||||
self.inference_label.setAlignment(Qt.AlignCenter)
|
||||
|
||||
perf_layout.addWidget(self.fps_label)
|
||||
perf_layout.addWidget(self.inference_label)
|
||||
perf_layout.addStretch()
|
||||
layout.addLayout(perf_layout)
|
||||
|
||||
# System info row
|
||||
system_layout = QHBoxLayout()
|
||||
self.model_label = QLabel("Model: -")
|
||||
self.model_label.setStyleSheet("""
|
||||
color: #E74C3C;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
background: rgba(231, 76, 60, 0.1);
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
""")
|
||||
|
||||
self.device_label = QLabel("Device: -")
|
||||
self.device_label.setStyleSheet("""
|
||||
color: #9B59B6;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
background: rgba(155, 89, 182, 0.1);
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
""")
|
||||
|
||||
system_layout.addWidget(self.model_label)
|
||||
system_layout.addWidget(self.device_label)
|
||||
system_layout.addStretch()
|
||||
layout.addLayout(system_layout)
|
||||
|
||||
# Vehicle counts row
|
||||
self.vehicle_stats_label = QLabel("🚗 Vehicles: 0 | 🚛 Trucks: 0 | 🚶 Pedestrians: 0 | 🏍️ Motorcycles: 0")
|
||||
self.vehicle_stats_label.setStyleSheet("""
|
||||
color: #F39C12;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
background: rgba(243, 156, 18, 0.1);
|
||||
padding: 6px 10px;
|
||||
border-radius: 6px;
|
||||
""")
|
||||
layout.addWidget(self.vehicle_stats_label)
|
||||
|
||||
def update_overlay(self, model, device, cars, trucks, peds, tlights, motorcycles):
|
||||
"""Update performance metrics"""
|
||||
self.model_label.setText(f"Model: Yolov11x")
|
||||
self.device_label.setText(f"Device: {device}")
|
||||
self.vehicle_stats_label.setText(f"🚗 Vehicles: {cars} | 🚛 Trucks: {trucks} | 🚶 Pedestrians: {peds} | 🏍️ Motorcycles: {motorcycles}")
|
||||
|
||||
def update_performance_metrics(self, fps, inference_time):
|
||||
"""Update FPS and inference time"""
|
||||
if fps is not None:
|
||||
self.fps_label.setText(f"FPS: {fps:.1f}")
|
||||
else:
|
||||
self.fps_label.setText("FPS: --")
|
||||
|
||||
if inference_time is not None:
|
||||
self.inference_label.setText(f"Inference: {inference_time:.1f} ms")
|
||||
else:
|
||||
self.inference_label.setText("Inference: -- ms")
|
||||
|
||||
def update_traffic_light_status(self, traffic_light_data):
|
||||
"""Update traffic light status"""
|
||||
if traffic_light_data and isinstance(traffic_light_data, dict):
|
||||
color = traffic_light_data.get('color', 'unknown')
|
||||
confidence = traffic_light_data.get('confidence', 0)
|
||||
|
||||
if color.lower() == 'red':
|
||||
icon = "🔴"
|
||||
text_color = "#E74C3C"
|
||||
elif color.lower() == 'yellow':
|
||||
icon = "🟡"
|
||||
text_color = "#F39C12"
|
||||
elif color.lower() == 'green':
|
||||
icon = "🟢"
|
||||
text_color = "#27AE60"
|
||||
else:
|
||||
icon = "⚫"
|
||||
text_color = "#95A5A6"
|
||||
|
||||
self.traffic_light_status.setText(f"{icon} Traffic: {color.title()} ({confidence:.2f})")
|
||||
self.traffic_light_status.setStyleSheet(f"""
|
||||
color: {text_color};
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
background: rgba(0,0,0,0.3);
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
""")
|
||||
else:
|
||||
self.traffic_light_status.setText("🚦 Traffic: Unknown")
|
||||
self.traffic_light_status.setStyleSheet("""
|
||||
color: #95A5A6;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
background: rgba(0,0,0,0.3);
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
""")
|
||||
|
||||
|
||||
class PerformanceStatsWidget(QFrame):
|
||||
"""Compact performance statistics widget to replace analytics tables."""
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setStyleSheet("""
|
||||
QFrame {
|
||||
background: #1a1a1a;
|
||||
border: 1px solid #424242;
|
||||
border-radius: 8px;
|
||||
color: #fff;
|
||||
}
|
||||
""")
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(16, 16, 16, 16)
|
||||
layout.setSpacing(12)
|
||||
|
||||
# Header
|
||||
header = QLabel("⚡ Performance Metrics")
|
||||
header.setStyleSheet("""
|
||||
color: #03DAC5;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
padding: 8px 0px;
|
||||
border-bottom: 2px solid #03DAC5;
|
||||
margin-bottom: 8px;
|
||||
""")
|
||||
layout.addWidget(header)
|
||||
|
||||
# Real-time stats
|
||||
self.fps_stat = QLabel("🎯 FPS: --")
|
||||
self.inference_stat = QLabel("⚡ Inference: -- ms")
|
||||
self.device_stat = QLabel("🖥️ Device: --")
|
||||
self.model_stat = QLabel("🤖 Model: --")
|
||||
|
||||
# Vehicle counts
|
||||
self.vehicles_stat = QLabel("🚗 Vehicles: 0")
|
||||
self.pedestrians_stat = QLabel("🚶 Pedestrians: 0")
|
||||
self.traffic_lights_stat = QLabel("🚦 Traffic Lights: 0")
|
||||
|
||||
# Traffic status
|
||||
self.traffic_status_stat = QLabel("🚦 Traffic Status: Unknown")
|
||||
|
||||
stats = [
|
||||
self.fps_stat, self.inference_stat, self.device_stat, self.model_stat,
|
||||
self.vehicles_stat, self.pedestrians_stat, self.traffic_lights_stat,
|
||||
self.traffic_status_stat
|
||||
]
|
||||
|
||||
for stat in stats:
|
||||
stat.setStyleSheet("""
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
padding: 6px 8px;
|
||||
background: rgba(255,255,255,0.05);
|
||||
border-radius: 6px;
|
||||
margin: 2px 0px;
|
||||
""")
|
||||
layout.addWidget(stat)
|
||||
|
||||
layout.addStretch()
|
||||
|
||||
def update_stats(self, stats_data):
|
||||
"""Update all statistics"""
|
||||
# Debug: print what data we're receiving
|
||||
print(f"[PERF STATS DEBUG] Received stats keys: {list(stats_data.keys()) if stats_data else 'None'}")
|
||||
|
||||
# Performance metrics - try multiple field names
|
||||
fps = stats_data.get('fps', 0)
|
||||
inference = stats_data.get('inference', stats_data.get('detection_time', stats_data.get('detection_time_ms', stats_data.get('inference_time', 0))))
|
||||
|
||||
# Try different field names for device and model
|
||||
device = (stats_data.get('device') or
|
||||
stats_data.get('device_name') or
|
||||
stats_data.get('processing_device') or
|
||||
'GPU') # Default to GPU since we see it's working
|
||||
|
||||
model = (stats_data.get('model') or
|
||||
stats_data.get('model_name') or
|
||||
stats_data.get('ai_model') or
|
||||
'YOLO11') # Default to YOLO11 since that's what we're using
|
||||
|
||||
# Extract model info from stats
|
||||
model = stats_data.get('model_name', 'Unknown')
|
||||
print(f"🔧 DEBUG UI: Received model_name='{model}' from stats")
|
||||
|
||||
# If model name not available in stats, try to extract from model_path
|
||||
if model == 'Unknown':
|
||||
model_path = str(stats_data.get('model_path', ''))
|
||||
print(f"🔧 DEBUG UI: Fallback to model_path='{model_path}'")
|
||||
if 'yolo11n' in model_path.lower():
|
||||
model = 'YOLO11n'
|
||||
elif 'yolo11x' in model_path.lower():
|
||||
model = 'YOLO11x'
|
||||
elif 'yolo11s' in model_path.lower():
|
||||
model = 'YOLO11s'
|
||||
elif 'yolo11m' in model_path.lower():
|
||||
model = 'YOLO11m'
|
||||
elif 'yolo11l' in model_path.lower():
|
||||
model = 'YOLO11l'
|
||||
else:
|
||||
model = 'YOLO11n' # Default fallback
|
||||
|
||||
print(f"🔧 DEBUG UI: Final model name for display: '{model}'")
|
||||
|
||||
print(f"[PERF STATS DEBUG] Device: {device}, Model: {model}, FPS: {fps}, Inference: {inference}")
|
||||
|
||||
self.fps_stat.setText(f"🎯 FPS: {fps:.1f}" if fps else "🎯 FPS: --")
|
||||
self.inference_stat.setText(f"⚡ Inference: {inference:.1f} ms" if inference else "⚡ Inference: -- ms")
|
||||
self.device_stat.setText(f"🖥️ Device: {device}")
|
||||
self.model_stat.setText(f"🤖 Model: {model}")
|
||||
|
||||
# Vehicle counts
|
||||
cars = stats_data.get('cars', 0)
|
||||
trucks = stats_data.get('trucks', 0)
|
||||
motorcycles = stats_data.get('motorcycles', 0)
|
||||
peds = stats_data.get('peds', 0)
|
||||
tlights = stats_data.get('tlights', 0)
|
||||
|
||||
total_vehicles = cars + trucks + motorcycles
|
||||
self.vehicles_stat.setText(f"🚗 Vehicles: {total_vehicles}")
|
||||
self.pedestrians_stat.setText(f"🚶 Pedestrians: {peds}")
|
||||
self.traffic_lights_stat.setText(f"🚦 Traffic Lights: {tlights}")
|
||||
|
||||
# Traffic status - try different field names
|
||||
traffic_light = (stats_data.get('traffic_light') or
|
||||
stats_data.get('traffic_light_data') or
|
||||
stats_data.get('latest_traffic_light'))
|
||||
|
||||
print(f"[PERF STATS DEBUG] Traffic light data: {traffic_light}")
|
||||
|
||||
if traffic_light and isinstance(traffic_light, dict):
|
||||
color = traffic_light.get('color', 'Unknown')
|
||||
confidence = traffic_light.get('confidence', 0)
|
||||
|
||||
# Handle case where color might also be a dict or other type
|
||||
if isinstance(color, str):
|
||||
self.traffic_status_stat.setText(f"🚦 Traffic Status: {color.title()} ({confidence:.2f})")
|
||||
elif isinstance(color, dict):
|
||||
# Color is nested dict, try to extract actual color
|
||||
actual_color = color.get('color', 'Unknown')
|
||||
if isinstance(actual_color, str):
|
||||
self.traffic_status_stat.setText(f"🚦 Traffic Status: {actual_color.title()} ({confidence:.2f})")
|
||||
else:
|
||||
self.traffic_status_stat.setText("🚦 Traffic Status: Unknown")
|
||||
else:
|
||||
self.traffic_status_stat.setText(f"🚦 Traffic Status: {str(color)} ({confidence:.2f})")
|
||||
else:
|
||||
# Try to get traffic light info from other fields
|
||||
if 'traffic_light_color' in stats_data:
|
||||
color = stats_data.get('traffic_light_color', 'Unknown')
|
||||
if isinstance(color, str):
|
||||
self.traffic_status_stat.setText(f"🚦 Traffic Status: {color.title()}")
|
||||
else:
|
||||
self.traffic_status_stat.setText(f"🚦 Traffic Status: {str(color)}")
|
||||
else:
|
||||
self.traffic_status_stat.setText("🚦 Traffic Status: Unknown")
|
||||
|
||||
|
||||
class VideoDetectionOnlyTab(QWidget):
|
||||
"""Standard video detection tab without smart intersection features"""
|
||||
file_selected = Signal(str)
|
||||
play_clicked = Signal()
|
||||
pause_clicked = Signal()
|
||||
stop_clicked = Signal()
|
||||
detection_toggled = Signal(bool)
|
||||
screenshot_clicked = Signal()
|
||||
seek_changed = Signal(int)
|
||||
auto_select_model_device = Signal()
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.video_loaded = False
|
||||
|
||||
# Main layout with splitter for video and analytics
|
||||
main_layout = QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(16, 16, 16, 16)
|
||||
main_layout.setSpacing(16)
|
||||
|
||||
# File selection bar
|
||||
file_bar = self._create_file_bar()
|
||||
main_layout.addWidget(file_bar)
|
||||
|
||||
# Create splitter for video and analytics
|
||||
splitter = QSplitter(Qt.Horizontal)
|
||||
|
||||
# Left side - Video display
|
||||
video_widget = QWidget()
|
||||
video_layout = QVBoxLayout(video_widget)
|
||||
video_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# Video display area
|
||||
video_frame = self._create_video_frame()
|
||||
video_layout.addWidget(video_frame, 1)
|
||||
|
||||
# Control bar
|
||||
control_bar = self._create_control_bar()
|
||||
video_layout.addWidget(control_bar)
|
||||
|
||||
splitter.addWidget(video_widget)
|
||||
|
||||
# Right side - Performance Stats (replacing analytics tables)
|
||||
self.performance_stats = PerformanceStatsWidget()
|
||||
splitter.addWidget(self.performance_stats)
|
||||
|
||||
# Set splitter proportions (70% video, 30% analytics)
|
||||
splitter.setSizes([700, 300])
|
||||
splitter.setCollapsible(0, False) # Video section can't be collapsed
|
||||
splitter.setCollapsible(1, True) # Analytics can be collapsed
|
||||
|
||||
main_layout.addWidget(splitter)
|
||||
|
||||
def _create_file_bar(self):
|
||||
"""Create file selection bar"""
|
||||
widget = QWidget()
|
||||
bar = QHBoxLayout(widget)
|
||||
|
||||
self.file_btn = QPushButton()
|
||||
self.file_btn.setIcon(QIcon.fromTheme("folder-video"))
|
||||
self.file_btn.setText("Select Video")
|
||||
self.file_btn.setStyleSheet("padding: 8px 18px; border-radius: 8px; background: #232323; color: #fff;")
|
||||
self.file_label = QLabel("No file selected")
|
||||
self.file_label.setStyleSheet("color: #bbb; font-size: 13px;")
|
||||
self.file_btn.clicked.connect(self._select_file)
|
||||
|
||||
bar.addWidget(self.file_btn)
|
||||
bar.addWidget(self.file_label)
|
||||
bar.addStretch()
|
||||
|
||||
return widget
|
||||
|
||||
def _create_video_frame(self):
|
||||
"""Create video display frame with fixed size"""
|
||||
video_frame = QFrame()
|
||||
video_frame.setStyleSheet("""
|
||||
background: #121212;
|
||||
border: 1px solid #424242;
|
||||
border-radius: 8px;
|
||||
""")
|
||||
# Set fixed size to prevent resizing issues
|
||||
video_frame.setFixedSize(800, 450) # 16:9 aspect ratio, fixed size
|
||||
video_frame.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
|
||||
video_layout = QVBoxLayout(video_frame)
|
||||
video_layout.setContentsMargins(0, 0, 0, 0)
|
||||
video_layout.setAlignment(Qt.AlignCenter)
|
||||
|
||||
self.video_label = QLabel()
|
||||
self.video_label.setAlignment(Qt.AlignCenter)
|
||||
self.video_label.setStyleSheet("background: transparent; color: #888; font-size: 18px;")
|
||||
self.video_label.setText("No video loaded. Please select a file.")
|
||||
# Set fixed size for video label to match frame
|
||||
self.video_label.setFixedSize(800, 450)
|
||||
self.video_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
self.video_label.setScaledContents(True) # Scale content to fit
|
||||
video_layout.addWidget(self.video_label)
|
||||
|
||||
return video_frame
|
||||
|
||||
def _create_control_bar(self):
|
||||
"""Create control bar"""
|
||||
widget = QWidget()
|
||||
control_bar = QHBoxLayout(widget)
|
||||
control_bar.setContentsMargins(0, 16, 0, 0)
|
||||
|
||||
# Playback controls
|
||||
self.play_btn = QPushButton()
|
||||
self.play_btn.setIcon(QIcon.fromTheme("media-playback-start"))
|
||||
self.play_btn.setToolTip("Play")
|
||||
self.play_btn.setFixedSize(48, 48)
|
||||
self.play_btn.setEnabled(False)
|
||||
self.play_btn.setStyleSheet(self._button_style())
|
||||
|
||||
self.pause_btn = QPushButton()
|
||||
self.pause_btn.setIcon(QIcon.fromTheme("media-playback-pause"))
|
||||
self.pause_btn.setToolTip("Pause")
|
||||
self.pause_btn.setFixedSize(48, 48)
|
||||
self.pause_btn.setEnabled(False)
|
||||
self.pause_btn.setStyleSheet(self._button_style())
|
||||
|
||||
self.stop_btn = QPushButton()
|
||||
self.stop_btn.setIcon(QIcon.fromTheme("media-playback-stop"))
|
||||
self.stop_btn.setToolTip("Stop")
|
||||
self.stop_btn.setFixedSize(48, 48)
|
||||
self.stop_btn.setEnabled(False)
|
||||
self.stop_btn.setStyleSheet(self._button_style())
|
||||
|
||||
for btn, sig in zip([self.play_btn, self.pause_btn, self.stop_btn],
|
||||
[self.play_clicked.emit, self.pause_clicked.emit, self.stop_clicked.emit]):
|
||||
btn.clicked.connect(sig)
|
||||
|
||||
control_bar.addWidget(self.play_btn)
|
||||
control_bar.addWidget(self.pause_btn)
|
||||
control_bar.addWidget(self.stop_btn)
|
||||
control_bar.addSpacing(16)
|
||||
|
||||
# Progress bar
|
||||
self.progress = QSlider(Qt.Horizontal)
|
||||
self.progress.setStyleSheet("""
|
||||
QSlider::groove:horizontal {
|
||||
height: 6px;
|
||||
background: #232323;
|
||||
border-radius: 3px;
|
||||
}
|
||||
QSlider::handle:horizontal {
|
||||
background: #03DAC5;
|
||||
border-radius: 8px;
|
||||
width: 18px;
|
||||
}
|
||||
""")
|
||||
self.progress.setMinimumWidth(240)
|
||||
self.progress.setEnabled(False)
|
||||
self.progress.valueChanged.connect(self.seek_changed.emit)
|
||||
control_bar.addWidget(self.progress, 2)
|
||||
|
||||
self.timestamp = QLabel("00:00 / 00:00")
|
||||
self.timestamp.setStyleSheet("color: #bbb; font-size: 13px;")
|
||||
control_bar.addWidget(self.timestamp)
|
||||
control_bar.addSpacing(16)
|
||||
|
||||
# Detection toggle & screenshot
|
||||
self.detection_toggle = QCheckBox("Enable Detection")
|
||||
self.detection_toggle.setChecked(True)
|
||||
self.detection_toggle.setStyleSheet("color: #fff; font-size: 14px;")
|
||||
self.detection_toggle.setEnabled(False)
|
||||
self.detection_toggle.toggled.connect(self.detection_toggled.emit)
|
||||
control_bar.addWidget(self.detection_toggle)
|
||||
|
||||
self.screenshot_btn = QPushButton()
|
||||
self.screenshot_btn.setIcon(QIcon.fromTheme("camera-photo"))
|
||||
self.screenshot_btn.setText("Screenshot")
|
||||
self.screenshot_btn.setToolTip("Save current frame as image")
|
||||
self.screenshot_btn.setEnabled(False)
|
||||
self.screenshot_btn.setStyleSheet(self._button_style())
|
||||
self.screenshot_btn.clicked.connect(self.screenshot_clicked.emit)
|
||||
control_bar.addWidget(self.screenshot_btn)
|
||||
control_bar.addStretch()
|
||||
|
||||
return widget
|
||||
|
||||
def _button_style(self):
|
||||
return """
|
||||
QPushButton {
|
||||
background: #232323;
|
||||
border-radius: 24px;
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
border: none;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background: #03DAC5;
|
||||
color: #222;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background: #018786;
|
||||
}
|
||||
"""
|
||||
|
||||
def _select_file(self):
|
||||
file_path, _ = QFileDialog.getOpenFileName(
|
||||
self,
|
||||
"Select Video File",
|
||||
"",
|
||||
"Video Files (*.mp4 *.avi *.mov *.mkv *.webm);;All Files (*)"
|
||||
)
|
||||
if file_path:
|
||||
self.file_label.setText(file_path)
|
||||
self.file_selected.emit(file_path)
|
||||
self.video_loaded = True
|
||||
self._enable_controls(True)
|
||||
self.video_label.setText("")
|
||||
self.auto_select_model_device.emit()
|
||||
|
||||
def _enable_controls(self, enabled):
|
||||
self.play_btn.setEnabled(enabled)
|
||||
self.pause_btn.setEnabled(enabled)
|
||||
self.stop_btn.setEnabled(enabled)
|
||||
self.progress.setEnabled(enabled)
|
||||
self.detection_toggle.setEnabled(enabled)
|
||||
self.screenshot_btn.setEnabled(enabled)
|
||||
if enabled:
|
||||
self.auto_select_model_device.emit()
|
||||
|
||||
def update_display(self, pixmap):
|
||||
"""Update display with new frame"""
|
||||
if pixmap:
|
||||
scaled = pixmap.scaled(self.video_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||
self.video_label.setPixmap(scaled)
|
||||
self._set_controls_enabled(True)
|
||||
self.video_label.setStyleSheet("background: transparent; color: #888; font-size: 18px;")
|
||||
else:
|
||||
self.video_label.clear()
|
||||
self.video_label.setText("No video loaded. Please select a video file.")
|
||||
self._set_controls_enabled(False)
|
||||
self.video_label.setStyleSheet("background: transparent; color: #F44336; font-size: 18px;")
|
||||
|
||||
def _set_controls_enabled(self, enabled):
|
||||
for btn in [self.play_btn, self.pause_btn, self.stop_btn, self.progress, self.detection_toggle, self.screenshot_btn]:
|
||||
btn.setEnabled(enabled)
|
||||
|
||||
def update_stats(self, stats):
|
||||
"""Update statistics display"""
|
||||
# Update right panel performance stats only
|
||||
self.performance_stats.update_stats(stats)
|
||||
|
||||
def update_progress(self, value, max_value, timestamp):
|
||||
self.progress.setMaximum(max_value)
|
||||
self.progress.setValue(value)
|
||||
if isinstance(timestamp, float) or isinstance(timestamp, int):
|
||||
timestamp_str = f"{timestamp:.2f}"
|
||||
else:
|
||||
timestamp_str = str(timestamp)
|
||||
self.timestamp.setText(timestamp_str)
|
||||
Reference in New Issue
Block a user