Skip to content

Commit

Permalink
BUG: Make translatable Editor effects titles in Segmentations
Browse files Browse the repository at this point in the history
  • Loading branch information
mhdiop committed Sep 6, 2023
1 parent 4540594 commit 934e344
Show file tree
Hide file tree
Showing 25 changed files with 379 additions and 269 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import vtk

import slicer
from slicer.i18n import tr as _

from .AbstractScriptedSegmentEditorEffect import *

Expand Down Expand Up @@ -86,14 +87,14 @@ def isBackgroundLabelmap(labelmapOrientedImageData, label=None):
return False

def setupOptionsFrame(self):
self.autoUpdateCheckBox = qt.QCheckBox("Auto-update")
self.autoUpdateCheckBox.setToolTip("Auto-update results preview when input segments change.")
self.autoUpdateCheckBox = qt.QCheckBox(_("Auto-update"))
self.autoUpdateCheckBox.setToolTip(_("Auto-update results preview when input segments change."))
self.autoUpdateCheckBox.setChecked(True)
self.autoUpdateCheckBox.setEnabled(False)

self.previewButton = qt.QPushButton("Initialize")
self.previewButton = qt.QPushButton(_("Initialize"))
self.previewButton.objectName = self.__class__.__name__ + 'Preview'
self.previewButton.setToolTip("Preview complete segmentation")
self.previewButton.setToolTip(_("Preview complete segmentation"))
# qt.QSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Expanding)
# fails on some systems, therefore set the policies using separate method calls
qSize = qt.QSizePolicy()
Expand All @@ -103,35 +104,35 @@ def setupOptionsFrame(self):
previewFrame = qt.QHBoxLayout()
previewFrame.addWidget(self.autoUpdateCheckBox)
previewFrame.addWidget(self.previewButton)
self.scriptedEffect.addLabeledOptionsWidget("Preview:", previewFrame)
self.scriptedEffect.addLabeledOptionsWidget(_("Preview:"), previewFrame)

self.previewOpacitySlider = ctk.ctkSliderWidget()
self.previewOpacitySlider.setToolTip("Adjust visibility of results preview.")
self.previewOpacitySlider.setToolTip(_("Adjust visibility of results preview."))
self.previewOpacitySlider.minimum = 0
self.previewOpacitySlider.maximum = 1.0
self.previewOpacitySlider.value = 0.0
self.previewOpacitySlider.singleStep = 0.05
self.previewOpacitySlider.pageStep = 0.1
self.previewOpacitySlider.spinBoxVisible = False

self.previewShow3DButton = qt.QPushButton("Show 3D")
self.previewShow3DButton.setToolTip("Preview results in 3D.")
self.previewShow3DButton = qt.QPushButton(_("Show 3D"))
self.previewShow3DButton.setToolTip(_("Preview results in 3D."))
self.previewShow3DButton.setCheckable(True)

displayFrame = qt.QHBoxLayout()
displayFrame.addWidget(qt.QLabel("inputs"))
displayFrame.addWidget(qt.QLabel(_("inputs")))
displayFrame.addWidget(self.previewOpacitySlider)
displayFrame.addWidget(qt.QLabel("results"))
displayFrame.addWidget(qt.QLabel(_("results")))
displayFrame.addWidget(self.previewShow3DButton)
self.scriptedEffect.addLabeledOptionsWidget("Display:", displayFrame)
self.scriptedEffect.addLabeledOptionsWidget(_("Display:"), displayFrame)

self.cancelButton = qt.QPushButton("Cancel")
self.cancelButton = qt.QPushButton(_("Cancel"))
self.cancelButton.objectName = self.__class__.__name__ + 'Cancel'
self.cancelButton.setToolTip("Clear preview and cancel auto-complete")
self.cancelButton.setToolTip(_("Clear preview and cancel auto-complete"))

self.applyButton = qt.QPushButton("Apply")
self.applyButton = qt.QPushButton(_("Apply"))
self.applyButton.objectName = self.__class__.__name__ + 'Apply'
self.applyButton.setToolTip("Replace segments by previewed result")
self.applyButton.setToolTip(_("Replace segments by previewed result"))

finishFrame = qt.QHBoxLayout()
finishFrame.addWidget(self.cancelButton)
Expand Down Expand Up @@ -244,13 +245,13 @@ def updateGUIFromMRML(self):
wasBlocked = self.previewOpacitySlider.blockSignals(True)
self.previewOpacitySlider.value = self.getPreviewOpacity()
self.previewOpacitySlider.blockSignals(wasBlocked)
self.previewButton.text = "Update"
self.previewButton.text = _("Update")
self.previewShow3DButton.setEnabled(True)
self.previewShow3DButton.setChecked(self.getPreviewShow3D())
self.autoUpdateCheckBox.setEnabled(True)
self.observeSegmentation(self.autoUpdateCheckBox.isChecked())
else:
self.previewButton.text = "Initialize"
self.previewButton.text = _("Initialize")
self.autoUpdateCheckBox.setEnabled(False)
self.previewShow3DButton.setEnabled(False)
self.delayedAutoUpdateTimer.stop()
Expand All @@ -276,7 +277,7 @@ def onPreview(self):
return
self.previewComputationInProgress = True

slicer.util.showStatusMessage(f"Running {self.scriptedEffect.name} auto-complete...", 2000)
slicer.util.showStatusMessage(_("Running {effectName} auto-complete...").format(effectName=self.scriptedEffect.name), 2000)
try:
# This can be a long operation - indicate it to the user
qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import vtk

import slicer
from slicer.i18n import tr as _

from SegmentEditorEffects import *

Expand All @@ -16,6 +17,7 @@ class SegmentEditorDrawEffect(AbstractScriptedSegmentEditorLabelEffect):

def __init__(self, scriptedEffect):
scriptedEffect.name = 'Draw'
scriptedEffect.title = _('Draw')
self.drawPipelines = {}
AbstractScriptedSegmentEditorLabelEffect.__init__(self, scriptedEffect)

Expand All @@ -32,13 +34,13 @@ def icon(self):
return qt.QIcon()

def helpText(self):
return """<html>Draw segment outline in slice viewers<br>.
<p><ul style="margin: 0">
<li><b>Left-click:</b> add point.</li>
<li><b>Left-button drag-and-drop:</b> add multiple points.</li>
<li><b>x:</b> delete last point.</li>
<li><b>Double-left-click</b> or <b>right-click</b> or <b>a</b> or <b>enter</b>: apply outline.</li>
</ul><p></html>"""
return _("""<html>Draw segment outline in slice viewers<br>.
<p><ul style="margin: 0">
<li><b>Left-click:</b> add point.</li>
<li><b>Left-button drag-and-drop:</b> add multiple points.</li>
<li><b>x:</b> delete last point.</li>
<li><b>Double-left-click</b> or <b>right-click</b> or <b>a</b> or <b>enter</b>: apply outline.</li>
</ul><p></html>""")

def deactivate(self):
# Clear draw pipelines
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import qt
import vtk

from slicer.i18n import tr as _

from SegmentEditorEffects import *


Expand All @@ -15,6 +17,7 @@ class SegmentEditorFillBetweenSlicesEffect(AbstractScriptedSegmentEditorAutoComp
def __init__(self, scriptedEffect):
AbstractScriptedSegmentEditorAutoCompleteEffect.__init__(self, scriptedEffect)
scriptedEffect.name = 'Fill between slices'
scriptedEffect.title = _('Fill between slices')

def clone(self):
import qSlicerSegmentationsEditorEffectsPythonQt as effects
Expand All @@ -29,17 +32,17 @@ def icon(self):
return qt.QIcon()

def helpText(self):
return """<html>Interpolate segmentation between slices<br>. Instructions:
<p><ul>
<li>Create complete segmentation on selected slices using any editor effect.
Segmentation will only expanded if a slice is segmented but none of the direct neighbors are segmented, therefore
do not use sphere brush with Paint effect and always leave at least one empty slice between segmented slices.</li>
<li>All visible segments will be interpolated, not just the selected segment.</li>
<li>The complete segmentation will be created by interpolating segmentations in empty slices.</li>
</ul><p>
Masking settings are ignored. If segments overlap, segment higher in the segments table will have priority.
The effect uses <a href="https://insight-journal.org/browse/publication/977">morphological contour interpolation method</a>.
<p></html>"""
return _("""<html>Interpolate segmentation between slices<br>. Instructions:
<p><ul>
<li>Create complete segmentation on selected slices using any editor effect.
Segmentation will only expanded if a slice is segmented but none of the direct neighbors are segmented, therefore
do not use sphere brush with Paint effect and always leave at least one empty slice between segmented slices.</li>
<li>All visible segments will be interpolated, not just the selected segment.</li>
<li>The complete segmentation will be created by interpolating segmentations in empty slices.</li>
</ul><p>
Masking settings are ignored. If segments overlap, segment higher in the segments table will have priority.
The effect uses <a href="https://insight-journal.org/browse/publication/977">morphological contour interpolation method</a>.
<p></html>""")

def computePreviewLabelmap(self, mergedImage, outputLabelmap):
import vtkITK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import vtk

import slicer
from slicer.i18n import tr as _

from SegmentEditorEffects import *

Expand All @@ -19,6 +20,7 @@ class SegmentEditorGrowFromSeedsEffect(AbstractScriptedSegmentEditorAutoComplete
def __init__(self, scriptedEffect):
AbstractScriptedSegmentEditorAutoCompleteEffect.__init__(self, scriptedEffect)
scriptedEffect.name = 'Grow from seeds'
scriptedEffect.title = _('Grow from seeds')
self.minimumNumberOfSegments = 2
self.clippedMasterImageDataRequired = True # source volume intensities are used by this effect
self.clippedMaskImageDataRequired = True # masking is used
Expand All @@ -37,21 +39,21 @@ def icon(self):
return qt.QIcon()

def helpText(self):
return """<html>Growing segments to create complete segmentation<br>.
Location, size, and shape of initial segments and content of source volume are taken into account.
Final segment boundaries will be placed where source volume brightness changes abruptly. Instructions:<p>
<ul style="margin: 0">
<li>Use Paint or other offects to draw seeds in each region that should belong to a separate segment.
Paint each seed with a different segment. Minimum two segments are required.</li>
<li>Click <dfn>Initialize</dfn> to compute preview of full segmentation.</li>
<li>Browse through image slices. If previewed segmentation result is not correct then switch to
Paint or other effects and add more seeds in the misclassified region. Full segmentation will be
updated automatically within a few seconds</li>
<li>Click <dfn>Apply</dfn> to update segmentation with the previewed result.</li>
</ul><p>
If segments overlap, segment higher in the segments table will have priority.
The effect uses <a href="http://interactivemedical.org/imic2014/CameraReadyPapers/Paper%204/IMIC_ID4_FastGrowCut.pdf">fast grow-cut method</a>.
<p></html>"""
return _("""<html>Growing segments to create complete segmentation<br>.
Location, size, and shape of initial segments and content of source volume are taken into account.
Final segment boundaries will be placed where source volume brightness changes abruptly. Instructions:<p>
<ul style="margin: 0">
<li>Use Paint or other offects to draw seeds in each region that should belong to a separate segment.
Paint each seed with a different segment. Minimum two segments are required.</li>
<li>Click <dfn>Initialize</dfn> to compute preview of full segmentation.</li>
<li>Browse through image slices. If previewed segmentation result is not correct then switch to
Paint or other effects and add more seeds in the misclassified region. Full segmentation will be
updated automatically within a few seconds</li>
<li>Click <dfn>Apply</dfn> to update segmentation with the previewed result.</li>
</ul><p>
If segments overlap, segment higher in the segments table will have priority.
The effect uses <a href="http://interactivemedical.org/imic2014/CameraReadyPapers/Paper%204/IMIC_ID4_FastGrowCut.pdf">fast grow-cut method</a>.
<p></html>""")

def reset(self):
self.growCutFilter = None
Expand All @@ -70,10 +72,10 @@ def setupOptionsFrame(self):
self.seedLocalityFactorSlider.decimals = 1
self.seedLocalityFactorSlider.singleStep = 0.1
self.seedLocalityFactorSlider.pageStep = 1.0
self.seedLocalityFactorSlider.setToolTip('Increasing this value makes the effect of seeds more localized,'
' thereby reducing leaks, but requires seed regions to be more evenly distributed in the image.'
' The value is specified as an additional "intensity level difference" per "unit distance."')
self.scriptedEffect.addLabeledOptionsWidget("Seed locality:", self.seedLocalityFactorSlider)
self.seedLocalityFactorSlider.setToolTip(_('Increasing this value makes the effect of seeds more localized,'
' thereby reducing leaks, but requires seed regions to be more evenly distributed in the image.'
' The value is specified as an additional "intensity level difference" per "unit distance."'))
self.scriptedEffect.addLabeledOptionsWidget(_("Seed locality:"), self.seedLocalityFactorSlider)
self.seedLocalityFactorSlider.connect('valueChanged(double)', self.updateAlgorithmParameterFromGUI)

def setMRMLDefaults(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import vtk

import slicer
from slicer.i18n import tr as _

from SegmentEditorEffects import *

Expand All @@ -15,6 +16,7 @@ class SegmentEditorHollowEffect(AbstractScriptedSegmentEditorEffect):

def __init__(self, scriptedEffect):
scriptedEffect.name = 'Hollow'
scriptedEffect.title = _('Hollow')
AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect)

def clone(self):
Expand All @@ -30,47 +32,48 @@ def icon(self):
return qt.QIcon()

def helpText(self):
return """Make the selected segment hollow by replacing the segment with a uniform-thickness shell defined by the segment boundary."""
return _("""Make the selected segment hollow by replacing the segment with a uniform-thickness shell defined by the segment boundary.""")

def setupOptionsFrame(self):

operationLayout = qt.QVBoxLayout()

self.insideSurfaceOptionRadioButton = qt.QRadioButton("inside surface")
self.medialSurfaceOptionRadioButton = qt.QRadioButton("medial surface")
self.outsideSurfaceOptionRadioButton = qt.QRadioButton("outside surface")
self.insideSurfaceOptionRadioButton = qt.QRadioButton(_("inside surface"))
self.medialSurfaceOptionRadioButton = qt.QRadioButton(_("medial surface"))
self.outsideSurfaceOptionRadioButton = qt.QRadioButton(_("outside surface"))
operationLayout.addWidget(self.insideSurfaceOptionRadioButton)
operationLayout.addWidget(self.medialSurfaceOptionRadioButton)
operationLayout.addWidget(self.outsideSurfaceOptionRadioButton)
self.insideSurfaceOptionRadioButton.setChecked(True)

self.scriptedEffect.addLabeledOptionsWidget("Use current segment as:", operationLayout)
self.scriptedEffect.addLabeledOptionsWidget(_("Use current segment as:"), operationLayout)

self.shellThicknessMMSpinBox = slicer.qMRMLSpinBox()
self.shellThicknessMMSpinBox.setMRMLScene(slicer.mrmlScene)
self.shellThicknessMMSpinBox.setToolTip("Thickness of the hollow shell.")
self.shellThicknessMMSpinBox.setToolTip(_("Thickness of the hollow shell."))
self.shellThicknessMMSpinBox.quantity = "length"
self.shellThicknessMMSpinBox.minimum = 0.0
self.shellThicknessMMSpinBox.value = 3.0
self.shellThicknessMMSpinBox.singleStep = 1.0

self.shellThicknessLabel = qt.QLabel()
self.shellThicknessLabel.setToolTip("Closest achievable thickness. Constrained by the segmentation's binary labelmap representation spacing.")
self.shellThicknessLabel.setToolTip(_("Closest achievable thickness. Constrained by the segmentation's binary labelmap representation spacing."))

shellThicknessFrame = qt.QHBoxLayout()
shellThicknessFrame.addWidget(self.shellThicknessMMSpinBox)
self.shellThicknessMMLabel = self.scriptedEffect.addLabeledOptionsWidget("Shell thickness:", shellThicknessFrame)
self.shellThicknessMMLabel = self.scriptedEffect.addLabeledOptionsWidget(_("Shell thickness:"), shellThicknessFrame)
self.scriptedEffect.addLabeledOptionsWidget("", self.shellThicknessLabel)

self.applyToAllVisibleSegmentsCheckBox = qt.QCheckBox()
self.applyToAllVisibleSegmentsCheckBox.setToolTip("Apply hollow effect to all visible segments in this segmentation node. \
This operation may take a while.")
self.applyToAllVisibleSegmentsCheckBox.setToolTip(_("Apply hollow effect to all visible segments in this segmentation node. \
This operation may take a while."))
self.applyToAllVisibleSegmentsCheckBox.objectName = self.__class__.__name__ + 'ApplyToAllVisibleSegments'
self.applyToAllVisibleSegmentsLabel = self.scriptedEffect.addLabeledOptionsWidget("Apply to visible segments:", self.applyToAllVisibleSegmentsCheckBox)
self.applyToAllVisibleSegmentsLabel = self.scriptedEffect.addLabeledOptionsWidget(
_("Apply to visible segments:"), self.applyToAllVisibleSegmentsCheckBox)

self.applyButton = qt.QPushButton("Apply")
self.applyButton = qt.QPushButton(_("Apply"))
self.applyButton.objectName = self.__class__.__name__ + 'Apply'
self.applyButton.setToolTip("Makes the segment hollow by replacing it with a thick shell at the segment boundary.")
self.applyButton.setToolTip(_("Makes the segment hollow by replacing it with a thick shell at the segment boundary."))
self.scriptedEffect.addOptionsWidget(self.applyButton)

self.applyButton.connect('clicked()', self.onApply)
Expand Down Expand Up @@ -124,14 +127,14 @@ def updateGUIFromMRML(self):
selectedSegmentLabelmapSpacing = selectedSegmentLabelmap.GetSpacing()
shellThicknessPixel = self.getShellThicknessPixel()
if shellThicknessPixel[0] < 1 or shellThicknessPixel[1] < 1 or shellThicknessPixel[2] < 1:
self.shellThicknessLabel.text = "Not feasible at current resolution."
self.shellThicknessLabel.text = _("Not feasible at current resolution.")
self.applyButton.setEnabled(False)
else:
thicknessMM = self.getShellThicknessMM()
self.shellThicknessLabel.text = "Actual: {} x {} x {} mm ({}x{}x{} pixel)".format(*thicknessMM, *shellThicknessPixel)
self.shellThicknessLabel.text = _("Actual:") + " {} x {} x {} mm ({}x{}x{} pixel)".format(*thicknessMM, *shellThicknessPixel)
self.applyButton.setEnabled(True)
else:
self.shellThicknessLabel.text = "Empty segment"
self.shellThicknessLabel.text = _("Empty segment")

self.setWidgetMinMaxStepFromImageSpacing(self.shellThicknessMMSpinBox, self.scriptedEffect.selectedSegmentLabelmap())

Expand Down Expand Up @@ -242,7 +245,8 @@ def onApply(self):
# select input segments one by one, process
for index in range(inputSegmentIDs.GetNumberOfValues()):
segmentID = inputSegmentIDs.GetValue(index)
self.showStatusMessage(f'Processing {segmentationNode.GetSegmentation().GetSegment(segmentID).GetName()}...')
self.showStatusMessage(_('Processing {segmentName}...')
.format(segmentName=segmentationNode.GetSegmentation().GetSegment(segmentID).GetName()))
self.scriptedEffect.parameterSetNode().SetSelectedSegmentID(segmentID)
self.processHollowing()
# restore segment selection
Expand Down
Loading

0 comments on commit 934e344

Please sign in to comment.