from enum import Enum from typing import Any import logging from PySide6.QtWidgets import QGridLayout, QHBoxLayout, QMainWindow, QStyle, QToolButton, QWidget, QVBoxLayout, QPushButton, QLabel from PySide6.QtGui import QIcon, QPixmap from PySide6.QtCore import Qt, QSize from .video_stream.controller import VideoStreamController, FrameMetrics logger = logging.getLogger(__name__) class VideoMode(Enum): STREAMING = "streaming" RECORDING = "recording" PLAYING = "playing" STOPPED = "stopped" icons = { "load_file":{ "path": "assets/folder-svgrepo-com.svg", "tooltip": "Open File", "size": QSize(48, 48) }, "close_file": { "path": "assets/close-square-svgrepo-com.svg", "tooltip": "Close File", "size": QSize(32, 32) }, "record_on": { "path": "assets/record_btn_on.svg", "tooltip": "Start Recording", "size": QSize(48, 48) }, "record_off": { "path": "assets/record_btn_off.svg", "tooltip": "Stop Recording", "size": QSize(48, 48) }, "play": { "path": "assets/play-svgrepo-com.svg", "tooltip": "Play Video", "size": QSize(48, 48) }, "pause": { "path": "assets/pause-svgrepo-com.svg", "tooltip": "Pause Video", "size": QSize(32, 32) } } def set_icon(button: QToolButton, icon: str): button.setIcon(QIcon(icons[icon]["path"])) button.setToolTip(icons[icon]["tooltip"]) button.setIconSize(icons[icon]["size"]) def create_tool_button(icon: str) -> QToolButton: button = QToolButton() set_icon(button, icon) button.setFixedSize(48, 48) button.setStyleSheet("background-color: transparent; border: none;") return button class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Duck Stain OCR") self.resize(1280, 720) self.video_mode = VideoMode.STREAMING logger.debug(f"Initial video mode: {self.video_mode}") self.video_controller = VideoStreamController() self.video_controller.change_source(0) # Start with default camera self.video_controller.image_ready.connect(self.update_frame) self.setup_ui() self.video_controller.start() def setup_ui(self): self.central_widget = QWidget() self.setCentralWidget(self.central_widget) self.central_widget.setStyleSheet("background-color: #001e1e;") self.video_label = QLabel(self.central_widget) self.toolbar_widget = QWidget(self.central_widget) self.toolbar_widget.setMinimumWidth(400) self.toolbar_widget.setObjectName("bottomToolbar") self.toolbar_widget.setStyleSheet( """ QWidget#bottomToolbar { background: rgba(0, 0, 0, 0.1); border-radius: 18px; } """ ) left_layout = QHBoxLayout() right_layout = QHBoxLayout() self.toolbar_layout = QGridLayout(self.toolbar_widget) self.toolbar_layout.setContentsMargins(18, 16, 18, 16) self.toolbar_layout.setColumnStretch(0, 1) self.toolbar_layout.setColumnStretch(1, 0) self.toolbar_layout.setColumnStretch(2, 1) self.toolbar_layout.addLayout(left_layout, 0, 0) self.toolbar_layout.addLayout(right_layout, 0, 2) # Add buttons to the left layout self.open_file_button = create_tool_button("load_file") self.open_file_button.clicked.connect(self.load_video) left_layout.addWidget(self.open_file_button) self.close_file_button = create_tool_button("close_file") self.close_file_button.clicked.connect(self.close_video) self.close_file_button.setEnabled(False) left_layout.addWidget(self.close_file_button) left_layout.addStretch() # Add the record button to the center of the toolbar self.action_button = create_tool_button("record_off") self.action_button.clicked.connect(self.toggle_action) self.toolbar_layout.addWidget(self.action_button, 0, 1, alignment=Qt.AlignmentFlag.AlignCenter) # Add buttons to the right layout right_layout.addStretch() def resizeEvent(self, event: Any) -> None: super().resizeEvent(event) self.toolbar_widget.adjustSize() toolbar_size = self.toolbar_widget.sizeHint() self.toolbar_widget.setGeometry( (self.central_widget.width() - self.toolbar_widget.width()) // 2, self.central_widget.height() - self.toolbar_widget.height() - 80, self.toolbar_widget.width(), self.toolbar_widget.height(), ) self.toolbar_widget.raise_() # Ensure the toolbar is above other widgets def toggle_action(self): match self.video_mode: case VideoMode.STREAMING: self.start_recording() case VideoMode.RECORDING: self.stop_recording() case VideoMode.PLAYING: self.pause_video() case VideoMode.STOPPED: self.play_video() case _: pass def start_recording(self): logger.debug("Starting recording") self.video_mode = VideoMode.RECORDING set_icon(self.action_button, "record_on") self.open_file_button.setEnabled(False) self.close_file_button.setEnabled(False) def stop_recording(self): logger.debug("Stopping recording") self.video_mode = VideoMode.STREAMING set_icon(self.action_button, "record_off") self.open_file_button.setEnabled(True) self.close_file_button.setEnabled(False) def load_video(self): logger.debug("Loading video") self.video_mode = VideoMode.STOPPED set_icon(self.action_button, "play") self.close_file_button.setEnabled(True) def close_video(self): logger.debug("Closing video") self.video_mode = VideoMode.STREAMING set_icon(self.action_button, "record_off") self.close_file_button.setEnabled(False) def play_video(self): logger.debug("Playing video") self.video_mode = VideoMode.PLAYING set_icon(self.action_button, "pause") def pause_video(self): logger.debug("Pausing video") self.video_mode = VideoMode.STOPPED set_icon(self.action_button, "play") def closeEvent(self, event: Any) -> None: logger.debug("Closing main window, cleaning up resources.") self.video_controller.cleanup() super().closeEvent(event) def update_frame(self, frame): self.video_label.setPixmap(QPixmap.fromImage(frame))