diff --git a/src/capture_method/__init__.py b/src/capture_method/__init__.py index 1110e7cc..110d2a9b 100644 --- a/src/capture_method/__init__.py +++ b/src/capture_method/__init__.py @@ -1,11 +1,14 @@ import os import sys from collections import OrderedDict +from collections.abc import Callable from dataclasses import dataclass from enum import EnumMeta, StrEnum, auto, unique from itertools import starmap from typing import TYPE_CHECKING, Never, TypedDict, cast +from cv2.typing import MatLike +from PySide6 import QtCore from typing_extensions import override from capture_method.CaptureMethodBase import CaptureMethodBase @@ -221,3 +224,27 @@ def get_camera_info(index: int, device_name: str): for camera_info in starmap(get_camera_info, enumerate(named_video_inputs)) if camera_info is not None ] + + +class CaptureMethodSignal(QtCore.QObject): + """Made to look like a `QtCore.SignalInstance`, but with safe `connect`/`disconnect` methods.""" + + __frame_signal = QtCore.Signal(object) + + def subscribe_to_new_frame(self, slot: Callable[[MatLike | None], object]): + try: + return self.__frame_signal.connect(slot, QtCore.Qt.ConnectionType.UniqueConnection) + except RuntimeError: + pass + + def unsubscribe_from_new_frame(self, slot: Callable[[MatLike | None], object]): + try: + self.__frame_signal.disconnect(slot) + except RuntimeError: + pass + + def _push_new_frame_to_subscribers(self, frame: MatLike | None, /): + return self.__frame_signal.emit(frame) + + def __init__(self): + super().__init__()