Slicer 5.9
Slicer is a multi-platform, free and open source software package for visualization and medical image computing
Loading...
Searching...
No Matches
AbstractScriptedSegmentEditorEffect.py
Go to the documentation of this file.
1import qt
2
3import slicer
4
5
7 """Abstract scripted segment editor effects for effects implemented in python.
8
9 An example of generic effect is ``ThresholdEffect``.
10
11 To use the effect, follow these steps:
12
13 1. Instantiation and registration
14
15 Instantiate segment editor effect adaptor class from
16 module (e.g. from setup function), and set python source::
17
18 import qSlicerSegmentationsEditorEffectsPythonQt as effects
19 scriptedEffect = effects.qSlicerSegmentEditorScriptedEffect(None)
20 scriptedEffect.setPythonSource(MyEffect.filePath)
21 scriptedEffect.self().register()
22
23 If effect name is added to ``slicer.modules.segmenteditorscriptedeffectnames``
24 list then the above instantiation and registration steps are not necessary,
25 as the `SegmentEditor` module do all these.
26
27 2. Call host C++ implementation using::
28
29 self.scriptedEffect.functionName()
30
31 2.a. Most frequently used methods are:
32
33 - Parameter get/set: ``parameter``, ``integerParameter``, ``doubleParameter``, ``setParameter``
34 - Add options widget: ``addOptionsWidget``
35 - Coordinate transforms: ``rasToXy``, ``xyzToRas``, ``xyToRas``, ``xyzToIjk``, ``xyToIjk``
36 - Convenience getters: ``renderWindow``, ``renderer``, ``viewNode``
37
38 2.b. Always call API functions (the ones that are defined in the adaptor \
39 class ``qSlicerSegmentEditorScriptedEffect``) using the adaptor accessor ``self.scriptedEffect``.
40
41 For example::
42
43 self.scriptedEffect.updateGUIFromMRML()
44
45 3. To prevent deactivation of an effect by clicking place fiducial toolbar button, \
46 override ``interactionNodeModified(self, interactionNode)``
47 """
48
49 def __init__(self, scriptedEffect):
50 self.scriptedEffect = scriptedEffect
51
52 def register(self):
53 effectFactorySingleton = slicer.qSlicerSegmentEditorEffectFactory.instance()
54 effectFactorySingleton.registerEffect(self.scriptedEffect)
55
56 def cleanup(self):
57 """
58 Clean up resources, event observers, and Qt signal/slot connections
59 to ensure proper object deletion.
60
61 Subclasses should override this method to disconnect any subclass-specific
62 signals and slots. This method should be called before the object is
63 garbage collected or explicitly deleted on the C++ side.
64
65 Failing to disconnect signals/slots may prevent the object from being
66 garbage collected, leading to memory leaks.
67
68 For more details, see: https://github.com/Slicer/Slicer/issues/7392
69 """
70 pass
71
72 #
73 # Utility functions for convenient coordinate transformations
74 #
75 def rasToXy(self, ras, viewWidget):
76 rasVector = qt.QVector3D(ras[0], ras[1], ras[2])
77 xyPoint = self.scriptedEffect.rasToXy(rasVector, viewWidget)
78 return [xyPoint.x(), xyPoint.y()]
79
80 def xyzToRas(self, xyz, viewWidget):
81 xyzVector = qt.QVector3D(xyz[0], xyz[1], xyz[2])
82 rasVector = self.scriptedEffect.xyzToRas(xyzVector, viewWidget)
83 return [rasVector.x(), rasVector.y(), rasVector.z()]
84
85 def xyToRas(self, xy, viewWidget):
86 xyPoint = qt.QPoint(xy[0], xy[1])
87 rasVector = self.scriptedEffect.xyToRas(xyPoint, viewWidget)
88 return [rasVector.x(), rasVector.y(), rasVector.z()]
89
90 def xyzToIjk(self, xyz, viewWidget, image, parentTransformNode=None):
91 xyzVector = qt.QVector3D(xyz[0], xyz[1], xyz[2])
92 ijkVector = self.scriptedEffect.xyzToIjk(xyzVector, viewWidget, image, parentTransformNode)
93 return [int(ijkVector.x()), int(ijkVector.y()), int(ijkVector.z())]
94
95 def xyToIjk(self, xy, viewWidget, image, parentTransformNode=None):
96 xyPoint = qt.QPoint(xy[0], xy[1])
97 ijkVector = self.scriptedEffect.xyToIjk(xyPoint, viewWidget, image, parentTransformNode)
98 return [int(ijkVector.x()), int(ijkVector.y()), int(ijkVector.z())]
99
100 def setWidgetMinMaxStepFromImageSpacing(self, spinbox, imageData):
101 # Set spinbox minimum, maximum, and step size from vtkImageData spacing:
102 # Set widget minimum spacing and step size to be 1/10th or less than minimum spacing
103 # Set widget minimum spacing to be 100x or more than minimum spacing
104 if not imageData:
105 return
106 import math
107 spinbox.unitAwareProperties &= ~(slicer.qMRMLSpinBox.MinimumValue | slicer.qMRMLSpinBox.MaximumValue | slicer.qMRMLSpinBox.Precision)
108 stepSize = 10 ** (math.floor(math.log10(min(imageData.GetSpacing()) / 10.0)))
109 spinbox.minimum = stepSize
110 spinbox.maximum = 10 ** (math.ceil(math.log10(max(imageData.GetSpacing()) * 100.0)))
111 spinbox.singleStep = stepSize
112 # number of decimals is set to be able to show the step size (e.g., stepSize = 0.01 => decimals = 2)
113 spinbox.decimals = max(int(-math.floor(math.log10(stepSize))), 0)