Source code for coperniFUS.modules.tooltip
from coperniFUS import *
from coperniFUS.modules.module_base import Module
import functools
[docs]
class Tooltip(Module):
_DEFAULT_PARAMS = {
'tooltip_transforms_str' : 'Rx0deg Tz0um',
'axes_length': 1e-3
}
""" Default configuration parameters used when a parameter value is not yet cached """
def __init__(self, parent_viewer, **kwargs) -> None:
super().__init__(parent_viewer, 'tooltip', **kwargs)
self._tooltip_tmat = None
self.x_glaxis = None
self.y_glaxis = None
self.z_glaxis = None
# --- Module specific public attributes ---
[docs]
def release_from_modules(self, sender_module=None):
""" All modules allowing Tooltip capture have to implement the release_tooltip method """
# Builtin modules
if sender_module is not self.parent_viewer.stereotaxic_frame:
self.parent_viewer.stereotaxic_frame.release_tooltip()
if sender_module is not self.parent_viewer.anat_calib:
self.parent_viewer.anat_calib.release_tooltip()
# Optionnal modules
for mm in self.parent_viewer._modules:
if hasattr(mm, 'release_tooltip') and sender_module is not mm:
mm.release_tooltip()
@property
def tooltip_coordinates(self):
""" Returns the current Tooltip 3D coordinates (x, y, z) """
_tooltip_coordinates = None
if self.tooltip_tmat is not None:
_tooltip_coordinates = self.tooltip_tmat[3, :3]
return _tooltip_coordinates
@property
def tooltip_tmat(self):
""" Holds the current Tooltip affine transformation matrix """
if self._tooltip_tmat is None:
# Compute tooltip transform matrix from tooltip_transforms_str (status bar qedit)
transforms_matrices = af_tr_from_str.transform_matrices_from_str(
self.get_user_param('tooltip_transforms_str')
)
self._tooltip_tmat = af_tr.scale_mat(1)
for tr_mat in transforms_matrices:
self._tooltip_tmat = self._tooltip_tmat @ tr_mat
return self._tooltip_tmat
@tooltip_tmat.setter
def tooltip_tmat(self, value):
if value is not None:
if value.shape != (4, 4):
raise ValueError('Transformation matrix should be of shape (4, 4)')
self._tooltip_tmat = value
# --- Required module attributes ---
[docs]
def add_rendered_object(self):
""" Called when populating the viewer with the module rendered objects """
self.x_glaxis = gl.GLLinePlotItem(pos=[[0,0,0], [self.get_user_param('axes_length'),0,0]], width=6, color=self.parent_viewer.x_RED, antialias=True, glOptions='additive')
self.y_glaxis = gl.GLLinePlotItem(pos=[[0,0,0], [0,self.get_user_param('axes_length'),0]], width=6, color=self.parent_viewer.y_GREEN, antialias=True, glOptions='additive')
self.z_glaxis = gl.GLLinePlotItem(pos=[[0,0,0], [0,0,self.get_user_param('axes_length')]], width=6, color=self.parent_viewer.z_BLUE, antialias=True, glOptions='additive')
self.parent_viewer.gl_view.addItem(self.x_glaxis, name='Tooltip X axis')
self.parent_viewer.gl_view.addItem(self.y_glaxis, name='Tooltip Y axis')
self.parent_viewer.gl_view.addItem(self.z_glaxis, name='Tooltip Z axis')
self.x_glaxis.setDepthValue(10) # Tooltip -> render tree foreground
self.y_glaxis.setDepthValue(10) # Tooltip -> render tree foreground
self.z_glaxis.setDepthValue(10) # Tooltip -> render tree foreground
self._update_transform()
[docs]
def update_rendered_object(self):
""" Called on render view updates """
if self.x_glaxis is not None:
self._update_transform()
[docs]
def delete_rendered_object(self):
""" Called on deletion of the module rendered objects """
if self.x_glaxis in self.parent_viewer.gl_view.items:
self.parent_viewer.gl_view.removeItem(self.x_glaxis)
self.x_glaxis = None
if self.y_glaxis in self.parent_viewer.gl_view.items:
self.parent_viewer.gl_view.removeItem(self.y_glaxis)
self.y_glaxis = None
if self.z_glaxis in self.parent_viewer.gl_view.items:
self.parent_viewer.gl_view.removeItem(self.z_glaxis)
self.z_glaxis = None
# --- Module specific attributes ---
def _on_editor_parsed(self, param_name, edited_value):
self.set_user_param(param_name, edited_value)
self.parent_viewer.update_rendered_view()
def _parse_editor(self, src_editor, param_name, unit='', param_type='float'): # TODO move to Module base class?
if param_type == 'int':
edited_value = int(src_editor.text())
elif param_type == 'float':
edited_text = src_editor.text().replace(' ', '') # remove spaces
edited_text_nounit = edited_text[:-len(unit)]
edited_value = si_parse(edited_text_nounit.replace('u', 'ยต'))
else: # raw str
edited_value = src_editor.text()
self._on_editor_parsed(param_name, edited_value)
def _init_status_bar_widget(self):
seperator_ui_label = pyqtw.QLabel(' |')
self.parent_viewer.statusBar().addPermanentWidget(seperator_ui_label)
# Tooltip global coordinates widget
space_conv = self.parent_viewer.ATLAS_SPACE_CONVENTION
self.statusbar_label = pyqtw.QLabel(' Tooltip global coords: ')
self.statusbar_x_coord_label = pyqtw.QLabel(f"{space_conv[0][:4]}. (x): 0 m")
self.statusbar_x_coord_label.setStyleSheet(f'border: 0; color: {self.parent_viewer.x_RED};')
self.statusbar_y_coord_label = pyqtw.QLabel(f"{space_conv[1][:4]}. (y): 0 m")
self.statusbar_y_coord_label.setStyleSheet(f'border: 0; color: {self.parent_viewer.y_GREEN};')
self.statusbar_z_coord_label = pyqtw.QLabel(f"{space_conv[2][:4]}. (z): 0 m")
self.statusbar_z_coord_label.setStyleSheet(f'border: 0; color: {self.parent_viewer.z_BLUE};')
self.parent_viewer.statusBar().addPermanentWidget(self.statusbar_label)
self.parent_viewer.statusBar().addPermanentWidget(self.statusbar_x_coord_label)
self.parent_viewer.statusBar().addPermanentWidget(self.statusbar_y_coord_label)
self.parent_viewer.statusBar().addPermanentWidget(self.statusbar_z_coord_label)
self.parent_viewer.statusBar().addPermanentWidget(pyqtw.QLabel(" ")) # Termination spacer
# Tooltip coordinate transform editor
self.tooltip_transform_editor = descriptive_line_edit(str(self.get_user_param('tooltip_transforms_str')), 'Default Tooltip transform')
self.tooltip_transform_editor.editingFinished.connect(functools.partial(self._parse_editor, self.tooltip_transform_editor, 'tooltip_transforms_str', '', 'str'))
self.parent_viewer.statusBar().addPermanentWidget(self.tooltip_transform_editor)
self.parent_viewer.statusBar().addPermanentWidget(pyqtw.QLabel(" ")) # Termination spacer
self.tooltip_transform_editor.setFixedWidth(500)
self.tooltip_transform_editor.setToolTip('STL mesh transformations<br> - S0.5: Apply a 0.5 scaling factor (Use Sx to scale along x)<br> - Ty1mm: 1mm translation along y<br> - Rz90deg: Rotate by 90 degrees around z axis')
def _update_statusbar_coordinates(self):
coords = self.tooltip_coordinates
if coords is not None:
space_conv = self.parent_viewer.ATLAS_SPACE_CONVENTION
formated_coords = [si_format(cc, format_str='{value} {prefix}m') for cc in coords]
self.statusbar_x_coord_label.setText(f"{space_conv[0][:4]}. (x): {formated_coords[0]}")
self.statusbar_y_coord_label.setText(f"{space_conv[1][:4]}. (y): {formated_coords[1]}")
self.statusbar_z_coord_label.setText(f"{space_conv[2][:4]}. (z): {formated_coords[2]}")
def _update_transform(self):
self.x_glaxis.resetTransform()
self.x_glaxis.applyTransform(pyqtg.QMatrix4x4(self.tooltip_tmat.T.ravel()), local=False)
self.y_glaxis.resetTransform()
self.y_glaxis.applyTransform(pyqtg.QMatrix4x4(self.tooltip_tmat.T.ravel()), local=False)
self.z_glaxis.resetTransform()
self.z_glaxis.applyTransform(pyqtg.QMatrix4x4(self.tooltip_tmat.T.ravel()), local=False)
self._update_statusbar_coordinates()