Exemplo n.º 1
0
class JupyterConsole(BaseExtension):
    
    preferences_ui = 'extensions.JupyterConsole.preferences'

    @BaseExtension.as_thread(wait=500)
    def event_startup(self):

        from jupyter_tabwidget import ConsoleTabWidget

        self.set_busy(True)
        self._jupyter_console = ConsoleTabWidget(self.main_window)
        self._dock_widget = QDockWidget(u'Console', self.main_window)
        self._dock_widget.setObjectName(u'JupyterConsole')
        self._dock_widget.setWidget(self._jupyter_console)
        self._dock_widget.closeEvent = self._on_close_event
        self.main_window.addDockWidget(
            Qt.BottomDockWidgetArea,
            self._dock_widget
        )
        self._set_visible(cfg.jupyter_visible)
        self._shortcut_focus = QShortcut(
            QKeySequence(cfg.jupyter_focus_shortcut),
            self.main_window,
            self._focus,
            context=Qt.ApplicationShortcut
        )
        self.set_busy(False)
        
    def fire(self, event, **kwdict):
        
        if event != 'startup':
            oslogger.debug('ignoring events until after startup')
            return
        JupyterConsole.fire = BaseExtension.fire
        self.fire(event, **kwdict)

    def activate(self):

        if not hasattr(self, '_jupyter_console'):
            oslogger.debug('ignoring activate until after startup')
            return
        self._set_visible(not cfg.jupyter_visible)

    def event_run_experiment(self, fullscreen):

        oslogger.debug(u'capturing stdout')
        self._jupyter_console.current.capture_stdout()

    def event_end_experiment(self, ret_val):

        self._jupyter_console.current.release_stdout()
        self._jupyter_console.current.show_prompt()
        oslogger.debug(u'releasing stdout')

    def event_jupyter_start_kernel(self, kernel):

        self._jupyter_console.add(kernel=kernel)

    def event_jupyter_run_file(self, path, debug=False):

        self._set_visible(True)
        if not os.path.isfile(path):
            return
        self._jupyter_console.current.change_dir(os.path.dirname(path))
        if debug:
            self._jupyter_console.current.run_debug(
                path,
                breakpoints=self.extension_manager.provide(
                    'pyqode_breakpoints'
                )
            )
        else:
            self._jupyter_console.current.run_file(path)

    def event_jupyter_change_dir(self, path):

        self._jupyter_console.current.change_dir(path)

    def event_jupyter_run_code(self, code, editor=None):

        self._set_visible(True)
        self._jupyter_console.current.execute(code)
    
    def event_jupyter_run_silent(self, code):

        self._jupyter_console.current.execute(code)
        
    def event_jupyter_run_system_command(self, cmd):

        self._jupyter_console.current.run_system_command(cmd)

    def event_jupyter_write(self, msg):

        try:
            self._jupyter_console.current.write(msg)
        except AttributeError:
            oslogger.error(safe_decode(msg))

    def event_jupyter_focus(self):

        self._jupyter_console.current.focus()

    def event_jupyter_show_prompt(self):

        self._jupyter_console.current.show_prompt()

    def event_jupyter_restart(self):

        self._jupyter_console.current.restart()

    def event_jupyter_interrupt(self):

        self._jupyter_console.current.interrupt()

    def event_set_workspace_globals(self, global_dict):

        self._jupyter_console.current.set_workspace_globals(global_dict)

    def provide_jupyter_workspace_name(self):

        try:
            return self._jupyter_console.current.name
        except AttributeError:
            return None
        
    def provide_workspace_kernel(self):
        
        try:
            return self._jupyter_console.current._kernel
        except AttributeError:
            return None
        
    def provide_workspace_language(self):

        try:
            return self._jupyter_console.current.language
        except AttributeError:
            return None
        
    def provide_workspace_logging_commands(self):

        from jupyter_tabwidget.constants import LOGGING_LEVEL_CMD
        try:
            kernel = self._jupyter_console.current.language
        except AttributeError:
            return None
        return LOGGING_LEVEL_CMD.get(kernel, None)

    def provide_jupyter_workspace_globals(self):

        return self.get_workspace_globals()

    def provide_jupyter_list_workspace_globals(self):

        return self.list_workspace_globals()

    def provide_jupyter_workspace_variable(self, name):

        return self._jupyter_console.current.get_workspace_variable(name)
        
    def provide_jupyter_kernel_running(self):
        
        try:
            return self._jupyter_console.current.running()
        except AttributeError:
            return False

    def provide_jupyter_check_syntax(self, code):

        return self._jupyter_console.current.check_syntax(code)

    def get_workspace_globals(self):

        try:
            return self._jupyter_console.current.get_workspace_globals()
        except AttributeError:
            return {u'no reply': None}

    def list_workspace_globals(self):

        try:
            return self._jupyter_console.current.list_workspace_globals()
        except Exception as e:
            print(e)
            return []

    def event_close(self):

        if not hasattr(self, '_jupyter_console'):
            oslogger.debug('ignoring close all')
            return
        self._jupyter_console.close_all()

    def _set_visible(self, visible):

        cfg.jupyter_visible = visible
        self.set_checked(visible)
        if visible:
            self._dock_widget.show()
            self._jupyter_console.current.focus()
        else:
            self._dock_widget.hide()

    def _focus(self):

        self._set_visible(True)
        self._jupyter_console.current.focus()

    def _on_close_event(self, e):

        self._set_visible(False)
Exemplo n.º 2
0
class BaseMainWindow(QMainWindow):
    """
    Base for main windows of subprograms

    :ivar settings: store state of application. initial value is obtained from :py:attr:`.settings_class`
    :ivar files_num: maximal number of files accepted by drag and rop event
    :param config_folder: path to directory in which application save state. If `settings` parameter is note
        then settings object is created with passing this path to :py:attr:`.settings_class`.
        If this parameter and `settings`
        are None then constructor fail with :py:exc:`ValueError`.
    :param title: Window default title
    :param settings: object to store application state
    :param signal_fun: function which need to be called when window shown.
    """

    show_signal = Signal()
    """Signal emitted when window has shown. Used to hide Launcher."""
    @classmethod
    def get_setting_class(cls) -> Type[BaseSettings]:
        """Get constructor for :py:attr:`settings`"""
        return BaseSettings

    def __init__(
        self,
        config_folder: Union[str, Path, None] = None,
        title="PartSeg",
        settings: Optional[BaseSettings] = None,
        load_dict: Optional[Register] = None,
        signal_fun=None,
    ):
        if settings is None:
            if config_folder is None:
                raise ValueError("wrong config folder")
            if not os.path.exists(config_folder):
                import_config()
            settings: BaseSettings = self.get_setting_class()(config_folder)
            errors = settings.load()
            if errors:
                errors_message = QMessageBox()
                errors_message.setText("There are errors during start")
                errors_message.setInformativeText(
                    "During load saved state some of data could not be load properly\n"
                    "The files has prepared backup copies in "
                    " state directory (Help > State directory)")
                errors_message.setStandardButtons(QMessageBox.Ok)
                text = "\n".join("File: " + x[0] + "\n" + str(x[1])
                                 for x in errors)
                errors_message.setDetailedText(text)
                errors_message.exec_()

        super().__init__()
        if signal_fun is not None:
            self.show_signal.connect(signal_fun)
        self.settings = settings
        self._load_dict = load_dict
        self.viewer_list: List[Viewer] = []
        self.files_num = 1
        self.setAcceptDrops(True)
        self.setWindowTitle(title)
        self.title_base = title
        app = QApplication.instance()
        if app is not None:
            app.setStyleSheet(settings.style_sheet)
        self.settings.theme_changed.connect(self.change_theme)
        self.channel_info = ""
        self.multiple_files = None
        self.settings.request_load_files.connect(self.read_drop)
        self.recent_file_menu = QMenu("Open recent")
        self._refresh_recent()
        self.settings.connect_(FILE_HISTORY, self._refresh_recent)
        self.settings.napari_settings.appearance.events.theme.connect(
            self.change_theme)
        self.settings.set_parent(self)
        self.console = None
        self.console_dock = QDockWidget("console", self)
        self.console_dock.setAllowedAreas(Qt.LeftDockWidgetArea
                                          | Qt.BottomDockWidgetArea)
        # self.console_dock.setWidget(self.console)
        self.console_dock.hide()
        self.addDockWidget(Qt.BottomDockWidgetArea, self.console_dock)

    def _toggle_console(self):
        if self.console is None:
            self.console = QtConsole(self)
            self.console_dock.setWidget(self.console)
        self.console_dock.setVisible(not self.console_dock.isVisible())

    def _refresh_recent(self):

        self.recent_file_menu.clear()
        for name_list, method in self.settings.get_last_files():
            action = self.recent_file_menu.addAction(
                f"{name_list[0]}, {method}")
            action.setData((name_list, method))
            action.triggered.connect(self._load_recent)

    def _load_recent(self):
        sender: QAction = self.sender()
        data = sender.data()
        try:
            method: LoadBase = self._load_dict[data[1]]
            dial = ExecuteFunctionDialog(
                method.load, [data[0]],
                exception_hook=load_data_exception_hook)
            if dial.exec_():
                result = dial.get_result()
                self.main_menu.set_data(result)
                self.settings.add_last_files(data[0], method.get_name())
                self.settings.set(OPEN_DIRECTORY, os.path.dirname(data[0][0]))
                self.settings.set(OPEN_FILE, data[0][0])
                self.settings.set(OPEN_FILE_FILTER, data[1])
        except KeyError:
            self.read_drop(data[0])

    def toggle_multiple_files(self):
        self.settings.set("multiple_files_widget",
                          not self.settings.get("multiple_files_widget"))

    def get_colormaps(self) -> List[Optional[colormap.Colormap]]:
        channel_num = self.settings.image.channels
        if not self.channel_info:
            return [None for _ in range(channel_num)]
        colormaps_name = [
            self.settings.get_channel_colormap_name(self.channel_info, i)
            for i in range(channel_num)
        ]
        return [
            self.settings.colormap_dict[name][0] for name in colormaps_name
        ]

    def napari_viewer_show(self):
        viewer = Viewer(title="Additional output",
                        settings=self.settings,
                        partseg_viewer_name=self.channel_info)
        viewer.theme = self.settings.theme_name
        viewer.create_initial_layers(image=True,
                                     roi=True,
                                     additional_layers=False,
                                     points=True)
        self.viewer_list.append(viewer)
        viewer.window.qt_viewer.destroyed.connect(
            lambda x: self.close_viewer(viewer))

    def additional_layers_show(self, with_channels=False):
        if not self.settings.additional_layers:
            QMessageBox().information(
                self, "No data",
                "Last executed algoritm does not provide additional data")
            return
        viewer = Viewer(title="Additional output",
                        settings=self.settings,
                        partseg_viewer_name=self.channel_info)
        viewer.theme = self.settings.theme_name
        viewer.create_initial_layers(image=with_channels,
                                     roi=False,
                                     additional_layers=True,
                                     points=False)
        self.viewer_list.append(viewer)
        viewer.window.qt_viewer.destroyed.connect(
            lambda x: self.close_viewer(viewer))

    def close_viewer(self, obj):
        for i, el in enumerate(self.viewer_list):
            if el == obj:
                self.viewer_list.pop(i)
                break

    # @ensure_main_thread
    def change_theme(self, event):
        style_sheet = self.settings.style_sheet
        app = QApplication.instance()
        if app is not None:
            app.setStyleSheet(style_sheet)
        self.setStyleSheet(style_sheet)

    def showEvent(self, a0: QShowEvent):
        self.show_signal.emit()

    def dragEnterEvent(self, event: QDragEnterEvent):  # pylint: disable=R0201
        if event.mimeData().hasUrls():
            event.acceptProposedAction()

    def read_drop(self, paths: List[str]):
        """Function to process loading files by drag and drop."""
        self._read_drop(paths, self._load_dict)

    def _read_drop(self, paths, load_dict):
        ext_set = {os.path.splitext(x)[1].lower() for x in paths}

        def exception_hook(exception):
            if isinstance(exception, OSError):
                QMessageBox().warning(
                    self, "IO Error",
                    "Disc operation error: " + ", ".join(exception.args),
                    QMessageBox.Ok)

        for load_class in load_dict.values():
            if load_class.partial(
            ) or load_class.number_of_files() != len(paths):
                continue
            if ext_set.issubset(load_class.get_extensions()):
                dial = ExecuteFunctionDialog(load_class.load, [paths],
                                             exception_hook=exception_hook)
                if dial.exec_():
                    result = dial.get_result()
                    self.main_menu.set_data(result)
                    self.settings.add_last_files(paths, load_class.get_name())
                return
        QMessageBox.information(
            self, "No method", "No methods for load files: " + ",".join(paths))

    def dropEvent(self, event: QDropEvent):
        """
        Support for load files by drag and drop.
        At beginning it check number of files and if it greater than :py:attr:`.files_num` it refuse loading. Otherwise
        it call :py:meth:`.read_drop` method and this method should be overwritten in sub classes
        """
        if not all(x.isLocalFile() for x in event.mimeData().urls()):
            QMessageBox().warning(
                self, "Load error",
                "Not all files are locally. Cannot load data.", QMessageBox.Ok)
        paths = [x.toLocalFile() for x in event.mimeData().urls()]
        if self.files_num != -1 and len(paths) > self.files_num:
            QMessageBox.information(
                self, "To many files",
                "currently support only drag and drop one file")
            return
        self.read_drop(paths)

    def show_settings_directory(self):
        DirectoryDialog(
            self.settings.json_folder_path,
            "Path to place where PartSeg store the data between runs").exec_()

    @staticmethod
    def show_about_dialog():
        """Show about dialog."""
        AboutDialog().exec_()

    @staticmethod
    def get_project_info(file_path, image):
        raise NotADirectoryError()

    def image_adjust_exec(self):
        dial = ImageAdjustmentDialog(self.settings.image)
        if dial.exec_():
            algorithm = dial.result_val.algorithm
            dial2 = ExecuteFunctionDialog(algorithm.transform, [], {
                "image": self.settings.image,
                "arguments": dial.result_val.values
            })
            if dial2.exec_():
                result: Image = dial2.get_result()
                self.settings.set_project_info(
                    self.get_project_info(result.file_path, result))

    def closeEvent(self, event: QCloseEvent):
        for el in self.viewer_list:
            el.close()
            del el
        self.settings.napari_settings.appearance.events.theme.disconnect(
            self.change_theme)
        self.settings.dump()
        super().closeEvent(event)

    def screenshot(self, viewer: ImageView):
        def _screenshot():
            data = viewer.viewer_widget.screenshot()
            dial = PSaveDialog(
                SaveScreenshot,
                settings=self.settings,
                system_widget=False,
                path="io.save_screenshot",
                file_mode=PSaveDialog.AnyFile,
            )

            if not dial.exec_():
                return
            res = dial.get_result()
            res.save_class.save(res.save_destination, data, res.parameters)

        return _screenshot

    def image_read(self):
        folder_name, file_name = os.path.split(self.settings.image_path)
        self.setWindowTitle(
            f"{self.title_base}: {os.path.join(os.path.basename(folder_name), file_name)}"
        )
        self.statusBar().showMessage(self.settings.image_path)

    def deleteLater(self) -> None:
        self.settings.napari_settings.appearance.events.theme.disconnect(
            self.change_theme)
        super().deleteLater()
Exemplo n.º 3
0
class WorkspaceExplorer(BaseExtension):
    @BaseExtension.as_thread(wait=2500)
    def event_startup(self):

        dm = DataMatrix(length=0)
        dm.initializing = -1
        self._workspace_cache = {}
        self._label_kernel_not_supported = QLabel('<b>' +
                                                  _('Kernel not supported') +
                                                  '</b>')
        self._label_kernel_not_supported.setAlignment(Qt.AlignHCenter
                                                      | Qt.AlignVCenter)
        self._qdm = WorkspaceMatrix(dm, read_only=True)
        self._qdm.setFont(QFont(cfg.pyqode_font_name, cfg.pyqode_font_size))
        self._qdm.cell_double_clicked.connect(self._inspect_variable)
        self._dock_widget = QDockWidget(self.main_window)
        self._dock_widget.setWidget(self._qdm)
        self._dock_widget.closeEvent = self._on_close_event
        self._dock_widget.setWindowTitle(_(u'Workspace'))
        self._dock_widget.setObjectName('WorkspaceExplorer')
        self._dock_widget.visibilityChanged.connect(
            self._on_visibility_changed)
        self.main_window.addDockWidget(Qt.RightDockWidgetArea,
                                       self._dock_widget)
        self._set_visible(cfg.workspace_visible)

    def _inspect_variable(self, row, column):

        if self.extension_manager.provide('jupyter_kernel_running'):
            self.extension_manager.fire(
                'notify',
                message=_(u'Cannot inspect variables in running kernel'))
            return
        if not row:
            return
        self.extension_manager.fire(
            'data_viewer_inspect',
            name=self._qdm.dm.name[row - 1],
            workspace=self.extension_manager.provide('jupyter_workspace_name'))

    def activate(self):

        if not hasattr(self, '_qdm'):
            oslogger.info('ignoring activate until after startup')
            return
        self._set_visible(not cfg.workspace_visible)

    def _on_visibility_changed(self, visible):

        if not visible:
            return
        self._update(
            self.extension_manager.provide('jupyter_workspace_name'), lambda:
            self.extension_manager.provide('jupyter_workspace_globals'))

    def _on_close_event(self, e):

        self._set_visible(False)

    def _update(self, name, workspace_func):

        if (not hasattr(self, '_dock_widget')
                or not self._dock_widget.isVisible()):
            return
        self._dock_widget.setWidget(self._qdm)
        self._dock_widget.setWindowTitle(_(u'Workspace ({})').format(name))
        workspace = workspace_func()
        self._workspace_cache[name] = workspace
        # If the workspace didn't reply, we try again in a second
        if workspace is None or workspace.get(u'no reply', False) is None:
            QTimer.singleShot(1000, lambda: self._update(name, workspace_func))
            return
        # If the current kernel doesn't expose its workspace, indicate this
        if workspace.get(u'not supported', False) is None:
            dm = DataMatrix(length=0)
            dm.kernel_not_supported = -1
            self._dock_widget.setWidget(self._label_kernel_not_supported)
        # Create a DataMatrix that exposes the workspace
        else:
            dm = DataMatrix(length=len(workspace))
            dm.sorted = False
            dm.name = ''
            dm.value = ''
            dm.shape = ''
            dm.type = ''
            for row, (var, data) in zip(dm, workspace.items()):
                if data is None:
                    oslogger.warning(u'invalid workspace data: {}'.format(var))
                    continue
                value, type_, shape = data
                row.value = value
                row.name = var
                if shape is not None:
                    row.shape = repr(shape)
                row.type = type_
        self._qdm.dm = dm
        self._qdm.refresh()

    def _set_visible(self, visible):

        cfg.workspace_visible = visible
        self.set_checked(visible)
        if visible:
            self._dock_widget.show()
        else:
            self._dock_widget.hide()

    def event_workspace_update(self, name, workspace_func):

        self._update(name, workspace_func)

    def event_workspace_restart(self, name, workspace_func):

        self._update(name, workspace_func)

    def event_workspace_switch(self, name, workspace_func):

        # When a kernel is running (which includes being a debugging session)
        # it doesn't respond to silent requests for the workspace. Therefore
        # we use cached copy of the last update.
        if self.extension_manager.provide('jupyter_kernel_running'):
            self._update(name, lambda: self._workspace_cache.get(name, {}))
            return
        self._update(name, workspace_func)

    def event_workspace_new(self, name, workspace_func):

        self._update(name, workspace_func)
Exemplo n.º 4
0
class WorkspaceExplorer(BaseExtension):
    @BaseExtension.as_thread(wait=2500)
    def event_startup(self):

        dm = DataMatrix(length=0)
        dm.initializing = -1
        self._qdm = WorkspaceMatrix(dm, read_only=True)
        self._qdm.cell_double_clicked.connect(self._inspect_variable)
        self._dock_widget = QDockWidget(self.main_window)
        self._dock_widget.setWidget(self._qdm)
        self._dock_widget.closeEvent = self._on_close_event
        self._dock_widget.setWindowTitle(_(u'Workspace'))
        self._dock_widget.setObjectName('WorkspaceExplorer')
        self._dock_widget.visibilityChanged.connect(
            self._on_visibility_changed)
        self.main_window.addDockWidget(Qt.RightDockWidgetArea,
                                       self._dock_widget)
        self._set_visible(cfg.workspace_visible)

    def _inspect_variable(self, row, column):

        if not row:
            return
        name = self._qdm.dm.name[row - 1]
        self.extension_manager.fire(
            'data_viewer_inspect',
            name=name,
            workspace=self.extension_manager.provide('jupyter_workspace_name'))

    def activate(self):

        if not hasattr(self, '_qdm'):
            oslogger.info('ignoring activate until after startup')
            return
        self._set_visible(not cfg.workspace_visible)

    def _on_visibility_changed(self, visible):

        if not visible:
            return
        self._update(
            self.extension_manager.provide('jupyter_workspace_name'), lambda:
            self.extension_manager.provide('jupyter_workspace_globals'))

    def _on_close_event(self, e):

        self._set_visible(False)

    def _update(self, name, workspace_func):

        if (not hasattr(self, '_dock_widget')
                or not self._dock_widget.isVisible()):
            return
        self._dock_widget.setWindowTitle(_(u'Workspace ({})').format(name))
        workspace = workspace_func()
        # If the workspace didn't reply, we try again in a second
        if workspace is None or workspace.get(u'no reply', False) is None:
            QTimer.singleShot(1000, lambda: self._update(name, workspace_func))
            return
        # If the current kernel doesn't expose its workspace, indicate this
        if workspace.get(u'not supported', False) is None:
            dm = DataMatrix(length=0)
            dm.kernel_not_supported = -1
        # Create a DataMatrix that exposes the workspace
        else:
            dm = DataMatrix(length=len(workspace))
            dm.sorted = False
            dm.name = ''
            dm.value = ''
            dm.shape = ''
            dm.type = ''
            for row, (var, data) in zip(dm, workspace.items()):
                if data is None:
                    oslogger.warning(u'invalid workspace data: {}'.format(var))
                    continue
                value, type_, shape = data
                row.value = value
                row.name = var
                if shape is not None:
                    row.shape = repr(shape)
                row.type = type_
        self._qdm.dm = dm
        self._qdm.refresh()

    def _set_visible(self, visible):

        cfg.workspace_visible = visible
        self.set_checked(visible)
        if visible:
            self._dock_widget.show()
        else:
            self._dock_widget.hide()

    def event_workspace_update(self, name, workspace_func):

        self._update(name, workspace_func)

    def event_workspace_restart(self, name, workspace_func):

        self._update(name, workspace_func)

    def event_workspace_switch(self, name, workspace_func):

        self._update(name, workspace_func)

    def event_workspace_new(self, name, workspace_func):

        self._update(name, workspace_func)