16 Commits

Author SHA1 Message Date
2cc496a2b2 refactor: enhance SplitView to support image rotation and handle null pixmap in ZoomableImageView 2025-10-13 22:22:35 +02:00
0bc6c01e7e refactor: enhance camera rotation functionality and integrate with live view 2025-10-13 20:54:04 +02:00
4d00f83083 refactor: update camera start button behavior to handle camera detection and live view 2025-10-13 19:39:34 +02:00
2c1233f304 refactor: initialize QTimer with parent and ensure proper thread cleanup on stop 2025-10-13 19:36:09 +02:00
feebc5153a refactor: update SplitView to use ViewWithOverlay for live and reference images 2025-10-13 05:17:59 +02:00
49a1405340 Merge branch 'feature/camera-manager' 2025-10-13 05:15:27 +02:00
c6345c569d refactor: update camera control signals and improve live view handling 2025-10-13 05:14:27 +02:00
73b51c696e Merge branch 'feature/camera-worker' into feature/camera-manager 2025-10-12 19:31:28 +02:00
511e668cb3 feat: add isConnected method to CameraWorker and is_connected method to CameraController 2025-10-12 19:29:23 +02:00
ce1b864b17 refactor: streamline MainController initialization and signal connections 2025-10-12 19:02:53 +02:00
46734208e7 refactor. new qobjct thread approuch insted qthread 2025-10-12 18:51:37 +02:00
bbdc2af605 refactor: change CameraController to inherit from QObject and manage threading in CameraManager 2025-10-12 13:41:13 +02:00
2a5f570e5e feat: implement CameraDetectionWorker for asynchronous camera detection 2025-10-12 13:08:40 +02:00
d63d616675 working on camera-manager 2025-10-12 10:31:40 +02:00
eb72b25fdb feat: add error label and update related methods in CameraPlaceholder 2025-10-12 10:28:53 +02:00
86b9cc70a6 fix: correct variable name in abilities extraction loop 2025-10-12 10:28:22 +02:00
5 changed files with 473 additions and 187 deletions

View File

@@ -1,100 +1,167 @@
from PySide6.QtWidgets import QPushButton
from pathlib import Path
from PySide6.QtCore import Slot
from PySide6.QtGui import QPixmap
from PySide6.QtWidgets import QPushButton
from core.database import DatabaseManager
from core.media import MediaRepository
from core.camera.camera_manager import CameraManager
from ui.widgets.color_list_widget import ColorListWidget
from ui.widgets.thumbnail_list_widget import ThumbnailListWidget
from ui.widgets.split_view_widget import SplitView
# from .camera_controller import CameraController
from core.camera.camera_controller import CameraController
from core.camera.camera_manager import CameraManager
from ui.widgets.split_view_widget import SplitView, CameraPlaceholder, ViewWithOverlay
from core.camera.gphoto_camera import GPhotoCamera
from core.camera.camera_controller import CameraController
class MainController:
def __init__(self, view):
self.db = DatabaseManager()
self.db.connect()
self.media_repo = MediaRepository(self.db)
self.media_repo.sync_media()
def __init__(self, view):
self.view = view
self.db = DatabaseManager()
self.media_repo = MediaRepository(self.db)
self.camera_manager = CameraManager()
# camera = GPhotoCamera()
# self.manager = CameraController(camera)
manager = CameraManager()
manager.detect_gphoto()
manager.detect_opencv()
# --- UI Widgets ---
self.color_list: ColorListWidget = view.color_list_widget
self.thumbnail_list: ThumbnailListWidget = view.thumbnail_widget
self.split_view: SplitView = view.preview_widget
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.record_button: QPushButton = view.record_button
# self.camera_controller = CameraController()
self._connect_signals()
self.view = view
self.color_list: ColorListWidget = view.color_list_widget
self.thumbnail_list: ThumbnailListWidget = view.thumbnail_widget
self.split_view: SplitView = view.preview_widget
self.db.connect()
self.media_repo.sync_media()
self.camera_manager.detect_cameras()
self.photo_button: QPushButton = view.photo_button
self.photo_button.clicked.connect(self.take_photo)
def _connect_signals(self):
"""Connects all signals to slots."""
# Database and media signals
self.color_list.colorSelected.connect(self.on_color_selected)
self.color_list.editColor.connect(self.on_edit_color)
self.thumbnail_list.selectedThumbnail.connect(self.on_thumbnail_selected)
self.record_button: QPushButton = view.record_button
# self.record_button.clicked.connect(self.fun_test)
# Camera signals
self.camera_manager.cameras_detected.connect(self.on_cameras_detected)
self.camera_manager.frame_ready.connect(self.on_frame_ready)
self.camera_manager.error_occurred.connect(self.on_camera_error)
self.camera_manager.camera_started.connect(self.on_camera_started)
self.camera_manager.camera_stopped.connect(self.on_camera_stopped)
self.color_list.colorSelected.connect(self.on_color_selected)
self.color_list.editColor.connect(self.on_edit_color)
self.thumbnail_list.selectedThumbnail.connect(self.on_thumbnail_selected)
# UI control signals
self.photo_button.clicked.connect(self.take_photo)
self.welcome_view.camera_start_btn.clicked.connect(self.camera_detect)
self.live_view.rotateCW.connect(self.camera_manager.rotate_right)
self.live_view.rotateCCW.connect(self.camera_manager.rotate_left)
# self.camera_controller.errorOccurred.connect(self.split_view.widget_start.set_info_text)
# self.manager.error_occurred.connect(self.split_view.widget_start.set_info_text)
# self.camera_controller.frameReady.connect(self.split_view.set_live_image)
# self.manager.frame_ready.connect(self.split_view.set_live_image)
# self.split_view.widget_start.camera_start_btn.clicked.connect(self.camera_controller.start)
self.split_view.widget_start.camera_start_btn.clicked.connect(self.start_liveview)
def load_colors(self) -> None:
"""Loads colors from the database and populates the list."""
colors = self.db.get_all_colors()
self.color_list.set_colors(colors)
def shutdown(self):
"""Cleans up resources before application exit."""
self.camera_manager.shutdown()
self.db.disconnect()
# --- Slots for Database/Media ---
@Slot(str)
def on_color_selected(self, color_name: str):
color_id = self.db.get_color_id(color_name)
if color_id is not None:
media_items = self.db.get_media_for_color(color_id)
self.thumbnail_list.list_widget.clear()
for media in media_items:
if media['file_type'] == 'photo':
file_name = Path(media['media_path']).name
self.thumbnail_list.add_thumbnail(media['media_path'], file_name, media['id'])
@Slot(str)
def on_edit_color(self, color_name: str):
print(f"Edycja koloru: {color_name}") # Placeholder
@Slot(int)
def on_thumbnail_selected(self, media_id: int):
media = self.db.get_media(media_id)
if media:
self.split_view.set_reference_image(media['media_path'])
# --- Slots for CameraManager ---
@Slot(list)
def on_cameras_detected(self, cameras: list[dict]):
"""Handles the list of detected cameras."""
print("Detected 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
# self.view.camera_combobox.clear()
# for camera in cameras:
# self.view.camera_combobox.addItem(camera['name'], userData=camera['id'])
@Slot(QPixmap)
def on_frame_ready(self, pixmap: QPixmap):
"""Displays a new frame from the camera."""
self.split_view.set_live_image(pixmap)
@Slot(str)
def on_camera_error(self, error_message: str):
"""Shows an error message from the camera."""
print(f"Camera Error: {error_message}")
self.welcome_view.set_error_text(error_message)
@Slot()
def on_camera_started(self):
"""Updates UI when the camera stream starts."""
self.split_view.toggle_live_view()
self.welcome_view.set_button_text("Stop Camera")
# Re-route button click to stop the camera
self.welcome_view.camera_start_btn.clicked.disconnect()
self.welcome_view.camera_start_btn.clicked.connect(self.stop_liveview)
def start_camera(self):
pass
@Slot()
def on_camera_stopped(self):
"""Updates UI when the camera stream stops."""
# self.split_view.show_placeholder()
self.welcome_view.set_button_text("Start Camera")
# Re-route button click to start the camera
self.welcome_view.camera_start_btn.clicked.disconnect()
self.welcome_view.camera_start_btn.clicked.connect(self.start_liveview)
def load_colors(self) -> None:
colors = self.db.get_all_colors()
print("Loaded colors:", colors)
self.color_list.set_colors(colors)
# --- UI Actions ---
def camera_detect(self):
self.camera_manager.detect_cameras()
def on_color_selected(self, color_name: str):
print(f"Wybrano kolor: {color_name}")
color_id = self.db.get_color_id(color_name)
if color_id is not None:
media_items = self.db.get_media_for_color(color_id)
print(f"Media dla koloru {color_name} (ID: {color_id}):", media_items)
def start_liveview(self):
"""Starts the camera feed."""
detected_cameras = self.camera_manager.get_detected_cameras()
if not detected_cameras:
self.on_camera_error("No cameras detected.")
return
# For now, just start the first detected camera.
# In a real app, you'd get the selected camera ID from the UI.
camera_id = detected_cameras[0]['id']
self.camera_manager.start_camera(camera_id)
self.thumbnail_list.list_widget.clear()
for media in media_items:
if media['file_type'] == 'photo':
file_name = Path(media['media_path']).name
self.thumbnail_list.add_thumbnail(media['media_path'], file_name, media['id'])
else:
print(f"Nie znaleziono koloru o nazwie: {color_name}")
def stop_liveview(self):
"""Stops the camera feed."""
self.camera_manager.stop_camera()
def on_edit_color(self, color_name: str):
print(f"Edycja koloru: {color_name}")
def on_thumbnail_selected(self, media_id: int):
media = self.db.get_media(media_id)
if media:
print(f"Wybrano miniaturę o ID: {media_id}, ścieżka: {media['media_path']}")
self.split_view.set_reference_image(media['media_path'])
else:
print(f"Nie znaleziono mediów o ID: {media_id}")
def take_photo(self):
print("Robienie zdjęcia...")
self.split_view.toglle_live_view()
def start_liveview(self):
pass
# self.manager.start_camera()
# self.manager.start_stream()
def shutdown(self):
pass
# self.manager.stop()
def take_photo(self):
"""Takes a photo with the active camera."""
print("Taking photo...") # Placeholder
# This needs to be implemented in CameraManager and called here.
# e.g., self.camera_manager.take_photo()
self.split_view.toggle_live_view() # This seems like a UI toggle, maybe rename?

View File

@@ -1,115 +1,194 @@
from PySide6.QtCore import QObject, QThread, QTimer, Signal, Slot, QMutex, QMutexLocker
from PySide6.QtCore import QObject, QTimer, Signal, Slot, QMutex, QMutexLocker, QThread
from PySide6.QtGui import QImage, QPixmap
import cv2
from .base_camera import BaseCamera
class CameraController(QThread):
class CameraWorker(QObject):
frame_ready = Signal(QPixmap)
photo_ready = Signal(QPixmap)
error_occurred = Signal(str)
_enable_timer = Signal(bool)
camera_ready = Signal(bool)
def __init__(self, parent: QObject | None = None) -> None:
super().__init__(parent)
self.camera = None
self.timer = None
self.camera: BaseCamera | None = None
self.timer: QTimer | None = None
self.fps = 15
self.is_streaming = False
self.is_connected = False
self._rotation_index = 0
self._camera_mutex = QMutex()
self.start()
def run(self) -> None:
self.timer = QTimer()
@Slot()
def initialize_worker(self):
"""Initializes the timer in the worker's thread."""
self.timer = QTimer(self)
self.timer.timeout.connect(self._update_frame)
self._enable_timer.connect(self._set_timer)
self.exec()
def stop(self):
self.stop_camera()
self.quit()
self.wait()
@Slot(BaseCamera, int)
def set_camera(self, camera: BaseCamera, fps: int = 15) -> None:
with QMutexLocker(self._camera_mutex):
self.stop_stream()
self.stop_camera()
if self.is_streaming:
self.stop_stream()
if self.is_connected:
self.stop_camera()
self.camera = camera
self.fps = fps
@Slot()
def start_camera(self) -> None:
if self.camera is None or self.is_connected:
return
if self.camera.connect():
self.is_connected = True
else:
self.is_connected = False
self.error_occurred.emit(self.camera.get_error_msg())
def stop_camera(self) -> None:
if self.is_streaming:
self.stop_stream()
if self.camera is not None:
self.camera.disconnect()
self.is_connected = False
def start_stream(self):
if not self.is_connected:
return
if self.is_streaming:
return
if self.timer:
self.is_streaming = True
# self.timer.start()
self._enable_timer.emit(True)
def stop_stream(self) -> None:
if self.is_streaming:
self.is_streaming = False
if self.timer:
# self.timer.stop()
self._enable_timer.emit(False)
def _update_frame(self) -> None:
with QMutexLocker(self._camera_mutex):
if self.camera is None or not self.is_connected:
if self.camera is None or self.is_connected:
return
if self.camera.connect():
self.is_connected = True
self.camera_ready.emit(True)
else:
self.is_connected = False
self.camera_ready.emit(False)
self.error_occurred.emit(self.camera.get_error_msg())
@Slot()
def stop_camera(self) -> None:
with QMutexLocker(self._camera_mutex):
if self.is_streaming:
self.stop_stream()
if self.camera is not None and self.is_connected:
self.camera.disconnect()
self.is_connected = False
@Slot()
def start_stream(self):
if not self.is_connected or self.is_streaming or self.timer is None:
return
self.is_streaming = True
self.timer.setInterval(int(1000 / self.fps))
self.timer.start()
@Slot()
def stop_stream(self) -> None:
if self.is_streaming and self.timer is not None:
self.is_streaming = False
self.timer.stop()
@Slot()
def _update_frame(self) -> None:
# This method is called by the timer, which is in the same thread.
# A mutex is still good practice for accessing the shared camera object.
with QMutexLocker(self._camera_mutex):
if self.camera is None or not self.is_connected or not self.is_streaming:
return
if not self.is_streaming:
return
ret, frame = self.camera.get_frame()
if not ret:
self.error_occurred.emit(self.camera.get_error_msg())
error_msg = self.camera.get_error_msg()
if error_msg:
self.error_occurred.emit(error_msg)
return
if frame is not None:
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)
def _set_timer(self, enable: bool):
if not self.timer:
if frame is None:
return
if enable:
self.timer.setInterval(int(1000 / self.fps))
self.timer.start()
else:
self.timer.stop()
if self._rotation_index == 1:
frame = cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE)
elif self._rotation_index == 2:
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
class CameraController(QObject):
frame_ready = Signal(QPixmap)
photo_ready = Signal(QPixmap)
error_occurred = Signal(str)
camera_ready = Signal(bool)
# Signals to command the worker
_set_camera_requested = Signal(BaseCamera, int)
_start_camera_requested = Signal()
_stop_camera_requested = Signal()
_start_stream_requested = Signal()
_stop_stream_requested = Signal()
_rotate_left_requested = Signal()
_rotate_right_requested = Signal()
def __init__(self, parent: QObject | None = None) -> None:
super().__init__(parent)
self._thread = QThread()
self._worker = CameraWorker()
self._worker.moveToThread(self._thread)
# Connect worker signals to controller signals
self._worker.frame_ready.connect(self.frame_ready)
self._worker.photo_ready.connect(self.photo_ready)
self._worker.error_occurred.connect(self.error_occurred)
self._worker.camera_ready.connect(self.camera_ready)
# Connect controller's command signals to worker's slots
self._set_camera_requested.connect(self._worker.set_camera)
self._start_camera_requested.connect(self._worker.start_camera)
self._stop_camera_requested.connect(self._worker.stop_camera)
self._start_stream_requested.connect(self._worker.start_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
self._thread.started.connect(self._worker.initialize_worker)
self._thread.finished.connect(self._worker.stop_camera)
self._thread.start()
def stop(self):
if self._thread.isRunning():
self._thread.quit()
self._thread.wait()
self._thread.deleteLater()
def set_camera(self, camera: BaseCamera, fps: int = 15) -> None:
self._set_camera_requested.emit(camera, fps)
def start_camera(self) -> None:
self._start_camera_requested.emit()
def stop_camera(self) -> None:
self._stop_camera_requested.emit()
def start_stream(self):
self._start_stream_requested.emit()
def stop_stream(self) -> None:
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

@@ -1,20 +1,127 @@
from PySide6.QtCore import QObject, Signal
from PySide6.QtGui import QPixmap
from .camera_controller import CameraController
from .gphoto_camera import GPhotoCamera
from .opencv_camera import OpenCvCamera
from .camera_controller import CameraController
from .base_camera import BaseCamera
class CameraManager:
def __init__(self) -> None:
pass
class CameraManager(QObject):
"""
Zarządza wszystkimi operacjami związanymi z kamerami,
stanowiąc fasadę dla reszty aplikacji.
"""
frame_ready = Signal(QPixmap)
error_occurred = Signal(str)
cameras_detected = Signal(list)
camera_started = Signal()
camera_stopped = Signal()
def detect_gphoto(self):
camera_list = GPhotoCamera.detect()
print(camera_list)
return camera_list
def __init__(self, parent: QObject | None = None) -> None:
super().__init__(parent)
self._camera_controller = CameraController()
self._detected_cameras: list[dict] = []
self._active_camera: BaseCamera | None = None
self._active_camera_info: dict | None = None
def detect_opencv(self):
camera_list = OpenCvCamera.detect()
print(camera_list)
return camera_list
# Przekazywanie sygnałów z kontrolera kamery na zewnątrz
self._camera_controller.frame_ready.connect(self.frame_ready)
self._camera_controller.error_occurred.connect(self.error_occurred)
self._camera_controller.camera_ready.connect(self.start_liveview)
def detect_cameras(self) -> None:
"""Wykrywa wszystkie dostępne kamery (GPhoto i OpenCV)."""
self._detected_cameras.clear()
# Wykryj kamery GPhoto
try:
gphoto_cameras = GPhotoCamera.detect()
for index, info in gphoto_cameras.items():
self._detected_cameras.append({
"id": f"gphoto_{index}",
"name": f"{info['name']} ({info['port']})",
"type": "gphoto",
"index": index
})
except Exception as e:
self.error_occurred.emit(
f"Błąd podczas wykrywania kamer GPhoto: {e}")
# Wykryj kamery OpenCV
try:
opencv_cameras = OpenCvCamera.detect()
for index, info in opencv_cameras.items():
self._detected_cameras.append({
"id": f"opencv_{index}",
"name": f"OpenCV: {info['name']}",
"type": "opencv",
"index": index
})
except Exception as e:
self.error_occurred.emit(
f"Błąd podczas wykrywania kamer OpenCV: {e}")
self.cameras_detected.emit(self._detected_cameras)
def get_detected_cameras(self) -> list[dict]:
return self._detected_cameras
def start_camera(self, camera_id: str, fps: int = 15) -> None:
"""Uruchamia wybraną kamerę."""
if self._active_camera:
self.stop_camera()
camera_info = next(
(c for c in self._detected_cameras if c['id'] == camera_id), None)
if not camera_info:
self.error_occurred.emit(
f"Nie znaleziono kamery o ID: {camera_id}")
return
camera_type = camera_info['type']
camera_index = camera_info['index']
if camera_type == "gphoto":
self._active_camera = GPhotoCamera()
elif camera_type == "opencv":
self._active_camera = OpenCvCamera()
else:
self.error_occurred.emit(f"Nieznany typ kamery: {camera_type}")
return
self._active_camera_info = camera_info
self._camera_controller.set_camera(self._active_camera, fps)
self._camera_controller.start_camera()
def start_liveview(self, connected):
if connected:
self._camera_controller.start_stream()
self.camera_started.emit()
else:
self._active_camera = None
self._active_camera_info = None
def stop_camera(self) -> None:
"""Zatrzymuje aktywną kamerę."""
if self._active_camera:
self._camera_controller.stop_camera()
self._active_camera = None
self._active_camera_info = None
self.camera_stopped.emit()
def get_active_camera_info(self) -> dict | None:
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:
"""Zamyka kontroler kamery i jego wątek."""
self.stop_camera()
self._camera_controller.stop()

View File

@@ -57,9 +57,9 @@ class GPhotoCamera(BaseCamera):
abilities_index = abilities_list.lookup_model(name)
abilities = abilities_list.get_abilities(abilities_index)
abilities_name = []
for name, bit in operations:
for operation, bit in operations:
if abilities.operations & bit: # type: ignore
abilities_name.append(name)
abilities_name.append(operation)
camera_list[i] = {"name": name, "port": port, "abilities": abilities_name}
return camera_list

View File

@@ -1,5 +1,5 @@
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
import sys
from ui.widgets.placeholder_widget import PlaceholderWidget
@@ -33,10 +33,14 @@ class ZoomableImageView(QGraphicsView):
self._current_scale = 1.0
def set_image(self, pixmap: QPixmap):
# pixmap = QPixmap(image_path)
if pixmap.isNull():
return
self._pixmap_item.setPixmap(pixmap)
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):
"""Resetuje skalowanie i ustawia 1:1"""
@@ -100,15 +104,27 @@ class CameraPlaceholder(QWidget):
"background-color: transparent; color: #CECECE; font-size: 18px;")
self.info_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.error_label = QLabel()
self.error_label.setStyleSheet(
"background-color: transparent; color: #CECECE; font-size: 18px; font-style: italic;")
self.error_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addStretch()
layout.addWidget(self.camera_start_btn,
alignment=Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.info_label)
layout.addWidget(self.error_label)
layout.addStretch()
self.setLayout(layout)
def set_info_text(self, text: str):
self.info_label.setText(text)
def set_error_text(self, text: str):
self.error_label.setText(text)
def set_button_text(self, text:str):
self.camera_start_btn.setText(text)
class ViewWithOverlay(QWidget):
@@ -252,12 +268,8 @@ class SplitView(QSplitter):
self.setOrientation(Qt.Orientation.Vertical)
self.widget_start = CameraPlaceholder()
# self.widget_live = ZoomableImageView()
self.widget_live = ViewWithOverlay()
# self.widget_live = PlaceholderWidget("Camera View", "#750466")
# self.widget_ref = ZoomableImageView()
self.widget_live = ViewWithOverlay(live=True)
self.widget_ref = ViewWithOverlay()
# self.widget_ref = PlaceholderWidget("Image View", "#007981")
self.stack = QStackedWidget()
self.stack.addWidget(self.widget_start)
@@ -269,14 +281,19 @@ class SplitView(QSplitter):
self.setSizes([self.height(), 0])
pixmap = QPixmap("media/empty_guitar_h.jpg")
# pixmap = QPixmap("media/empty_guitar_h.jpg")
# 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_ref.toggleOrientation.connect(self.toggle_orientation)
self.widget_live.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):
if self.orientation() == Qt.Orientation.Vertical:
@@ -302,17 +319,33 @@ class SplitView(QSplitter):
def set_live_image(self, pixmap: QPixmap):
"""Ustawienie obrazu na żywo"""
self.widget_live.set_image(pixmap)
if self.stack.currentWidget() != self.widget_live:
self.stack.setCurrentWidget(self.widget_live)
# if self.stack.currentWidget() != self.widget_live:
# self.stack.setCurrentWidget(self.widget_live)
def set_reference_image(self, path_image: str):
"""Ustawienie obrazu referencyjnego"""
pixmap = QPixmap(path_image)
self.widget_ref.set_image(pixmap)
self.ref_pixmap = QPixmap(path_image)
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 toglle_live_view(self):
def toggle_live_view(self):
"""Przełączanie widoku na żywo"""
if self.stack.currentWidget() == self.widget_start:
self.stack.setCurrentWidget(self.widget_live)
else:
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)