Source code for QVideo.lib.QFilterBank

'''Composable pipeline of VideoFilter stages between a source and a display.'''
import logging
from collections.abc import Iterator

from qtpy import QtWidgets
from QVideo.lib.QVideoFilter import QVideoFilter
from QVideo.lib.videotypes import Image
import QVideo.filters as videofilters


__all__ = ['QFilterBank']

logger = logging.getLogger(__name__)


[docs] class QFilterBank(QtWidgets.QGroupBox): '''A vertical stack of :class:`~QVideo.lib.VideoFilter.QVideoFilter` widgets. Applies a sequence of video filters to each frame in order. Filters are added and removed at runtime via :meth:`register` and :meth:`deregister`, and may also be looked up by name from the :mod:`QVideo.filters` package using :meth:`registerByName`. Parameters ---------- parent : QtWidgets.QWidget or None Parent widget. ''' def __init__(self, parent: QtWidgets.QWidget | None = None) -> None: super().__init__('Display Filters', parent) self._filters: list[QVideoFilter] = [] self._setupUi() def _setupUi(self) -> None: self._layout = QtWidgets.QVBoxLayout(self) def __iter__(self) -> Iterator[QVideoFilter]: return iter(self._filters) @property def filters(self) -> list[QVideoFilter]: '''Read-only view of the registered filters.''' return list(self._filters) def __call__(self, image: Image) -> Image | None: '''Apply all registered filters to *image* in order. Parameters ---------- image : Image Input frame. Returns ------- Image or None Frame after all enabled filters have been applied. ''' for video_filter in self: image = video_filter(image) return image
[docs] def register(self, video_filter: QVideoFilter) -> None: '''Add a filter to the end of the pipeline. Parameters ---------- video_filter : QVideoFilter Filter widget to add. Raises ------ TypeError If *video_filter* is not a :class:`~QVideo.lib.VideoFilter.QVideoFilter`. ''' if not isinstance(video_filter, QVideoFilter): raise TypeError('expected QVideoFilter, ' f'got {type(video_filter).__name__}') self._filters.append(video_filter) self._layout.addWidget(video_filter)
[docs] def deregister(self, video_filter: QVideoFilter) -> None: '''Remove a filter from the pipeline. Parameters ---------- video_filter : QVideoFilter Filter widget to remove. Raises ------ ValueError If *video_filter* is not currently registered. ''' self._filters.remove(video_filter) self._layout.removeWidget(video_filter) video_filter.setParent(None)
[docs] def registerByName(self, name: str) -> None: '''Instantiate a filter by class name and add it to the pipeline. Looks up *name* in the :mod:`QVideo.filters` package and registers an instance if found. Parameters ---------- name : str Name of a :class:`~QVideo.lib.VideoFilter.QVideoFilter` subclass exported by :mod:`QVideo.filters`. Raises ------ ValueError If *name* is not found in :mod:`QVideo.filters` or does not refer to a :class:`~QVideo.lib.VideoFilter.QVideoFilter` subclass. ''' cls = getattr(videofilters, name, None) if cls is None or not (isinstance(cls, type) and issubclass(cls, QVideoFilter)): raise ValueError(f'{name!r} is not a known filter') try: self.register(cls()) except Exception as e: logger.warning(f'Failed to instantiate filter {name!r}: {e}')