Files
duck-stain-ocr/app/main_window.py
2026-05-11 14:39:53 +02:00

241 lines
8.0 KiB
Python

from dataclasses import asdict
from enum import Enum
from typing import Any
import logging
from PySide6.QtWidgets import QGridLayout, QHBoxLayout, QMainWindow, QStyle, QToolButton, QWidget, QVBoxLayout, QPushButton, QLabel, QTextEdit
from PySide6.QtGui import QIcon, QPixmap
from PySide6.QtCore import QTimer, 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()
self._counter = 0
self.timer = QTimer()
self.timer.timeout.connect(self.update_metrics)
self.timer.start(500) # Update metrics every second
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.video_label.setText("Video Stream")
self.video_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.video_label.setStyleSheet("color: white; font-size: 24px; background-color: #001e1e;")
self.metrics_label = QTextEdit(self.central_widget)
self.metrics_label.setReadOnly(True)
# self.metrics_label.setFixedWidth(300)
self.metrics_label.setStyleSheet("background-color: rgba(20, 20, 20, 0.5); color: white; font-size: 14px; border: none; padding: 8px;")
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.video_label.setGeometry(self.central_widget.rect())
panel_width = min(300, max(280, self.central_widget.width() // 3))
self.metrics_label.setGeometry(18, 18, panel_width, 500)
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))
# pass
def update_metrics(self):
self._counter += 1
metrics = self.video_controller.get_metrics()
if metrics is None:
self.metrics_label.setPlainText("No metrics available")
return
metrics_text = ""
for key, value in asdict( metrics).items():
metrics_text += f"{key}: {value}\n"
self.metrics_label.setPlainText(metrics_text)
if self._counter >= 20:
self._counter = 0
self.video_controller.reset_metrics()