i hate python
This commit is contained in:
449
mucapy/main.py
449
mucapy/main.py
@@ -10,16 +10,25 @@ from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout
|
|||||||
QActionGroup, QSizePolicy, QGridLayout, QGroupBox,
|
QActionGroup, QSizePolicy, QGridLayout, QGroupBox,
|
||||||
QDockWidget, QScrollArea, QToolButton, QDialog,
|
QDockWidget, QScrollArea, QToolButton, QDialog,
|
||||||
QShortcut, QListWidget, QFormLayout, QLineEdit,
|
QShortcut, QListWidget, QFormLayout, QLineEdit,
|
||||||
QCheckBox, QTabWidget, QListWidgetItem)
|
QCheckBox, QTabWidget, QListWidgetItem, QSplitter)
|
||||||
from PyQt5.QtCore import Qt, QTimer, QDir, QSize, QSettings, QDateTime, QRect
|
from PyQt5.QtCore import Qt, QTimer, QDir, QSize, QSettings, QDateTime, QRect
|
||||||
from PyQt5.QtGui import (QImage, QPixmap, QIcon, QColor, QKeySequence, QPainter,
|
from PyQt5.QtGui import (QImage, QPixmap, QIcon, QColor, QKeySequence, QPainter,
|
||||||
QPen, QBrush)
|
QPen, QBrush)
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# Use JSON file in current working directory instead of QSettings
|
# Use JSON file in current working directory
|
||||||
self.config_file = os.path.join(os.getcwd(), 'mucapy_config.json')
|
self.config_file = os.path.join(os.getcwd(), 'mucapy_config.json')
|
||||||
self.settings = {}
|
self.settings = {
|
||||||
|
'network_cameras': {}, # Store network cameras configuration
|
||||||
|
'last_model_dir': '',
|
||||||
|
'last_screenshot_dir': os.path.expanduser('~/Pictures/MuCaPy'),
|
||||||
|
'last_layout': 0,
|
||||||
|
'last_fps': 10,
|
||||||
|
'last_selected_cameras': [],
|
||||||
|
'window_geometry': None,
|
||||||
|
'confidence_threshold': 0.35,
|
||||||
|
}
|
||||||
self.load_config()
|
self.load_config()
|
||||||
|
|
||||||
def load_config(self):
|
def load_config(self):
|
||||||
@@ -27,14 +36,17 @@ class Config:
|
|||||||
try:
|
try:
|
||||||
if os.path.exists(self.config_file):
|
if os.path.exists(self.config_file):
|
||||||
with open(self.config_file, 'r') as f:
|
with open(self.config_file, 'r') as f:
|
||||||
self.settings = json.load(f)
|
loaded_settings = json.load(f)
|
||||||
|
# Update settings while preserving default values for new keys
|
||||||
|
self.settings.update(loaded_settings)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error loading config: {e}")
|
print(f"Error loading config: {e}")
|
||||||
self.settings = {}
|
|
||||||
|
|
||||||
def save_config(self):
|
def save_config(self):
|
||||||
"""Save configuration to JSON file"""
|
"""Save configuration to JSON file"""
|
||||||
try:
|
try:
|
||||||
|
# Ensure the file's directory exists
|
||||||
|
os.makedirs(os.path.dirname(self.config_file), exist_ok=True)
|
||||||
with open(self.config_file, 'w') as f:
|
with open(self.config_file, 'w') as f:
|
||||||
json.dump(self.settings, f, indent=4)
|
json.dump(self.settings, f, indent=4)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -61,12 +73,19 @@ class MultiCamYOLODetector:
|
|||||||
self.available_cameras = []
|
self.available_cameras = []
|
||||||
self.model_dir = ""
|
self.model_dir = ""
|
||||||
self.cuda_available = self.check_cuda()
|
self.cuda_available = self.check_cuda()
|
||||||
self.confidence_threshold = 0.35
|
|
||||||
self.config = Config()
|
self.config = Config()
|
||||||
|
|
||||||
# Load saved network cameras
|
# Load settings
|
||||||
|
self.confidence_threshold = self.config.load_setting('confidence_threshold', 0.35)
|
||||||
self.network_cameras = self.config.load_setting('network_cameras', {})
|
self.network_cameras = self.config.load_setting('network_cameras', {})
|
||||||
|
self.target_fps = self.config.load_setting('last_fps', 10)
|
||||||
|
self.frame_interval = 1.0 / self.target_fps
|
||||||
|
|
||||||
|
# Load last used model if available
|
||||||
|
last_model = self.config.load_setting('last_model_dir')
|
||||||
|
if last_model and os.path.exists(last_model):
|
||||||
|
self.load_yolo_model(last_model)
|
||||||
|
|
||||||
def check_cuda(self):
|
def check_cuda(self):
|
||||||
"""Check if CUDA is available"""
|
"""Check if CUDA is available"""
|
||||||
try:
|
try:
|
||||||
@@ -78,13 +97,17 @@ class MultiCamYOLODetector:
|
|||||||
def add_network_camera(self, name, camera_info):
|
def add_network_camera(self, name, camera_info):
|
||||||
"""Add a network camera to the saved list"""
|
"""Add a network camera to the saved list"""
|
||||||
self.network_cameras[name] = camera_info
|
self.network_cameras[name] = camera_info
|
||||||
self.config.save_setting('network_cameras', self.network_cameras)
|
# Save to configuration immediately
|
||||||
|
self.config.settings['network_cameras'] = self.network_cameras
|
||||||
|
self.config.save_config()
|
||||||
|
|
||||||
def remove_network_camera(self, name):
|
def remove_network_camera(self, name):
|
||||||
"""Remove a network camera from the saved list"""
|
"""Remove a network camera from the saved list"""
|
||||||
if name in self.network_cameras:
|
if name in self.network_cameras:
|
||||||
del self.network_cameras[name]
|
del self.network_cameras[name]
|
||||||
self.config.save_setting('network_cameras', self.network_cameras)
|
# Save to configuration immediately
|
||||||
|
self.config.settings['network_cameras'] = self.network_cameras
|
||||||
|
self.config.save_config()
|
||||||
|
|
||||||
def scan_for_cameras(self, max_to_check=10):
|
def scan_for_cameras(self, max_to_check=10):
|
||||||
"""Check for available cameras including network cameras"""
|
"""Check for available cameras including network cameras"""
|
||||||
@@ -725,7 +748,7 @@ class CameraSelectorDialog(QDialog):
|
|||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.setWindowTitle("Camera Selector")
|
self.setWindowTitle("Camera Selector")
|
||||||
self.setModal(True)
|
self.setModal(True)
|
||||||
self.resize(600, 500)
|
self.resize(800, 600) # Increased size for better visibility
|
||||||
|
|
||||||
self.detector = parent.detector if parent else None
|
self.detector = parent.detector if parent else None
|
||||||
self.selected_cameras = []
|
self.selected_cameras = []
|
||||||
@@ -733,183 +756,321 @@ class CameraSelectorDialog(QDialog):
|
|||||||
# Main layout
|
# Main layout
|
||||||
layout = QVBoxLayout(self)
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
# Create tab widget
|
# Instructions with better formatting
|
||||||
tabs = QTabWidget()
|
instructions = QLabel(
|
||||||
|
"Camera Selection Guide:\n\n"
|
||||||
|
"• Local Cameras: Built-in and USB cameras\n"
|
||||||
|
"• Network Cameras: IP cameras, DroidCam, etc.\n"
|
||||||
|
"• Use checkboxes to select/deselect cameras\n"
|
||||||
|
"• Double-click a camera to test the connection\n"
|
||||||
|
"• Selected cameras will appear in the preview below"
|
||||||
|
)
|
||||||
|
instructions.setStyleSheet("QLabel { background-color: #2A2A2A; padding: 10px; border-radius: 4px; }")
|
||||||
|
instructions.setWordWrap(True)
|
||||||
|
layout.addWidget(instructions)
|
||||||
|
|
||||||
# Local Cameras Tab
|
# Split view for cameras
|
||||||
local_tab = QWidget()
|
splitter = QSplitter(Qt.Horizontal)
|
||||||
local_layout = QVBoxLayout(local_tab)
|
|
||||||
|
|
||||||
# Local camera list
|
# Left side - Available Cameras
|
||||||
|
left_widget = QWidget()
|
||||||
|
left_layout = QVBoxLayout(left_widget)
|
||||||
|
|
||||||
|
# Local Cameras Group
|
||||||
local_group = QGroupBox("Local Cameras")
|
local_group = QGroupBox("Local Cameras")
|
||||||
local_list_layout = QVBoxLayout()
|
local_layout = QVBoxLayout()
|
||||||
self.local_list = QListWidget()
|
self.local_list = QListWidget()
|
||||||
self.local_list.setSelectionMode(QListWidget.MultiSelection)
|
self.local_list.setSelectionMode(QListWidget.ExtendedSelection)
|
||||||
local_list_layout.addWidget(self.local_list)
|
local_layout.addWidget(self.local_list)
|
||||||
|
local_group.setLayout(local_layout)
|
||||||
|
left_layout.addWidget(local_group)
|
||||||
|
|
||||||
# Refresh button for local cameras
|
# Network Cameras Group
|
||||||
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_group = QGroupBox("Network Cameras")
|
||||||
network_list_layout = QVBoxLayout()
|
network_layout = QVBoxLayout()
|
||||||
self.network_list = QListWidget()
|
self.network_list = QListWidget()
|
||||||
self.network_list.setSelectionMode(QListWidget.MultiSelection)
|
self.network_list.setSelectionMode(QListWidget.ExtendedSelection)
|
||||||
network_list_layout.addWidget(self.network_list)
|
network_layout.addWidget(self.network_list)
|
||||||
|
network_group.setLayout(network_layout)
|
||||||
|
left_layout.addWidget(network_group)
|
||||||
|
|
||||||
# Network camera controls
|
# Camera management buttons
|
||||||
net_btn_layout = QHBoxLayout()
|
btn_layout = QHBoxLayout()
|
||||||
|
refresh_btn = QPushButton("Refresh")
|
||||||
|
refresh_btn.clicked.connect(self.refresh_cameras)
|
||||||
add_net_btn = QPushButton("Add Network Camera")
|
add_net_btn = QPushButton("Add Network Camera")
|
||||||
add_net_btn.clicked.connect(self.show_network_dialog)
|
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)
|
btn_layout.addWidget(refresh_btn)
|
||||||
network_layout.addWidget(network_group)
|
btn_layout.addWidget(add_net_btn)
|
||||||
tabs.addTab(network_tab, "Network Cameras")
|
left_layout.addLayout(btn_layout)
|
||||||
|
|
||||||
layout.addWidget(tabs)
|
splitter.addWidget(left_widget)
|
||||||
|
|
||||||
|
# Right side - Selected Cameras Preview
|
||||||
|
right_widget = QWidget()
|
||||||
|
right_layout = QVBoxLayout(right_widget)
|
||||||
|
|
||||||
|
preview_label = QLabel("Selected Cameras Preview")
|
||||||
|
preview_label.setStyleSheet("font-weight: bold;")
|
||||||
|
right_layout.addWidget(preview_label)
|
||||||
|
|
||||||
# Selected cameras preview
|
|
||||||
preview_group = QGroupBox("Selected Cameras")
|
|
||||||
preview_layout = QVBoxLayout()
|
|
||||||
self.preview_list = QListWidget()
|
self.preview_list = QListWidget()
|
||||||
preview_layout.addWidget(self.preview_list)
|
self.preview_list.setDragDropMode(QListWidget.InternalMove)
|
||||||
preview_group.setLayout(preview_layout)
|
self.preview_list.setSelectionMode(QListWidget.ExtendedSelection)
|
||||||
layout.addWidget(preview_group)
|
right_layout.addWidget(self.preview_list)
|
||||||
|
|
||||||
# Buttons
|
# Preview controls
|
||||||
btn_layout = QHBoxLayout()
|
preview_btn_layout = QHBoxLayout()
|
||||||
|
remove_btn = QPushButton("Remove Selected")
|
||||||
|
remove_btn.clicked.connect(self.remove_selected)
|
||||||
|
clear_btn = QPushButton("Clear All")
|
||||||
|
clear_btn.clicked.connect(self.clear_selection)
|
||||||
|
|
||||||
|
preview_btn_layout.addWidget(remove_btn)
|
||||||
|
preview_btn_layout.addWidget(clear_btn)
|
||||||
|
right_layout.addLayout(preview_btn_layout)
|
||||||
|
|
||||||
|
splitter.addWidget(right_widget)
|
||||||
|
layout.addWidget(splitter)
|
||||||
|
|
||||||
|
# Bottom buttons
|
||||||
|
bottom_layout = QHBoxLayout()
|
||||||
select_all_btn = QPushButton("Select All")
|
select_all_btn = QPushButton("Select All")
|
||||||
select_all_btn.clicked.connect(self.select_all)
|
select_all_btn.clicked.connect(self.select_all)
|
||||||
clear_btn = QPushButton("Clear Selection")
|
test_selected_btn = QPushButton("Test Selected")
|
||||||
clear_btn.clicked.connect(self.clear_selection)
|
test_selected_btn.clicked.connect(self.test_selected_cameras)
|
||||||
ok_btn = QPushButton("OK")
|
ok_btn = QPushButton("OK")
|
||||||
ok_btn.clicked.connect(self.accept)
|
ok_btn.clicked.connect(self.accept)
|
||||||
cancel_btn = QPushButton("Cancel")
|
cancel_btn = QPushButton("Cancel")
|
||||||
cancel_btn.clicked.connect(self.reject)
|
cancel_btn.clicked.connect(self.reject)
|
||||||
|
|
||||||
btn_layout.addWidget(select_all_btn)
|
bottom_layout.addWidget(select_all_btn)
|
||||||
btn_layout.addWidget(clear_btn)
|
bottom_layout.addWidget(test_selected_btn)
|
||||||
btn_layout.addStretch()
|
bottom_layout.addStretch()
|
||||||
btn_layout.addWidget(ok_btn)
|
bottom_layout.addWidget(ok_btn)
|
||||||
btn_layout.addWidget(cancel_btn)
|
bottom_layout.addWidget(cancel_btn)
|
||||||
layout.addLayout(btn_layout)
|
layout.addLayout(bottom_layout)
|
||||||
|
|
||||||
# Connect selection change signals
|
# Connect signals
|
||||||
self.local_list.itemSelectionChanged.connect(self.update_preview)
|
self.local_list.itemChanged.connect(self.update_preview)
|
||||||
self.network_list.itemSelectionChanged.connect(self.update_preview)
|
self.network_list.itemChanged.connect(self.update_preview)
|
||||||
|
self.preview_list.model().rowsMoved.connect(self.update_camera_order)
|
||||||
|
|
||||||
|
# Set splitter sizes
|
||||||
|
splitter.setSizes([400, 400])
|
||||||
|
|
||||||
|
# Initial camera refresh
|
||||||
self.refresh_cameras()
|
self.refresh_cameras()
|
||||||
|
|
||||||
|
# Restore last selection if available
|
||||||
|
if self.detector:
|
||||||
|
last_selected = self.detector.config.load_setting('last_selected_cameras', [])
|
||||||
|
if last_selected:
|
||||||
|
self.restore_selection(last_selected)
|
||||||
|
|
||||||
def refresh_cameras(self):
|
def refresh_cameras(self):
|
||||||
"""Refresh both local and network camera lists"""
|
"""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()
|
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()
|
self.network_list.clear()
|
||||||
if self.detector:
|
|
||||||
for name, camera_info in self.detector.network_cameras.items():
|
if not self.detector:
|
||||||
if isinstance(camera_info, dict):
|
return
|
||||||
url = camera_info.get('url', '')
|
|
||||||
has_auth = camera_info.get('username') is not None
|
# Add local cameras
|
||||||
display_text = f"{name} ({url})"
|
for i in range(10): # Check first 10 indices
|
||||||
if has_auth:
|
try:
|
||||||
display_text += " [Auth]"
|
cap = cv2.VideoCapture(i)
|
||||||
else:
|
if cap.isOpened():
|
||||||
display_text = f"{name} ({camera_info})"
|
item = QListWidgetItem(f"Camera {i}")
|
||||||
|
item.setData(Qt.UserRole, str(i))
|
||||||
item = QListWidgetItem(display_text)
|
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
|
||||||
item.setData(Qt.UserRole, f"net:{name}")
|
item.setCheckState(Qt.Unchecked)
|
||||||
self.network_list.addItem(item)
|
self.local_list.addItem(item)
|
||||||
|
cap.release()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error checking camera {i}: {e}")
|
||||||
|
|
||||||
|
# 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(f"{os.path.basename(device_path)}")
|
||||||
|
item.setData(Qt.UserRole, device_path)
|
||||||
|
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
|
||||||
|
item.setCheckState(Qt.Unchecked)
|
||||||
|
self.local_list.addItem(item)
|
||||||
|
cap.release()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error checking device {device_path}: {e}")
|
||||||
|
|
||||||
|
# Add network cameras
|
||||||
|
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 += " 🔒"
|
||||||
|
else:
|
||||||
|
display_text = f"{name} ({camera_info})"
|
||||||
|
|
||||||
|
item = QListWidgetItem(display_text)
|
||||||
|
item.setData(Qt.UserRole, f"net:{name}")
|
||||||
|
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
|
||||||
|
item.setCheckState(Qt.Unchecked)
|
||||||
|
self.network_list.addItem(item)
|
||||||
|
|
||||||
def show_network_dialog(self):
|
def restore_selection(self, last_selected):
|
||||||
"""Show the network camera configuration dialog"""
|
"""Restore previous camera selection"""
|
||||||
dialog = NetworkCameraDialog(self.parent())
|
for cam_id in last_selected:
|
||||||
if dialog.exec_() == QDialog.Accepted:
|
# Check local cameras
|
||||||
self.refresh_network_cameras()
|
for i in range(self.local_list.count()):
|
||||||
|
item = self.local_list.item(i)
|
||||||
def remove_network_camera(self):
|
if item.data(Qt.UserRole) == cam_id:
|
||||||
"""Remove selected network cameras"""
|
item.setCheckState(Qt.Checked)
|
||||||
for item in self.network_list.selectedItems():
|
|
||||||
cam_path = item.data(Qt.UserRole)
|
# Check network cameras
|
||||||
if cam_path.startswith('net:'):
|
for i in range(self.network_list.count()):
|
||||||
name = cam_path[4:]
|
item = self.network_list.item(i)
|
||||||
if self.detector:
|
if item.data(Qt.UserRole) == cam_id:
|
||||||
self.detector.remove_network_camera(name)
|
item.setCheckState(Qt.Checked)
|
||||||
self.refresh_network_cameras()
|
|
||||||
self.update_preview()
|
|
||||||
|
|
||||||
def update_preview(self):
|
def update_preview(self):
|
||||||
"""Update the preview list with currently selected cameras"""
|
"""Update the preview list with currently selected cameras"""
|
||||||
self.preview_list.clear()
|
self.preview_list.clear()
|
||||||
self.selected_cameras = []
|
self.selected_cameras = []
|
||||||
|
|
||||||
# Add selected local cameras
|
# Get selected local cameras
|
||||||
for item in self.local_list.selectedItems():
|
for i in range(self.local_list.count()):
|
||||||
cam_path = item.data(Qt.UserRole) if item.data(Qt.UserRole) else item.text().split()[-1]
|
item = self.local_list.item(i)
|
||||||
self.selected_cameras.append(cam_path)
|
if item.checkState() == Qt.Checked:
|
||||||
self.preview_list.addItem(f"✓ {item.text()}")
|
cam_id = item.data(Qt.UserRole)
|
||||||
|
preview_item = QListWidgetItem(f"📷 {item.text()}")
|
||||||
|
preview_item.setData(Qt.UserRole, cam_id)
|
||||||
|
self.preview_list.addItem(preview_item)
|
||||||
|
self.selected_cameras.append(cam_id)
|
||||||
|
|
||||||
# Add selected network cameras
|
# Get selected network cameras
|
||||||
for item in self.network_list.selectedItems():
|
for i in range(self.network_list.count()):
|
||||||
cam_path = item.data(Qt.UserRole)
|
item = self.network_list.item(i)
|
||||||
self.selected_cameras.append(cam_path)
|
if item.checkState() == Qt.Checked:
|
||||||
self.preview_list.addItem(f"✓ {item.text()}")
|
cam_id = item.data(Qt.UserRole)
|
||||||
|
preview_item = QListWidgetItem(f"🌐 {item.text()}")
|
||||||
|
preview_item.setData(Qt.UserRole, cam_id)
|
||||||
|
self.preview_list.addItem(preview_item)
|
||||||
|
self.selected_cameras.append(cam_id)
|
||||||
|
|
||||||
|
# Save the current selection to config
|
||||||
|
if self.detector:
|
||||||
|
self.detector.config.save_setting('last_selected_cameras', self.selected_cameras)
|
||||||
|
|
||||||
|
def update_camera_order(self):
|
||||||
|
"""Update the camera order based on preview list order"""
|
||||||
|
self.selected_cameras = []
|
||||||
|
for i in range(self.preview_list.count()):
|
||||||
|
item = self.preview_list.item(i)
|
||||||
|
self.selected_cameras.append(item.data(Qt.UserRole))
|
||||||
|
|
||||||
|
# Save the new order
|
||||||
|
if self.detector:
|
||||||
|
self.detector.config.save_setting('last_selected_cameras', self.selected_cameras)
|
||||||
|
|
||||||
def select_all(self):
|
def select_all(self):
|
||||||
"""Select all cameras in both lists"""
|
"""Select all cameras in both lists"""
|
||||||
self.local_list.selectAll()
|
for i in range(self.local_list.count()):
|
||||||
self.network_list.selectAll()
|
self.local_list.item(i).setCheckState(Qt.Checked)
|
||||||
|
for i in range(self.network_list.count()):
|
||||||
|
self.network_list.item(i).setCheckState(Qt.Checked)
|
||||||
|
|
||||||
def clear_selection(self):
|
def clear_selection(self):
|
||||||
"""Clear selection in both lists"""
|
"""Clear all selections"""
|
||||||
self.local_list.clearSelection()
|
for i in range(self.local_list.count()):
|
||||||
self.network_list.clearSelection()
|
self.local_list.item(i).setCheckState(Qt.Unchecked)
|
||||||
|
for i in range(self.network_list.count()):
|
||||||
|
self.network_list.item(i).setCheckState(Qt.Unchecked)
|
||||||
|
|
||||||
|
def remove_selected(self):
|
||||||
|
"""Remove selected items from the preview list"""
|
||||||
|
selected_items = self.preview_list.selectedItems()
|
||||||
|
for item in selected_items:
|
||||||
|
cam_id = item.data(Qt.UserRole)
|
||||||
|
# Uncheck corresponding items in source lists
|
||||||
|
for i in range(self.local_list.count()):
|
||||||
|
if self.local_list.item(i).data(Qt.UserRole) == cam_id:
|
||||||
|
self.local_list.item(i).setCheckState(Qt.Unchecked)
|
||||||
|
for i in range(self.network_list.count()):
|
||||||
|
if self.network_list.item(i).data(Qt.UserRole) == cam_id:
|
||||||
|
self.network_list.item(i).setCheckState(Qt.Unchecked)
|
||||||
|
|
||||||
|
def test_selected_cameras(self):
|
||||||
|
"""Test connection to selected cameras"""
|
||||||
|
selected_cameras = []
|
||||||
|
for i in range(self.preview_list.count()):
|
||||||
|
selected_cameras.append(self.preview_list.item(i).data(Qt.UserRole))
|
||||||
|
|
||||||
|
if not selected_cameras:
|
||||||
|
QMessageBox.warning(self, "Warning", "No cameras selected to test!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create progress dialog
|
||||||
|
progress = QMessageBox(self)
|
||||||
|
progress.setIcon(QMessageBox.Information)
|
||||||
|
progress.setWindowTitle("Testing Cameras")
|
||||||
|
progress.setText("Testing camera connections...\nThis may take a few seconds.")
|
||||||
|
progress.setStandardButtons(QMessageBox.NoButton)
|
||||||
|
progress.show()
|
||||||
|
QApplication.processEvents()
|
||||||
|
|
||||||
|
# Test each camera
|
||||||
|
results = []
|
||||||
|
for cam_id in selected_cameras:
|
||||||
|
try:
|
||||||
|
if cam_id.startswith('net:'):
|
||||||
|
name = cam_id[4:]
|
||||||
|
camera_info = self.detector.network_cameras.get(name)
|
||||||
|
if isinstance(camera_info, dict):
|
||||||
|
url = camera_info['url']
|
||||||
|
if 'username' in camera_info and 'password' in camera_info:
|
||||||
|
parsed = urllib.parse.urlparse(url)
|
||||||
|
netloc = f"{camera_info['username']}:{camera_info['password']}@{parsed.netloc}"
|
||||||
|
url = parsed._replace(netloc=netloc).geturl()
|
||||||
|
else:
|
||||||
|
url = camera_info
|
||||||
|
cap = cv2.VideoCapture(url)
|
||||||
|
else:
|
||||||
|
cap = cv2.VideoCapture(int(cam_id) if cam_id.isdigit() else cam_id)
|
||||||
|
|
||||||
|
if cap.isOpened():
|
||||||
|
ret, frame = cap.read()
|
||||||
|
if ret:
|
||||||
|
results.append(f"✅ {cam_id}: Connection successful")
|
||||||
|
else:
|
||||||
|
results.append(f"⚠️ {cam_id}: Connected but no frame received")
|
||||||
|
else:
|
||||||
|
results.append(f"❌ {cam_id}: Failed to connect")
|
||||||
|
cap.release()
|
||||||
|
except Exception as e:
|
||||||
|
results.append(f"❌ {cam_id}: Error - {str(e)}")
|
||||||
|
|
||||||
|
progress.close()
|
||||||
|
|
||||||
|
# Show results
|
||||||
|
result_dialog = QMessageBox(self)
|
||||||
|
result_dialog.setWindowTitle("Camera Test Results")
|
||||||
|
result_dialog.setText("\n".join(results))
|
||||||
|
result_dialog.setIcon(QMessageBox.Information)
|
||||||
|
result_dialog.exec_()
|
||||||
|
|
||||||
|
def show_network_dialog(self):
|
||||||
|
"""Show the network camera configuration dialog"""
|
||||||
|
dialog = NetworkCameraDialog(self)
|
||||||
|
if dialog.exec_() == QDialog.Accepted:
|
||||||
|
self.refresh_cameras()
|
||||||
|
|
||||||
class MainWindow(QMainWindow):
|
class MainWindow(QMainWindow):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user