Skip to content

Commit

Permalink
Type Annotation Enhancement in mantidimaging/gui/ Directory (#2282)
Browse files Browse the repository at this point in the history
  • Loading branch information
MikeSullivan7 authored Jul 18, 2024
2 parents a19a7df + 5c6ca39 commit 8cf298f
Show file tree
Hide file tree
Showing 18 changed files with 104 additions and 97 deletions.
2 changes: 1 addition & 1 deletion mantidimaging/gui/dialogs/async_task/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
16 changes: 8 additions & 8 deletions mantidimaging/gui/dialogs/async_task/presenter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -39,29 +39,29 @@ 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.
"""
self.model.do_execute_async()
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 '')
6 changes: 3 additions & 3 deletions mantidimaging/gui/dialogs/async_task/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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()
Expand Down
26 changes: 15 additions & 11 deletions mantidimaging/gui/dialogs/cor_inspection/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
from __future__ import annotations
from dataclasses import replace
from logging import getLogger
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
Expand All @@ -26,7 +30,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
Expand All @@ -41,13 +45,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.
Expand All @@ -59,7 +63,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.
"""
Expand All @@ -70,26 +74,26 @@ 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:
return self.centre_value
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
6 changes: 3 additions & 3 deletions mantidimaging/gui/dialogs/cor_inspection/presenter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
8 changes: 4 additions & 4 deletions mantidimaging/gui/dialogs/cor_inspection/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
6 changes: 3 additions & 3 deletions mantidimaging/gui/mvp_base/presenter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
4 changes: 2 additions & 2 deletions mantidimaging/gui/mvp_base/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down
15 changes: 8 additions & 7 deletions mantidimaging/gui/utility/qt_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,26 +46,27 @@ 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")

# open file dialog and set the text if file is selected
field.setText(QFileDialog.getExistingDirectory(caption=caption))


def get_value_from_qwidget(widget: QWidget):
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):
Expand All @@ -82,7 +83,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.
Expand Down Expand Up @@ -129,7 +130,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.
"""
Expand Down Expand Up @@ -237,7 +238,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.
Expand All @@ -257,7 +258,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()
Expand Down
Loading

0 comments on commit 8cf298f

Please sign in to comment.