Files
MayoStainHelper/controllers/main_controller.py
bartool 96c2495a8b refactor(MainController): Simplify camera button signal handling
Refactor the signal handling for the camera start/stop button in `MainController` to simplify logic and improve reliability.

- Replaced the dynamic connect/disconnect pattern with a single, persistent signal connection to `_on_start_button_clicked`.
- Centralized UI state updates (e.g., button text) into a new `_update_start_button_state` method.
- This eliminates potential errors from mismatched signal connections and makes the control flow easier to follow.
- Also fixes major indentation errors caused by a previous faulty replacement.
2025-10-14 08:49:20 +02:00

172 lines
6.6 KiB
Python

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, CameraPlaceholder, ViewWithOverlay
class MainController:
def __init__(self, view):
self.view = view
self.db = DatabaseManager()
self.media_repo = MediaRepository(self.db)
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()
self.camera_manager.detect_cameras()
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()
def _update_start_button_state(self):
"""Updates the text and state of the start/stop button."""
if self.camera_manager.get_active_camera_info():
self.welcome_view.set_button_text("Zatrzymaj kamerę")
elif self.camera_manager.get_detected_cameras():
self.welcome_view.set_button_text("Uruchom kamerę")
else:
self.welcome_view.set_button_text("Wykryj kamery")
# --- 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._update_start_button_state()
# 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)
self._update_start_button_state()
@Slot()
def on_camera_started(self):
"""Updates UI when the camera stream starts."""
self.split_view.toggle_live_view()
self._update_start_button_state()
@Slot()
def on_camera_stopped(self):
"""Updates UI when the camera stream stops."""
self.split_view.toggle_live_view()
self._update_start_button_state()
# --- UI Actions ---
def _on_start_button_clicked(self):
"""Handles clicks on the main start/stop/detect button."""
if self.camera_manager.get_active_camera_info():
self.stop_liveview()
elif self.camera_manager.get_detected_cameras():
self.start_liveview()
else:
self.camera_detect()
def camera_detect(self):
self.welcome_view.set_info_text("Wykrywanie kamer...")
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.
# 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)
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?