better bullshit
This commit is contained in:
419
mucapy/main.py
419
mucapy/main.py
@@ -2,15 +2,18 @@ import os
|
||||
import sys
|
||||
import cv2
|
||||
import json
|
||||
import urllib.parse
|
||||
import numpy as np
|
||||
from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout,
|
||||
QWidget, QLabel, QPushButton, QComboBox, QSpinBox,
|
||||
QFileDialog, QMessageBox, QMenu, QAction, QMenuBar,
|
||||
QActionGroup, QSizePolicy, QGridLayout, QGroupBox,
|
||||
QDockWidget, QScrollArea, QToolButton, QDialog,
|
||||
QShortcut, QListWidget, QFormLayout, QLineEdit)
|
||||
from PyQt5.QtCore import Qt, QTimer, QDir, QSize, QSettings, QDateTime
|
||||
from PyQt5.QtGui import QImage, QPixmap, QIcon, QColor, QKeySequence
|
||||
QShortcut, QListWidget, QFormLayout, QLineEdit,
|
||||
QCheckBox, QTabWidget, QListWidgetItem)
|
||||
from PyQt5.QtCore import Qt, QTimer, QDir, QSize, QSettings, QDateTime, QRect
|
||||
from PyQt5.QtGui import (QImage, QPixmap, QIcon, QColor, QKeySequence, QPainter,
|
||||
QPen, QBrush)
|
||||
|
||||
class Config:
|
||||
def __init__(self):
|
||||
@@ -72,9 +75,9 @@ class MultiCamYOLODetector:
|
||||
except:
|
||||
return False
|
||||
|
||||
def add_network_camera(self, name, url):
|
||||
def add_network_camera(self, name, camera_info):
|
||||
"""Add a network camera to the saved list"""
|
||||
self.network_cameras[name] = url
|
||||
self.network_cameras[name] = camera_info
|
||||
self.config.save_setting('network_cameras', self.network_cameras)
|
||||
|
||||
def remove_network_camera(self, name):
|
||||
@@ -87,34 +90,37 @@ class MultiCamYOLODetector:
|
||||
"""Check for available cameras including network cameras"""
|
||||
self.available_cameras = []
|
||||
|
||||
# Check standard video devices
|
||||
# Try numeric indices first (this works more reliably)
|
||||
for i in range(max_to_check):
|
||||
try:
|
||||
cap = cv2.VideoCapture(i, cv2.CAP_V4L2)
|
||||
cap = cv2.VideoCapture(i)
|
||||
if cap.isOpened():
|
||||
self.available_cameras.append(str(i))
|
||||
cap.release()
|
||||
except:
|
||||
continue
|
||||
|
||||
# Check direct device paths
|
||||
v4l_paths = [f"/dev/video{i}" for i in range(max_to_check)]
|
||||
v4l_paths += [f"/dev/v4l/video{i}" for i in range(max_to_check)]
|
||||
|
||||
for path in v4l_paths:
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
cap = cv2.VideoCapture(path, cv2.CAP_V4L2)
|
||||
if cap.isOpened():
|
||||
if path not in self.available_cameras:
|
||||
self.available_cameras.append(path)
|
||||
# Also check device paths as fallback
|
||||
if os.path.exists('/dev'):
|
||||
for i in range(max_to_check):
|
||||
device_path = f"/dev/video{i}"
|
||||
if os.path.exists(device_path) and device_path not in self.available_cameras:
|
||||
try:
|
||||
cap = cv2.VideoCapture(device_path)
|
||||
if cap.isOpened():
|
||||
self.available_cameras.append(device_path)
|
||||
cap.release()
|
||||
except:
|
||||
continue
|
||||
except:
|
||||
continue
|
||||
|
||||
# Add saved network cameras
|
||||
for name, url in self.network_cameras.items():
|
||||
self.available_cameras.append(f"net:{url}")
|
||||
for name, camera_info in self.network_cameras.items():
|
||||
if isinstance(camera_info, dict):
|
||||
url = camera_info.get('url', '')
|
||||
self.available_cameras.append(f"net:{name}") # Use name instead of URL for better identification
|
||||
else:
|
||||
# Handle old format where camera_info was just the URL
|
||||
self.available_cameras.append(f"net:{name}")
|
||||
|
||||
return self.available_cameras
|
||||
|
||||
@@ -161,7 +167,7 @@ class MultiCamYOLODetector:
|
||||
return False
|
||||
|
||||
def connect_cameras(self, camera_paths):
|
||||
"""Connect to multiple cameras including network cameras"""
|
||||
"""Connect to multiple cameras including network cameras with authentication"""
|
||||
self.disconnect_cameras()
|
||||
|
||||
for cam_path in camera_paths:
|
||||
@@ -169,16 +175,30 @@ class MultiCamYOLODetector:
|
||||
if isinstance(cam_path, str):
|
||||
if cam_path.startswith('net:'):
|
||||
# Handle network camera
|
||||
url = cam_path[4:] # Remove 'net:' prefix
|
||||
camera_name = cam_path[4:] # Remove 'net:' prefix to get camera name
|
||||
camera_info = self.network_cameras.get(camera_name)
|
||||
|
||||
if isinstance(camera_info, dict):
|
||||
url = camera_info['url']
|
||||
# Add authentication to URL if provided
|
||||
if 'username' in camera_info and 'password' in camera_info:
|
||||
# Parse URL and add authentication
|
||||
parsed = urllib.parse.urlparse(url)
|
||||
netloc = f"{camera_info['username']}:{camera_info['password']}@{parsed.netloc}"
|
||||
url = parsed._replace(netloc=netloc).geturl()
|
||||
else:
|
||||
# Handle old format where camera_info was just the URL
|
||||
url = camera_info
|
||||
|
||||
cap = cv2.VideoCapture(url)
|
||||
elif cam_path.startswith('/dev/'):
|
||||
# Handle device path
|
||||
cap = cv2.VideoCapture(cam_path, cv2.CAP_V4L2)
|
||||
cap = cv2.VideoCapture(cam_path)
|
||||
else:
|
||||
# Handle numeric index
|
||||
cap = cv2.VideoCapture(int(cam_path), cv2.CAP_V4L2)
|
||||
cap = cv2.VideoCapture(int(cam_path))
|
||||
else:
|
||||
cap = cv2.VideoCapture(int(cam_path), cv2.CAP_V4L2)
|
||||
cap = cv2.VideoCapture(int(cam_path))
|
||||
|
||||
if not cap.isOpened():
|
||||
print(f"Warning: Could not open camera {cam_path}")
|
||||
@@ -299,6 +319,7 @@ class CameraDisplay(QLabel):
|
||||
self.fullscreen_timer = None
|
||||
self.config = Config()
|
||||
self.screenshot_dir = self.config.load_setting('screenshot_dir', os.path.expanduser('~/Pictures/MuCaPy'))
|
||||
self.camera_name = None
|
||||
|
||||
# Create screenshot directory if it doesn't exist
|
||||
if not os.path.exists(self.screenshot_dir):
|
||||
@@ -308,6 +329,11 @@ class CameraDisplay(QLabel):
|
||||
"""Set camera identifier for this display"""
|
||||
self.cam_id = cam_id
|
||||
|
||||
def set_camera_name(self, name):
|
||||
"""Set the camera name for display"""
|
||||
self.camera_name = name
|
||||
self.update()
|
||||
|
||||
def take_screenshot(self):
|
||||
"""Take a screenshot of the current frame"""
|
||||
if not self.pixmap():
|
||||
@@ -407,6 +433,23 @@ class CameraDisplay(QLabel):
|
||||
self.fullscreen_window = None
|
||||
self.fullscreen_timer = None
|
||||
|
||||
def paintEvent(self, event):
|
||||
"""Override paint event to draw camera name overlay"""
|
||||
super().paintEvent(event)
|
||||
if self.camera_name and self.pixmap():
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
|
||||
# Draw semi-transparent background
|
||||
painter.setPen(Qt.NoPen)
|
||||
painter.setBrush(QBrush(QColor(0, 0, 0, 180)))
|
||||
rect = QRect(10, 10, painter.fontMetrics().width(self.camera_name) + 20, 30)
|
||||
painter.drawRoundedRect(rect, 5, 5)
|
||||
|
||||
# Draw text
|
||||
painter.setPen(QPen(QColor(255, 255, 255)))
|
||||
painter.drawText(rect, Qt.AlignCenter, self.camera_name)
|
||||
|
||||
class CollapsibleDock(QDockWidget):
|
||||
"""Custom dock widget with collapse/expand functionality"""
|
||||
def __init__(self, title, parent=None):
|
||||
@@ -539,7 +582,7 @@ class NetworkCameraDialog(QDialog):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("Network Camera Settings")
|
||||
self.setModal(True)
|
||||
self.resize(400, 300)
|
||||
self.resize(500, 400)
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
@@ -548,7 +591,8 @@ class NetworkCameraDialog(QDialog):
|
||||
"Enter network camera details:\n"
|
||||
"- For DroidCam: Use the IP and port shown in the app\n"
|
||||
" Example: http://192.168.1.100:4747/video\n"
|
||||
"- For other IP cameras: Enter the full stream URL"
|
||||
"- For other IP cameras: Enter the full stream URL\n"
|
||||
"- Enable authentication if the camera requires username/password"
|
||||
)
|
||||
instructions.setWordWrap(True)
|
||||
layout.addWidget(instructions)
|
||||
@@ -559,12 +603,38 @@ class NetworkCameraDialog(QDialog):
|
||||
|
||||
# Input fields
|
||||
form_layout = QFormLayout()
|
||||
|
||||
# Name and URL
|
||||
self.name_edit = QLineEdit()
|
||||
self.url_edit = QLineEdit()
|
||||
form_layout.addRow("Name:", self.name_edit)
|
||||
form_layout.addRow("URL:", self.url_edit)
|
||||
|
||||
# Authentication group
|
||||
auth_group = QGroupBox("Authentication")
|
||||
auth_layout = QVBoxLayout()
|
||||
|
||||
self.auth_checkbox = QCheckBox("Enable Authentication")
|
||||
self.auth_checkbox.stateChanged.connect(self.toggle_auth_fields)
|
||||
auth_layout.addWidget(self.auth_checkbox)
|
||||
|
||||
auth_form = QFormLayout()
|
||||
self.username_edit = QLineEdit()
|
||||
self.password_edit = QLineEdit()
|
||||
self.password_edit.setEchoMode(QLineEdit.Password)
|
||||
auth_form.addRow("Username:", self.username_edit)
|
||||
auth_form.addRow("Password:", self.password_edit)
|
||||
auth_layout.addLayout(auth_form)
|
||||
|
||||
auth_group.setLayout(auth_layout)
|
||||
form_layout.addRow(auth_group)
|
||||
|
||||
layout.addLayout(form_layout)
|
||||
|
||||
# Initially disable auth fields
|
||||
self.username_edit.setEnabled(False)
|
||||
self.password_edit.setEnabled(False)
|
||||
|
||||
# Buttons
|
||||
btn_layout = QHBoxLayout()
|
||||
add_btn = QPushButton("Add Camera")
|
||||
@@ -582,14 +652,32 @@ class NetworkCameraDialog(QDialog):
|
||||
self.detector = parent.detector if parent else None
|
||||
self.load_cameras()
|
||||
|
||||
def toggle_auth_fields(self, state):
|
||||
"""Enable/disable authentication fields based on checkbox state"""
|
||||
enabled = state == Qt.Checked
|
||||
self.username_edit.setEnabled(enabled)
|
||||
self.password_edit.setEnabled(enabled)
|
||||
if not enabled:
|
||||
self.username_edit.clear()
|
||||
self.password_edit.clear()
|
||||
|
||||
def load_cameras(self):
|
||||
"""Load saved network cameras into the list"""
|
||||
if not self.detector:
|
||||
return
|
||||
|
||||
self.camera_list.clear()
|
||||
for name, url in self.detector.network_cameras.items():
|
||||
self.camera_list.addItem(f"{name} ({url})")
|
||||
for name, camera_info in self.detector.network_cameras.items():
|
||||
if isinstance(camera_info, dict):
|
||||
url = camera_info.get('url', '')
|
||||
has_auth = camera_info.get('username') is not None
|
||||
display_text = f"{name} ({url})"
|
||||
if has_auth:
|
||||
display_text += " [Auth]"
|
||||
else:
|
||||
# Handle old format where camera_info was just the URL
|
||||
display_text = f"{name} ({camera_info})"
|
||||
self.camera_list.addItem(display_text)
|
||||
|
||||
def add_camera(self):
|
||||
"""Add a new network camera"""
|
||||
@@ -601,10 +689,25 @@ class NetworkCameraDialog(QDialog):
|
||||
return
|
||||
|
||||
if self.detector:
|
||||
self.detector.add_network_camera(name, url)
|
||||
camera_info = {'url': url}
|
||||
|
||||
# Add authentication if enabled
|
||||
if self.auth_checkbox.isChecked():
|
||||
username = self.username_edit.text().strip()
|
||||
password = self.password_edit.text().strip()
|
||||
if username and password:
|
||||
camera_info['username'] = username
|
||||
camera_info['password'] = password
|
||||
|
||||
self.detector.add_network_camera(name, camera_info)
|
||||
self.load_cameras()
|
||||
|
||||
# Clear fields
|
||||
self.name_edit.clear()
|
||||
self.url_edit.clear()
|
||||
self.username_edit.clear()
|
||||
self.password_edit.clear()
|
||||
self.auth_checkbox.setChecked(False)
|
||||
|
||||
def remove_camera(self):
|
||||
"""Remove selected network camera"""
|
||||
@@ -617,6 +720,197 @@ class NetworkCameraDialog(QDialog):
|
||||
self.detector.remove_network_camera(name)
|
||||
self.load_cameras()
|
||||
|
||||
class CameraSelectorDialog(QDialog):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("Camera Selector")
|
||||
self.setModal(True)
|
||||
self.resize(600, 500)
|
||||
|
||||
self.detector = parent.detector if parent else None
|
||||
self.selected_cameras = []
|
||||
|
||||
# Main layout
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
# Create tab widget
|
||||
tabs = QTabWidget()
|
||||
|
||||
# Local Cameras Tab
|
||||
local_tab = QWidget()
|
||||
local_layout = QVBoxLayout(local_tab)
|
||||
|
||||
# Local camera list
|
||||
local_group = QGroupBox("Local Cameras")
|
||||
local_list_layout = QVBoxLayout()
|
||||
self.local_list = QListWidget()
|
||||
self.local_list.setSelectionMode(QListWidget.MultiSelection)
|
||||
local_list_layout.addWidget(self.local_list)
|
||||
|
||||
# Refresh button for local cameras
|
||||
refresh_btn = QPushButton("Refresh Local Cameras")
|
||||
refresh_btn.clicked.connect(self.refresh_local_cameras)
|
||||
local_list_layout.addWidget(refresh_btn)
|
||||
|
||||
local_group.setLayout(local_list_layout)
|
||||
local_layout.addWidget(local_group)
|
||||
tabs.addTab(local_tab, "Local Cameras")
|
||||
|
||||
# Network Cameras Tab
|
||||
network_tab = QWidget()
|
||||
network_layout = QVBoxLayout(network_tab)
|
||||
|
||||
# Network camera list
|
||||
network_group = QGroupBox("Network Cameras")
|
||||
network_list_layout = QVBoxLayout()
|
||||
self.network_list = QListWidget()
|
||||
self.network_list.setSelectionMode(QListWidget.MultiSelection)
|
||||
network_list_layout.addWidget(self.network_list)
|
||||
|
||||
# Network camera controls
|
||||
net_btn_layout = QHBoxLayout()
|
||||
add_net_btn = QPushButton("Add Network Camera")
|
||||
add_net_btn.clicked.connect(self.show_network_dialog)
|
||||
remove_net_btn = QPushButton("Remove Selected")
|
||||
remove_net_btn.clicked.connect(self.remove_network_camera)
|
||||
net_btn_layout.addWidget(add_net_btn)
|
||||
net_btn_layout.addWidget(remove_net_btn)
|
||||
network_list_layout.addLayout(net_btn_layout)
|
||||
|
||||
network_group.setLayout(network_list_layout)
|
||||
network_layout.addWidget(network_group)
|
||||
tabs.addTab(network_tab, "Network Cameras")
|
||||
|
||||
layout.addWidget(tabs)
|
||||
|
||||
# Selected cameras preview
|
||||
preview_group = QGroupBox("Selected Cameras")
|
||||
preview_layout = QVBoxLayout()
|
||||
self.preview_list = QListWidget()
|
||||
preview_layout.addWidget(self.preview_list)
|
||||
preview_group.setLayout(preview_layout)
|
||||
layout.addWidget(preview_group)
|
||||
|
||||
# Buttons
|
||||
btn_layout = QHBoxLayout()
|
||||
select_all_btn = QPushButton("Select All")
|
||||
select_all_btn.clicked.connect(self.select_all)
|
||||
clear_btn = QPushButton("Clear Selection")
|
||||
clear_btn.clicked.connect(self.clear_selection)
|
||||
ok_btn = QPushButton("OK")
|
||||
ok_btn.clicked.connect(self.accept)
|
||||
cancel_btn = QPushButton("Cancel")
|
||||
cancel_btn.clicked.connect(self.reject)
|
||||
|
||||
btn_layout.addWidget(select_all_btn)
|
||||
btn_layout.addWidget(clear_btn)
|
||||
btn_layout.addStretch()
|
||||
btn_layout.addWidget(ok_btn)
|
||||
btn_layout.addWidget(cancel_btn)
|
||||
layout.addLayout(btn_layout)
|
||||
|
||||
# Connect selection change signals
|
||||
self.local_list.itemSelectionChanged.connect(self.update_preview)
|
||||
self.network_list.itemSelectionChanged.connect(self.update_preview)
|
||||
|
||||
self.refresh_cameras()
|
||||
|
||||
def refresh_cameras(self):
|
||||
"""Refresh both local and network camera lists"""
|
||||
self.refresh_local_cameras()
|
||||
self.refresh_network_cameras()
|
||||
|
||||
def refresh_local_cameras(self):
|
||||
"""Refresh the local camera list"""
|
||||
self.local_list.clear()
|
||||
if self.detector:
|
||||
# Get local cameras
|
||||
for i in range(10): # Check first 10 indices
|
||||
try:
|
||||
cap = cv2.VideoCapture(i)
|
||||
if cap.isOpened():
|
||||
self.local_list.addItem(QListWidgetItem(f"Camera {i}"))
|
||||
cap.release()
|
||||
except:
|
||||
continue
|
||||
|
||||
# Check device paths
|
||||
if os.path.exists('/dev'):
|
||||
for i in range(10):
|
||||
device_path = f"/dev/video{i}"
|
||||
if os.path.exists(device_path):
|
||||
try:
|
||||
cap = cv2.VideoCapture(device_path)
|
||||
if cap.isOpened():
|
||||
item = QListWidgetItem(os.path.basename(device_path))
|
||||
item.setData(Qt.UserRole, device_path)
|
||||
self.local_list.addItem(item)
|
||||
cap.release()
|
||||
except:
|
||||
continue
|
||||
|
||||
def refresh_network_cameras(self):
|
||||
"""Refresh the network camera list"""
|
||||
self.network_list.clear()
|
||||
if self.detector:
|
||||
for name, camera_info in self.detector.network_cameras.items():
|
||||
if isinstance(camera_info, dict):
|
||||
url = camera_info.get('url', '')
|
||||
has_auth = camera_info.get('username') is not None
|
||||
display_text = f"{name} ({url})"
|
||||
if has_auth:
|
||||
display_text += " [Auth]"
|
||||
else:
|
||||
display_text = f"{name} ({camera_info})"
|
||||
|
||||
item = QListWidgetItem(display_text)
|
||||
item.setData(Qt.UserRole, f"net:{name}")
|
||||
self.network_list.addItem(item)
|
||||
|
||||
def show_network_dialog(self):
|
||||
"""Show the network camera configuration dialog"""
|
||||
dialog = NetworkCameraDialog(self.parent())
|
||||
if dialog.exec_() == QDialog.Accepted:
|
||||
self.refresh_network_cameras()
|
||||
|
||||
def remove_network_camera(self):
|
||||
"""Remove selected network cameras"""
|
||||
for item in self.network_list.selectedItems():
|
||||
cam_path = item.data(Qt.UserRole)
|
||||
if cam_path.startswith('net:'):
|
||||
name = cam_path[4:]
|
||||
if self.detector:
|
||||
self.detector.remove_network_camera(name)
|
||||
self.refresh_network_cameras()
|
||||
self.update_preview()
|
||||
|
||||
def update_preview(self):
|
||||
"""Update the preview list with currently selected cameras"""
|
||||
self.preview_list.clear()
|
||||
self.selected_cameras = []
|
||||
|
||||
# Add selected local cameras
|
||||
for item in self.local_list.selectedItems():
|
||||
cam_path = item.data(Qt.UserRole) if item.data(Qt.UserRole) else item.text().split()[-1]
|
||||
self.selected_cameras.append(cam_path)
|
||||
self.preview_list.addItem(f"✓ {item.text()}")
|
||||
|
||||
# Add selected network cameras
|
||||
for item in self.network_list.selectedItems():
|
||||
cam_path = item.data(Qt.UserRole)
|
||||
self.selected_cameras.append(cam_path)
|
||||
self.preview_list.addItem(f"✓ {item.text()}")
|
||||
|
||||
def select_all(self):
|
||||
"""Select all cameras in both lists"""
|
||||
self.local_list.selectAll()
|
||||
self.network_list.selectAll()
|
||||
|
||||
def clear_selection(self):
|
||||
"""Clear selection in both lists"""
|
||||
self.local_list.clearSelection()
|
||||
self.network_list.clearSelection()
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -773,11 +1067,15 @@ class MainWindow(QMainWindow):
|
||||
|
||||
# Camera menu
|
||||
self.camera_menu = menubar.addMenu('Cameras')
|
||||
self.refresh_cameras_action = QAction('Refresh Camera List', self)
|
||||
self.refresh_cameras_action.triggered.connect(self.populate_camera_menu)
|
||||
self.camera_menu.addAction(self.refresh_cameras_action)
|
||||
|
||||
# Add Network Camera action
|
||||
# Add Camera Selector action
|
||||
select_cameras_action = QAction('Select Cameras...', self)
|
||||
select_cameras_action.triggered.connect(self.show_camera_selector)
|
||||
self.camera_menu.addAction(select_cameras_action)
|
||||
|
||||
self.camera_menu.addSeparator()
|
||||
|
||||
# Add Network Camera Settings action
|
||||
network_camera_action = QAction('Network Camera Settings...', self)
|
||||
network_camera_action.triggered.connect(self.show_network_camera_dialog)
|
||||
self.camera_menu.addAction(network_camera_action)
|
||||
@@ -790,19 +1088,22 @@ class MainWindow(QMainWindow):
|
||||
|
||||
def populate_camera_menu(self):
|
||||
"""Populate the camera menu with available cameras"""
|
||||
# Clear existing camera actions (except refresh)
|
||||
for action in self.camera_menu.actions()[2:]:
|
||||
# Clear existing camera actions (except refresh and network camera settings)
|
||||
for action in self.camera_menu.actions()[3:]:
|
||||
self.camera_menu.removeAction(action)
|
||||
|
||||
available_cams = self.detector.scan_for_cameras()
|
||||
for cam_path in available_cams:
|
||||
# Display friendly name
|
||||
if cam_path.startswith('/dev/'):
|
||||
cam_name = os.path.basename(cam_path)
|
||||
if cam_path.startswith('net:'):
|
||||
name = cam_path[4:] # Use the camera name directly
|
||||
display_name = f"{name}"
|
||||
elif cam_path.startswith('/dev/'):
|
||||
display_name = os.path.basename(cam_path)
|
||||
else:
|
||||
cam_name = f"Camera {cam_path}"
|
||||
display_name = f"Camera {cam_path}"
|
||||
|
||||
action = QAction(cam_name, self, checkable=True)
|
||||
action = QAction(display_name, self, checkable=True)
|
||||
action.setData(cam_path)
|
||||
self.camera_action_group.addAction(action)
|
||||
self.camera_menu.addAction(action)
|
||||
@@ -1060,7 +1361,7 @@ class MainWindow(QMainWindow):
|
||||
"""Update the camera feeds in the display"""
|
||||
frames = self.detector.get_frames()
|
||||
|
||||
for i, frame in enumerate(frames):
|
||||
for i, (cam_path, frame) in enumerate(zip(self.detector.cameras, frames)):
|
||||
if i >= len(self.camera_displays):
|
||||
break
|
||||
|
||||
@@ -1076,6 +1377,20 @@ class MainWindow(QMainWindow):
|
||||
display.setPixmap(pixmap.scaled(display.width(), display.height(),
|
||||
Qt.KeepAspectRatio, Qt.SmoothTransformation))
|
||||
|
||||
# Update camera name
|
||||
cam_path = cam_path[0] if isinstance(cam_path, tuple) else cam_path
|
||||
if isinstance(cam_path, str):
|
||||
if cam_path.startswith('net:'):
|
||||
# For network cameras, show the saved name
|
||||
camera_name = cam_path[4:] # Get the name directly
|
||||
display.set_camera_name(camera_name)
|
||||
elif cam_path.startswith('/dev/'):
|
||||
# For device paths, show the device name
|
||||
display.set_camera_name(os.path.basename(cam_path))
|
||||
else:
|
||||
# For numeric indices, show Camera N
|
||||
display.set_camera_name(f"Camera {cam_path}")
|
||||
|
||||
def take_screenshot(self):
|
||||
"""Take screenshot of active camera displays"""
|
||||
active_displays = [d for d in self.camera_displays if d.isVisible() and d.pixmap()]
|
||||
@@ -1097,6 +1412,24 @@ class MainWindow(QMainWindow):
|
||||
# Refresh camera list after dialog closes
|
||||
self.populate_camera_menu()
|
||||
|
||||
def show_camera_selector(self):
|
||||
"""Show the camera selector dialog"""
|
||||
dialog = CameraSelectorDialog(self)
|
||||
if dialog.exec_() == QDialog.Accepted and dialog.selected_cameras:
|
||||
# Stop current detection if running
|
||||
was_running = False
|
||||
if self.stop_btn.isEnabled():
|
||||
was_running = True
|
||||
self.stop_detection()
|
||||
|
||||
# Update selected cameras
|
||||
for action in self.camera_action_group.actions():
|
||||
action.setChecked(action.data() in dialog.selected_cameras)
|
||||
|
||||
# Restart detection if it was running
|
||||
if was_running:
|
||||
self.start_detection()
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Handle window close event"""
|
||||
self.stop_detection()
|
||||
|
||||
Reference in New Issue
Block a user