import cv2, numpy as np import os from Tbd.helper import CameraObject from UI.UILogger import UILogger from PySide6.QtCore import Qt, QTimer from PySide6.QtGui import QImage, QPixmap from PySide6.QtWidgets import QLineEdit from PySide6.QtGui import QIntValidator from PySide6.QtWidgets import QWidget, QLabel, QVBoxLayout, QPushButton, QHBoxLayout from typing import List class SetupPage(QWidget): """ Page that handles camera preview & cycling through cameras. """ def __init__(self, cameraList: List[CameraObject], on_cameraList_changed, logger : UILogger): super().__init__() self.on_cameraList_changed = on_cameraList_changed self.cameraList = cameraList self.logger = logger self.cameraObject = CameraObject( capture=None, index=None ) # --- UI --- self.setUpLayout = QVBoxLayout(self) self.setUpLayout.addLayout(self._make_Setup_Controls()) self.setUpLayout.addLayout(self._make_Setup_Options()) def _make_Setup_Options(self): setupOptions = QHBoxLayout() setupOptions2 = QVBoxLayout() self.cameraIndexLable = QLabel("Camera Index:") self.cameraIndex = QLineEdit("0") self.cameraIndex.setValidator(QIntValidator(0, 9999, self)) self.addCameraBTN = QPushButton("Add Camera") self.nextCameraBTN = QPushButton("Next Camera") self.previousCameraBTN = QPushButton("Previous Camera") setupOptions.addWidget(self.cameraIndexLable) setupOptions.addWidget(self.cameraIndex, 1) setupOptions.addWidget(self.addCameraBTN) setupOptions.addWidget(self.nextCameraBTN) setupOptions.addWidget(self.previousCameraBTN) self.nextCameraBTN.clicked.connect(self._nextIndex) self.previousCameraBTN.clicked.connect(self._previousIndex) #self.cameraIndex.editingFinished.connect(self._updateCamera) self.addCameraBTN.clicked.connect(self._addCamera) # --- self.camera_preview = QLabel("No camera") self.camera_preview.setAlignment(Qt.AlignCenter) self.camera_preview.setMinimumSize(640, 360) self.camera_preview.setStyleSheet("background: #222; color: #aaa;") self.timer = QTimer(self) self.timer.timeout.connect(self._updateCamera) setupOptions2.addLayout(setupOptions) setupOptions2.addWidget(self.camera_preview) return setupOptions2 def _addCamera(self): if any(camera.index == int(self.cameraIndex.text()) for camera in self.cameraList): print("Camera already added.") return self.cameraList.append(self.cameraObject) self._cameraList_changed() def load_setup(self): if (os.path.exists(self.setupFilePath.text())): self.logger.log("Path found") else: self.logger.log("Path not found") indexToAdd = [0, 3] #self.cameraObject = CameraObject( # capture=None, # index=None #) for index in indexToAdd: capture = cv2.VideoCapture(index, cv2.CAP_MSMF) #capture = cv2.VideoCapture(index, cv2.CAP_DSHOW) capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG')) capture.set(cv2.CAP_PROP_FPS, 30) capture.set(cv2.CAP_PROP_FRAME_WIDTH, 2592) capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 1944) #capture.set(cv2.CAP_PROP_FRAME_WIDTH, 2048.0) #capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 1536.0) width = capture.get(cv2.CAP_PROP_FRAME_WIDTH) height = capture.get(cv2.CAP_PROP_FRAME_HEIGHT) fps = capture.get(cv2.CAP_PROP_FPS) fourcc = int(capture.get(cv2.CAP_PROP_FOURCC)) fourcc_str = "".join([chr((fourcc >> 8*i) & 0xFF) for i in range(4)]) print(f"Resolution: {width}x{height}") print(f"FPS: {fps}") print(f"Format (FOURCC): {fourcc_str}") self.logger.log(f"Resolution: {width}x{height}") self.logger.log(f"FPS: {fps}") self.logger.log(f"Format (FOURCC): {fourcc_str}") #currentcapture = cv2.VideoCapture(index, cv2.CAP_MSMF) #self.logger.log("Initial frame:", w, "x", h) self.cameraList.append(CameraObject( capture=capture, index=index )) self._cameraList_changed() return def save_setup(self): print("ToDo") return True def _cameraList_changed(self): if self.on_cameraList_changed: self.on_cameraList_changed() def _updateCamera(self): if self.cameraObject.capture is None: return ret, frame = self.cameraObject.capture.read() if not ret: return frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch = frame_rgb.shape bytes_per_line = ch * w qimg = QImage(frame_rgb.data, w, h, bytes_per_line, QImage.Format_RGB888) self.camera_preview.setPixmap(QPixmap.fromImage(qimg)) def _nextIndex(self): cameraIndex = int(self.cameraIndex.text()) self.cameraIndex.setText(str(cameraIndex + 1)) if self.open_camera(): if not self.timer.isActive(): self.timer.start(30) self._updateCamera() def _previousIndex(self): cameraIndex = int(self.cameraIndex.text()) if((cameraIndex - 1) < 0): self.cameraIndex.setText("0") self.cameraIndex.setText(str(cameraIndex - 1)) if self.open_camera(): if not self.timer.isActive(): self.timer.start(30) self._updateCamera() def open_camera(self): index = int(self.cameraIndex.text()) self.release_camera() self.cameraObject.capture = cv2.VideoCapture(index) self.cameraObject.index = index if not self.cameraObject.capture.isOpened(): self.cameraObject.capture = None self.cameraObject.index = None self.camera_preview.setText(f"Failed to open camera {index}") return False self.camera_preview.setText("") return True def release_camera(self): if self.cameraObject.capture is not None: self.timer.stop() self.cameraObject.capture.release() self.cameraObject.capture = None self.cameraObject.index = None def closeEvent(self, event): self.release_camera() event.accept() def _make_Setup_Controls(self): # Create Setup Input setupFileLayout = QHBoxLayout() self.setupFilePathLable = QLabel("Setupfile path:") self.setupFilePath = QLineEdit() self.loadBTN = QPushButton("Load") self.saveBTN = QPushButton("Save") setupFileLayout.addWidget(self.setupFilePathLable) setupFileLayout.addWidget(self.setupFilePath, 1) setupFileLayout.addWidget(self.loadBTN) setupFileLayout.addWidget(self.saveBTN) self.loadBTN.clicked.connect(self.load_setup) self.saveBTN.clicked.connect(self.save_setup) return setupFileLayout