Skip to content

Commit

Permalink
2027 adjustable ROI (#2031)
Browse files Browse the repository at this point in the history
  • Loading branch information
JackEAllen authored Feb 20, 2024
2 parents 5f3802f + a34a968 commit e9df08c
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#2027: The Spectrum ROI details display in an adjustable table via spinboxes
51 changes: 44 additions & 7 deletions mantidimaging/gui/ui/spectrum_viewer.ui
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>921</width>
<height>739</height>
<width>905</width>
<height>752</height>
</rect>
</property>
<property name="windowTitle">
Expand Down Expand Up @@ -351,17 +351,54 @@
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<widget class="QGroupBox" name="roiPropertiesGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>200</height>
</size>
</property>
<property name="title">
<string>ROI Properties</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QTableWidget" name="roiPropertiesTableWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>200</height>
</size>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContentsOnFirstShow</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="PropertiesSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
<height>40</height>
</size>
</property>
</spacer>
Expand Down
7 changes: 7 additions & 0 deletions mantidimaging/gui/windows/spectrum_viewer/presenter.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def handle_sample_change(self, uuid: Optional['UUID']) -> None:
self.add_rits_roi()
self.view.set_normalise_error(self.model.normalise_issue())
self.show_new_sample()
self.view.on_visibility_change()

def handle_normalise_stack_change(self, normalise_uuid: Optional['UUID']) -> None:
if normalise_uuid == self.current_norm_stack_uuid:
Expand Down Expand Up @@ -118,6 +119,8 @@ def show_new_sample(self) -> None:
self.view.set_image(self.model.get_averaged_image())
self.view.spectrum_widget.spectrum_plot_widget.add_range(*self.model.tof_range)
self.view.auto_range_image()
if self.view.get_roi_properties_spinboxes():
self.view.set_roi_properties()

def handle_range_slide_moved(self, tof_range) -> None:
self.model.tof_range = tof_range
Expand All @@ -133,6 +136,10 @@ def handle_roi_moved(self, force_new_spectrums: bool = False) -> None:
self.model.set_roi(name, roi)
self.view.set_spectrum(name, self.model.get_spectrum(name, self.spectrum_mode))

def handle_roi_clicked(self, roi) -> None:
self.view.current_roi = roi.name
self.view.set_roi_properties()

def redraw_spectrum(self, name: str) -> None:
"""
Redraw the spectrum with the given name
Expand Down
24 changes: 23 additions & 1 deletion mantidimaging/gui/windows/spectrum_viewer/spectrum_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def __init__(self, name: str, sensible_roi: SensibleROI, *args, **kwargs):
self.addScaleHandle([0, 0], [1, 1])
self.addScaleHandle([0, 1], [1, 0])
self._selected_row = None
self.roi.setAcceptedMouseButtons(Qt.MouseButton.LeftButton)

self.menu = QMenu()
change_color_action = QAction("Change ROI Colour", self)
Expand Down Expand Up @@ -82,6 +83,10 @@ def colour(self, colour: tuple[int, int, int, int]) -> None:
def selected_row(self) -> Optional[int]:
return self._selected_row

def adjust_spec_roi(self, roi: SensibleROI) -> None:
self.setPos((roi.left, roi.top))
self.setSize((roi.width, roi.height))


class SpectrumWidget(QWidget):
"""
Expand All @@ -91,6 +96,13 @@ class SpectrumWidget(QWidget):
"""
image: MIMiniImageView
spectrum: PlotItem

range_control: LinearRegionItem
roi_dict: dict[Optional[str], ROI]
last_clicked_roi: str

range_changed = pyqtSignal(object)
roi_clicked = pyqtSignal(object)
roi_changed = pyqtSignal()
roiColorChangeRequested = pyqtSignal(str, tuple)

Expand Down Expand Up @@ -160,8 +172,9 @@ def set_roi_visibility_flags(self, name: str, visible: bool) -> None:
for handle in handles:
handle.setVisible(visible)
self.roi_dict[name].setVisible(visible)
self.roi_dict[name].setAcceptedMouseButtons(Qt.NoButton)
self.roi_dict[name].setAcceptedMouseButtons(Qt.MouseButton.LeftButton)
self.roi_dict[name].sigRegionChanged.connect(self.roi_changed.emit)
self.roi_dict[name].sigClicked.connect(self.roi_clicked.emit)

def set_roi_alpha(self, name: str, alpha: float) -> None:
"""
Expand Down Expand Up @@ -190,9 +203,18 @@ def add_roi(self, roi: SensibleROI, name: str) -> None:
self.roi_dict[name] = roi_object.roi
self.max_roi_size = roi_object.size()
self.roi_dict[name].sigRegionChanged.connect(self.roi_changed.emit)
self.roi_dict[name].sigClicked.connect(self.roi_clicked.emit)
self.image.vb.addItem(self.roi_dict[name])
self.roi_dict[name].hoverPen = mkPen(self.roi_dict[name].colour, width=3)

def adjust_roi(self, new_roi: SensibleROI, roi_name: str):
"""
Adjust the existing ROI with the given name.
@param new_roi: The new SpectrumROI to replace the existing SpectrumROI
@param roi_name: The name of the existing ROI.
"""
self.roi_dict[roi_name].adjust_spec_roi(new_roi)

def get_roi(self, roi_name: str) -> SensibleROI:
"""
Get the ROI with the given name. If no name is given, the default ROI is returned.
Expand Down
123 changes: 122 additions & 1 deletion mantidimaging/gui/windows/spectrum_viewer/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

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

from mantidimaging.core.utility import finder
from mantidimaging.gui.mvp_base import BaseMainWindowView
Expand All @@ -17,6 +18,7 @@
from mantidimaging.gui.widgets import RemovableRowTableView
from .spectrum_widget import SpectrumWidget
from mantidimaging.gui.windows.spectrum_viewer.roi_table_model import TableModel
from mantidimaging.core.utility.sensible_roi import SensibleROI

import numpy as np

Expand All @@ -42,6 +44,11 @@ class SpectrumViewerWindowView(BaseMainWindowView):
bin_size_spinBox: QSpinBox
bin_step_spinBox: QSpinBox

roiPropertiesTableWidget: QTableWidget
roiPropertiesGroupBox: QGroupBox

last_clicked_roi: str

spectrum_widget: SpectrumWidget

def __init__(self, main_window: 'MainWindowView'):
Expand All @@ -55,6 +62,8 @@ def __init__(self, main_window: 'MainWindowView'):
self.selected_row: int = 0
self.current_roi: str = ""
self.selected_row_data: Optional[list] = None
self.roiPropertiesSpinBoxes: dict[str, QSpinBox] = {}
self.roiPropertiesLabels: dict[str, QLabel] = {}

self.presenter = SpectrumViewerWindowPresenter(self, main_window)

Expand All @@ -64,6 +73,8 @@ def __init__(self, main_window: 'MainWindowView'):
self.imageLayout.addWidget(self.spectrum_widget)

self.spectrum.range_changed.connect(self.presenter.handle_range_slide_moved)

self.spectrum_widget.roi_clicked.connect(self.presenter.handle_roi_clicked)
self.spectrum_widget.roi_changed.connect(self.presenter.handle_roi_moved)
self.spectrum_widget.roiColorChangeRequested.connect(self.presenter.change_roi_colour)

Expand Down Expand Up @@ -98,7 +109,53 @@ def __init__(self, main_window: 'MainWindowView'):
self.tableView.setSelectionMode(QAbstractItemView.SingleSelection)
self.tableView.setAlternatingRowColors(True)

# 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)

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.adjust_roi)
self.roiPropertiesSpinBoxes[prop] = spin_box
for prop in self.roi_table_properties_secondary:
label = QLabel()
self.roiPropertiesLabels[prop] = label

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.spectrum_widget.roi_changed.connect(self.set_roi_properties)

_ = self.roi_table_model # Initialise model
self.current_roi = self.last_clicked_roi = self.roi_table_model.roi_names()[0]
self.set_roi_properties()

def on_row_change(item, _) -> None:
"""
Expand All @@ -110,6 +167,7 @@ def on_row_change(item, _) -> None:
selected_row_data = self.roi_table_model.row_data(item.row())
self.selected_row = item.row()
self.current_roi = selected_row_data[0]
self.set_roi_properties()

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

Expand Down Expand Up @@ -156,6 +214,11 @@ def on_visibility_change(self) -> None:
When the visibility of an ROI is changed, update the visibility of the ROI in the spectrum widget
"""
if self.presenter.export_mode == ExportMode.ROI_MODE:
self.current_roi = self.last_clicked_roi
if self.roi_table_model.rowCount() == 0:
self.disable_roi_properties()
if not self.roi_table_model.rowCount() == 0:
self.set_roi_properties()
for roi_name, _, roi_visible in self.roi_table_model:
if roi_visible is False:
self.set_roi_alpha(0, roi_name)
Expand All @@ -169,6 +232,12 @@ def on_visibility_change(self) -> None:
if self.presenter.export_mode == ExportMode.IMAGE_MODE:
self.set_roi_alpha(255, ROI_RITS)
self.presenter.redraw_spectrum(ROI_RITS)
if self.current_roi != ROI_RITS:
self.last_clicked_roi = self.current_roi
self.current_roi = ROI_RITS
for _, spinbox in self.roiPropertiesSpinBoxes.items():
spinbox.setEnabled(True)
self.set_roi_properties()
else:
self.set_roi_alpha(0, ROI_RITS)

Expand Down Expand Up @@ -266,6 +335,10 @@ def set_new_roi(self) -> None:
Set a new ROI on the image
"""
self.presenter.do_add_roi()
for _, spinbox in self.roiPropertiesSpinBoxes.items():
if not spinbox.isEnabled():
spinbox.setEnabled(True)
self.set_roi_properties()

def update_roi_color_in_table(self, roi_name: str, new_color: tuple):
"""
Expand Down Expand Up @@ -339,6 +412,7 @@ def remove_roi(self) -> None:

if self.roi_table_model.rowCount() == 0:
self.removeBtn.setEnabled(False)
self.disable_roi_properties()

def clear_all_rois(self) -> None:
"""
Expand All @@ -348,6 +422,7 @@ def clear_all_rois(self) -> None:
self.spectrum_widget.spectrum_data_dict = {}
self.spectrum_widget.spectrum.clearPlots()
self.removeBtn.setEnabled(False)
self.disable_roi_properties()

@property
def transmission_error_mode(self) -> str:
Expand All @@ -371,3 +446,49 @@ def set_binning_visibility(self) -> None:
self.bin_size_spinBox.setHidden(hide_binning)
self.bin_step_label.setHidden(hide_binning)
self.bin_step_spinBox.setHidden(hide_binning)

def set_roi_properties(self) -> None:
if self.presenter.export_mode == ExportMode.IMAGE_MODE:
self.current_roi = ROI_RITS
if self.current_roi not in self.presenter.model.get_list_of_roi_names() or not self.roiPropertiesSpinBoxes:
return
else:
current_roi = self.presenter.model.get_roi(self.current_roi)
self.roiPropertiesGroupBox.setTitle(f"Roi Properties: {self.current_roi}")
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()
self.presenter.redraw_spectrum(self.current_roi)
self.roiPropertiesLabels["Width"].setText(str(current_roi.width))
self.roiPropertiesLabels["Height"].setText(str(current_roi.height))
for spinbox in self.roiPropertiesSpinBoxes.values():
spinbox.setEnabled(True)

def adjust_roi(self) -> None:
roi_iter_order = ["Left", "Top", "Right", "Bottom"]
new_points = [self.roiPropertiesSpinBoxes[prop].value() for prop in roi_iter_order]
new_roi = SensibleROI().from_list(new_points)
self.presenter.model.set_roi(self.current_roi, new_roi)
self.spectrum_widget.adjust_roi(new_roi, self.current_roi)

def set_roi_spinbox_ranges(self):
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):
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):
return self.roiPropertiesSpinBoxes

0 comments on commit e9df08c

Please sign in to comment.