Skip to content

Commit

Permalink
Spectrum viewer tab for exporting to RITS (#1942)
Browse files Browse the repository at this point in the history
  • Loading branch information
JackEAllen authored Oct 10, 2023
2 parents f981096 + 708117d commit 42873a3
Show file tree
Hide file tree
Showing 12 changed files with 436 additions and 327 deletions.
3 changes: 0 additions & 3 deletions .github/workflows/conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ on:
- 'main'
- 'release-*'
pull_request:
branches:
- 'main'
- 'release-*'
release:
merge_group:

Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/cos7_testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ on:
- 'main'
- 'release-*'
pull_request:
branches:
- 'main'
- 'release-*'
merge_group:

jobs:
Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ on:
- 'main'
- 'release-*'
pull_request:
branches:
- 'main'
- 'release-*'
release:
merge_group:

Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/license_check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ on:
- 'main'
- 'release-*'
pull_request:
branches:
- 'main'
- 'release-*'
merge_group:

jobs:
Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/u18_testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ on:
- 'main'
- 'release-*'
pull_request:
branches:
- 'main'
- 'release-*'
merge_group:

jobs:
Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ on:
- 'main'
- 'release-*'
pull_request:
branches:
- 'main'
- 'release-*'
release:
merge_group:

Expand Down
2 changes: 2 additions & 0 deletions docs/release_notes/next/feature-1941-rits-gui
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#1941 : Spectrum viewer tab for exporting to RITS

615 changes: 336 additions & 279 deletions mantidimaging/gui/ui/spectrum_viewer.ui

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions mantidimaging/gui/windows/spectrum_viewer/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import numpy as np

from logging import getLogger
from mantidimaging.core.data import ImageStack
from mantidimaging.core.io.csv_output import CSVOutput
from mantidimaging.core.io import saver
Expand All @@ -16,6 +17,8 @@
if TYPE_CHECKING:
from mantidimaging.gui.windows.spectrum_viewer.presenter import SpectrumViewerWindowPresenter

LOG = getLogger(__name__)


class SpecType(Enum):
SAMPLE = 1
Expand Down Expand Up @@ -203,6 +206,8 @@ def save_rits(self, path: Path, normalized: bool) -> None:
raise RuntimeError("No normalisation stack selected")
transmission = self.get_spectrum(default_roi, SpecType.SAMPLE_NORMED)
self.export_spectrum_to_rits(path, tof, transmission, transmission_error)
else:
LOG.error("Data is not normalised to open beam. This will not export to a valid RITS format")

def get_roi_coords_filename(self, path: Path) -> Path:
"""
Expand Down
26 changes: 22 additions & 4 deletions mantidimaging/gui/windows/spectrum_viewer/presenter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Copyright (C) 2023 ISIS Rutherford Appleton Laboratory UKRI
# SPDX - License - Identifier: GPL-3.0-or-later
from __future__ import annotations

from enum import Enum
from typing import TYPE_CHECKING, Optional

from logging import getLogger
Expand All @@ -17,6 +19,12 @@
LOG = getLogger(__name__)


class ExportMode(Enum):
# Needs to match GUI tab order
ROI_MODE = 0
IMAGE_MODE = 1


class SpectrumViewerWindowPresenter(BasePresenter):
"""
The presenter for the spectrum viewer window.
Expand All @@ -29,13 +37,15 @@ class SpectrumViewerWindowPresenter(BasePresenter):
spectrum_mode: SpecType = SpecType.SAMPLE
current_stack_uuid: Optional['UUID'] = None
current_norm_stack_uuid: Optional['UUID'] = None
export_mode: ExportMode

def __init__(self, view: 'SpectrumViewerWindowView', main_window: 'MainWindowView'):
super().__init__(view)

self.view = view
self.main_window = main_window
self.model = SpectrumViewerWindowModel(self)
self.export_mode = ExportMode.ROI_MODE

def handle_sample_change(self, uuid: Optional['UUID']) -> None:
if uuid == self.current_stack_uuid:
Expand All @@ -53,7 +63,6 @@ def handle_sample_change(self, uuid: Optional['UUID']) -> None:
if uuid is None:
self.model.set_stack(None)
self.view.clear()
self.handle_button_enabled()
return

self.model.set_stack(self.main_window.get_stack(uuid))
Expand All @@ -68,7 +77,6 @@ def handle_sample_change(self, uuid: Optional['UUID']) -> None:
self.do_add_roi()
self.view.set_normalise_error(self.model.normalise_issue())
self.show_new_sample()
self.handle_button_enabled()

def handle_normalise_stack_change(self, normalise_uuid: Optional['UUID']) -> None:
if normalise_uuid == self.current_norm_stack_uuid:
Expand Down Expand Up @@ -147,9 +155,15 @@ def do_set_roi_alpha(self, name: str, alpha: float) -> None:

def handle_button_enabled(self) -> None:
"""
Enable the export button if the current stack is not None
Enable the export button if the current stack is not None and normalisation is valid
"""
self.view.set_export_button_enabled(self.model.has_stack())
has_stack = self.model.has_stack()
normalisation_on = self.view.normalisation_enabled()
normalisation_no_error = (normalisation_on and self.model.normalise_issue() == "") or not normalisation_on

self.view.exportButton.setEnabled(has_stack and normalisation_no_error)
self.view.exportButtonRITS.setEnabled(has_stack and normalisation_on and normalisation_no_error)
self.view.addBtn.setEnabled(has_stack)

def handle_export_csv(self) -> None:
path = self.view.get_csv_filename()
Expand Down Expand Up @@ -235,3 +249,7 @@ def do_remove_roi(self, roi_name=None) -> None:
self.view.spectrum.remove_roi(roi_name)
self.view.set_spectrum(roi_name, self.model.get_spectrum(roi_name, self.spectrum_mode))
self.model.remove_roi(roi_name)

def handle_export_tab_change(self, index: int) -> None:
self.export_mode = ExportMode(index)
self.view.on_visibility_change()
45 changes: 43 additions & 2 deletions mantidimaging/gui/windows/spectrum_viewer/test/presenter_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from pathlib import Path
from unittest import mock

from PyQt5.QtWidgets import QPushButton
from parameterized import parameterized

from mantidimaging.core.data.dataset import StrictDataset, MixedDataset
Expand All @@ -27,6 +28,9 @@ def setUp(self) -> None:
self.view.current_dataset_id = uuid.uuid4()
mock_spectrum_roi_dict = mock.create_autospec(dict)
self.view.spectrum = mock.create_autospec(SpectrumWidget, roi_dict=mock_spectrum_roi_dict)
self.view.exportButton = mock.create_autospec(QPushButton)
self.view.exportButtonRITS = mock.create_autospec(QPushButton)
self.view.addBtn = mock.create_autospec(QPushButton)
self.presenter = SpectrumViewerWindowPresenter(self.view, self.main_window)

def test_get_dataset_id_for_stack_no_stack_id(self):
Expand Down Expand Up @@ -74,7 +78,6 @@ def test_handle_sample_change_no_new_stack(self):
self.view.try_to_select_relevant_normalise_stack.assert_not_called()
self.assertIsNone(self.view.current_dataset_id)
self.presenter.show_new_sample.assert_not_called()
self.view.set_export_button_enabled.assert_called_once_with(False)

def test_handle_sample_change_dataset_unchanged(self):
initial_dataset_id = self.view.current_dataset_id
Expand All @@ -86,7 +89,6 @@ def test_handle_sample_change_dataset_unchanged(self):
self.presenter.handle_sample_change(uuid.uuid4())
self.presenter.main_window.get_dataset.assert_not_called()
self.assertEqual(self.view.current_dataset_id, initial_dataset_id)
self.view.set_export_button_enabled.assert_called_once_with(True)

def test_handle_sample_change_to_MixedDataset(self):
self.presenter.get_dataset_id_for_stack = mock.Mock(return_value=uuid.uuid4())
Expand All @@ -112,6 +114,45 @@ def test_handle_sample_change_no_flat(self):
self.presenter.main_window.get_dataset.assert_called_once()
self.view.try_to_select_relevant_normalise_stack.assert_not_called()

@mock.patch("mantidimaging.gui.windows.spectrum_viewer.model.SpectrumViewerWindowModel.has_stack")
def test_WHEN_no_stack_THEN_buttons_disabled(self, has_stack):
has_stack.return_value = False
self.presenter.handle_button_enabled()
self.view.exportButton.setEnabled.assert_called_once_with(False)
self.view.exportButtonRITS.setEnabled.assert_called_once_with(False)
self.view.addBtn.setEnabled.assert_called_once_with(False)

@mock.patch("mantidimaging.gui.windows.spectrum_viewer.model.SpectrumViewerWindowModel.has_stack")
def test_WHEN_has_stack_no_norm_THEN_buttons_set(self, has_stack):
has_stack.return_value = True
self.view.normalisation_enabled.return_value = False
self.presenter.handle_button_enabled()
self.view.exportButton.setEnabled.assert_called_once_with(True)
self.view.exportButtonRITS.setEnabled.assert_called_once_with(False) # RITS export needs norm
self.view.addBtn.setEnabled.assert_called_once_with(True)

@mock.patch("mantidimaging.gui.windows.spectrum_viewer.model.SpectrumViewerWindowModel.has_stack")
@mock.patch("mantidimaging.gui.windows.spectrum_viewer.model.SpectrumViewerWindowModel.normalise_issue")
def test_WHEN_has_stack_has_good_norm_THEN_buttons_set(self, normalise_issue, has_stack):
has_stack.return_value = True
normalise_issue.return_value = ""
self.view.normalisation_enabled.return_value = True
self.presenter.handle_button_enabled()
self.view.exportButton.setEnabled.assert_called_once_with(True)
self.view.exportButtonRITS.setEnabled.assert_called_once_with(True)
self.view.addBtn.setEnabled.assert_called_once_with(True)

@mock.patch("mantidimaging.gui.windows.spectrum_viewer.model.SpectrumViewerWindowModel.has_stack")
@mock.patch("mantidimaging.gui.windows.spectrum_viewer.model.SpectrumViewerWindowModel.normalise_issue")
def test_WHEN_has_stack_has_bad_norm_THEN_buttons_set(self, normalise_issue, has_stack):
has_stack.return_value = True
normalise_issue.return_value = "Something wrong"
self.view.normalisation_enabled.return_value = True
self.presenter.handle_button_enabled()
self.view.exportButton.setEnabled.assert_called_once_with(False)
self.view.exportButtonRITS.setEnabled.assert_called_once_with(False)
self.view.addBtn.setEnabled.assert_called_once_with(True)

def test_WHEN_show_sample_call_THEN_add_range_set(self):
self.presenter.model.tof_range = (0, 9)
self.presenter.show_new_sample()
Expand Down
52 changes: 28 additions & 24 deletions mantidimaging/gui/windows/spectrum_viewer/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
from typing import TYPE_CHECKING, Optional

from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QCheckBox, QVBoxLayout, QFileDialog, QPushButton, QLabel, QAbstractItemView, QHeaderView
from PyQt5.QtWidgets import QCheckBox, QVBoxLayout, QFileDialog, QPushButton, QLabel, QAbstractItemView, QHeaderView, \
QTabWidget

from mantidimaging.core.utility import finder
from mantidimaging.gui.mvp_base import BaseMainWindowView
from mantidimaging.gui.widgets.dataset_selector import DatasetSelectorWidgetView
from .presenter import SpectrumViewerWindowPresenter
from .presenter import SpectrumViewerWindowPresenter, ExportMode
from mantidimaging.gui.widgets import RemovableRowTableView
from .spectrum_widget import SpectrumWidget
from mantidimaging.gui.windows.spectrum_viewer.roi_table_model import TableModel
Expand All @@ -30,6 +31,7 @@ class SpectrumViewerWindowView(BaseMainWindowView):
normaliseCheckBox: QCheckBox
imageLayout: QVBoxLayout
exportButton: QPushButton
exportTabs: QTabWidget
normaliseErrorIcon: QLabel
_current_dataset_id: Optional['UUID']
normalise_error_issue: str = ""
Expand All @@ -56,9 +58,14 @@ def __init__(self, main_window: 'MainWindowView'):

self._current_dataset_id = None
self.sampleStackSelector.stack_selected_uuid.connect(self.presenter.handle_sample_change)
self.sampleStackSelector.stack_selected_uuid.connect(self.presenter.handle_button_enabled)
self.normaliseStackSelector.stack_selected_uuid.connect(self.presenter.handle_normalise_stack_change)
self.normaliseStackSelector.stack_selected_uuid.connect(self.presenter.handle_button_enabled)
self.normaliseCheckBox.stateChanged.connect(self.normaliseStackSelector.setEnabled)
self.normaliseCheckBox.stateChanged.connect(self.presenter.handle_enable_normalised)
self.normaliseCheckBox.stateChanged.connect(self.presenter.handle_button_enabled)

self.exportTabs.currentChanged.connect(self.presenter.handle_export_tab_change)

# ROI action buttons
self.addBtn.clicked.connect(self.set_new_roi)
Expand All @@ -71,6 +78,7 @@ def __init__(self, main_window: 'MainWindowView'):
self.try_to_select_relevant_normalise_stack("Flat")

self.exportButton.clicked.connect(self.presenter.handle_export_csv)
self.exportButtonRITS.clicked.connect(self.presenter.handle_rits_export)

# Point table
self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
Expand All @@ -92,18 +100,6 @@ def on_row_change(item, _) -> None:

self.tableView.selectionModel().currentRowChanged.connect(on_row_change)

def on_visibility_change() -> None:
"""
When the visibility of an ROI is changed, update the visibility of the ROI in the spectrum widget
"""
for roi_item in range(self.roi_table_model.rowCount()):
if self.roi_table_model.row_data(roi_item)[2] is False:
self.set_roi_alpha(0, roi_item)
else:
self.set_roi_alpha(255, roi_item)
self.presenter.redraw_spectrum(self.roi_table_model.row_data(roi_item)[0])
return

def on_data_in_table_change() -> None:
"""
Check if an ROI name has changed in the table or if the visibility of an ROI has changed.
Expand All @@ -124,7 +120,7 @@ def on_data_in_table_change() -> None:
selected_row_data[0] = self.current_roi

selected_row_data[0] = self.current_roi
on_visibility_change()
self.on_visibility_change()
return

self.roi_table_model.dataChanged.connect(on_data_in_table_change)
Expand All @@ -142,6 +138,23 @@ def cleanup(self):
self.normaliseStackSelector.unsubscribe_from_main_window()
self.main_window.spectrum_viewer = None

def on_visibility_change(self) -> None:
"""
When the visibility of an ROI is changed, update the visibility of the ROI in the spectrum widget
"""
for roi_item in range(self.roi_table_model.rowCount()):
if self.presenter.export_mode == ExportMode.ROI_MODE:
roi_name, _, roi_visible = self.roi_table_model.row_data(roi_item)
if roi_visible is False:
self.set_roi_alpha(0, roi_item)
else:
self.set_roi_alpha(255, roi_item)
self.presenter.redraw_spectrum(roi_name)
else:
self.set_roi_alpha(0, roi_item)

return

@property
def roi_table_model(self) -> TableModel:
if self.tableView.model() is None:
Expand Down Expand Up @@ -229,15 +242,6 @@ def set_new_roi(self) -> None:
"""
self.presenter.do_add_roi()

def set_export_button_enabled(self, enabled: bool):
"""
Toggle enabled state of the export button
@param enabled: True to enable the button, False to disable it
"""
self.exportButton.setEnabled(enabled)
self.addBtn.setEnabled(enabled)

def set_roi_alpha(self, alpha: float, roi) -> None:
"""
Set the alpha value for the selected ROI and update the spectrum to reflect the change.
Expand Down

0 comments on commit 42873a3

Please sign in to comment.