Skip to content

Commit

Permalink
2378 Part One: A New ROI Properties Table Class (#2493)
Browse files Browse the repository at this point in the history
  • Loading branch information
samtygier-stfc authored Feb 14, 2025
2 parents d088873 + d72767a commit 34d5abd
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 82 deletions.
11 changes: 3 additions & 8 deletions mantidimaging/gui/windows/spectrum_viewer/presenter.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from mantidimaging.gui.windows.spectrum_viewer.spectrum_widget import SpectrumROI
from mantidimaging.core.data import ImageStack
from uuid import UUID
from PyQt5.QtWidgets import QAction, QSpinBox
from PyQt5.QtWidgets import QAction

LOG = getLogger(__name__)

Expand Down Expand Up @@ -185,7 +185,7 @@ def show_new_sample(self) -> None:
self.view.spectrum_widget.spectrum_plot_widget.add_range(*self.model.tof_plot_range)
self.view.spectrum_widget.spectrum_plot_widget.set_image_index_range_label(*self.model.tof_range)
self.view.auto_range_image()
if self.view.get_roi_properties_spinboxes():
if self.view.roi_properties_widget.roiPropertiesSpinBoxes:
self.view.set_roi_properties()

def handle_range_slide_moved(self, tof_range: tuple[float, float] | tuple[int, int]) -> None:
Expand Down Expand Up @@ -438,7 +438,7 @@ def change_selected_menu_option(self, opt: str) -> None:
self.check_action(action, False)

def do_adjust_roi(self) -> None:
new_roi = self.convert_spinbox_roi_to_SensibleROI(self.view.roiPropertiesSpinBoxes)
new_roi = self.view.roi_properties_widget.as_roi()
self.view.spectrum_widget.adjust_roi(new_roi, self.view.current_roi_name)

def handle_storing_current_roi_name_on_tab_change(self) -> None:
Expand All @@ -458,8 +458,3 @@ def handle_storing_current_roi_name_on_tab_change(self) -> None:
@staticmethod
def check_action(action: QAction, param: bool) -> None:
action.setChecked(param)

def convert_spinbox_roi_to_SensibleROI(self, spinboxes: dict[str, QSpinBox]) -> SensibleROI:
roi_iter_order = ["Left", "Top", "Right", "Bottom"]
new_points = [spinboxes[prop].value() for prop in roi_iter_order]
return SensibleROI.from_list(new_points)
12 changes: 9 additions & 3 deletions mantidimaging/gui/windows/spectrum_viewer/test/presenter_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from unittest import mock

import numpy as np
from PyQt5.QtWidgets import QPushButton, QActionGroup, QGroupBox, QAction, QCheckBox, QTabWidget
from PyQt5.QtWidgets import QPushButton, QActionGroup, QGroupBox, QAction, QCheckBox, QTabWidget, QWidget, QSpinBox
from parameterized import parameterized

from mantidimaging.core.data.dataset import Dataset
Expand All @@ -31,6 +31,13 @@ def setUp(self) -> None:
self.main_window = MainWindowView()
self.view = mock.create_autospec(SpectrumViewerWindowView, instance=True)
self.view.current_dataset_id = uuid.uuid4()
self.view.roi_properties_widget = mock.create_autospec(QWidget, instance=True)
self.view.roi_properties_widget.roiPropertiesSpinBoxes = {
"Top": mock.create_autospec(QSpinBox, instance=True),
"Bottom": mock.create_autospec(QSpinBox, instance=True),
"Left": mock.create_autospec(QSpinBox, instance=True),
"Right": mock.create_autospec(QSpinBox, instance=True)
}
mock_spectrum_roi_dict = mock.create_autospec(dict, instance=True)
self.view.spectrum_widget = mock.create_autospec(SpectrumWidget, roi_dict=mock_spectrum_roi_dict, instance=True)
self.view.spectrum_widget.spectrum_plot_widget = mock.create_autospec(SpectrumPlotWidget,
Expand Down Expand Up @@ -387,8 +394,7 @@ def test_WHEN_menu_option_selected_THEN_menu_option_changed(self):
self.presenter.check_action.assert_has_calls(calls)

def test_WHEN_roi_changed_via_spinboxes_THEN_roi_adjusted(self):
self.presenter.view.roiPropertiesSpinBoxes = mock.Mock()
self.presenter.convert_spinbox_roi_to_SensibleROI = mock.Mock(return_value=SensibleROI(10, 10, 20, 30))
self.view.roi_properties_widget.as_roi = mock.Mock(return_value=SensibleROI(10, 10, 20, 30))
self.view.current_roi_name = "roi_1"
self.presenter.do_adjust_roi()
self.view.spectrum_widget.adjust_roi.assert_called_once_with(SensibleROI(10, 10, 20, 30), "roi_1")
Expand Down
189 changes: 118 additions & 71 deletions mantidimaging/gui/windows/spectrum_viewer/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@

from pathlib import Path
from typing import TYPE_CHECKING, Any
from collections.abc import Callable

from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QCheckBox, QVBoxLayout, QFileDialog, QPushButton, QLabel, QAbstractItemView, QHeaderView, \
QTabWidget, QComboBox, QSpinBox, QTableWidget, QTableWidgetItem, QGroupBox, QActionGroup, QAction
QTabWidget, QComboBox, QSpinBox, QTableWidget, QTableWidgetItem, QGroupBox, QActionGroup, QAction, QWidget
from PyQt5.QtCore import QSignalBlocker, Qt, QModelIndex

from mantidimaging.core.utility import finder
from mantidimaging.core.utility.sensible_roi import SensibleROI
from mantidimaging.gui.mvp_base import BaseMainWindowView
from mantidimaging.gui.widgets.dataset_selector import DatasetSelectorWidgetView
from .model import ROI_RITS, allowed_modes
Expand All @@ -26,6 +28,103 @@
from uuid import UUID


class ROIPropertiesTableWidget(QWidget):
"""
A class to represent the ROI properties table widget in the spectrum viewer window.
This class helps improve modularity of the spectrum viewer UI components by moving
the ROI properties table widget to its own class.
"""

roiPropertiesTableWidget: QTableWidget
roiPropertiesGroupBox: QGroupBox
roiPropertiesSpinBoxes: dict[str, QSpinBox]
roiPropertiesLabels: dict[str, QLabel]

def __init__(self, parent=None, roiPropertiesTableWidget=QTableWidget, roiPropertiesGroupBox=QGroupBox):
super().__init__(parent)
self.roi_table_properties = ["Top", "Bottom", "Left", "Right"]
self.roi_table_properties_secondary = ["Width", "Height"]

self.roiPropertiesTableWidget = roiPropertiesTableWidget
self.roiPropertiesGroupBox = roiPropertiesGroupBox
self.roiPropertiesSpinBoxes = {}
self.roiPropertiesLabels = {}
self.initialize_roi_properties()

def initialize_roi_properties(self) -> None:
self.roiPropertiesSpinBoxes["Left"] = QSpinBox()
self.roiPropertiesSpinBoxes["Right"] = QSpinBox()
self.roiPropertiesSpinBoxes["Top"] = QSpinBox()
self.roiPropertiesSpinBoxes["Bottom"] = QSpinBox()

# set up the labels
self.roiPropertiesLabels["Width"] = QLabel()
self.roiPropertiesLabels["Height"] = QLabel()

# set spinbox ranges
self.roiPropertiesSpinBoxes["Left"].setMaximum(self.roiPropertiesSpinBoxes["Right"].value() - 1)
self.roiPropertiesSpinBoxes["Right"].setMinimum(self.roiPropertiesSpinBoxes["Left"].value() + 1)
self.roiPropertiesSpinBoxes["Top"].setMaximum(self.roiPropertiesSpinBoxes["Bottom"].value() - 1)
self.roiPropertiesSpinBoxes["Bottom"].setMinimum(self.roiPropertiesSpinBoxes["Top"].value() + 1)

# set up table
self.roiPropertiesTableWidget.setColumnCount(3)
self.roiPropertiesTableWidget.setRowCount(3)
self.roiPropertiesTableWidget.setColumnWidth(0, 80)
self.roiPropertiesTableWidget.setColumnWidth(1, 50)
self.roiPropertiesTableWidget.setColumnWidth(2, 50)
self.roiPropertiesTableWidget.horizontalHeader().hide()
self.roiPropertiesTableWidget.verticalHeader().hide()
self.roiPropertiesTableWidget.setShowGrid(False)

def populate_roi_properties_table_text(self) -> None:
roiPropertiesTableText = ["x1, x2", "y1, y2", "Size"]
self.roiPropertiesTableTextDict = {}
for text in roiPropertiesTableText:
item = QTableWidgetItem(text)
item.setFlags(Qt.ItemIsSelectable)
self.roiPropertiesTableTextDict[text] = item

self.roiPropertiesTableWidget.setItem(0, 0, self.roiPropertiesTableTextDict["x1, x2"])
self.roiPropertiesTableWidget.setCellWidget(0, 1, self.roiPropertiesSpinBoxes["Left"])
self.roiPropertiesTableWidget.setCellWidget(0, 2, self.roiPropertiesSpinBoxes["Right"])
self.roiPropertiesTableWidget.setItem(1, 0, self.roiPropertiesTableTextDict["y1, y2"])
self.roiPropertiesTableWidget.setCellWidget(1, 1, self.roiPropertiesSpinBoxes["Top"])
self.roiPropertiesTableWidget.setCellWidget(1, 2, self.roiPropertiesSpinBoxes["Bottom"])
self.roiPropertiesTableWidget.setItem(2, 0, self.roiPropertiesTableTextDict["Size"])
self.roiPropertiesTableWidget.setCellWidget(2, 1, self.roiPropertiesLabels["Width"])
self.roiPropertiesTableWidget.setCellWidget(2, 2, self.roiPropertiesLabels["Height"])

def setup_roi_properties_spinboxes(self, spectrum_widget: SpectrumWidget, do_adjust_roi: Callable) -> None:
assert spectrum_widget.image.image_data is not None
for prop in self.roi_table_properties:
spin_box = QSpinBox()
if prop == "Top" or prop == "Bottom":
spin_box.setMaximum(spectrum_widget.image.image_data.shape[0])
if prop == "Left" or prop == "Right":
spin_box.setMaximum(spectrum_widget.image.image_data.shape[1])
spin_box.valueChanged.connect(do_adjust_roi) # THis will need moving out of presenter in
self.roiPropertiesSpinBoxes[prop] = spin_box
for prop in self.roi_table_properties_secondary:
label = QLabel()
self.roiPropertiesLabels[prop] = label

def disable_roi_properties(self) -> None:
self.roiPropertiesGroupBox.setTitle("Roi Properties: None selected")
for _, spinbox in self.roiPropertiesSpinBoxes.items():
with QSignalBlocker(spinbox):
spinbox.setMinimum(0)
spinbox.setValue(0)
spinbox.setDisabled(True)
for _, label in self.roiPropertiesLabels.items():
label.setText("0")

def as_roi(self):
roi_iter_order = ["Left", "Top", "Right", "Bottom"]
new_points = [self.roiPropertiesSpinBoxes[prop].value() for prop in roi_iter_order]
return SensibleROI.from_list(new_points)


class SpectrumViewerWindowView(BaseMainWindowView):
tableView: RemovableRowTableView
sampleStackSelector: DatasetSelectorWidgetView
Expand All @@ -50,6 +149,7 @@ class SpectrumViewerWindowView(BaseMainWindowView):

roiPropertiesTableWidget: QTableWidget
roiPropertiesGroupBox: QGroupBox
roi_properties_widget: ROIPropertiesTableWidget

last_clicked_roi: str

Expand All @@ -71,8 +171,10 @@ def __init__(self, main_window: MainWindowView):
self.selected_row: int = 0
self.last_clicked_roi = ""
self.current_roi_name: str = ""
self.roiPropertiesSpinBoxes: dict[str, QSpinBox] = {}
self.roiPropertiesLabels: dict[str, QLabel] = {}

self.roi_properties_widget = ROIPropertiesTableWidget(self, self.roiPropertiesTableWidget,
self.roiPropertiesGroupBox)

self.old_table_names: list[str] = []

self.presenter = SpectrumViewerWindowPresenter(self, main_window)
Expand Down Expand Up @@ -143,34 +245,9 @@ def __init__(self, main_window: MainWindowView):
# Roi Prop table
self.roi_table_properties = ["Top", "Bottom", "Left", "Right"]
self.roi_table_properties_secondary = ["Width", "Height"]
self.roiPropertiesTableWidget.setColumnCount(3)
self.roiPropertiesTableWidget.setRowCount(3)
self.roiPropertiesTableWidget.setColumnWidth(0, 80)
self.roiPropertiesTableWidget.setColumnWidth(1, 50)
self.roiPropertiesTableWidget.setColumnWidth(2, 50)

self.setup_roi_properties_spinboxes()

self.roiPropertiesTableWidget.horizontalHeader().hide()
self.roiPropertiesTableWidget.verticalHeader().hide()
self.roiPropertiesTableWidget.setShowGrid(False)

roiPropertiesTableText = ["x1, x2", "y1, y2", "Size"]
self.roiPropertiesTableTextDict = {}
for text in roiPropertiesTableText:
item = QTableWidgetItem(text)
item.setFlags(Qt.ItemIsSelectable)
self.roiPropertiesTableTextDict[text] = item

self.roiPropertiesTableWidget.setItem(0, 0, self.roiPropertiesTableTextDict["x1, x2"])
self.roiPropertiesTableWidget.setCellWidget(0, 1, self.roiPropertiesSpinBoxes["Left"])
self.roiPropertiesTableWidget.setCellWidget(0, 2, self.roiPropertiesSpinBoxes["Right"])
self.roiPropertiesTableWidget.setItem(1, 0, self.roiPropertiesTableTextDict["y1, y2"])
self.roiPropertiesTableWidget.setCellWidget(1, 1, self.roiPropertiesSpinBoxes["Top"])
self.roiPropertiesTableWidget.setCellWidget(1, 2, self.roiPropertiesSpinBoxes["Bottom"])
self.roiPropertiesTableWidget.setItem(2, 0, self.roiPropertiesTableTextDict["Size"])
self.roiPropertiesTableWidget.setCellWidget(2, 1, self.roiPropertiesLabels["Width"])
self.roiPropertiesTableWidget.setCellWidget(2, 2, self.roiPropertiesLabels["Height"])
self.roi_properties_widget.setup_roi_properties_spinboxes(self.spectrum_widget, self.presenter.do_adjust_roi)
self.roi_properties_widget.populate_roi_properties_table_text()

self.spectrum_widget.roi_changed.connect(self.set_roi_properties)

Expand Down Expand Up @@ -268,7 +345,7 @@ def on_visibility_change(self) -> None:
self.presenter.redraw_spectrum(ROI_RITS)
self.current_roi_name = ROI_RITS

for _, spinbox in self.roiPropertiesSpinBoxes.items():
for _, spinbox in self.roi_properties_widget.roiPropertiesSpinBoxes.items():
spinbox.setEnabled(True)
self.set_roi_properties()

Expand Down Expand Up @@ -386,7 +463,7 @@ def set_new_roi(self) -> None:
Set a new ROI on the image
"""
self.presenter.do_add_roi()
for _, spinbox in self.roiPropertiesSpinBoxes.items():
for _, spinbox in self.roi_properties_widget.roiPropertiesSpinBoxes.items():
if not spinbox.isEnabled():
spinbox.setEnabled(True)
self.set_roi_properties()
Expand Down Expand Up @@ -521,40 +598,24 @@ def tof_units_mode(self) -> str:
def set_roi_properties(self) -> None:
if self.presenter.export_mode == ExportMode.IMAGE_MODE:
self.current_roi_name = ROI_RITS
if self.current_roi_name not in self.presenter.view.spectrum_widget.roi_dict or not self.roiPropertiesSpinBoxes:
if (self.current_roi_name not in self.presenter.view.spectrum_widget.roi_dict
or not self.roi_properties_widget.roiPropertiesSpinBoxes):
return
current_roi = self.presenter.view.spectrum_widget.get_roi(self.current_roi_name)
self.roiPropertiesGroupBox.setTitle(f"Roi Properties: {self.current_roi_name}")
self.roi_properties_widget.roiPropertiesGroupBox.setTitle(f"Roi Properties: {self.current_roi_name}")
roi_iter_order = ["Left", "Top", "Right", "Bottom"]
for row, pos in enumerate(current_roi):
with QSignalBlocker(self.roiPropertiesSpinBoxes[roi_iter_order[row]]):
self.roiPropertiesSpinBoxes[roi_iter_order[row]].setValue(pos)
self.set_roi_spinbox_ranges()
with QSignalBlocker(self.roi_properties_widget.roiPropertiesSpinBoxes[roi_iter_order[row]]):
self.roi_properties_widget.roiPropertiesSpinBoxes[roi_iter_order[row]].setValue(pos)
self.presenter.redraw_spectrum(self.current_roi_name)
self.roiPropertiesLabels["Width"].setText(str(current_roi.width))
self.roiPropertiesLabels["Height"].setText(str(current_roi.height))
for spinbox in self.roiPropertiesSpinBoxes.values():
self.roi_properties_widget.roiPropertiesLabels["Width"].setText(str(current_roi.width))
self.roi_properties_widget.roiPropertiesLabels["Height"].setText(str(current_roi.height))
for spinbox in self.roi_properties_widget.roiPropertiesSpinBoxes.values():
spinbox.setEnabled(True)

def set_roi_spinbox_ranges(self) -> None:
self.roiPropertiesSpinBoxes["Left"].setMaximum(self.roiPropertiesSpinBoxes["Right"].value() - 1)
self.roiPropertiesSpinBoxes["Right"].setMinimum(self.roiPropertiesSpinBoxes["Left"].value() + 1)
self.roiPropertiesSpinBoxes["Top"].setMaximum(self.roiPropertiesSpinBoxes["Bottom"].value() - 1)
self.roiPropertiesSpinBoxes["Bottom"].setMinimum(self.roiPropertiesSpinBoxes["Top"].value() + 1)

def disable_roi_properties(self) -> None:
self.roiPropertiesGroupBox.setTitle("Roi Properties: None selected")
self.last_clicked_roi = "roi"
for _, spinbox in self.roiPropertiesSpinBoxes.items():
with QSignalBlocker(spinbox):
spinbox.setMinimum(0)
spinbox.setValue(0)
spinbox.setDisabled(True)
for _, label in self.roiPropertiesLabels.items():
label.setText("0")

def get_roi_properties_spinboxes(self) -> dict[str, QSpinBox]:
return self.roiPropertiesSpinBoxes
self.roi_properties_widget.disable_roi_properties()

def get_checked_menu_option(self) -> QAction:
return self.tof_mode_select_group.checkedAction()
Expand All @@ -565,17 +626,3 @@ def set_old_table_names(self) -> None:
self.old_table_names.remove('all')
if 'rits_roi' in self.old_table_names:
self.old_table_names.remove('rits_roi')

def setup_roi_properties_spinboxes(self) -> None:
assert self.spectrum_widget.image.image_data is not None
for prop in self.roi_table_properties:
spin_box = QSpinBox()
if prop == "Top" or prop == "Bottom":
spin_box.setMaximum(self.spectrum_widget.image.image_data.shape[0])
if prop == "Left" or prop == "Right":
spin_box.setMaximum(self.spectrum_widget.image.image_data.shape[1])
spin_box.valueChanged.connect(self.presenter.do_adjust_roi)
self.roiPropertiesSpinBoxes[prop] = spin_box
for prop in self.roi_table_properties_secondary:
label = QLabel()
self.roiPropertiesLabels[prop] = label

0 comments on commit 34d5abd

Please sign in to comment.