from pathlib import Path from PySide6.QtCore import Slot from PySide6.QtGui import QPixmap from PySide6.QtWidgets import QPushButton from core.database import db_manager 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, CameraPlaceholder, ViewWithOverlay from core.camera.states import CameraState, NoCamerasState, ReadyToStreamState, StreamingState, DetectingState class MainController: def __init__(self, view): self.view = view self.db = db_manager self.media_repo = MediaRepository() self.camera_manager = CameraManager() # --- 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._connect_signals() self.db.connect() self.media_repo.sync_media() # Initialize state machine self.state: CameraState = NoCamerasState() self.state.enter_state(self) def transition_to(self, new_state: CameraState): """Transitions the controller to a new state.""" print(f"Transitioning to state: {new_state.__class__.__name__}") self.state = new_state self.state.enter_state(self) 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) # 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) # UI control signals self.photo_button.clicked.connect(self.take_photo) self.welcome_view.camera_start_btn.clicked.connect(self._on_start_button_clicked) self.live_view.rotateCW.connect(self.camera_manager.rotate_right) self.live_view.rotateCCW.connect(self.camera_manager.rotate_left) 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 and transitions state.""" print("Detected cameras:", cameras) if cameras: self.transition_to(ReadyToStreamState()) else: self.transition_to(NoCamerasState()) @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 and transitions to a safe state.""" print(f"Camera Error: {error_message}") self.welcome_view.set_error_text(error_message) self.transition_to(NoCamerasState()) @Slot() def on_camera_started(self): """Transitions to StreamingState when the camera starts.""" self.transition_to(StreamingState()) @Slot() def on_camera_stopped(self): """Transitions to a post-streaming state.""" self.split_view.toggle_live_view() if self.camera_manager.get_detected_cameras(): self.transition_to(ReadyToStreamState()) else: self.transition_to(NoCamerasState()) # --- UI Actions --- def _on_start_button_clicked(self): """Delegates the button click to the current state.""" self.state.handle_start_button(self) def camera_detect(self): """Initiates camera detection and transitions to DetectingState.""" self.transition_to(DetectingState()) self.camera_manager.detect_cameras() 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. camera_id = detected_cameras[0]['id'] self.camera_manager.start_camera(camera_id) def stop_liveview(self): """Stops the camera feed.""" self.camera_manager.stop_camera() 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?