Source code for mantidimaging.gui.windows.live_viewer.view

# Copyright (C) 2021 ISIS Rutherford Appleton Laboratory UKRI
# SPDX - License - Identifier: GPL-3.0-or-later
from __future__ import annotations
from pathlib import Path
from typing import TYPE_CHECKING

from PyQt5.QtCore import QSignalBlocker, Qt
from PyQt5.QtWidgets import QVBoxLayout, QSplitter
from PyQt5.Qt import QAction, QActionGroup

from mantidimaging.gui.mvp_base import BaseMainWindowView
from .live_view_widget import LiveViewWidget
from .presenter import LiveViewerWindowPresenter

import numpy as np

from ..spectrum_viewer.spectrum_widget import SpectrumPlotWidget

if TYPE_CHECKING:
    from mantidimaging.gui.windows.main import MainWindowView  # noqa:F401  # pragma: no cover


[docs] class LiveViewerWindowView(BaseMainWindowView): """ Live Viewer window view class. This class is responsible for handling user interaction with the window. """ imageLayout: QVBoxLayout def __init__(self, main_window: MainWindowView, live_dir_path: Path) -> None: super().__init__(None, 'gui/ui/live_viewer_window.ui') self.main_window = main_window self.path = live_dir_path self.setWindowTitle(f"Mantid Imaging - Live Viewer - {str(self.path)}") self.presenter = LiveViewerWindowPresenter(self, main_window) self.live_viewer = LiveViewWidget() self.splitter = QSplitter(Qt.Vertical) self.imageLayout.addWidget(self.splitter) self.live_viewer.z_slider.valueChanged.connect(self.presenter.select_image) self.spectrum_plot_widget = SpectrumPlotWidget() self.intensity_profile = self.spectrum_plot_widget.spectrum self.live_viewer.roi_changing.connect(self.presenter.handle_notify_roi_moved) self.splitter.addWidget(self.live_viewer) self.splitter.addWidget(self.spectrum_plot_widget) widget_height = self.frameGeometry().height() self.splitter.setSizes([widget_height, 0]) self.filter_params: dict[str, dict] = {} self.right_click_menu = self.live_viewer.image.vb.menu operations_menu = self.right_click_menu.addMenu("Operations") rotate_menu = operations_menu.addMenu("Rotate Image") self.rotate_angles_group = QActionGroup(self) allowed_angles = [0, 90, 180, 270] for angle in allowed_angles: action = QAction(str(angle) + "°", self.rotate_angles_group) action.setCheckable(True) rotate_menu.addAction(action) action.triggered.connect(self.set_image_rotation_angle) if angle == 0: action.setChecked(True) self.load_as_dataset_action = self.right_click_menu.addAction("Load as dataset") self.load_as_dataset_action.triggered.connect(self.presenter.load_as_dataset) self.intensity_action = QAction("Calculate Intensity Profile", self) self.intensity_action.setCheckable(True) operations_menu.addAction(self.intensity_action) self.intensity_action.triggered.connect(self.set_intensity_visibility) self.live_viewer.set_roi_visibility_flags(False)
[docs] def show(self) -> None: """Show the window""" super().show() self.activateWindow() self.watch_directory()
[docs] def show_most_recent_image(self, image: np.ndarray) -> None: """ Show the most recently modified image in the image view. @param image: The image to show """ self.live_viewer.show_image(image)
[docs] def watch_directory(self) -> None: """Show the most recent image arrived in the selected directory""" self.presenter.set_dataset_path(self.path)
[docs] def remove_image(self) -> None: """Remove the image from the view.""" self.live_viewer.handle_deleted()
[docs] def set_image_range(self, index_range: tuple[int, int]) -> None: """Set the range on the z-slider, without triggering valueChanged signal""" with QSignalBlocker(self.live_viewer.z_slider): self.live_viewer.z_slider.set_range(*index_range)
[docs] def set_image_index(self, index: int) -> None: """Set the position on the z-slider, triggering valueChanged signal once""" with QSignalBlocker(self.live_viewer.z_slider): self.live_viewer.z_slider.set_value(index) self.live_viewer.z_slider.valueChanged.emit(index)
[docs] def closeEvent(self, e) -> None: """Close the window and remove it from the main window list""" self.main_window.live_viewer_list.remove(self) self.presenter.close() self.live_viewer.handle_deleted() super().closeEvent(e) self.presenter = None # type: ignore # View instance to be destroyed -type can be inconsistent
[docs] def set_image_rotation_angle(self) -> None: """Set the image rotation angle which will be read in by the presenter""" if self.rotate_angles_group.checkedAction().text() == "0°": if "Rotate Stack" in self.filter_params: del self.filter_params["Rotate Stack"] else: image_rotation_angle = int(self.rotate_angles_group.checkedAction().text().replace('°', '')) self.filter_params["Rotate Stack"] = {"params": {"angle": image_rotation_angle}} self.presenter.update_image_operation()
[docs] def set_load_as_dataset_enabled(self, enabled: bool) -> None: self.load_as_dataset_action.setEnabled(enabled)
[docs] def set_intensity_visibility(self) -> None: widget_height = self.frameGeometry().height() if self.intensity_action.isChecked(): if not self.live_viewer.roi_object: self.live_viewer.add_roi() self.live_viewer.set_roi_visibility_flags(True) self.splitter.setSizes([int(0.7 * widget_height), int(0.3 * widget_height)]) self.presenter.model.roi = self.live_viewer.get_roi() self.presenter.model.clear_mean_partial() if self.presenter.model.images: self.presenter.handle_roi_moved() self.presenter.update_intensity(self.presenter.model.mean) else: self.live_viewer.set_roi_visibility_flags(False) self.splitter.setSizes([widget_height, 0])