bci_framework.framework.extensions_handler — BCI-Framework documentation

# Source code for bci_framework.framework.extensions_handler

    """
    ===============
    Stream handlers
    ===============
    """

    import os
    import sys
    from datetime import datetime
    from typing import Optional, Literal, List, Callable

    from PySide2.QtUiTools import QUiLoader
    from PySide2.QtCore import QTimer, Qt
    from PySide2.QtWidgets import QMdiSubWindow, QMenu, QAction, QMenuBar

    from .subprocess_handler import LoadSubprocess
    from .config_manager import ConfigManager

    ########################################################################
    class ExtensionMenu:
        """MDIArea menu for extensions."""

        # ----------------------------------------------------------------------

        # ----------------------------------------------------------------------

        # ----------------------------------------------------------------------
        def set_dpi(self, menu_dpi, text: str, dpi: int) -> Callable:
            """Set the DPI value for matplotlib figures."""
            def wrap():
                [action.setChecked(False) for action in menu_dpi.actions()]
                [action.setChecked(
                    True) for action in menu_dpi.actions() if action.text() == text]
                self.main.DPI = dpi
                menu_dpi.setTitle(f'DPI ({dpi})')
            return wrap

        # ----------------------------------------------------------------------
        def set_extension(self, visualization: str) -> Callable:
            """Load extension from menu."""
            def wrap():
                self.load_extension(visualization)
            return wrap

    ########################################################################
    class ExtensionWidget(QMdiSubWindow, ExtensionMenu):
        """MDIArea with extension."""

        # ----------------------------------------------------------------------
        def __init__(self, mdi_area,
                     mode: str,
                     extensions_list: List[str] = [],
                     autostart: Optional[str] = None,
                     hide_menu: Optional[bool] = False,
                     directory: Optional[str] = None):
            """"""
            super().__init__(None)
            ui = os.path.realpath(os.path.join(
                os.environ['BCISTREAM_ROOT'], 'framework', 'qtgui', 'extension_widget.ui'))
            self.main = QUiLoader().load(ui, self)
            self.mdi_area = mdi_area

            self.main.DPI = 60
            self.mode = mode
            self.current_viz = None

            if autostart:
                self.extensions_list = [autostart]
            else:
                self.extensions_list = extensions_list

            if directory:
                self.projects_dir = directory
            else:
                if '--local' in sys.argv:
                    self.projects_dir = os.path.join(
                        os.getenv('BCISTREAM_ROOT'), 'default_extensions')
                else:
                    self.projects_dir = os.path.join(
                        os.getenv('BCISTREAM_HOME'), 'default_extensions')

            self.setWindowFlag(Qt.FramelessWindowHint)
            self.setWidget(self.main)
            self.config = ConfigManager()

            self.setStyleSheet(f"""
            * {{
            border: 2px solid {os.getenv('QTMATERIAL_SECONDARYCOLOR', '#000000')};
            border-width: 0px 2px 2px 2px;
            }}
            QMenuBar {{
            background: {os.getenv('QTMATERIAL_SECONDARYCOLOR', '#000000')};
            border-width: 0;
            }}
            """)

            if autostart:
                self.load_extension(autostart)

            if hide_menu:
                self.main.widget.hide()

        # ----------------------------------------------------------------------
        @property
        def is_visualization(self) -> str:
            """"""
            return self.mode == 'visualization'

        # ----------------------------------------------------------------------
        @property
        def is_stimuli(self) -> str:
            """"""
            return self.mode in ['stimuli', 'delivery']

        # ----------------------------------------------------------------------
        @property
        def is_analysis(self) -> str:
            """"""
            return self.mode == 'analysis'

        # ----------------------------------------------------------------------
        def load_extension(self, extension: str, debugger: Optional[bool] = False) -> None:
            """Load project."""
            self.current_viz = extension
            module = os.path.join(self.projects_dir, extension, 'main.py')
            self.stream_subprocess = LoadSubprocess(
                self.main, module, use_webview=not self.is_analysis, debugger=debugger)
            self.update_menu_bar(extension, debugger)
            self.update_ip(self.stream_subprocess.port)
            self.loaded()

        # ----------------------------------------------------------------------
        def loaded(self, *args, **kwargs) -> None:
            """Method to connect events."""
            # keep this

        # ----------------------------------------------------------------------
        def update_ip(self, *args, **kwargs) -> None:
            """Method to connect events."""
            # keep this

        # ----------------------------------------------------------------------
        def remove(self) -> None:
            """Remove active extension."""
            self.stop_preview()
            if hasattr(self, 'stream_subprocess'):
                del self.stream_subprocess

            if self.is_visualization:
                self.deleteLater()
                QTimer().singleShot(1000 / 50, self.mdi_area.tileSubWindows)
            elif self.is_stimuli:
                self.update_menu_bar()
                self.loaded()

        # ----------------------------------------------------------------------
        def save_img(self, name: Optional[str] = None) -> None:
            """Save capture of visualization in the project directory."""
            if name is None:
                name = f"{self.current_viz.replace(' ', '')} {str(datetime.now()).replace(':', '_')}.jpg"
            captures_dir = os.path.join(
                self.projects_dir, self.current_viz, 'captures')
            filename = os.path.join(captures_dir, name)

            if not os.path.exists(captures_dir):
                os.makedirs(captures_dir, exist_ok=True)

            if os.path.exists(f'{filename}'):
                os.remove(f'{filename}')

            if web_engine := getattr(self.stream_subprocess.main, 'web_engine', False):
                web_engine.grab().save(filename, 'JPG')
            return filename

        # ----------------------------------------------------------------------

        # ----------------------------------------------------------------------
        def stop_preview(self) -> None:
            """Stop process."""
            if hasattr(self, 'stream_subprocess'):
                self.stream_subprocess.stop_preview()

        # ----------------------------------------------------------------------
        def reload(self) -> None:
            """Restart process."""
            self.stream_subprocess.reload()