i hate python

This commit is contained in:
rattatwinko
2025-05-26 16:39:58 +02:00
parent b24426c1d3
commit c264acac29

View File

@@ -10,16 +10,25 @@ from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout
QActionGroup, QSizePolicy, QGridLayout, QGroupBox,
QDockWidget, QScrollArea, QToolButton, QDialog,
QShortcut, QListWidget, QFormLayout, QLineEdit,
QCheckBox, QTabWidget, QListWidgetItem)
QCheckBox, QTabWidget, QListWidgetItem, QSplitter)
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):
# 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.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()
def load_config(self):
@@ -27,14 +36,17 @@ class Config:
try:
if os.path.exists(self.config_file):
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:
print(f"Error loading config: {e}")
self.settings = {}
def save_config(self):
"""Save configuration to JSON file"""
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:
json.dump(self.settings, f, indent=4)
except Exception as e:
@@ -61,12 +73,19 @@ class MultiCamYOLODetector:
self.available_cameras = []
self.model_dir = ""
self.cuda_available = self.check_cuda()
self.confidence_threshold = 0.35
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.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):
"""Check if CUDA is available"""
try:
@@ -78,13 +97,17 @@ class MultiCamYOLODetector:
def add_network_camera(self, name, camera_info):
"""Add a network camera to the saved list"""
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):
"""Remove a network camera from the saved list"""
if name in self.network_cameras:
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):
"""Check for available cameras including network cameras"""
@@ -725,7 +748,7 @@ class CameraSelectorDialog(QDialog):
super().__init__(parent)
self.setWindowTitle("Camera Selector")
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.selected_cameras = []
@@ -733,183 +756,321 @@ class CameraSelectorDialog(QDialog):
# Main layout
layout = QVBoxLayout(self)
# Create tab widget
tabs = QTabWidget()
# Instructions with better formatting
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
local_tab = QWidget()
local_layout = QVBoxLayout(local_tab)
# Split view for cameras
splitter = QSplitter(Qt.Horizontal)
# Local camera list
# Left side - Available Cameras
left_widget = QWidget()
left_layout = QVBoxLayout(left_widget)
# Local Cameras Group
local_group = QGroupBox("Local Cameras")
local_list_layout = QVBoxLayout()
local_layout = QVBoxLayout()
self.local_list = QListWidget()
self.local_list.setSelectionMode(QListWidget.MultiSelection)
local_list_layout.addWidget(self.local_list)
self.local_list.setSelectionMode(QListWidget.ExtendedSelection)
local_layout.addWidget(self.local_list)
local_group.setLayout(local_layout)
left_layout.addWidget(local_group)
# 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 Cameras Group
network_group = QGroupBox("Network Cameras")
network_list_layout = QVBoxLayout()
network_layout = QVBoxLayout()
self.network_list = QListWidget()
self.network_list.setSelectionMode(QListWidget.MultiSelection)
network_list_layout.addWidget(self.network_list)
self.network_list.setSelectionMode(QListWidget.ExtendedSelection)
network_layout.addWidget(self.network_list)
network_group.setLayout(network_layout)
left_layout.addWidget(network_group)
# Network camera controls
net_btn_layout = QHBoxLayout()
# Camera management buttons
btn_layout = QHBoxLayout()
refresh_btn = QPushButton("Refresh")
refresh_btn.clicked.connect(self.refresh_cameras)
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")
btn_layout.addWidget(refresh_btn)
btn_layout.addWidget(add_net_btn)
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()
preview_layout.addWidget(self.preview_list)
preview_group.setLayout(preview_layout)
layout.addWidget(preview_group)
self.preview_list.setDragDropMode(QListWidget.InternalMove)
self.preview_list.setSelectionMode(QListWidget.ExtendedSelection)
right_layout.addWidget(self.preview_list)
# Buttons
btn_layout = QHBoxLayout()
# Preview controls
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.clicked.connect(self.select_all)
clear_btn = QPushButton("Clear Selection")
clear_btn.clicked.connect(self.clear_selection)
test_selected_btn = QPushButton("Test Selected")
test_selected_btn.clicked.connect(self.test_selected_cameras)
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)
bottom_layout.addWidget(select_all_btn)
bottom_layout.addWidget(test_selected_btn)
bottom_layout.addStretch()
bottom_layout.addWidget(ok_btn)
bottom_layout.addWidget(cancel_btn)
layout.addLayout(bottom_layout)
# Connect selection change signals
self.local_list.itemSelectionChanged.connect(self.update_preview)
self.network_list.itemSelectionChanged.connect(self.update_preview)
# Connect signals
self.local_list.itemChanged.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()
# 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):
"""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)
if not self.detector:
return
# Add local cameras
for i in range(10): # Check first 10 indices
try:
cap = cv2.VideoCapture(i)
if cap.isOpened():
item = QListWidgetItem(f"Camera {i}")
item.setData(Qt.UserRole, str(i))
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 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):
"""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 restore_selection(self, last_selected):
"""Restore previous camera selection"""
for cam_id in last_selected:
# Check local cameras
for i in range(self.local_list.count()):
item = self.local_list.item(i)
if item.data(Qt.UserRole) == cam_id:
item.setCheckState(Qt.Checked)
# Check network cameras
for i in range(self.network_list.count()):
item = self.network_list.item(i)
if item.data(Qt.UserRole) == cam_id:
item.setCheckState(Qt.Checked)
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()}")
# Get selected local cameras
for i in range(self.local_list.count()):
item = self.local_list.item(i)
if item.checkState() == Qt.Checked:
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
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()}")
# Get selected network cameras
for i in range(self.network_list.count()):
item = self.network_list.item(i)
if item.checkState() == Qt.Checked:
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):
"""Select all cameras in both lists"""
self.local_list.selectAll()
self.network_list.selectAll()
for i in range(self.local_list.count()):
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):
"""Clear selection in both lists"""
self.local_list.clearSelection()
self.network_list.clearSelection()
"""Clear all selections"""
for i in range(self.local_list.count()):
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):
def __init__(self):