Source code for mantidimaging.gui.windows.image_load_dialog.field

# Copyright (C) 2023 ISIS Rutherford Appleton Laboratory UKRI
# SPDX - License - Identifier: GPL-3.0-or-later
from __future__ import annotations
from pathlib import Path

import numpy as np
from typing import Optional, List, Union, Tuple

from PyQt5.QtWidgets import QTreeWidgetItem, QWidget, QSpinBox, QTreeWidget, QHBoxLayout, QLabel, QCheckBox, QPushButton

from mantidimaging.core.utility import size_calculator
from mantidimaging.core.utility.data_containers import Indices, FILE_TYPES


[docs] class Field: _widget: QTreeWidgetItem _use: QCheckBox _spinbox_widget: Optional[QWidget] = None _start_spinbox: Optional[QSpinBox] = None _stop_spinbox: Optional[QSpinBox] = None _increment_spinbox: Optional[QSpinBox] = None _shape_widget: Optional[QTreeWidgetItem] = None _tree: QTreeWidget _path: QTreeWidgetItem def __init__(self, tree: QTreeWidget, widget: QTreeWidgetItem, use: QCheckBox, select_button: QPushButton, file_info: FILE_TYPES): self._tree = tree self._widget = widget self._use = use self._path = QTreeWidgetItem(self._widget) self._path.setText(0, "Path") self.select_button = select_button self.file_info = file_info if file_info == FILE_TYPES.SAMPLE: self._init_indices()
[docs] def set_images(self, image_files: List[Path]) -> None: if len(image_files) > 0: self.path = image_files[0] self.update_shape(len(image_files))
@property def widget(self) -> QTreeWidgetItem: """ Returns the top-level widget of the section. All other fields are nested under it :return: """ return self._widget @property def use(self) -> QCheckBox: return self._use @use.setter def use(self, value: bool) -> None: self._use.setChecked(value) @property def path_widget(self) -> QTreeWidgetItem: return self._path @property def path(self) -> Optional[Path]: if path_text := self.path_widget.text(1): return Path(path_text) else: return None @path.setter def path(self, value: Path) -> None: if not isinstance(value, Path): raise RuntimeError(f"The object passed as path for this field is not a Path. Instead got {type(value)}") if value != "": self.path_widget.setText(1, str(value)) self.widget.setText(1, value.name) self.use.setChecked(True) def _init_indices(self) -> None: indices_item = QTreeWidgetItem(self._widget) indices_item.setText(0, "File indices") _spinbox_layout = QHBoxLayout() self._start_spinbox = QSpinBox(self._tree.parent()) _spinbox_layout.addWidget(QLabel("Start", self._tree.parent())) _spinbox_layout.addWidget(self._start_spinbox) self._stop_spinbox = QSpinBox(self._tree.parent()) _spinbox_layout.addWidget(QLabel("Stop", self._tree.parent())) _spinbox_layout.addWidget(self._stop_spinbox) self._increment_spinbox = QSpinBox(self._tree.parent()) self._increment_spinbox.setMinimum(1) _spinbox_layout.addWidget(QLabel("Increment", self._tree.parent())) _spinbox_layout.addWidget(self._increment_spinbox) self._spinbox_widget = QWidget(self._tree.parent()) self._spinbox_widget.setLayout(_spinbox_layout) self._tree.setItemWidget(indices_item, 1, self._spinbox_widget) @property def _start(self) -> QSpinBox: if self._spinbox_widget is None: self._init_indices() # assert to clear up mypy error for wrong type assert self._start_spinbox is not None return self._start_spinbox @_start.setter def _start(self, value: int) -> None: self._start.setValue(value) @property def _stop(self) -> QSpinBox: if self._spinbox_widget is None: self._init_indices() # assert to clear up mypy error for wrong type assert self._stop_spinbox is not None return self._stop_spinbox @_stop.setter def _stop(self, value: int) -> None: self._stop.setValue(value) @property def _increment(self) -> QSpinBox: if self._spinbox_widget is None: self._init_indices() # assert to clear up mypy error for wrong type assert self._increment_spinbox is not None return self._increment_spinbox @_increment.setter def _increment(self, value: int) -> None: self._increment.setValue(value) @property def _shape(self) -> QTreeWidgetItem: if self._shape_widget is None: self._shape_widget = QTreeWidgetItem(self._widget) self._shape_widget.setText(0, "") return self._shape_widget @_shape.setter def _shape(self, value: str) -> None: self._shape.setText(1, value) @property def indices(self) -> Indices: return Indices(self._start.value(), self._stop.value(), self._increment.value())
[docs] def update_indices(self, number_of_images: int) -> None: """ :param number_of_images: Number of images that will be loaded in from the current selection """ # Cap the end value FIRST, otherwise setValue might fail if the # previous max val is smaller self._stop.setMaximum(number_of_images) self._stop.setValue(number_of_images) # Cap the start value to be end - 1 (ensure no negative value can be # set in case of loading failure) self._start.setMaximum(max(number_of_images - 1, 0)) # Enforce the maximum step (ensure a minimum of 1) self._increment.setMaximum(max(number_of_images, 1))
[docs] def set_preview(self, preview_mode: bool) -> None: if self._increment_spinbox is not None: if preview_mode: self._increment_spinbox.setValue(self._stop.maximum() // 10) else: self._increment_spinbox.setValue(1)
def _update_expected_mem_usage(self, shape: Tuple[int, int]) -> Tuple[int, float]: num_images = size_calculator.number_of_images_from_indices(self._start.value(), self._stop.value(), self._increment.value()) single_mem = size_calculator.full_size_MB(shape, dtype=np.float32) exp_mem = round(single_mem * num_images, 2) return num_images, exp_mem
[docs] def update_shape(self, shape: Union[int, Tuple[int, int]]) -> None: if isinstance(shape, int): self._shape = f"{str(shape)} images" else: num_images, exp_mem = self._update_expected_mem_usage(shape) self._shape = f"{num_images} images x {shape[0]} x {shape[1]}, {exp_mem}MB"