Source code for napari_locan.widgets.widget_render_image_3d

"""
Render 3d image.

A QWidget plugin to render SMLM data as image by binning localization
properties into 3d pixels.
"""

from __future__ import annotations

import logging

import locan as lc
from napari.utils import progress
from napari.viewer import Viewer
from qtpy.QtWidgets import (
    QCheckBox,
    QComboBox,
    QDoubleSpinBox,
    QHBoxLayout,
    QLabel,
    QPushButton,
    QSpinBox,
    QVBoxLayout,
    QWidget,
)

from napari_locan import smlm_data
from napari_locan.data_model.smlm_data import SmlmData

logger = logging.getLogger(__name__)


[docs] class RenderImage3dQWidget(QWidget): # type: ignore def __init__(self, napari_viewer: Viewer, smlm_data: SmlmData = smlm_data): super().__init__() self.viewer = napari_viewer self.smlm_data = smlm_data self._add_loc_properties_selection() self._add_other_properties_selection() self._add_bin_size() self._add_bin_range() self._add_rescale() self._add_render_buttons() self._set_layout() def _add_loc_properties_selection(self) -> None: self._loc_properties_x_label = QLabel("x:") self._loc_properties_x_combobox = QComboBox() self._loc_properties_x_combobox.setToolTip( "Choose localization property for selected SMLM dataset as x coordinate." ) self.smlm_data.index_changed_signal.connect( self._loc_properties_x_combobox_slot_for_smlm_data_index ) # condition excludes smlm_data.locdata to be None in what comes: if self.smlm_data.index != -1 and bool(self.smlm_data.locdata): self._loc_properties_x_combobox.addItems( self.smlm_data.locdata.data.columns # type: ignore ) key_index = list(self.smlm_data.locdata.data.columns).index( # type: ignore self.smlm_data.locdata.coordinate_keys[0] # type: ignore ) self._loc_properties_x_combobox.setCurrentIndex(key_index) self._loc_properties_y_label = QLabel("y:") self._loc_properties_y_combobox = QComboBox() self._loc_properties_y_combobox.setToolTip( "Choose localization property for selected SMLM dataset as y coordinate." ) self.smlm_data.index_changed_signal.connect( self._loc_properties_y_combobox_slot_for_smlm_data_index ) if self.smlm_data.index != -1 and bool(self.smlm_data.locdata): self._loc_properties_y_combobox.addItems( self.smlm_data.locdata.data.columns # type: ignore ) key_index = list(self.smlm_data.locdata.data.columns).index( # type: ignore self.smlm_data.locdata.coordinate_keys[1] # type: ignore ) self._loc_properties_y_combobox.setCurrentIndex(key_index) self._loc_properties_z_label = QLabel("z:") self._loc_properties_z_combobox = QComboBox() self._loc_properties_z_combobox.setToolTip( "Choose localization property for selected SMLM dataset as z coordinate." ) self.smlm_data.index_changed_signal.connect( self._loc_properties_z_combobox_slot_for_smlm_data_index ) if self.smlm_data.index != -1 and bool(self.smlm_data.locdata): self._loc_properties_z_combobox.addItems( self.smlm_data.locdata.data.columns # type: ignore ) key_index = list(self.smlm_data.locdata.data.columns).index( # type: ignore self.smlm_data.locdata.coordinate_keys[2] # type: ignore ) self._loc_properties_z_combobox.setCurrentIndex(key_index) self._loc_properties_layout = QHBoxLayout() self._loc_properties_layout.addWidget(self._loc_properties_x_label) self._loc_properties_layout.addWidget(self._loc_properties_x_combobox) self._loc_properties_layout.addWidget(self._loc_properties_y_label) self._loc_properties_layout.addWidget(self._loc_properties_y_combobox) self._loc_properties_layout.addWidget(self._loc_properties_z_label) self._loc_properties_layout.addWidget(self._loc_properties_z_combobox) def _add_other_properties_selection(self) -> None: self._loc_properties_other_label = QLabel("other:") self._loc_properties_other_combobox = QComboBox() self._loc_properties_other_combobox.setToolTip( "Choose localization property for selected SMLM dataset as pixel value." ) self.smlm_data.index_changed_signal.connect( self._loc_properties_other_combobox_slot_for_smlm_data_index ) if self.smlm_data.index != -1 and bool(self.smlm_data.locdata): self._loc_properties_other_combobox.addItem("") self._loc_properties_other_combobox.addItems( self.smlm_data.locdata.data.columns # type: ignore ) key_index = 0 self._loc_properties_other_combobox.setCurrentIndex(key_index) self._other_properties_layout = QHBoxLayout() self._other_properties_layout.addWidget(self._loc_properties_other_label) self._other_properties_layout.addWidget(self._loc_properties_other_combobox) def _loc_properties_x_combobox_slot_for_smlm_data_index(self, index: int) -> None: key_index = self._loc_properties_x_combobox.currentIndex() self._loc_properties_x_combobox.clear() if index != -1: self._loc_properties_x_combobox.addItems( self.smlm_data.locdata.data.columns # type: ignore ) if key_index == -1: if bool(self.smlm_data.locdata): new_key_index = list(self.smlm_data.locdata.data.columns).index( # type: ignore # noqa: E501 self.smlm_data.locdata.coordinate_keys[0] # type: ignore ) self._loc_properties_x_combobox.setCurrentIndex(new_key_index) else: self._loc_properties_x_combobox.setCurrentIndex(-1) else: self._loc_properties_x_combobox.setCurrentIndex(key_index) def _loc_properties_y_combobox_slot_for_smlm_data_index(self, index: int) -> None: key_index = self._loc_properties_y_combobox.currentIndex() self._loc_properties_y_combobox.clear() if index != -1: self._loc_properties_y_combobox.addItems( self.smlm_data.locdata.data.columns # type: ignore ) if key_index == -1: if bool(self.smlm_data.locdata): new_key_index = list(self.smlm_data.locdata.data.columns).index( # type: ignore # noqa: E501 self.smlm_data.locdata.coordinate_keys[1] # type: ignore ) self._loc_properties_y_combobox.setCurrentIndex(new_key_index) else: self._loc_properties_y_combobox.setCurrentIndex(-1) else: self._loc_properties_y_combobox.setCurrentIndex(key_index) def _loc_properties_z_combobox_slot_for_smlm_data_index(self, index: int) -> None: key_index = self._loc_properties_z_combobox.currentIndex() self._loc_properties_z_combobox.clear() if index != -1: self._loc_properties_z_combobox.addItems( self.smlm_data.locdata.data.columns # type: ignore ) if key_index == -1: if bool(self.smlm_data.locdata): new_key_index = list(self.smlm_data.locdata.data.columns).index( # type: ignore # noqa: E501 self.smlm_data.locdata.coordinate_keys[2] # type: ignore ) self._loc_properties_z_combobox.setCurrentIndex(new_key_index) else: self._loc_properties_z_combobox.setCurrentIndex(-1) else: self._loc_properties_z_combobox.setCurrentIndex(key_index) def _loc_properties_other_combobox_slot_for_smlm_data_index( self, index: int ) -> None: key_index = self._loc_properties_other_combobox.currentIndex() self._loc_properties_other_combobox.clear() self._loc_properties_other_combobox.addItem("") if index != -1: self._loc_properties_other_combobox.addItems( self.smlm_data.locdata.data.columns # type: ignore ) if key_index == -1: self._loc_properties_other_combobox.setCurrentIndex(0) else: self._loc_properties_other_combobox.setCurrentIndex(key_index) def _add_bin_size(self) -> None: self._bin_size_label = QLabel("Bin size:") self._bin_size_spin_box = QSpinBox() self._bin_size_spin_box.setRange(1, 2147483647) self._bin_size_spin_box.setValue(10) self._bin_size_layout = QHBoxLayout() self._bin_size_layout.addWidget(self._bin_size_label) self._bin_size_layout.addWidget(self._bin_size_spin_box) def _add_bin_range(self) -> None: self._bin_range_check_box = QCheckBox() self._bin_range_check_box.setChecked(False) self._bin_range_check_box.stateChanged.connect( self._bin_range_check_box_on_changed ) self._bin_range_label = QLabel("Bin range:") self._bin_range_min_label = QLabel("min:") self._bin_range_min_spin_box = QDoubleSpinBox() self._bin_range_min_spin_box.setRange(-1e10, 1e10) self._bin_range_min_spin_box.setValue(0) self._bin_range_max_label = QLabel("max:") self._bin_range_max_spin_box = QDoubleSpinBox() self._bin_range_max_spin_box.setRange(-1e10, 1e10) self._bin_range_max_spin_box.setValue(1e10) self._bin_range_layout = QHBoxLayout() self._bin_range_layout.addWidget(self._bin_range_label) self._bin_range_layout.addWidget(self._bin_range_min_label) self._bin_range_layout.addWidget(self._bin_range_min_spin_box) self._bin_range_layout.addWidget(self._bin_range_max_label) self._bin_range_layout.addWidget(self._bin_range_max_spin_box) self._bin_range_layout.addStretch() self._bin_range_layout.addWidget(self._bin_range_check_box) self._bin_range_check_box_on_changed() def _bin_range_check_box_on_changed(self) -> None: self._bin_range_min_label.setVisible(self._bin_range_check_box.isChecked()) self._bin_range_min_spin_box.setVisible(self._bin_range_check_box.isChecked()) self._bin_range_max_label.setVisible(self._bin_range_check_box.isChecked()) self._bin_range_max_spin_box.setVisible(self._bin_range_check_box.isChecked()) def _add_rescale(self) -> None: self._rescale_label = QLabel("Rescale intensity:") self._rescale_combobox = QComboBox() trafos = list(lc.Trafo.__members__.keys()) self._rescale_combobox.addItems(trafos) self._rescale_combobox.setCurrentIndex(lc.Trafo.EQUALIZE.value) self._rescale_layout = QHBoxLayout() self._rescale_layout.addWidget(self._rescale_label) self._rescale_layout.addWidget(self._rescale_combobox) def _add_render_buttons(self) -> None: self._render_button = QPushButton("Render image") self._render_button.setToolTip("Render selected SMLM data in new image layer.") self._render_button.clicked.connect(self._render_button_on_click) def _set_layout(self) -> None: layout = QVBoxLayout() layout.addLayout(self._loc_properties_layout) layout.addLayout(self._other_properties_layout) layout.addLayout(self._bin_size_layout) layout.addLayout(self._bin_range_layout) layout.addLayout(self._rescale_layout) layout.addWidget(self._render_button) self.setLayout(layout) def _render_button_on_click(self) -> None: locdata = self.smlm_data.locdata if locdata is None: raise ValueError("There is no SMLM data available.") elif bool(locdata) is False: raise ValueError("Locdata is empty.") loc_properties = [ self._loc_properties_x_combobox.currentText(), self._loc_properties_y_combobox.currentText(), self._loc_properties_z_combobox.currentText(), ] other_property: str | None = self._loc_properties_other_combobox.currentText() other_property = other_property if other_property != "" else None # set bins if self._bin_range_check_box.isChecked(): bin_range_ = ( self._bin_range_min_spin_box.value(), self._bin_range_max_spin_box.value(), ) bin_range = [bin_range_] * locdata.dimension else: bin_range = None # optional kwargs for the corresponding viewer.add_* method add_kwargs = {"name": self.smlm_data.locdata_name} # render data with progress() as progress_bar: progress_bar.set_description("Rendering:") lc.render_3d_napari( locdata=locdata, loc_properties=loc_properties, other_property=other_property, viewer=self.viewer, bin_size=int(self._bin_size_spin_box.value()), bin_range=bin_range, rescale=self._rescale_combobox.currentText(), cmap=lc.COLORMAP_DEFAULTS["CONTINUOUS"], **add_kwargs, # type: ignore[arg-type] )