Compare commits

..

4 Commits

4 changed files with 96 additions and 20 deletions

View File

@@ -8,7 +8,7 @@ from core.media import MediaRepository
from core.camera.camera_manager import CameraManager from core.camera.camera_manager import CameraManager
from ui.widgets.color_list_widget import ColorListWidget from ui.widgets.color_list_widget import ColorListWidget
from ui.widgets.thumbnail_list_widget import ThumbnailListWidget from ui.widgets.thumbnail_list_widget import ThumbnailListWidget
from ui.widgets.split_view_widget import SplitView, CameraPlaceholder from ui.widgets.split_view_widget import SplitView, CameraPlaceholder, ViewWithOverlay
class MainController: class MainController:
@@ -23,6 +23,7 @@ class MainController:
self.thumbnail_list: ThumbnailListWidget = view.thumbnail_widget self.thumbnail_list: ThumbnailListWidget = view.thumbnail_widget
self.split_view: SplitView = view.preview_widget self.split_view: SplitView = view.preview_widget
self.welcome_view: CameraPlaceholder = self.split_view.widget_start self.welcome_view: CameraPlaceholder = self.split_view.widget_start
self.live_view: ViewWithOverlay = self.split_view.widget_live
self.photo_button: QPushButton = view.photo_button self.photo_button: QPushButton = view.photo_button
self.record_button: QPushButton = view.record_button self.record_button: QPushButton = view.record_button
@@ -48,10 +49,9 @@ class MainController:
# UI control signals # UI control signals
self.photo_button.clicked.connect(self.take_photo) self.photo_button.clicked.connect(self.take_photo)
# self.record_button.clicked.connect(self.toggle_record) # Placeholder self.welcome_view.camera_start_btn.clicked.connect(self.camera_detect)
self.welcome_view.camera_start_btn.clicked.connect(self.start_liveview) self.live_view.rotateCW.connect(self.camera_manager.rotate_right)
# You will need a way to select a camera, e.g., a combobox. self.live_view.rotateCCW.connect(self.camera_manager.rotate_left)
# self.view.camera_combobox.currentIndexChanged.connect(self.on_camera_selected_in_ui)
def load_colors(self) -> None: def load_colors(self) -> None:
"""Loads colors from the database and populates the list.""" """Loads colors from the database and populates the list."""
@@ -93,6 +93,16 @@ class MainController:
"""Handles the list of detected cameras.""" """Handles the list of detected cameras."""
print("Detected cameras:", cameras) print("Detected cameras:", cameras)
self.welcome_view.set_info_text(f"Detected {len(cameras)} cameras.") self.welcome_view.set_info_text(f"Detected {len(cameras)} cameras.")
self.welcome_view.camera_start_btn.clicked.disconnect()
if len(cameras) == 0:
self.welcome_view.set_button_text("Wykryj kamery")
self.welcome_view.camera_start_btn.clicked.connect(self.camera_detect)
else:
self.welcome_view.set_button_text("Uruchom kamere")
self.welcome_view.camera_start_btn.clicked.connect(self.start_liveview)
# Populate a combobox in the UI here # Populate a combobox in the UI here
# self.view.camera_combobox.clear() # self.view.camera_combobox.clear()
# for camera in cameras: # for camera in cameras:
@@ -130,6 +140,9 @@ class MainController:
# --- UI Actions --- # --- UI Actions ---
def camera_detect(self):
self.camera_manager.detect_cameras()
def start_liveview(self): def start_liveview(self):
"""Starts the camera feed.""" """Starts the camera feed."""
detected_cameras = self.camera_manager.get_detected_cameras() detected_cameras = self.camera_manager.get_detected_cameras()

View File

@@ -18,12 +18,13 @@ class CameraWorker(QObject):
self.fps = 15 self.fps = 15
self.is_streaming = False self.is_streaming = False
self.is_connected = False self.is_connected = False
self._rotation_index = 0
self._camera_mutex = QMutex() self._camera_mutex = QMutex()
@Slot() @Slot()
def initialize_worker(self): def initialize_worker(self):
"""Initializes the timer in the worker's thread.""" """Initializes the timer in the worker's thread."""
self.timer = QTimer() self.timer = QTimer(self)
self.timer.timeout.connect(self._update_frame) self.timer.timeout.connect(self._update_frame)
@Slot(BaseCamera, int) @Slot(BaseCamera, int)
@@ -93,13 +94,30 @@ class CameraWorker(QObject):
self.error_occurred.emit(error_msg) self.error_occurred.emit(error_msg)
return return
if frame is not None: if frame is None:
# Process the frame and emit it. return
rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgb_image.shape if self._rotation_index == 1:
qimg = QImage(rgb_image.data, w, h, ch * w, QImage.Format.Format_RGB888) frame = cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE)
pixmap = QPixmap.fromImage(qimg) elif self._rotation_index == 2:
self.frame_ready.emit(pixmap) frame = cv2.rotate(frame, cv2.ROTATE_180)
elif self._rotation_index == 3:
frame = cv2.rotate(frame, cv2.ROTATE_90_COUNTERCLOCKWISE)
# Process the frame and emit it.
rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgb_image.shape
qimg = QImage(rgb_image.data, w, h, ch * w, QImage.Format.Format_RGB888)
pixmap = QPixmap.fromImage(qimg)
self.frame_ready.emit(pixmap)
@Slot()
def rotate_left(self):
self._rotation_index = (self._rotation_index - 1) % 4
@Slot()
def rotate_right(self):
self._rotation_index = (self._rotation_index + 1) % 4
@@ -115,6 +133,8 @@ class CameraController(QObject):
_stop_camera_requested = Signal() _stop_camera_requested = Signal()
_start_stream_requested = Signal() _start_stream_requested = Signal()
_stop_stream_requested = Signal() _stop_stream_requested = Signal()
_rotate_left_requested = Signal()
_rotate_right_requested = Signal()
def __init__(self, parent: QObject | None = None) -> None: def __init__(self, parent: QObject | None = None) -> None:
super().__init__(parent) super().__init__(parent)
@@ -135,15 +155,20 @@ class CameraController(QObject):
self._stop_camera_requested.connect(self._worker.stop_camera) self._stop_camera_requested.connect(self._worker.stop_camera)
self._start_stream_requested.connect(self._worker.start_stream) self._start_stream_requested.connect(self._worker.start_stream)
self._stop_stream_requested.connect(self._worker.stop_stream) self._stop_stream_requested.connect(self._worker.stop_stream)
self._rotate_left_requested.connect(self._worker.rotate_left)
self._rotate_right_requested.connect(self._worker.rotate_right)
# Initialize worker when thread starts # Initialize worker when thread starts
self._thread.started.connect(self._worker.initialize_worker) self._thread.started.connect(self._worker.initialize_worker)
self._thread.finished.connect(self._worker.stop_camera)
self._thread.start() self._thread.start()
def stop(self): def stop(self):
self._thread.quit() if self._thread.isRunning():
self._thread.wait() self._thread.quit()
self._thread.wait()
self._thread.deleteLater()
def set_camera(self, camera: BaseCamera, fps: int = 15) -> None: def set_camera(self, camera: BaseCamera, fps: int = 15) -> None:
self._set_camera_requested.emit(camera, fps) self._set_camera_requested.emit(camera, fps)
@@ -159,4 +184,11 @@ class CameraController(QObject):
def stop_stream(self) -> None: def stop_stream(self) -> None:
self._stop_stream_requested.emit() self._stop_stream_requested.emit()
def rotate_left(self):
self._rotate_left_requested.emit()
def rotate_right(self):
self._rotate_right_requested.emit()

View File

@@ -115,6 +115,12 @@ class CameraManager(QObject):
def get_active_camera_info(self) -> dict | None: def get_active_camera_info(self) -> dict | None:
return self._active_camera_info return self._active_camera_info
def rotate_left(self):
self._camera_controller.rotate_left()
def rotate_right(self):
self._camera_controller.rotate_right()
def shutdown(self) -> None: def shutdown(self) -> None:
"""Zamyka kontroler kamery i jego wątek.""" """Zamyka kontroler kamery i jego wątek."""
self.stop_camera() self.stop_camera()

View File

@@ -1,5 +1,5 @@
from PySide6.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QApplication, QMainWindow, QWidget, QVBoxLayout, QSplitter, QStackedWidget, QPushButton, QLabel, QToolButton from PySide6.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QApplication, QMainWindow, QWidget, QVBoxLayout, QSplitter, QStackedWidget, QPushButton, QLabel, QToolButton
from PySide6.QtGui import QEnterEvent, QPixmap, QWheelEvent, QPainter, QBrush, QColor, QIcon from PySide6.QtGui import QEnterEvent, QPixmap, QWheelEvent, QPainter, QBrush, QColor, QIcon, QImage, QTransform
from PySide6.QtCore import Qt, QSize, Signal, QEvent from PySide6.QtCore import Qt, QSize, Signal, QEvent
import sys import sys
from ui.widgets.placeholder_widget import PlaceholderWidget from ui.widgets.placeholder_widget import PlaceholderWidget
@@ -33,10 +33,14 @@ class ZoomableImageView(QGraphicsView):
self._current_scale = 1.0 self._current_scale = 1.0
def set_image(self, pixmap: QPixmap): def set_image(self, pixmap: QPixmap):
# pixmap = QPixmap(image_path) if pixmap.isNull():
return
self._pixmap_item.setPixmap(pixmap) self._pixmap_item.setPixmap(pixmap)
self._scene.setSceneRect(pixmap.rect()) self._scene.setSceneRect(pixmap.rect())
# self.reset_transform() if self._current_scale == 1.0:
self.fitInView(self._pixmap_item, Qt.AspectRatioMode.KeepAspectRatio)
self._first_image = False
def reset_transform(self): def reset_transform(self):
"""Resetuje skalowanie i ustawia 1:1""" """Resetuje skalowanie i ustawia 1:1"""
@@ -281,10 +285,15 @@ class SplitView(QSplitter):
# pixmap.fill(Qt.GlobalColor.lightGray) # pixmap.fill(Qt.GlobalColor.lightGray)
# self.widget_live.set_image(pixmap) # self.widget_live.set_image(pixmap)
self.ref_image_rotate = 0
self.ref_pixmap = None
self.widget_live.toggleOrientation.connect(self.toggle_orientation) self.widget_live.toggleOrientation.connect(self.toggle_orientation)
self.widget_ref.toggleOrientation.connect(self.toggle_orientation) self.widget_ref.toggleOrientation.connect(self.toggle_orientation)
self.widget_live.swapViews.connect(self.swap_views) self.widget_live.swapViews.connect(self.swap_views)
self.widget_ref.swapViews.connect(self.swap_views) self.widget_ref.swapViews.connect(self.swap_views)
self.widget_ref.rotateCCW.connect(self.rotate_left)
self.widget_ref.rotateCW.connect(self.rotate_right)
def toggle_orientation(self): def toggle_orientation(self):
if self.orientation() == Qt.Orientation.Vertical: if self.orientation() == Qt.Orientation.Vertical:
@@ -315,8 +324,10 @@ class SplitView(QSplitter):
def set_reference_image(self, path_image: str): def set_reference_image(self, path_image: str):
"""Ustawienie obrazu referencyjnego""" """Ustawienie obrazu referencyjnego"""
pixmap = QPixmap(path_image) self.ref_pixmap = QPixmap(path_image)
self.widget_ref.set_image(pixmap) if self.ref_image_rotate != 0:
self.ref_pixmap = self.ref_pixmap.transformed(QTransform().rotate(self.ref_image_rotate))
self.widget_ref.set_image(self.ref_pixmap)
def toggle_live_view(self): def toggle_live_view(self):
"""Przełączanie widoku na żywo""" """Przełączanie widoku na żywo"""
@@ -324,3 +335,17 @@ class SplitView(QSplitter):
self.stack.setCurrentWidget(self.widget_live) self.stack.setCurrentWidget(self.widget_live)
else: else:
self.stack.setCurrentWidget(self.widget_start) self.stack.setCurrentWidget(self.widget_start)
def rotate_left(self):
if not self.ref_pixmap:
return
self.ref_image_rotate = (self.ref_image_rotate - 90) % 360
self.ref_pixmap = self.ref_pixmap.transformed(QTransform().rotate(-90))
self.widget_ref.set_image(self.ref_pixmap)
def rotate_right(self):
if not self.ref_pixmap:
return
self.ref_image_rotate = (self.ref_image_rotate + 90) % 360
self.ref_pixmap = self.ref_pixmap.transformed(QTransform().rotate(90))
self.widget_ref.set_image(self.ref_pixmap)