From eafbd61e8a6afb4f64aa6f442acdc690e4a26a15 Mon Sep 17 00:00:00 2001 From: ashmeigh Date: Tue, 16 Jul 2024 15:42:34 +0100 Subject: [PATCH 1/6] added type annotations to gui section of mantidimaging codesbase Signed-off-by: ashmeigh --- mantidimaging/gui/dialogs/async_task/model.py | 2 +- .../gui/dialogs/async_task/presenter.py | 16 +++++----- mantidimaging/gui/dialogs/async_task/view.py | 6 ++-- .../gui/dialogs/cor_inspection/model.py | 24 ++++++++------- .../gui/dialogs/cor_inspection/presenter.py | 10 +++---- .../cor_inspection/recon_slice_view.py | 2 +- .../gui/dialogs/cor_inspection/view.py | 8 ++--- mantidimaging/gui/mvp_base/presenter.py | 6 ++-- mantidimaging/gui/mvp_base/view.py | 4 +-- mantidimaging/gui/utility/qt_helpers.py | 14 ++++----- .../bad_data_overlay/bad_data_overlay.py | 22 +++++++------- .../dataset_selector_dialog.py | 2 +- .../gui/widgets/indicator_icon/view.py | 4 +-- .../gui/widgets/mi_image_view/presenter.py | 4 +-- .../gui/widgets/mi_image_view/view.py | 30 ++++++++++--------- .../gui/widgets/mi_mini_image_view/view.py | 28 ++++++++--------- .../gui/widgets/palette_changer/presenter.py | 16 +++++----- .../gui/widgets/removable_row_table_view.py | 4 +-- 18 files changed, 103 insertions(+), 99 deletions(-) diff --git a/mantidimaging/gui/dialogs/async_task/model.py b/mantidimaging/gui/dialogs/async_task/model.py index 724bfee650d..cf74f003f37 100644 --- a/mantidimaging/gui/dialogs/async_task/model.py +++ b/mantidimaging/gui/dialogs/async_task/model.py @@ -24,7 +24,7 @@ def __init__(self) -> None: self.on_complete_function: Callable | None = None self.tracker: set | None = None - def set_tracker(self, tracker: set): + def set_tracker(self, tracker: set) -> None: self.tracker = tracker self.tracker.add(self) diff --git a/mantidimaging/gui/dialogs/async_task/presenter.py b/mantidimaging/gui/dialogs/async_task/presenter.py index 822bd1f900c..c00fa56be64 100644 --- a/mantidimaging/gui/dialogs/async_task/presenter.py +++ b/mantidimaging/gui/dialogs/async_task/presenter.py @@ -30,7 +30,7 @@ def __init__(self, view): self.model = AsyncTaskDialogModel() self.model.task_done.connect(self.view.handle_completion) - def notify(self, signal): + def notify(self, signal) -> None: try: if signal == Notification.START: self.do_start_processing() @@ -39,19 +39,19 @@ def notify(self, signal): self.show_error(e, traceback.format_exc()) getLogger(__name__).exception("Notification handler failed") - def set_task(self, f: Callable): + def set_task(self, f: Callable) -> None: self.model.task.task_function = f - def set_parameters(self, **kwargs): + def set_parameters(self, **kwargs) -> None: self.model.task.kwargs = kwargs - def set_on_complete(self, f: Callable): + def set_on_complete(self, f: Callable) -> None: self.model.on_complete_function = f - def set_tracker(self, tracker: set): + def set_tracker(self, tracker: set) -> None: self.model.set_tracker(tracker) - def do_start_processing(self): + def do_start_processing(self) -> None: """ Starts async task execution and shows GUI. """ @@ -59,9 +59,9 @@ def do_start_processing(self): self.view.show_delayed(1000) @property - def task_is_running(self): + def task_is_running(self) -> None: return self.model.task_is_running - def progress_update(self): + def progress_update(self) -> None: msg = self.progress.last_status_message() self.progress_updated.emit(self.progress.completion(), msg if msg is not None else '') diff --git a/mantidimaging/gui/dialogs/async_task/view.py b/mantidimaging/gui/dialogs/async_task/view.py index a9b79e3df87..d3277246114 100644 --- a/mantidimaging/gui/dialogs/async_task/view.py +++ b/mantidimaging/gui/dialogs/async_task/view.py @@ -33,7 +33,7 @@ def presenter(self) -> AsyncTaskDialogPresenter: raise RuntimeError("Presenter accessed after handle_completion") return self._presenter - def handle_completion(self, successful: bool): + def handle_completion(self, successful: bool) -> None: """ Updates the UI after the task has been completed. @@ -61,11 +61,11 @@ def set_progress(self, progress: float, message: str): # Update progress bar self.progressBar.setValue(int(progress * 1000)) - def show_delayed(self, timeout): + def show_delayed(self, timeout) -> None: self.show_timer.singleShot(timeout, self.show_from_timer) self.show_timer.start() - def show_from_timer(self): + def show_from_timer(self) -> None: # Might not run until after handle_completion if self._presenter is not None and self.presenter.task_is_running: self.show() diff --git a/mantidimaging/gui/dialogs/cor_inspection/model.py b/mantidimaging/gui/dialogs/cor_inspection/model.py index 21d0fe60346..85320645bde 100644 --- a/mantidimaging/gui/dialogs/cor_inspection/model.py +++ b/mantidimaging/gui/dialogs/cor_inspection/model.py @@ -4,6 +4,8 @@ from dataclasses import replace from logging import getLogger +import numpy as np + from mantidimaging.core.data import ImageStack from mantidimaging.core.reconstruct import get_reconstructor_for from mantidimaging.core.utility.data_containers import ScalarCoR, ReconstructionParameters @@ -26,7 +28,7 @@ def __init__(self, images: ImageStack, slice_idx: int, initial_cor: ScalarCoR, # Initial parameters if iters_mode: self.centre_value: int | float = INIT_ITERS_CENTRE_VALUE - self.step = INIT_ITERS_STEP + self.step: float = INIT_ITERS_STEP self.initial_cor = initial_cor self._recon_preview = self._recon_iters_preview self._divide_step = self._divide_iters_step @@ -41,13 +43,13 @@ def __init__(self, images: ImageStack, slice_idx: int, initial_cor: ScalarCoR, self.recon_params = recon_params self.reconstructor = get_reconstructor_for(recon_params.algorithm) - def _divide_iters_step(self): + def _divide_iters_step(self) -> None: self.step = self.step // 2 - def _divide_cor_step(self): + def _divide_cor_step(self) -> None: self.step /= 2 - def adjust(self, image): + def adjust(self, image: ImageType) -> None: """ Adjusts the rotation centre/number of iterations and step after an image is selected as the optimal of an iteration. @@ -59,7 +61,7 @@ def adjust(self, image): elif image == ImageType.CURRENT: self._divide_step() - def cor(self, image): + def cor(self, image: ImageType) -> float: """ Gets the rotation centre for a given image in the current iteration. """ @@ -70,7 +72,7 @@ def cor(self, image): elif image == ImageType.MORE: return min(self.cor_extents[1], self.centre_value + self.step) - def iterations(self, image): + def iterations(self, image: ImageType) -> float: if image == ImageType.LESS: return max(1, self.centre_value - self.step) elif image == ImageType.CURRENT: @@ -78,18 +80,18 @@ def iterations(self, image): elif image == ImageType.MORE: return self.centre_value + self.step - def _recon_cor_preview(self, image): + def _recon_cor_preview(self, image: ImageType) -> np.ndarray: cor = ScalarCoR(self.cor(image)) return self.reconstructor.single_sino(self.sino, cor, self.proj_angles, self.recon_params) - def _recon_iters_preview(self, image): + def _recon_iters_preview(self, image: ImageType) -> np.ndarray: iters = self.iterations(image) - new_params = replace(self.recon_params, num_iter=iters) + new_params = replace(self.recon_params, num_iter=int(iters)) return self.reconstructor.single_sino(self.sino, self.initial_cor, self.proj_angles, new_params) - def recon_preview(self, image): + def recon_preview(self, image: ImageType) -> np.ndarray: return self._recon_preview(image) @property - def cor_extents(self): + def cor_extents(self) -> tuple[int, int]: return 0, self.sino.shape[1] - 1 diff --git a/mantidimaging/gui/dialogs/cor_inspection/presenter.py b/mantidimaging/gui/dialogs/cor_inspection/presenter.py index 47e83b68c19..94994db0acf 100644 --- a/mantidimaging/gui/dialogs/cor_inspection/presenter.py +++ b/mantidimaging/gui/dialogs/cor_inspection/presenter.py @@ -46,7 +46,7 @@ def __init__(self, view, images: ImageStack, slice_index: int, initial_cor: Scal self.model = CORInspectionDialogModel(images, slice_index, initial_cor, recon_params, iters_mode) - def notify(self, signal): + def notify(self, signal) -> None: try: if signal == Notification.IMAGE_CLICKED_LESS: self.on_select_image(ImageType.LESS) @@ -65,11 +65,11 @@ def notify(self, signal): self.show_error(e, traceback.format_exc()) getLogger(__name__).exception("Notification handler failed") - def on_load(self): + def on_load(self) -> None: self.view.set_maximum_cor(self.model.cor_extents[1]) self.notify(Notification.FULL_UPDATE) - def on_select_image(self, img): + def on_select_image(self, img) -> None: LOG.debug(f'Image selected: {img}') # Adjust COR/iterations step @@ -108,5 +108,5 @@ def optimal_rotation_centre(self) -> ScalarCoR: return ScalarCoR(self.model.centre_value) @property - def optimal_iterations(self): - return self.model.centre_value + def optimal_iterations(self) -> int: + return int(self.model.centre_value) diff --git a/mantidimaging/gui/dialogs/cor_inspection/recon_slice_view.py b/mantidimaging/gui/dialogs/cor_inspection/recon_slice_view.py index d67ec34e2a3..bcd45b5e452 100644 --- a/mantidimaging/gui/dialogs/cor_inspection/recon_slice_view.py +++ b/mantidimaging/gui/dialogs/cor_inspection/recon_slice_view.py @@ -47,7 +47,7 @@ def __init__(self, parent: CORInspectionDialogView): # Work around for https://github.com/mantidproject/mantidimaging/issues/565 self.scene().contextMenu = [item for item in self.scene().contextMenu if "export" not in item.text().lower()] - def set_image(self, image_type: ImageType, recon_data: np.ndarray, title: str): + def set_image(self, image_type: ImageType, recon_data: np.ndarray, title: str) -> None: sumsq = np.sum(recon_data**2) if image_type == ImageType.LESS: self.imageview_less.clear() diff --git a/mantidimaging/gui/dialogs/cor_inspection/view.py b/mantidimaging/gui/dialogs/cor_inspection/view.py index 0ddffb17542..5ce718faf80 100644 --- a/mantidimaging/gui/dialogs/cor_inspection/view.py +++ b/mantidimaging/gui/dialogs/cor_inspection/view.py @@ -54,21 +54,21 @@ def __init__(self, parent, images: ImageStack, slice_index: int, initial_cor: Sc self.presenter.do_refresh() - def set_image(self, image_type: ImageType, recon_data: np.ndarray, title: str): + def set_image(self, image_type: ImageType, recon_data: np.ndarray, title: str) -> None: self.image_canvas.set_image(image_type, recon_data, title) - def set_maximum_cor(self, cor): + def set_maximum_cor(self, cor) -> None: """ Set the maximum valid rotation centre. """ self.stepCOR.setMaximum(cor) @property - def step_size(self): + def step_size(self) -> None: return self.spin_box.value() @step_size.setter - def step_size(self, value): + def step_size(self, value) -> None: with BlockQtSignals([self.spin_box]): self.spin_box.setValue(value) diff --git a/mantidimaging/gui/mvp_base/presenter.py b/mantidimaging/gui/mvp_base/presenter.py index 1c22774dae9..de95a0764e7 100644 --- a/mantidimaging/gui/mvp_base/presenter.py +++ b/mantidimaging/gui/mvp_base/presenter.py @@ -15,14 +15,14 @@ class BasePresenter: def __init__(self, view: BaseMainWindowView): self.view = view - def notify(self, signal): + def notify(self, signal) -> None: raise NotImplementedError("Presenter must implement the notify() method") - def show_error(self, error, traceback): + def show_error(self, error, traceback) -> None: getLogger(__name__).exception(f'Presenter error: {error}\n{traceback}') if hasattr(self.view, 'show_error_dialog'): # If the view knows how to handle an error message self.view.show_error_dialog(str(error)) - def show_information(self, info): + def show_information(self, info) -> None: self.view.show_info_dialog(info) diff --git a/mantidimaging/gui/mvp_base/view.py b/mantidimaging/gui/mvp_base/view.py index 4811ab3a2d9..738c5bec55d 100644 --- a/mantidimaging/gui/mvp_base/view.py +++ b/mantidimaging/gui/mvp_base/view.py @@ -31,7 +31,7 @@ def __init__(self, parent, ui_file=None): if ui_file is not None: compile_ui(ui_file, self) - def closeEvent(self, e): + def closeEvent(self, e) -> None: LOG.debug('UI window closed') self.cleanup() super().closeEvent(e) @@ -76,7 +76,7 @@ def __init__(self, parent, ui_file=None): if ui_file is not None: compile_ui(ui_file, self) - def show_error_dialog(self, msg=""): + def show_error_dialog(self, msg="") -> None: """ Shows an error message. diff --git a/mantidimaging/gui/utility/qt_helpers.py b/mantidimaging/gui/utility/qt_helpers.py index 83a95634ecf..ea8859784ce 100644 --- a/mantidimaging/gui/utility/qt_helpers.py +++ b/mantidimaging/gui/utility/qt_helpers.py @@ -46,12 +46,12 @@ def __exit__(self, *args): obj.blockSignals(prev) -def compile_ui(ui_file, qt_obj=None): +def compile_ui(ui_file: str, qt_obj=None) -> QWidget: base_path = finder.ROOT_PATH return uic.loadUi(os.path.join(base_path, ui_file), qt_obj) -def select_directory(field, caption): +def select_directory(field: QWidget, caption: str) -> None: assert isinstance(field, QLineEdit), (f"The passed object is of type {type(field)}. This function only works with " "QLineEdit") @@ -59,7 +59,7 @@ def select_directory(field, caption): field.setText(QFileDialog.getExistingDirectory(caption=caption)) -def get_value_from_qwidget(widget: QWidget): +def get_value_from_qwidget(widget: QWidget) -> Any: if isinstance(widget, QLineEdit): return widget.text() elif isinstance(widget, QSpinBox) or isinstance(widget, QDoubleSpinBox): @@ -82,7 +82,7 @@ class Type(IntEnum): BUTTON = auto() -def on_change_and_disable(widget: QWidget, on_change: Callable): +def on_change_and_disable(widget: QWidget, on_change: Callable) -> None: """ Makes sure the widget is disabled while running the on_update method. This is required for spin boxes that continue increasing when generating a preview image is computationally intensive. @@ -129,7 +129,7 @@ def add_property_to_form(label: str, if isinstance(default_value, str) and default_value.lower() == "none": default_value = None - def set_spin_box(box, cast_func): + def set_spin_box(box: QSpinBox | QDoubleSpinBox, cast_func: Callable[[str], Any]) -> None: """ Helper function to set default options on a spin box. """ @@ -237,7 +237,7 @@ def set_spin_box(box, cast_func): return left_widget, right_widget -def delete_all_widgets_from_layout(lo): +def delete_all_widgets_from_layout(lo: QLayout) -> None: """ Removes and deletes all child widgets form a layout. @@ -257,7 +257,7 @@ def delete_all_widgets_from_layout(lo): item.widget().setParent(None) -def populate_menu(menu: QMenu, actions_list: list[QAction]): +def populate_menu(menu: QMenu, actions_list: list[QAction]) -> None: for (menu_text, func) in actions_list: if func is None: menu.addSeparator() diff --git a/mantidimaging/gui/widgets/bad_data_overlay/bad_data_overlay.py b/mantidimaging/gui/widgets/bad_data_overlay/bad_data_overlay.py index df4c2585408..8c784b11db6 100644 --- a/mantidimaging/gui/widgets/bad_data_overlay/bad_data_overlay.py +++ b/mantidimaging/gui/widgets/bad_data_overlay/bad_data_overlay.py @@ -27,7 +27,7 @@ def __init__(self, check_function, indicator, overlay, color): self.setup_overlay() self.indicator.connected_overlay = self.overlay - def do_check(self, data): + def do_check(self, data) -> None: bad_data = self.check_function(data) any_bad = bad_data.any() # cast any_bad to python bool to prevent DeprecationWarning @@ -35,7 +35,7 @@ def do_check(self, data): self.overlay.setImage(bad_data, autoLevels=False) - def setup_overlay(self): + def setup_overlay(self) -> None: color = np.array([[0, 0, 0, 0], self.color], dtype=np.ubyte) color_map = ColorMap([0, 1], color) self.overlay.setVisible(False) @@ -44,12 +44,12 @@ def setup_overlay(self): self.overlay.setZValue(11) self.overlay.setLevels([0, 1]) - def remove(self): + def remove(self) -> None: self.overlay.getViewBox().removeItem(self.indicator) self.overlay.getViewBox().removeItem(self.overlay) self.overlay.clear() - def clear(self): + def clear(self) -> None: self.indicator.setVisible(False) self.overlay.clear() @@ -76,13 +76,13 @@ def image_item(self) -> ImageItem: def viewbox(self) -> ViewBox: raise NotImplementedError - def enable_nan_check(self, enable: bool = True, actions: list[tuple[str, Callable]] | None = None): + def enable_nan_check(self, enable: bool = True, actions: list[tuple[str, Callable]] | None = None) -> None: if enable: self.enable_check("nan", OVERLAY_COLOUR_NAN, 0, np.isnan, "Invalid values: Not a number", actions) else: self.disable_check("nan") - def enable_nonpositive_check(self, enable: bool = True, actions: list[tuple[str, Callable]] | None = None): + def enable_nonpositive_check(self, enable: bool = True, actions: list[tuple[str, Callable]] | None = None) -> None: if enable: def is_non_positive(data): @@ -93,7 +93,7 @@ def is_non_positive(data): self.disable_check("nonpos") def enable_check(self, name: str, color: list[int], pos: int, func: Callable, message: str, - actions: list[tuple[str, Callable]] | None): + actions: list[tuple[str, Callable]] | None) -> None: if name not in self.enabled_checks: icon_path = finder.ROOT_PATH + "/gui/ui/images/exclamation-triangle-red.png" indicator = IndicatorIconView(self.viewbox, icon_path, pos, color, message) @@ -106,7 +106,7 @@ def enable_check(self, name: str, color: list[int], pos: int, func: Callable, me self.enabled_checks[name] = check self.check_for_bad_data() - def disable_check(self, name: str): + def disable_check(self, name: str) -> None: if name in self.enabled_checks: self.enabled_checks[name].remove() self.enabled_checks.pop(name, None) @@ -115,17 +115,17 @@ def _get_current_slice(self) -> np.ndarray | None: data = self.image_item.image return data - def check_for_bad_data(self): + def check_for_bad_data(self) -> None: current_slice = self._get_current_slice() if current_slice is not None: for test in self.enabled_checks.values(): test.do_check(current_slice) - def clear_overlays(self): + def clear_overlays(self) -> None: for check in self.enabled_checks.values(): check.clear() - def enable_message(self, enable: bool = True): + def enable_message(self, enable: bool = True) -> None: if enable: icon_path = finder.ROOT_PATH + "/gui/ui/images/exclamation-triangle-red.png" self.message_indicator = IndicatorIconView(self.viewbox, icon_path, 0, OVERLAY_COLOUR_MESSAGE, "") diff --git a/mantidimaging/gui/widgets/dataset_selector_dialog/dataset_selector_dialog.py b/mantidimaging/gui/widgets/dataset_selector_dialog/dataset_selector_dialog.py index 597cff58712..9a0c29b77c0 100644 --- a/mantidimaging/gui/widgets/dataset_selector_dialog/dataset_selector_dialog.py +++ b/mantidimaging/gui/widgets/dataset_selector_dialog/dataset_selector_dialog.py @@ -54,6 +54,6 @@ def __init__(self, self.button_layout.addWidget(self.ok_button) self.vertical_layout.addLayout(self.button_layout) - def on_ok_clicked(self): + def on_ok_clicked(self) -> None: self.selected_id = self.dataset_selector_widget.current() self.done(QDialog.DialogCode.Accepted) diff --git a/mantidimaging/gui/widgets/indicator_icon/view.py b/mantidimaging/gui/widgets/indicator_icon/view.py index ee3404710c1..406e1923876 100644 --- a/mantidimaging/gui/widgets/indicator_icon/view.py +++ b/mantidimaging/gui/widgets/indicator_icon/view.py @@ -112,11 +112,11 @@ def mouseClickEvent(self, event) -> None: qm.exec(event.screenPos().toQPoint()) - def set_message(self, message): + def set_message(self, message) -> None: self.label.setText(message) self.position_icon() - def setVisible(self, visible: bool): + def setVisible(self, visible: bool) -> None: if not visible: self.label.setVisible(False) super().setVisible(visible) diff --git a/mantidimaging/gui/widgets/mi_image_view/presenter.py b/mantidimaging/gui/widgets/mi_image_view/presenter.py index 7b4ac0e06af..8c565a48bdc 100644 --- a/mantidimaging/gui/widgets/mi_image_view/presenter.py +++ b/mantidimaging/gui/widgets/mi_image_view/presenter.py @@ -10,7 +10,7 @@ class MIImagePresenter: @staticmethod - def get_roi(image, roi_pos, roi_size): + def get_roi(image: np.ndarray, roi_pos, roi_size) -> tuple: """ Get a ROI based on the current real ROI selected. Clips it to be in-bounds of the shape of the image, to prevent issues when passed to filters @@ -47,7 +47,7 @@ def get_roi(image, roi_pos, roi_size): return roi_pos, roi_size @staticmethod - def get_nearest_timeline_tick(x_pos_clicked: float, x_axis, view_range: list[int]): + def get_nearest_timeline_tick(x_pos_clicked: float, x_axis, view_range: list[int]) -> int: """ Calculate the closes point to the clicked position on the histogram's timeline. diff --git a/mantidimaging/gui/widgets/mi_image_view/view.py b/mantidimaging/gui/widgets/mi_image_view/view.py index 49b9145ca70..0dc1e1805c0 100644 --- a/mantidimaging/gui/widgets/mi_image_view/view.py +++ b/mantidimaging/gui/widgets/mi_image_view/view.py @@ -144,7 +144,7 @@ def angles(self) -> ProjectionAngles | None: return self._angles @angles.setter - def angles(self, angles: ProjectionAngles | None): + def angles(self, angles: ProjectionAngles | None) -> None: self._angles = angles self._update_message(self._last_mouse_hover_location) @@ -162,7 +162,7 @@ def setImage(self, image: np.ndarray, *args, **kwargs): self.set_roi(self.default_roi()) self.angles = None - def toggle_jumping_frame(self, images_to_jump_by=None): + def toggle_jumping_frame(self, images_to_jump_by=None) -> None: if not self.shifting_through_images and images_to_jump_by is not None: self.shifting_through_images = True else: @@ -172,7 +172,7 @@ def toggle_jumping_frame(self, images_to_jump_by=None): sleep(0.02) QApplication.processEvents() - def _refresh_message(self): + def _refresh_message(self) -> None: try: self._update_message(self._last_mouse_hover_location) except IndexError: @@ -180,7 +180,7 @@ def _refresh_message(self): # is outside of the new bounds. To prevent this happening again just reset back to 0, 0 self._last_mouse_hover_location = CloseEnoughPoint([0, 0]) - def roiChanged(self): + def roiChanged(self) -> None: """ Re-implements the roiChanged function to expect only 3D data, and uses a faster mean calculation on the ROI view of the data, @@ -223,14 +223,14 @@ def _update_roi_region_avg(self) -> SensibleROI | None: else: return None - def roiClicked(self): + def roiClicked(self) -> None: # When ROI area is hidden with the button, clear the message if not self.ui.roiBtn.isChecked() and hasattr(self, "_last_mouse_hover_location"): self.roiString = None self._refresh_message() super().roiClicked() - def extend_roi_plot_mouse_press_handler(self): + def extend_roi_plot_mouse_press_handler(self) -> None: original_handler = self.ui.roiPlot.mousePressEvent def extended_handler(ev): @@ -245,14 +245,14 @@ def get_roi(self) -> tuple[CloseEnoughPoint, CloseEnoughPoint]: roi_pos=CloseEnoughPoint(self.roi.pos()), roi_size=CloseEnoughPoint(self.roi.size())) - def image_hover_event(self, event: HoverEvent): + def image_hover_event(self, event: HoverEvent) -> None: if event.exit: return pt = CloseEnoughPoint(event.pos()) self._last_mouse_hover_location = pt self._update_message(pt) - def _update_message(self, pt): + def _update_message(self, pt) -> None: # event holds the coordinates in column-major coordinate # while the data is in row-major coordinate, hence why # the data access below is [y, x] @@ -274,23 +274,25 @@ def _update_message(self, pt): msg += f" | roi = {self.roiString}" self.details.setText(msg) - def set_timeline_to_tick_nearest(self, x_pos_clicked): + def set_timeline_to_tick_nearest(self, x_pos_clicked) -> None: x_axis = self.getRoiPlot().getAxis('bottom') view_range = self.getRoiPlot().viewRange()[0] nearest = self.presenter.get_nearest_timeline_tick(x_pos_clicked, x_axis, view_range) self.timeLine.setValue(nearest) - def set_selected_image(self, image_index: int): + def set_selected_image(self, image_index: int) -> None: self.timeLine.setValue(image_index) - def set_log_scale(self): + def set_log_scale(self) -> None: set_histogram_log_scale(self.getHistogramWidget().item) - def close(self): + def close(self) -> None: self.roi_changed_callback = None super().close() - def set_roi(self, coords: list[int]): + def set_roi(self, coords: list[int] | None) -> None: + if coords is None: + return roi = SensibleROI.from_list(coords) self.roi.setPos(roi.left, roi.top, update=False) # Keep default update=True for setSize otherwise the scale handle can become detached from the ROI box @@ -298,7 +300,7 @@ def set_roi(self, coords: list[int]): self.roiChanged() self._refresh_message() - def default_roi(self): + def default_roi(self) -> None | list[int]: # Recommend an ROI that covers the top left quadrant # However set min dimensions to avoid an ROI that is so small it's difficult to work with min_size = 20 diff --git a/mantidimaging/gui/widgets/mi_mini_image_view/view.py b/mantidimaging/gui/widgets/mi_mini_image_view/view.py index 3e5da82d3fd..2d1225ba3c6 100644 --- a/mantidimaging/gui/widgets/mi_mini_image_view/view.py +++ b/mantidimaging/gui/widgets/mi_mini_image_view/view.py @@ -71,11 +71,11 @@ def histogram(self) -> HistogramLUTItem: return self.hist @property - def histogram_region(self): + def histogram_region(self) -> tuple[int | list[int], int | list[int]]: return self.hist.region.getRegion() @histogram_region.setter - def histogram_region(self, new_region: tuple[int | list[int], int | list[int]]): + def histogram_region(self, new_region: tuple[int | list[int], int | list[int]]) -> None: self.hist.region.setRegion(new_region) @property @@ -105,7 +105,7 @@ def cleanup(self) -> None: self.clear() self.im.hoverEvent = None - def setImage(self, image: np.ndarray, *args, **kwargs): + def setImage(self, image: np.ndarray, *args, **kwargs) -> None: if self.bright_levels is not None: self.levels = [np.percentile(image, x) for x in self.bright_levels] self.im.setImage(image, *args, **kwargs, levels=self.levels) @@ -115,7 +115,7 @@ def setImage(self, image: np.ndarray, *args, **kwargs): self.set_auto_color_enabled(image is not None) @staticmethod - def set_siblings(sibling_views: list[MIMiniImageView], axis=False, hist=False): + def set_siblings(sibling_views: list[MIMiniImageView], axis=False, hist=False) -> None: for view1 in sibling_views: for view2 in sibling_views: if view2 is not view1: @@ -124,13 +124,13 @@ def set_siblings(sibling_views: list[MIMiniImageView], axis=False, hist=False): if hist: view1.add_hist_sibling(view2) - def add_axis_sibling(self, sibling: MIMiniImageView): + def add_axis_sibling(self, sibling: MIMiniImageView) -> None: self.axis_siblings.add(sibling) - def add_hist_sibling(self, sibling: MIMiniImageView): + def add_hist_sibling(self, sibling: MIMiniImageView) -> None: self.histogram_siblings.add(sibling) - def mouse_over(self, ev): + def mouse_over(self, ev) -> None: # Ignore events triggered by leaving window or right clicking if ev.exit: return @@ -140,14 +140,14 @@ def mouse_over(self, ev): for img_view in self.axis_siblings: img_view.show_details(pos) - def show_details(self, pos): + def show_details(self, pos) -> None: image = self.im.image if image is not None and pos.y < image.shape[0] and pos.x < image.shape[1]: pixel_value = image[pos.y, pos.x] value_string = ("%.6f" % pixel_value)[:8] self.details.setText(f"x={pos.x}, y={pos.y}, value={value_string}") - def link_sibling_axis(self): + def link_sibling_axis(self) -> None: # Linking multiple viewboxes with locked aspect ratios causes # odd resizing behaviour. Use workaround from # https://github.com/pyqtgraph/pyqtgraph/issues/1348 @@ -157,19 +157,19 @@ def link_sibling_axis(self): view2.vb.linkView(ViewBox.YAxis, view1.vb) view2.vb.setAspectLocked(False) - def unlink_sibling_axis(self): + def unlink_sibling_axis(self) -> None: for img_view in chain([self], self.axis_siblings): img_view.vb.linkView(ViewBox.XAxis, None) img_view.vb.linkView(ViewBox.YAxis, None) img_view.vb.setAspectLocked(True) - def link_sibling_histogram(self): + def link_sibling_histogram(self) -> None: for view1, view2 in pairwise(chain([self], self.histogram_siblings)): view1.hist.vb.linkView(ViewBox.YAxis, view2.hist.vb) for img_view in chain([self], self.histogram_siblings): img_view.hist.sigLevelChangeFinished.connect(img_view.update_sibling_histograms) - def unlink_sibling_histogram(self): + def unlink_sibling_histogram(self) -> None: for img_view in chain([self], self.histogram_siblings): img_view.hist.vb.linkView(ViewBox.YAxis, None) try: @@ -178,11 +178,11 @@ def unlink_sibling_histogram(self): # This is expected if there are slots currently connected pass - def update_sibling_histograms(self): + def update_sibling_histograms(self) -> None: hist_range = self.hist.getLevels() for img_view in self.histogram_siblings: with BlockQtSignals(img_view.hist): img_view.hist.setLevels(*hist_range) - def set_brightness_percentiles(self, percent_low: int, percent_high: int): + def set_brightness_percentiles(self, percent_low: int, percent_high: int) -> None: self.bright_levels = [percent_low, percent_high] diff --git a/mantidimaging/gui/widgets/palette_changer/presenter.py b/mantidimaging/gui/widgets/palette_changer/presenter.py index e3f537b0f96..fabc707aeac 100644 --- a/mantidimaging/gui/widgets/palette_changer/presenter.py +++ b/mantidimaging/gui/widgets/palette_changer/presenter.py @@ -31,10 +31,10 @@ def __init__(self, view, other_hists: list[HistogramLUTItem], main_hist: Histogr else: self.flattened_image = self.rng.choice(image.flatten(), min(SAMPLE_SIZE, image.size)) - def notify(self, signal): + def notify(self, signal) -> None: pass - def change_colour_palette(self): + def change_colour_palette(self) -> None: """ Change the colour palette and add ticks based on the output of the Jenks or Otsu algorithms. """ @@ -48,7 +48,7 @@ def change_colour_palette(self): self._remove_old_ticks() self._update_ticks() - def _record_old_tick_points(self): + def _record_old_tick_points(self) -> None: """ Records the default tick points for the recon histogram that are inserted when a new colour map is loaded. This means they can be easily removed once the new ticks have been added to the histogram. This step is @@ -57,7 +57,7 @@ def _record_old_tick_points(self): """ self.old_ticks = list(self.main_hist.gradient.ticks.keys()) - def _insert_new_ticks(self, tick_points: list[float]): + def _insert_new_ticks(self, tick_points: list[float]) -> None: """ Adds new ticks to the recon histogram. """ @@ -66,7 +66,7 @@ def _insert_new_ticks(self, tick_points: list[float]): for i in range(n_tick_points): self.main_hist.gradient.addTick(tick_points[i], color=colours[i], finish=False) - def _change_colour_map(self): + def _change_colour_map(self) -> None: """ Changes the colour map of all three histograms. """ @@ -99,14 +99,14 @@ def _normalise_tick_values(self, breaks: list[float]) -> list[float]: breaks = [min_val] + breaks + [max_val] return [(break_x - min_val) / val_range for break_x in breaks] - def _remove_old_ticks(self): + def _remove_old_ticks(self) -> None: """ Remove the default recon histogram ticks from the image. """ for t in self.old_ticks: self.main_hist.gradient.removeTick(t, finish=False) - def _update_ticks(self): + def _update_ticks(self) -> None: """ Tell the recon histogram ticks to update at the end of a change. """ @@ -123,7 +123,7 @@ def _get_colours(self, num_ticks: int) -> list[float]: norms = np.linspace(0, 1, num_ticks) return [self.main_hist.gradient.getColor(norm) for norm in norms] - def _get_sample_pixels(self, image: np.ndarray, count: int, width: float = 0.9): + def _get_sample_pixels(self, image: np.ndarray, count: int, width: float = 0.9) -> np.ndarray: """ Sample from a circle of the image to avoid recon artefacts at edges """ diff --git a/mantidimaging/gui/widgets/removable_row_table_view.py b/mantidimaging/gui/widgets/removable_row_table_view.py index ccbb9218fd5..d5db2e8552b 100644 --- a/mantidimaging/gui/widgets/removable_row_table_view.py +++ b/mantidimaging/gui/widgets/removable_row_table_view.py @@ -8,14 +8,14 @@ class RemovableRowTableView(QTableView): - def keyPressEvent(self, e): + def keyPressEvent(self, e) -> None: super().keyPressEvent(e) # Handle deletion of a row from the table by pressing the [Delete] key if e.key() == Qt.Key.Key_Delete: self.removeSelectedRows() - def removeSelectedRows(self): + def removeSelectedRows(self) -> None: """ Removes all selected rows from the table. """ From 3d12a6c473fec58eaa347f6185775b862c8a21b4 Mon Sep 17 00:00:00 2001 From: ashmeigh Date: Tue, 16 Jul 2024 15:57:22 +0100 Subject: [PATCH 2/6] fixing error by remove type annotation Signed-off-by: ashmeigh --- mantidimaging/gui/dialogs/cor_inspection/presenter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mantidimaging/gui/dialogs/cor_inspection/presenter.py b/mantidimaging/gui/dialogs/cor_inspection/presenter.py index 94994db0acf..6e79db6c04c 100644 --- a/mantidimaging/gui/dialogs/cor_inspection/presenter.py +++ b/mantidimaging/gui/dialogs/cor_inspection/presenter.py @@ -108,5 +108,5 @@ def optimal_rotation_centre(self) -> ScalarCoR: return ScalarCoR(self.model.centre_value) @property - def optimal_iterations(self) -> int: - return int(self.model.centre_value) + def optimal_iterations(self): + return self.model.centre_value From b23d1e9c934aa922b07fc5087aeccb9e9c66aa6b Mon Sep 17 00:00:00 2001 From: ashmeigh <56345053+ashmeigh@users.noreply.github.com> Date: Tue, 16 Jul 2024 17:31:22 +0100 Subject: [PATCH 3/6] Update mantidimaging/gui/dialogs/cor_inspection/model.py Co-authored-by: MikeSullivan7 <30868085+MikeSullivan7@users.noreply.github.com> --- mantidimaging/gui/dialogs/cor_inspection/model.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mantidimaging/gui/dialogs/cor_inspection/model.py b/mantidimaging/gui/dialogs/cor_inspection/model.py index 85320645bde..f07bbaf8477 100644 --- a/mantidimaging/gui/dialogs/cor_inspection/model.py +++ b/mantidimaging/gui/dialogs/cor_inspection/model.py @@ -4,7 +4,10 @@ from dataclasses import replace from logging import getLogger -import numpy as np +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + import numpy as np from mantidimaging.core.data import ImageStack from mantidimaging.core.reconstruct import get_reconstructor_for From 68df5abfb24252a21af68f9ad295b7fbdba71907 Mon Sep 17 00:00:00 2001 From: ashmeigh <56345053+ashmeigh@users.noreply.github.com> Date: Tue, 16 Jul 2024 17:33:10 +0100 Subject: [PATCH 4/6] Update mantidimaging/gui/utility/qt_helpers.py Co-authored-by: MikeSullivan7 <30868085+MikeSullivan7@users.noreply.github.com> --- mantidimaging/gui/utility/qt_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mantidimaging/gui/utility/qt_helpers.py b/mantidimaging/gui/utility/qt_helpers.py index ea8859784ce..c879c037238 100644 --- a/mantidimaging/gui/utility/qt_helpers.py +++ b/mantidimaging/gui/utility/qt_helpers.py @@ -59,7 +59,7 @@ def select_directory(field: QWidget, caption: str) -> None: field.setText(QFileDialog.getExistingDirectory(caption=caption)) -def get_value_from_qwidget(widget: QWidget) -> Any: +def get_value_from_qwidget(widget: QWidget) -> str | float | bool | None: if isinstance(widget, QLineEdit): return widget.text() elif isinstance(widget, QSpinBox) or isinstance(widget, QDoubleSpinBox): From 422b0627798112b24ac338f61ac7d16b2e857c3d Mon Sep 17 00:00:00 2001 From: ashmeigh Date: Tue, 16 Jul 2024 17:55:19 +0100 Subject: [PATCH 5/6] Add return Signed-off-by: ashmeigh --- mantidimaging/gui/dialogs/cor_inspection/model.py | 4 +++- mantidimaging/gui/utility/qt_helpers.py | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mantidimaging/gui/dialogs/cor_inspection/model.py b/mantidimaging/gui/dialogs/cor_inspection/model.py index 85320645bde..9d4d165ae62 100644 --- a/mantidimaging/gui/dialogs/cor_inspection/model.py +++ b/mantidimaging/gui/dialogs/cor_inspection/model.py @@ -3,8 +3,10 @@ from __future__ import annotations from dataclasses import replace from logging import getLogger +from typing import TYPE_CHECKING -import numpy as np +if TYPE_CHECKING: + import numpy as np from mantidimaging.core.data import ImageStack from mantidimaging.core.reconstruct import get_reconstructor_for diff --git a/mantidimaging/gui/utility/qt_helpers.py b/mantidimaging/gui/utility/qt_helpers.py index ea8859784ce..7cb923ea876 100644 --- a/mantidimaging/gui/utility/qt_helpers.py +++ b/mantidimaging/gui/utility/qt_helpers.py @@ -59,13 +59,14 @@ def select_directory(field: QWidget, caption: str) -> None: field.setText(QFileDialog.getExistingDirectory(caption=caption)) -def get_value_from_qwidget(widget: QWidget) -> Any: +def get_value_from_qwidget(widget: QWidget) -> str | float | bool | None: if isinstance(widget, QLineEdit): return widget.text() elif isinstance(widget, QSpinBox) or isinstance(widget, QDoubleSpinBox): return widget.value() elif isinstance(widget, QCheckBox): return widget.isChecked() + return None class Type(IntEnum): From 5c6ca393c4616c9342852a276abd906c41c16842 Mon Sep 17 00:00:00 2001 From: ashmeigh Date: Tue, 16 Jul 2024 17:58:25 +0100 Subject: [PATCH 6/6] Add return Signed-off-by: ashmeigh --- mantidimaging/gui/dialogs/cor_inspection/model.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mantidimaging/gui/dialogs/cor_inspection/model.py b/mantidimaging/gui/dialogs/cor_inspection/model.py index 85320645bde..9d4d165ae62 100644 --- a/mantidimaging/gui/dialogs/cor_inspection/model.py +++ b/mantidimaging/gui/dialogs/cor_inspection/model.py @@ -3,8 +3,10 @@ from __future__ import annotations from dataclasses import replace from logging import getLogger +from typing import TYPE_CHECKING -import numpy as np +if TYPE_CHECKING: + import numpy as np from mantidimaging.core.data import ImageStack from mantidimaging.core.reconstruct import get_reconstructor_for