From 222213be7d226e7d0cacd275b911316bf8b6dc39 Mon Sep 17 00:00:00 2001 From: Mouhamed DIOP Date: Wed, 6 Sep 2023 08:11:06 +0000 Subject: [PATCH] BUG: Make Segment Editor effect names translatable Segment Editor effects have now separate "name" (for modules to refer to the effect) and "title" (displayed on the GUI). This partially fixes these issues: - #6177 - https://github.com/Slicer/SlicerLanguagePacks/issues/12 --- Base/Python/slicer/i18n.py | 26 ++++- .../Loadable/Models/qSlicerModelsModule.cxx | 2 +- ...ScriptedSegmentEditorAutoCompleteEffect.py | 45 ++++---- .../AbstractScriptedSegmentEditorEffect.py | 0 ...bstractScriptedSegmentEditorLabelEffect.py | 0 ...bstractScriptedSegmentEditorPaintEffect.py | 0 .../{ => SegmentEditorEffects}/CMakeLists.txt | 7 +- .../Resources/Icons/Draw.png | Bin .../Resources/Icons/Erode.png | Bin .../Resources/Icons/FillBetweenSlices.png | Bin .../Resources/Icons/GrowFromSeeds.png | Bin .../Resources/Icons/Hollow.png | Bin .../Resources/Icons/Islands.png | Bin .../Resources/Icons/LevelTracing.png | Bin .../Resources/Icons/Logical.png | Bin .../Resources/Icons/Margin.png | Bin .../Resources/Icons/MaskVolume.png | Bin .../Resources/Icons/Smoothing.png | Bin .../Resources/Icons/Threshold.png | Bin .../SegmentEditorDrawEffect.py | 21 ++-- .../SegmentEditorFillBetweenSlicesEffect.py | 27 +++-- .../SegmentEditorGrowFromSeedsEffect.py | 42 +++---- .../SegmentEditorHollowEffect.py | 40 ++++--- .../SegmentEditorIslandsEffect.py | 54 ++++----- .../SegmentEditorLevelTracingEffect.py | 23 ++-- .../SegmentEditorLogicalEffect.py | 57 +++++----- .../SegmentEditorMarginEffect.py | 39 ++++--- .../SegmentEditorMaskVolumeEffect.py | 63 ++++++----- .../SegmentEditorSmoothingEffect.py | 59 +++++----- .../SegmentEditorThresholdEffect.py | 103 +++++++++--------- .../__init__.py} | 0 .../qSlicerSegmentEditorAbstractEffect.cxx | 24 ++++ .../qSlicerSegmentEditorAbstractEffect.h | 19 +++- .../qSlicerSegmentEditorEraseEffect.cxx | 1 + .../qSlicerSegmentEditorPaintEffect.cxx | 1 + .../qSlicerSegmentEditorScissorsEffect.cxx | 1 + .../Widgets/qMRMLSegmentEditorWidget.cxx | 6 +- ...RMLSegmentationRepresentationsListView.cxx | 3 +- 38 files changed, 381 insertions(+), 282 deletions(-) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/AbstractScriptedSegmentEditorAutoCompleteEffect.py (94%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/AbstractScriptedSegmentEditorEffect.py (100%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/AbstractScriptedSegmentEditorLabelEffect.py (100%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/AbstractScriptedSegmentEditorPaintEffect.py (100%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/CMakeLists.txt (84%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/Resources/Icons/Draw.png (100%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/Resources/Icons/Erode.png (100%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/Resources/Icons/FillBetweenSlices.png (100%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/Resources/Icons/GrowFromSeeds.png (100%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/Resources/Icons/Hollow.png (100%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/Resources/Icons/Islands.png (100%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/Resources/Icons/LevelTracing.png (100%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/Resources/Icons/Logical.png (100%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/Resources/Icons/Margin.png (100%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/Resources/Icons/MaskVolume.png (100%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/Resources/Icons/Smoothing.png (100%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/Resources/Icons/Threshold.png (100%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/SegmentEditorDrawEffect.py (94%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/SegmentEditorFillBetweenSlicesEffect.py (56%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/SegmentEditorGrowFromSeedsEffect.py (72%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/SegmentEditorHollowEffect.py (85%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/SegmentEditorIslandsEffect.py (88%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/SegmentEditorLevelTracingEffect.py (90%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/SegmentEditorLogicalEffect.py (85%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/SegmentEditorMarginEffect.py (84%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/SegmentEditorMaskVolumeEffect.py (88%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/SegmentEditorSmoothingEffect.py (90%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{ => SegmentEditorEffects}/SegmentEditorThresholdEffect.py (92%) rename Modules/Loadable/Segmentations/EditorEffects/Python/{SegmentEditorEffects.__init__.py.in => SegmentEditorEffects/__init__.py} (100%) diff --git a/Base/Python/slicer/i18n.py b/Base/Python/slicer/i18n.py index 93fc389b831..b965820c3ac 100644 --- a/Base/Python/slicer/i18n.py +++ b/Base/Python/slicer/i18n.py @@ -12,7 +12,17 @@ def translate(context, text): def getContext(sourceFile): - """Get the translation context based on the source file name.""" + """Get the translation context name. + + The context name is constructed from the Python module name (name of the folder that contains `__init__.py` file - if exists) + and the source file name (without the .py extension). + + Most Slicer modules do not have a __init__.py file in their folder, so the context name is simply the source file name + (for example, `DICOMEnhancedUSVolumePlugin`). + + Most helper Python scripts in Slicer are Python modules (subfolders containing addition Python scripts and an __init__.py file) + and their name is constructed as PythonModuleName.SourceFileName (for example, `SegmentEditorEffects.SegmentEditorDrawEffect.`). + """ if os.path.isfile(sourceFile): parentFolder = os.path.dirname(sourceFile) init_file_path = parentFolder + os.path.sep + '__init__.py' @@ -28,8 +38,18 @@ def getContext(sourceFile): def tr(text): - """Translation function for python scripted modules that gets context name from filename. - Experimental, not used yet.""" + """Translation function for python scripted modules that automatically determines context name. + + This is more convenient to use than `translate(context, text)` because the developer does not need to manually specify the context. + This function is typically imported as `_` function. + + Example:: + + from slicer.i18n import tr as _ + ... + statusText = _("Idle") if idle else _("Running") + + """ filename = inspect.stack()[1][1] contextName = getContext(filename) return translate(contextName, text) diff --git a/Modules/Loadable/Models/qSlicerModelsModule.cxx b/Modules/Loadable/Models/qSlicerModelsModule.cxx index 552ab9efcfd..27d0b662dd6 100644 --- a/Modules/Loadable/Models/qSlicerModelsModule.cxx +++ b/Modules/Loadable/Models/qSlicerModelsModule.cxx @@ -94,7 +94,7 @@ QString qSlicerModelsModule::helpText()const //----------------------------------------------------------------------------- QString qSlicerModelsModule::acknowledgementText()const { - return "This work was partially funded by NIH grants 3P41RR013218-12S1 and R01CA184354."; + return tr("This work was partially funded by NIH grants 3P41RR013218-12S1 and R01CA184354."); } //----------------------------------------------------------------------------- diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/AbstractScriptedSegmentEditorAutoCompleteEffect.py b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/AbstractScriptedSegmentEditorAutoCompleteEffect.py similarity index 94% rename from Modules/Loadable/Segmentations/EditorEffects/Python/AbstractScriptedSegmentEditorAutoCompleteEffect.py rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/AbstractScriptedSegmentEditorAutoCompleteEffect.py index 735efe52fa9..ed54b957221 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/Python/AbstractScriptedSegmentEditorAutoCompleteEffect.py +++ b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/AbstractScriptedSegmentEditorAutoCompleteEffect.py @@ -5,6 +5,7 @@ import vtk import slicer +from slicer.i18n import tr as _ from .AbstractScriptedSegmentEditorEffect import * @@ -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() @@ -103,10 +104,10 @@ 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 @@ -114,24 +115,24 @@ def setupOptionsFrame(self): 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) @@ -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() @@ -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) @@ -404,7 +405,8 @@ def effectiveExtentChanged(self): # Current extent used for auto-complete preview currentLabelExtent = self.mergedLabelmapGeometryImage.GetExtent() - # Determine if the current merged labelmap extent has less than a 3 voxel margin around the effective segment extent (limited by the master image extent) + # Determine if the current merged labelmap extent has less than a 3 voxel margin around the effective segment extent + # (limited by the master image extent) return ((masterImageExtent[0] != currentLabelExtent[0] and currentLabelExtent[0] > effectiveLabelExtent[0] - self.minimumExtentMargin) or (masterImageExtent[1] != currentLabelExtent[1] and currentLabelExtent[1] < effectiveLabelExtent[1] + self.minimumExtentMargin) or (masterImageExtent[2] != currentLabelExtent[2] and currentLabelExtent[2] > effectiveLabelExtent[2] - self.minimumExtentMargin) or @@ -522,8 +524,9 @@ def preview(self): # as the closed surfaces will be converted as necessary by the segmentation logic. mergedImage = slicer.vtkOrientedImageData() - segmentationNode.GenerateMergedLabelmapForAllSegments(mergedImage, - vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_EFFECTIVE_SEGMENTS, self.mergedLabelmapGeometryImage, self.selectedSegmentIds) + segmentationNode.GenerateMergedLabelmapForAllSegments( + mergedImage, + vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_EFFECTIVE_SEGMENTS, self.mergedLabelmapGeometryImage, self.selectedSegmentIds) outputLabelmap = slicer.vtkOrientedImageData() self.computePreviewLabelmap(mergedImage, outputLabelmap) diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/AbstractScriptedSegmentEditorEffect.py b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/AbstractScriptedSegmentEditorEffect.py similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/AbstractScriptedSegmentEditorEffect.py rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/AbstractScriptedSegmentEditorEffect.py diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/AbstractScriptedSegmentEditorLabelEffect.py b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/AbstractScriptedSegmentEditorLabelEffect.py similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/AbstractScriptedSegmentEditorLabelEffect.py rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/AbstractScriptedSegmentEditorLabelEffect.py diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/AbstractScriptedSegmentEditorPaintEffect.py b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/AbstractScriptedSegmentEditorPaintEffect.py similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/AbstractScriptedSegmentEditorPaintEffect.py rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/AbstractScriptedSegmentEditorPaintEffect.py diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/CMakeLists.txt b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/CMakeLists.txt similarity index 84% rename from Modules/Loadable/Segmentations/EditorEffects/Python/CMakeLists.txt rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/CMakeLists.txt index 5d0d2a35b9c..fd345eacd4d 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/Python/CMakeLists.txt +++ b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/CMakeLists.txt @@ -1,11 +1,6 @@ -#----------------------------------------------------------------------------- -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/SegmentEditorEffects.__init__.py.in - ${CMAKE_BINARY_DIR}/${Slicer_QTSCRIPTEDMODULES_LIB_DIR}/SegmentEditorEffects/__init__.py - ) - #----------------------------------------------------------------------------- set(SegmentEditorEffects_PYTHON_SCRIPTS + __init__ AbstractScriptedSegmentEditorEffect AbstractScriptedSegmentEditorLabelEffect AbstractScriptedSegmentEditorPaintEffect diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Draw.png b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Draw.png similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Draw.png rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Draw.png diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Erode.png b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Erode.png similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Erode.png rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Erode.png diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/FillBetweenSlices.png b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/FillBetweenSlices.png similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/FillBetweenSlices.png rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/FillBetweenSlices.png diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/GrowFromSeeds.png b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/GrowFromSeeds.png similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/GrowFromSeeds.png rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/GrowFromSeeds.png diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Hollow.png b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Hollow.png similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Hollow.png rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Hollow.png diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Islands.png b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Islands.png similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Islands.png rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Islands.png diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/LevelTracing.png b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/LevelTracing.png similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/LevelTracing.png rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/LevelTracing.png diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Logical.png b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Logical.png similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Logical.png rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Logical.png diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Margin.png b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Margin.png similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Margin.png rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Margin.png diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/MaskVolume.png b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/MaskVolume.png similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/MaskVolume.png rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/MaskVolume.png diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Smoothing.png b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Smoothing.png similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Smoothing.png rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Smoothing.png diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Threshold.png b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Threshold.png similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/Resources/Icons/Threshold.png rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/Resources/Icons/Threshold.png diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorDrawEffect.py b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorDrawEffect.py similarity index 94% rename from Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorDrawEffect.py rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorDrawEffect.py index b3e70323c4b..764a05a62ba 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorDrawEffect.py +++ b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorDrawEffect.py @@ -5,6 +5,7 @@ import vtk import slicer +from slicer.i18n import tr as _ from SegmentEditorEffects import * @@ -15,7 +16,8 @@ class SegmentEditorDrawEffect(AbstractScriptedSegmentEditorLabelEffect): """ def __init__(self, scriptedEffect): - scriptedEffect.name = 'Draw' + scriptedEffect.name = 'Draw' # no tr (don't translate it because modules find effects by this name) + scriptedEffect.title = _('Draw') self.drawPipelines = {} AbstractScriptedSegmentEditorLabelEffect.__init__(self, scriptedEffect) @@ -32,13 +34,13 @@ def icon(self): return qt.QIcon() def helpText(self): - return """Draw segment outline in slice viewers
. -

""" + return _("""Draw segment outline in slice viewers
. +

""") def deactivate(self): # Clear draw pipelines @@ -92,7 +94,8 @@ def processInteractionEvents(self, callerInteractor, eventId, viewWidget): sliceNode = viewWidget.sliceLogic().GetSliceNode() pipeline.lastInsertSliceNodeMTime = sliceNode.GetMTime() abortEvent = True - elif (eventId == vtk.vtkCommand.RightButtonReleaseEvent and pipeline.actionState == "finishing") or (eventId == vtk.vtkCommand.LeftButtonDoubleClickEvent and not anyModifierKeyPressed): + elif ((eventId == vtk.vtkCommand.RightButtonReleaseEvent and pipeline.actionState == "finishing") + or (eventId == vtk.vtkCommand.LeftButtonDoubleClickEvent and not anyModifierKeyPressed)): abortEvent = (pipeline.rasPoints.GetNumberOfPoints() > 1) sliceNode = viewWidget.sliceLogic().GetSliceNode() if abs(pipeline.lastInsertSliceNodeMTime - sliceNode.GetMTime()) < 2: diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorFillBetweenSlicesEffect.py b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorFillBetweenSlicesEffect.py similarity index 56% rename from Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorFillBetweenSlicesEffect.py rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorFillBetweenSlicesEffect.py index 7b881a8ec32..0a1b15b8ea2 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorFillBetweenSlicesEffect.py +++ b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorFillBetweenSlicesEffect.py @@ -3,6 +3,8 @@ import qt import vtk +from slicer.i18n import tr as _ + from SegmentEditorEffects import * @@ -14,7 +16,8 @@ class SegmentEditorFillBetweenSlicesEffect(AbstractScriptedSegmentEditorAutoComp def __init__(self, scriptedEffect): AbstractScriptedSegmentEditorAutoCompleteEffect.__init__(self, scriptedEffect) - scriptedEffect.name = 'Fill between slices' + scriptedEffect.name = 'Fill between slices' # no tr (don't translate it because modules find effects by this name) + scriptedEffect.title = _('Fill between slices') def clone(self): import qSlicerSegmentationsEditorEffectsPythonQt as effects @@ -29,17 +32,17 @@ def icon(self): return qt.QIcon() def helpText(self): - return """Interpolate segmentation between slices
. Instructions: -

-Masking settings are ignored. If segments overlap, segment higher in the segments table will have priority. -The effect uses morphological contour interpolation method. -

""" + return _("""Interpolate segmentation between slices
. Instructions: +

+ Masking settings are ignored. If segments overlap, segment higher in the segments table will have priority. + The effect uses morphological contour interpolation method. +

""") def computePreviewLabelmap(self, mergedImage, outputLabelmap): import vtkITK diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorGrowFromSeedsEffect.py b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorGrowFromSeedsEffect.py similarity index 72% rename from Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorGrowFromSeedsEffect.py rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorGrowFromSeedsEffect.py index 1d9b9980720..aeb383e38cc 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorGrowFromSeedsEffect.py +++ b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorGrowFromSeedsEffect.py @@ -6,6 +6,7 @@ import vtk import slicer +from slicer.i18n import tr as _ from SegmentEditorEffects import * @@ -18,7 +19,8 @@ class SegmentEditorGrowFromSeedsEffect(AbstractScriptedSegmentEditorAutoComplete def __init__(self, scriptedEffect): AbstractScriptedSegmentEditorAutoCompleteEffect.__init__(self, scriptedEffect) - scriptedEffect.name = 'Grow from seeds' + scriptedEffect.name = 'Grow from seeds' # no tr (don't translate it because modules find effects by this name) + 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 @@ -37,21 +39,21 @@ def icon(self): return qt.QIcon() def helpText(self): - return """Growing segments to create complete segmentation
. -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:

-

-If segments overlap, segment higher in the segments table will have priority. -The effect uses fast grow-cut method. -

""" + return _("""Growing segments to create complete segmentation
. + 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:

+

+ If segments overlap, segment higher in the segments table will have priority. + The effect uses fast grow-cut method. +

""") def reset(self): self.growCutFilter = None @@ -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): diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorHollowEffect.py b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorHollowEffect.py similarity index 85% rename from Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorHollowEffect.py rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorHollowEffect.py index d444f12124e..91afbf34c0c 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorHollowEffect.py +++ b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorHollowEffect.py @@ -6,6 +6,7 @@ import vtk import slicer +from slicer.i18n import tr as _ from SegmentEditorEffects import * @@ -14,7 +15,8 @@ class SegmentEditorHollowEffect(AbstractScriptedSegmentEditorEffect): """This effect makes a segment hollow by replacing it with a shell at the segment boundary""" def __init__(self, scriptedEffect): - scriptedEffect.name = 'Hollow' + scriptedEffect.name = 'Hollow' # no tr (don't translate it because modules find effects by this name) + scriptedEffect.title = _('Hollow') AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect) def clone(self): @@ -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) @@ -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()) @@ -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 diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorIslandsEffect.py b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorIslandsEffect.py similarity index 88% rename from Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorIslandsEffect.py rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorIslandsEffect.py index 0c93eb1c2d5..d1bd5f50ad6 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorIslandsEffect.py +++ b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorIslandsEffect.py @@ -6,6 +6,7 @@ import vtkITK import slicer +from slicer.i18n import tr as _ from SegmentEditorEffects import * @@ -15,7 +16,8 @@ class SegmentEditorIslandsEffect(AbstractScriptedSegmentEditorEffect): """ def __init__(self, scriptedEffect): - scriptedEffect.name = 'Islands' + scriptedEffect.name = 'Islands' # no tr (don't translate it because modules find effects by this name) + scriptedEffect.title = _('Islands') AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect) self.widgetToOperationNameMap = {} @@ -32,46 +34,46 @@ def icon(self): return qt.QIcon() def helpText(self): - return """Edit islands (connected components) in a segment
. To get more information -about each operation, hover the mouse over the option and wait for the tooltip to appear.""" + return _("""Edit islands (connected components) in a segment
. To get more information +about each operation, hover the mouse over the option and wait for the tooltip to appear.""") def setupOptionsFrame(self): self.operationRadioButtons = [] - self.keepLargestOptionRadioButton = qt.QRadioButton("Keep largest island") + self.keepLargestOptionRadioButton = qt.QRadioButton(_("Keep largest island")) self.keepLargestOptionRadioButton.setToolTip( - "Keep only the largest island in selected segment, remove all other islands in the segment.") + _("Keep only the largest island in selected segment, remove all other islands in the segment.")) self.operationRadioButtons.append(self.keepLargestOptionRadioButton) self.widgetToOperationNameMap[self.keepLargestOptionRadioButton] = KEEP_LARGEST_ISLAND - self.keepSelectedOptionRadioButton = qt.QRadioButton("Keep selected island") + self.keepSelectedOptionRadioButton = qt.QRadioButton(_("Keep selected island")) self.keepSelectedOptionRadioButton.setToolTip( - "Click on an island in a slice view to keep that island and remove all other islands in selected segment.") + _("Click on an island in a slice view to keep that island and remove all other islands in selected segment.")) self.operationRadioButtons.append(self.keepSelectedOptionRadioButton) self.widgetToOperationNameMap[self.keepSelectedOptionRadioButton] = KEEP_SELECTED_ISLAND - self.removeSmallOptionRadioButton = qt.QRadioButton("Remove small islands") + self.removeSmallOptionRadioButton = qt.QRadioButton(_("Remove small islands")) self.removeSmallOptionRadioButton.setToolTip( - "Remove all islands from the selected segment that are smaller than the specified minimum size.") + _("Remove all islands from the selected segment that are smaller than the specified minimum size.")) self.operationRadioButtons.append(self.removeSmallOptionRadioButton) self.widgetToOperationNameMap[self.removeSmallOptionRadioButton] = REMOVE_SMALL_ISLANDS - self.removeSelectedOptionRadioButton = qt.QRadioButton("Remove selected island") + self.removeSelectedOptionRadioButton = qt.QRadioButton(_("Remove selected island")) self.removeSelectedOptionRadioButton.setToolTip( - "Click on an island in a slice view to remove it from selected segment.") + _("Click on an island in a slice view to remove it from selected segment.")) self.operationRadioButtons.append(self.removeSelectedOptionRadioButton) self.widgetToOperationNameMap[self.removeSelectedOptionRadioButton] = REMOVE_SELECTED_ISLAND - self.addSelectedOptionRadioButton = qt.QRadioButton("Add selected island") + self.addSelectedOptionRadioButton = qt.QRadioButton(_("Add selected island")) self.addSelectedOptionRadioButton.setToolTip( - "Click on a region in a slice view to add it to selected segment.") + _("Click on a region in a slice view to add it to selected segment.")) self.operationRadioButtons.append(self.addSelectedOptionRadioButton) self.widgetToOperationNameMap[self.addSelectedOptionRadioButton] = ADD_SELECTED_ISLAND - self.splitAllOptionRadioButton = qt.QRadioButton("Split islands to segments") + self.splitAllOptionRadioButton = qt.QRadioButton(_("Split islands to segments")) self.splitAllOptionRadioButton.setToolTip( - "Create a new segment for each island of selected segment. Islands smaller than minimum size will be removed. " + - "Segments will be ordered by island size.") + _("Create a new segment for each island of selected segment. Islands smaller than minimum size will be removed. " + "Segments will be ordered by island size.")) self.operationRadioButtons.append(self.splitAllOptionRadioButton) self.widgetToOperationNameMap[self.splitAllOptionRadioButton] = SPLIT_ISLANDS_TO_SEGMENTS @@ -87,20 +89,21 @@ def setupOptionsFrame(self): self.scriptedEffect.addOptionsWidget(operationLayout) self.minimumSizeSpinBox = qt.QSpinBox() - self.minimumSizeSpinBox.setToolTip("Islands consisting of less voxels than this minimum size, will be deleted.") + self.minimumSizeSpinBox.setToolTip(_("Islands consisting of less voxels than this minimum size, will be deleted.")) self.minimumSizeSpinBox.setMinimum(0) self.minimumSizeSpinBox.setMaximum(vtk.VTK_INT_MAX) self.minimumSizeSpinBox.setValue(1000) - self.minimumSizeSpinBox.suffix = " voxels" - self.minimumSizeLabel = self.scriptedEffect.addLabeledOptionsWidget("Minimum size:", self.minimumSizeSpinBox) + self.minimumSizeSpinBox.suffix = _(" voxels") + self.minimumSizeLabel = self.scriptedEffect.addLabeledOptionsWidget(_("Minimum size:"), self.minimumSizeSpinBox) - self.applyButton = qt.QPushButton("Apply") + self.applyButton = qt.QPushButton(_("Apply")) self.applyButton.objectName = self.__class__.__name__ + 'Apply' self.scriptedEffect.addOptionsWidget(self.applyButton) for operationRadioButton in self.operationRadioButtons: - operationRadioButton.connect('toggled(bool)', - lambda toggle, widget=self.widgetToOperationNameMap[operationRadioButton]: self.onOperationSelectionChanged(widget, toggle)) + operationRadioButton.connect( + 'toggled(bool)', + lambda toggle, widget=self.widgetToOperationNameMap[operationRadioButton]: self.onOperationSelectionChanged(widget, toggle)) self.minimumSizeSpinBox.connect('valueChanged(int)', self.updateMRMLFromGUI) @@ -204,8 +207,9 @@ def splitSegments(self, minimumSize=0, maxNumberOfSegments=0, split=True): segment = slicer.vtkSegment() name = baseSegmentName + "_" + str(i + 1) segment.SetName(name) - segment.AddRepresentation(slicer.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName(), - selectedSegment.GetRepresentation(slicer.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName())) + segment.AddRepresentation( + slicer.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName(), + selectedSegment.GetRepresentation(slicer.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName())) segmentation.AddSegment(segment) segmentID = segmentation.GetSegmentIdBySegment(segment) segment.SetLabelValue(segmentation.GetUniqueLabelValueForSharedLabelmap(selectedSegmentID)) @@ -384,7 +388,7 @@ def updateGUIFromMRML(self): segmentSelectionRequired = self.currentOperationRequiresSegmentSelection() self.applyButton.setEnabled(not segmentSelectionRequired) if segmentSelectionRequired: - self.applyButton.setToolTip("Click in a slice view to select an island.") + self.applyButton.setToolTip(_("Click in a slice view to select an island.")) else: self.applyButton.setToolTip("") diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorLevelTracingEffect.py b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorLevelTracingEffect.py similarity index 90% rename from Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorLevelTracingEffect.py rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorLevelTracingEffect.py index c2137e96ce1..6869fc0f905 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorLevelTracingEffect.py +++ b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorLevelTracingEffect.py @@ -6,6 +6,7 @@ import vtkITK import slicer +from slicer.i18n import tr as _ from SegmentEditorEffects import * @@ -16,7 +17,8 @@ class SegmentEditorLevelTracingEffect(AbstractScriptedSegmentEditorLabelEffect): """ def __init__(self, scriptedEffect): - scriptedEffect.name = 'Level tracing' + scriptedEffect.name = 'Level tracing' # no tr (don't translate it because modules find effects by this name) + scriptedEffect.title = _('Level tracing') AbstractScriptedSegmentEditorLabelEffect.__init__(self, scriptedEffect) # Effect-specific members @@ -36,12 +38,12 @@ def icon(self): return qt.QIcon() def helpText(self): - return """Add uniform intensity region to selected segment
. -

""" + return _("""Add uniform intensity region to selected segment
. +

""") def setupOptionsFrame(self): self.sliceRotatedErrorLabel = qt.QLabel() @@ -99,9 +101,10 @@ def processInteractionEvents(self, callerInteractor, eventId, viewWidget): if pipeline.preview(xy): self.sliceRotatedErrorLabel.text = "" else: - self.sliceRotatedErrorLabel.text = ("" - + "Slice view is not aligned with segmentation axis.
To use this effect, click the 'Slice views orientation' warning button." - + "
") + self.sliceRotatedErrorLabel.text = ( + '' + + _("Slice view is not aligned with segmentation axis.
To use this effect, click the 'Slice views orientation' warning button.") + + '
') abortEvent = True self.lastXY = xy elif eventId == vtk.vtkCommand.EnterEvent: diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorLogicalEffect.py b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorLogicalEffect.py similarity index 85% rename from Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorLogicalEffect.py rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorLogicalEffect.py index b0901ddd74f..9484f85c114 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorLogicalEffect.py +++ b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorLogicalEffect.py @@ -5,6 +5,7 @@ import vtk import slicer +from slicer.i18n import tr as _ from SegmentEditorEffects import * @@ -14,7 +15,8 @@ class SegmentEditorLogicalEffect(AbstractScriptedSegmentEditorEffect): """ def __init__(self, scriptedEffect): - scriptedEffect.name = 'Logical operators' + scriptedEffect.name = 'Logical operators' # no tr (don't translate it because modules find effects by this name) + scriptedEffect.title = _('Logical operators') self.operationsRequireModifierSegment = [LOGICAL_COPY, LOGICAL_UNION, LOGICAL_SUBTRACT, LOGICAL_INTERSECT] AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect) @@ -31,7 +33,7 @@ def icon(self): return qt.QIcon() def helpText(self): - return """Apply logical operators or combine segments
. Available operations:

+ return _("""Apply logical operators or combine segments
. Available operations:

-Selected segment: segment selected in the segment list - above. Modifier segment: segment chosen in segment list in effect options - below. -

""" +Selected segment: segment selected in the segment list - above. Modifier segment: segment chosen in +segment list in effect options - below. +

""") def setupOptionsFrame(self): self.methodSelectorComboBox = qt.QComboBox() - self.methodSelectorComboBox.addItem("Copy", LOGICAL_COPY) - self.methodSelectorComboBox.addItem("Add", LOGICAL_UNION) - self.methodSelectorComboBox.addItem("Subtract", LOGICAL_SUBTRACT) - self.methodSelectorComboBox.addItem("Intersect", LOGICAL_INTERSECT) - self.methodSelectorComboBox.addItem("Invert", LOGICAL_INVERT) - self.methodSelectorComboBox.addItem("Clear", LOGICAL_CLEAR) - self.methodSelectorComboBox.addItem("Fill", LOGICAL_FILL) - self.methodSelectorComboBox.setToolTip('Click Show details link above for description of operations.') - - self.bypassMaskingCheckBox = qt.QCheckBox("Bypass masking") - self.bypassMaskingCheckBox.setToolTip("Ignore all masking options and only modify the selected segment.") + self.methodSelectorComboBox.addItem(_("Copy"), LOGICAL_COPY) + self.methodSelectorComboBox.addItem(_("Add"), LOGICAL_UNION) + self.methodSelectorComboBox.addItem(_("Subtract"), LOGICAL_SUBTRACT) + self.methodSelectorComboBox.addItem(_("Intersect"), LOGICAL_INTERSECT) + self.methodSelectorComboBox.addItem(_("Invert"), LOGICAL_INVERT) + self.methodSelectorComboBox.addItem(_("Clear"), LOGICAL_CLEAR) + self.methodSelectorComboBox.addItem(_("Fill"), LOGICAL_FILL) + self.methodSelectorComboBox.setToolTip(_('Click Show details link above for description of operations.')) + + self.bypassMaskingCheckBox = qt.QCheckBox(_("Bypass masking")) + self.bypassMaskingCheckBox.setToolTip(_("Ignore all masking options and only modify the selected segment.")) self.bypassMaskingCheckBox.objectName = self.__class__.__name__ + 'BypassMasking' - self.applyButton = qt.QPushButton("Apply") + self.applyButton = qt.QPushButton(_("Apply")) self.applyButton.objectName = self.__class__.__name__ + 'Apply' operationFrame = qt.QHBoxLayout() operationFrame.addWidget(self.methodSelectorComboBox) operationFrame.addWidget(self.applyButton) operationFrame.addWidget(self.bypassMaskingCheckBox) - self.marginSizeMmLabel = self.scriptedEffect.addLabeledOptionsWidget("Operation:", operationFrame) + self.marginSizeMmLabel = self.scriptedEffect.addLabeledOptionsWidget(_("Operation:"), operationFrame) - self.modifierSegmentSelectorLabel = qt.QLabel("Modifier segment:") + self.modifierSegmentSelectorLabel = qt.QLabel(_("Modifier segment:")) self.scriptedEffect.addOptionsWidget(self.modifierSegmentSelectorLabel) self.modifierSegmentSelector = slicer.qMRMLSegmentsTableView() @@ -79,7 +82,8 @@ def setupOptionsFrame(self): self.modifierSegmentSelector.opacityColumnVisible = False self.modifierSegmentSelector.setMRMLScene(slicer.mrmlScene) - self.modifierSegmentSelector.setToolTip('Contents of this segment will be used for modifying the selected segment. This segment itself will not be changed.') + self.modifierSegmentSelector.setToolTip(_('Contents of this segment will be used for modifying the selected segment. ' + 'This segment itself will not be changed.')) self.scriptedEffect.addOptionsWidget(self.modifierSegmentSelector) self.applyButton.connect('clicked()', self.onApply) @@ -127,18 +131,18 @@ def updateGUIFromMRML(self): self.modifierSegmentSelector.setVisible(modifierSegmentRequired) if operation == LOGICAL_COPY: - self.modifierSegmentSelectorLabel.text = "Copy from segment:" + self.modifierSegmentSelectorLabel.text = _("Copy from segment:") elif operation == LOGICAL_UNION: - self.modifierSegmentSelectorLabel.text = "Add segment:" + self.modifierSegmentSelectorLabel.text = _("Add segment:") elif operation == LOGICAL_SUBTRACT: - self.modifierSegmentSelectorLabel.text = "Subtract segment:" + self.modifierSegmentSelectorLabel.text = _("Subtract segment:") elif operation == LOGICAL_INTERSECT: - self.modifierSegmentSelectorLabel.text = "Intersect with segment:" + self.modifierSegmentSelectorLabel.text = _("Intersect with segment:") else: - self.modifierSegmentSelectorLabel.text = "Modifier segment:" + self.modifierSegmentSelectorLabel.text = _("Modifier segment:") if modifierSegmentRequired and not modifierSegmentID: - self.applyButton.setToolTip("Please select a modifier segment in the list below.") + self.applyButton.setToolTip(_("Please select a modifier segment in the list below.")) self.applyButton.enabled = False else: self.applyButton.setToolTip("") @@ -263,7 +267,8 @@ def onApply(self): elif operation == LOGICAL_CLEAR or operation == LOGICAL_FILL: selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() - vtkSegmentationCore.vtkOrientedImageDataResample.FillImage(selectedSegmentLabelmap, 1 if operation == LOGICAL_FILL else 0, selectedSegmentLabelmap.GetExtent()) + vtkSegmentationCore.vtkOrientedImageDataResample.FillImage( + selectedSegmentLabelmap, 1 if operation == LOGICAL_FILL else 0, selectedSegmentLabelmap.GetExtent()) self.scriptedEffect.modifySelectedSegmentByLabelmap( selectedSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet, bypassMasking) diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorMarginEffect.py b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorMarginEffect.py similarity index 84% rename from Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorMarginEffect.py rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorMarginEffect.py index 388fe4e53bb..896ceb4e9fe 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorMarginEffect.py +++ b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorMarginEffect.py @@ -6,6 +6,7 @@ import vtk import slicer +from slicer.i18n import tr as _ from SegmentEditorEffects import * @@ -15,7 +16,8 @@ class SegmentEditorMarginEffect(AbstractScriptedSegmentEditorEffect): """ def __init__(self, scriptedEffect): - scriptedEffect.name = 'Margin' + scriptedEffect.name = 'Margin' # no tr (don't translate it because modules find effects by this name) + scriptedEffect.title = _('Margin') AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect) def clone(self): @@ -31,44 +33,46 @@ def icon(self): return qt.QIcon() def helpText(self): - return "Grow or shrink selected segment by specified margin size." + return _("Grow or shrink selected segment by specified margin size.") def setupOptionsFrame(self): operationLayout = qt.QVBoxLayout() - self.shrinkOptionRadioButton = qt.QRadioButton("Shrink") - self.growOptionRadioButton = qt.QRadioButton("Grow") + self.shrinkOptionRadioButton = qt.QRadioButton(_("Shrink")) + self.growOptionRadioButton = qt.QRadioButton(_("Grow")) operationLayout.addWidget(self.shrinkOptionRadioButton) operationLayout.addWidget(self.growOptionRadioButton) self.growOptionRadioButton.setChecked(True) - self.scriptedEffect.addLabeledOptionsWidget("Operation:", operationLayout) + self.scriptedEffect.addLabeledOptionsWidget(_("Operation:"), operationLayout) self.marginSizeMMSpinBox = slicer.qMRMLSpinBox() self.marginSizeMMSpinBox.setMRMLScene(slicer.mrmlScene) - self.marginSizeMMSpinBox.setToolTip("Segment boundaries will be shifted by this distance. Positive value means the segments will grow, negative value means segment will shrink.") + self.marginSizeMMSpinBox.setToolTip(_("Segment boundaries will be shifted by this distance. " + "Positive value means the segments will grow, negative value means segment will shrink.")) self.marginSizeMMSpinBox.quantity = "length" self.marginSizeMMSpinBox.value = 3.0 self.marginSizeMMSpinBox.singleStep = 1.0 self.marginSizeLabel = qt.QLabel() - self.marginSizeLabel.setToolTip("Size change in pixel. Computed from the segment's spacing and the specified margin size.") + self.marginSizeLabel.setToolTip(_("Size change in pixel. Computed from the segment's spacing and the specified margin size.")) marginSizeFrame = qt.QHBoxLayout() marginSizeFrame.addWidget(self.marginSizeMMSpinBox) - self.marginSizeMMLabel = self.scriptedEffect.addLabeledOptionsWidget("Margin size:", marginSizeFrame) + self.marginSizeMMLabel = self.scriptedEffect.addLabeledOptionsWidget(_("Margin size:"), marginSizeFrame) self.scriptedEffect.addLabeledOptionsWidget("", self.marginSizeLabel) self.applyToAllVisibleSegmentsCheckBox = qt.QCheckBox() - self.applyToAllVisibleSegmentsCheckBox.setToolTip("Grow or shrink all visible segments in this segmentation node. \ - This operation may take a while.") + self.applyToAllVisibleSegmentsCheckBox.setToolTip( + _("Grow or shrink 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("Grows or shrinks selected segment /default) or all segments (checkbox) by the specified margin.") + self.applyButton.setToolTip(_("Grows or shrinks selected segment /default) or all segments (checkbox) by the specified margin.")) self.scriptedEffect.addOptionsWidget(self.applyButton) self.applyButton.connect('clicked()', self.onApply) @@ -115,14 +119,14 @@ def updateGUIFromMRML(self): selectedSegmentLabelmapSpacing = selectedSegmentLabelmap.GetSpacing() marginSizePixel = self.getMarginSizePixel() if marginSizePixel[0] < 1 or marginSizePixel[1] < 1 or marginSizePixel[2] < 1: - self.marginSizeLabel.text = "Not feasible at current resolution." + self.marginSizeLabel.text = _("Not feasible at current resolution.") self.applyButton.setEnabled(False) else: marginSizeMM = self.getMarginSizeMM() - self.marginSizeLabel.text = "Actual: {} x {} x {} mm ({}x{}x{} pixel)".format(*marginSizeMM, *marginSizePixel) + self.marginSizeLabel.text = _("Actual:") + " {} x {} x {} mm ({}x{}x{} pixel)".format(*marginSizeMM, *marginSizePixel) self.applyButton.setEnabled(True) else: - self.marginSizeLabel.text = "Empty segment" + self.marginSizeLabel.text = _("Empty segment") applyToAllVisibleSegments = qt.Qt.Unchecked if self.scriptedEffect.integerParameter("ApplyToAllVisibleSegments") == 0 else qt.Qt.Checked wasBlocked = self.applyToAllVisibleSegmentsCheckBox.blockSignals(True) @@ -234,7 +238,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.processMargin() # restore segment selection diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorMaskVolumeEffect.py b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorMaskVolumeEffect.py similarity index 88% rename from Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorMaskVolumeEffect.py rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorMaskVolumeEffect.py index 7d0ca7f8391..d31c1314a54 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorMaskVolumeEffect.py +++ b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorMaskVolumeEffect.py @@ -3,13 +3,16 @@ import logging from SegmentEditorEffects import * +from slicer.i18n import tr as _ + class SegmentEditorMaskVolumeEffect(AbstractScriptedSegmentEditorEffect): """This effect fills a selected volume node inside and/or outside a segment with a chosen value. """ def __init__(self, scriptedEffect): - scriptedEffect.name = 'Mask volume' + scriptedEffect.name = 'Mask volume' # no tr (don't translate it because modules find effects by this name) + scriptedEffect.title = _('Mask volume') scriptedEffect.perSegment = True # this effect operates on a single selected segment AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect) @@ -31,10 +34,10 @@ def icon(self): return qt.QIcon() def helpText(self): - return """Use the currently selected segment as a mask to blank out regions in a volume. + return "" + _("""Use the currently selected segment as a mask to blank out regions in a volume.
The mask is applied to the source volume by default.

Fill inside and outside operation creates a binary labelmap volume as output, with the inside and outside fill values modifiable. -""" +""") + "" def setupOptionsFrame(self): self.operationRadioButtons = [] @@ -43,16 +46,16 @@ def setupOptionsFrame(self): self.invisibleIcon = qt.QIcon(":/Icons/Small/SlicerInvisible.png") # Fill operation buttons - self.fillInsideButton = qt.QRadioButton("Fill inside") + self.fillInsideButton = qt.QRadioButton(_("Fill inside")) self.operationRadioButtons.append(self.fillInsideButton) self.buttonToOperationNameMap[self.fillInsideButton] = 'FILL_INSIDE' - self.fillOutsideButton = qt.QRadioButton("Fill outside") + self.fillOutsideButton = qt.QRadioButton(_("Fill outside")) self.operationRadioButtons.append(self.fillOutsideButton) self.buttonToOperationNameMap[self.fillOutsideButton] = 'FILL_OUTSIDE' - self.binaryMaskFillButton = qt.QRadioButton("Fill inside and outside") - self.binaryMaskFillButton.setToolTip("Create a labelmap volume with specified inside and outside fill values.") + self.binaryMaskFillButton = qt.QRadioButton(_("Fill inside and outside")) + self.binaryMaskFillButton.setToolTip(_("Create a labelmap volume with specified inside and outside fill values.")) self.operationRadioButtons.append(self.binaryMaskFillButton) self.buttonToOperationNameMap[self.binaryMaskFillButton] = 'FILL_INSIDE_AND_OUTSIDE' @@ -61,22 +64,22 @@ def setupOptionsFrame(self): operationLayout.addWidget(self.fillInsideButton, 0, 0) operationLayout.addWidget(self.fillOutsideButton, 1, 0) operationLayout.addWidget(self.binaryMaskFillButton, 0, 1) - self.scriptedEffect.addLabeledOptionsWidget("Operation:", operationLayout) + self.scriptedEffect.addLabeledOptionsWidget(_("Operation:"), operationLayout) # fill value self.fillValueEdit = ctk.ctkDoubleSpinBox() - self.fillValueEdit.setToolTip("Choose the voxel intensity that will be used to fill the masked region.") - self.fillValueLabel = qt.QLabel("Fill value: ") + self.fillValueEdit.setToolTip(_("Choose the voxel intensity that will be used to fill the masked region.")) + self.fillValueLabel = qt.QLabel(_("Fill value: ")) # Binary mask fill outside value self.binaryMaskFillOutsideEdit = ctk.ctkDoubleSpinBox() - self.binaryMaskFillOutsideEdit.setToolTip("Choose the voxel intensity that will be used to fill outside the mask.") - self.fillOutsideLabel = qt.QLabel("Outside fill value: ") + self.binaryMaskFillOutsideEdit.setToolTip(_("Choose the voxel intensity that will be used to fill outside the mask.")) + self.fillOutsideLabel = qt.QLabel(_("Outside fill value: ")) # Binary mask fill outside value self.binaryMaskFillInsideEdit = ctk.ctkDoubleSpinBox() - self.binaryMaskFillInsideEdit.setToolTip("Choose the voxel intensity that will be used to fill inside the mask.") - self.fillInsideLabel = qt.QLabel(" Inside fill value: ") + self.binaryMaskFillInsideEdit.setToolTip(_("Choose the voxel intensity that will be used to fill inside the mask.")) + self.fillInsideLabel = qt.QLabel(_(" Inside fill value: ")) for fillValueEdit in [self.fillValueEdit, self.binaryMaskFillOutsideEdit, self.binaryMaskFillInsideEdit]: fillValueEdit.decimalsOption = ctk.ctkDoubleSpinBox.DecimalsByValue + ctk.ctkDoubleSpinBox.DecimalsByKey + ctk.ctkDoubleSpinBox.InsertDecimals @@ -105,13 +108,13 @@ def setupOptionsFrame(self): # Soft edge self.softEdgeMmSpinBox = slicer.qMRMLSpinBox() self.softEdgeMmSpinBox.setMRMLScene(slicer.mrmlScene) - self.softEdgeMmSpinBox.setToolTip("Standard deviation of the Gaussian function that blurs the edge of the mask." - " Higher value makes the edge softer.") + self.softEdgeMmSpinBox.setToolTip(_("Standard deviation of the Gaussian function that blurs the edge of the mask." + " Higher value makes the edge softer.")) self.softEdgeMmSpinBox.quantity = "length" self.softEdgeMmSpinBox.value = 0 self.softEdgeMmSpinBox.minimum = 0 self.softEdgeMmSpinBox.singleStep = 0.5 - self.softEdgeMmLabel = self.scriptedEffect.addLabeledOptionsWidget("Soft edge:", self.softEdgeMmSpinBox) + self.softEdgeMmLabel = self.scriptedEffect.addLabeledOptionsWidget(_("Soft edge:"), self.softEdgeMmSpinBox) self.softEdgeMmSpinBox.connect("valueChanged(double)", self.softEdgeMmChanged) # input volume selector @@ -121,10 +124,10 @@ def setupOptionsFrame(self): self.inputVolumeSelector.addEnabled = True self.inputVolumeSelector.removeEnabled = True self.inputVolumeSelector.noneEnabled = True - self.inputVolumeSelector.noneDisplay = "(Source volume)" + self.inputVolumeSelector.noneDisplay = _("(Source volume)") self.inputVolumeSelector.showHidden = False self.inputVolumeSelector.setMRMLScene(slicer.mrmlScene) - self.inputVolumeSelector.setToolTip("Volume to mask. Default is current source volume node.") + self.inputVolumeSelector.setToolTip(_("Volume to mask. Default is current source volume node.")) self.inputVolumeSelector.connect("currentNodeChanged(vtkMRMLNode*)", self.onInputVolumeChanged) self.inputVisibilityButton = qt.QToolButton() @@ -133,7 +136,7 @@ def setupOptionsFrame(self): inputLayout = qt.QHBoxLayout() inputLayout.addWidget(self.inputVisibilityButton) inputLayout.addWidget(self.inputVolumeSelector) - self.scriptedEffect.addLabeledOptionsWidget("Input Volume: ", inputLayout) + self.scriptedEffect.addLabeledOptionsWidget(_("Input Volume: "), inputLayout) # output volume selector self.outputVolumeSelector = slicer.qMRMLNodeComboBox() @@ -143,10 +146,10 @@ def setupOptionsFrame(self): self.outputVolumeSelector.removeEnabled = True self.outputVolumeSelector.renameEnabled = True self.outputVolumeSelector.noneEnabled = True - self.outputVolumeSelector.noneDisplay = "(Create new Volume)" + self.outputVolumeSelector.noneDisplay = _("(Create new Volume)") self.outputVolumeSelector.showHidden = False self.outputVolumeSelector.setMRMLScene(slicer.mrmlScene) - self.outputVolumeSelector.setToolTip("Masked output volume. It may be the same as the input volume for cumulative masking.") + self.outputVolumeSelector.setToolTip(_("Masked output volume. It may be the same as the input volume for cumulative masking.")) self.outputVolumeSelector.connect("currentNodeChanged(vtkMRMLNode*)", self.onOutputVolumeChanged) self.outputVisibilityButton = qt.QToolButton() @@ -155,12 +158,12 @@ def setupOptionsFrame(self): outputLayout = qt.QHBoxLayout() outputLayout.addWidget(self.outputVisibilityButton) outputLayout.addWidget(self.outputVolumeSelector) - self.scriptedEffect.addLabeledOptionsWidget("Output Volume: ", outputLayout) + self.scriptedEffect.addLabeledOptionsWidget(_("Output Volume: "), outputLayout) # Apply button - self.applyButton = qt.QPushButton("Apply") + self.applyButton = qt.QPushButton(_("Apply")) self.applyButton.objectName = self.__class__.__name__ + 'Apply' - self.applyButton.setToolTip("Apply segment as volume mask. No undo operation available once applied.") + self.applyButton.setToolTip(_("Apply segment as volume mask. No undo operation available once applied.")) self.scriptedEffect.addOptionsWidget(self.applyButton) self.applyButton.connect('clicked()', self.onApply) @@ -223,12 +226,12 @@ def updateGUIFromMRML(self): self.binaryMaskFillOutsideEdit.setVisible(operationName == "FILL_INSIDE_AND_OUTSIDE") self.fillOutsideLabel.setVisible(operationName == "FILL_INSIDE_AND_OUTSIDE") if operationName in ["FILL_INSIDE", "FILL_OUTSIDE"]: - if self.outputVolumeSelector.noneDisplay != "(Create new Volume)": - self.outputVolumeSelector.noneDisplay = "(Create new Volume)" + if self.outputVolumeSelector.noneDisplay != _("(Create new Volume)"): + self.outputVolumeSelector.noneDisplay = _("(Create new Volume)") self.outputVolumeSelector.nodeTypes = ["vtkMRMLScalarVolumeNode", "vtkMRMLLabelMapVolumeNode"] else: - if self.outputVolumeSelector.noneDisplay != "(Create new Labelmap Volume)": - self.outputVolumeSelector.noneDisplay = "(Create new Labelmap Volume)" + if self.outputVolumeSelector.noneDisplay != _("(Create new Labelmap Volume)"): + self.outputVolumeSelector.noneDisplay = _("(Create new Labelmap Volume)") self.outputVolumeSelector.nodeTypes = ["vtkMRMLLabelMapVolumeNode", "vtkMRMLScalarVolumeNode"] self.inputVisibilityButton.setIcon(self.visibleIcon if self.isVolumeVisible(inputVolume) else self.invisibleIcon) @@ -295,7 +298,7 @@ def fillValueChanged(self): self.updateMRMLFromGUI() def onApply(self): - with slicer.util.tryWithErrorDisplay("Failed to apply mask to volume.", waitCursor=True): + with slicer.util.tryWithErrorDisplay(_("Failed to apply mask to volume."), waitCursor=True): inputVolume = self.getInputVolume() outputVolume = self.outputVolumeSelector.currentNode() operationMode = self.scriptedEffect.parameter("Operation") diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorSmoothingEffect.py b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorSmoothingEffect.py similarity index 90% rename from Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorSmoothingEffect.py rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorSmoothingEffect.py index 90605a237a4..315f408338a 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorSmoothingEffect.py +++ b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorSmoothingEffect.py @@ -6,6 +6,7 @@ import vtk import slicer +from slicer.i18n import tr as _ from SegmentEditorEffects import * @@ -15,7 +16,8 @@ class SegmentEditorSmoothingEffect(AbstractScriptedSegmentEditorPaintEffect): """ def __init__(self, scriptedEffect): - scriptedEffect.name = 'Smoothing' + scriptedEffect.name = 'Smoothing' # no tr (don't translate it because modules find effects by this name) + scriptedEffect.title = _('Smoothing') AbstractScriptedSegmentEditorPaintEffect.__init__(self, scriptedEffect) def clone(self): @@ -31,7 +33,7 @@ def icon(self): return qt.QIcon() def helpText(self): - return """Make segment boundaries smoother
by removing extrusions and filling small holes. The effect can be either applied locally + return _("""Make segment boundaries smoother
by removing extrusions and filling small holes. The effect can be either applied locally (by painting in viewers) or to the whole segment (by clicking Apply button). Available methods:

""" +

""") def setupOptionsFrame(self): self.methodSelectorComboBox = qt.QComboBox() - self.methodSelectorComboBox.addItem("Median", MEDIAN) - self.methodSelectorComboBox.addItem("Opening (remove extrusions)", MORPHOLOGICAL_OPENING) - self.methodSelectorComboBox.addItem("Closing (fill holes)", MORPHOLOGICAL_CLOSING) - self.methodSelectorComboBox.addItem("Gaussian", GAUSSIAN) - self.methodSelectorComboBox.addItem("Joint smoothing", JOINT_TAUBIN) - self.scriptedEffect.addLabeledOptionsWidget("Smoothing method:", self.methodSelectorComboBox) + self.methodSelectorComboBox.addItem(_("Median"), MEDIAN) + self.methodSelectorComboBox.addItem(_("Opening (remove extrusions)"), MORPHOLOGICAL_OPENING) + self.methodSelectorComboBox.addItem(_("Closing (fill holes)"), MORPHOLOGICAL_CLOSING) + self.methodSelectorComboBox.addItem(_("Gaussian"), GAUSSIAN) + self.methodSelectorComboBox.addItem(_("Joint smoothing"), JOINT_TAUBIN) + self.scriptedEffect.addLabeledOptionsWidget(_("Smoothing method:"), self.methodSelectorComboBox) self.kernelSizeMMSpinBox = slicer.qMRMLSpinBox() self.kernelSizeMMSpinBox.setMRMLScene(slicer.mrmlScene) - self.kernelSizeMMSpinBox.setToolTip("Diameter of the neighborhood that will be considered around each voxel. Higher value makes smoothing stronger (more details are suppressed).") + self.kernelSizeMMSpinBox.setToolTip(_("Diameter of the neighborhood that will be considered around each voxel. " + "Higher value makes smoothing stronger (more details are suppressed).")) self.kernelSizeMMSpinBox.quantity = "length" self.kernelSizeMMSpinBox.minimum = 0.0 self.kernelSizeMMSpinBox.value = 3.0 self.kernelSizeMMSpinBox.singleStep = 1.0 self.kernelSizePixel = qt.QLabel() - self.kernelSizePixel.setToolTip("Diameter of the neighborhood in pixel. Computed from the segment's spacing and the specified kernel size.") + self.kernelSizePixel.setToolTip(_("Diameter of the neighborhood in pixel. Computed from the segment's spacing and the specified kernel size.")) kernelSizeFrame = qt.QHBoxLayout() kernelSizeFrame.addWidget(self.kernelSizeMMSpinBox) kernelSizeFrame.addWidget(self.kernelSizePixel) - self.kernelSizeMMLabel = self.scriptedEffect.addLabeledOptionsWidget("Kernel size:", kernelSizeFrame) + self.kernelSizeMMLabel = self.scriptedEffect.addLabeledOptionsWidget(_("Kernel size:"), kernelSizeFrame) self.gaussianStandardDeviationMMSpinBox = slicer.qMRMLSpinBox() self.gaussianStandardDeviationMMSpinBox.setMRMLScene(slicer.mrmlScene) - self.gaussianStandardDeviationMMSpinBox.setToolTip("Standard deviation of the Gaussian smoothing filter coefficients. Higher value makes smoothing stronger (more details are suppressed).") + self.gaussianStandardDeviationMMSpinBox.setToolTip(_("Standard deviation of the Gaussian smoothing filter coefficients. " + "Higher value makes smoothing stronger (more details are suppressed).")) self.gaussianStandardDeviationMMSpinBox.quantity = "length" self.gaussianStandardDeviationMMSpinBox.value = 3.0 self.gaussianStandardDeviationMMSpinBox.singleStep = 1.0 - self.gaussianStandardDeviationMMLabel = self.scriptedEffect.addLabeledOptionsWidget("Standard deviation:", self.gaussianStandardDeviationMMSpinBox) + self.gaussianStandardDeviationMMLabel = self.scriptedEffect.addLabeledOptionsWidget(_("Standard deviation:"), self.gaussianStandardDeviationMMSpinBox) self.jointTaubinSmoothingFactorSlider = ctk.ctkSliderWidget() - self.jointTaubinSmoothingFactorSlider.setToolTip("Higher value means stronger smoothing.") + self.jointTaubinSmoothingFactorSlider.setToolTip(_("Higher value means stronger smoothing.")) self.jointTaubinSmoothingFactorSlider.minimum = 0.01 self.jointTaubinSmoothingFactorSlider.maximum = 1.0 self.jointTaubinSmoothingFactorSlider.value = 0.5 self.jointTaubinSmoothingFactorSlider.singleStep = 0.01 self.jointTaubinSmoothingFactorSlider.pageStep = 0.1 - self.jointTaubinSmoothingFactorLabel = self.scriptedEffect.addLabeledOptionsWidget("Smoothing factor:", self.jointTaubinSmoothingFactorSlider) + self.jointTaubinSmoothingFactorLabel = self.scriptedEffect.addLabeledOptionsWidget(_("Smoothing factor:"), self.jointTaubinSmoothingFactorSlider) self.applyToAllVisibleSegmentsCheckBox = qt.QCheckBox() - self.applyToAllVisibleSegmentsCheckBox.setToolTip("Apply smoothing effect to all visible segments in this segmentation node. \ - This operation may take a while.") + self.applyToAllVisibleSegmentsCheckBox.setToolTip( + _("Apply smoothing 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("Apply smoothing to selected segment") + self.applyButton.setToolTip(_("Apply smoothing to selected segment")) self.scriptedEffect.addOptionsWidget(self.applyButton) self.methodSelectorComboBox.connect("currentIndexChanged(int)", self.updateMRMLFromGUI) @@ -106,7 +111,7 @@ def setupOptionsFrame(self): # Customize smoothing brush self.scriptedEffect.setColorSmudgeCheckboxVisible(False) self.paintOptionsGroupBox = ctk.ctkCollapsibleGroupBox() - self.paintOptionsGroupBox.setTitle("Smoothing brush options") + self.paintOptionsGroupBox.setTitle(_("Smoothing brush options")) self.paintOptionsGroupBox.setLayout(qt.QVBoxLayout()) self.paintOptionsGroupBox.layout().addWidget(self.scriptedEffect.paintOptionsFrame()) self.paintOptionsGroupBox.collapsed = True @@ -224,7 +229,7 @@ def onApply(self, maskImage=None, maskExtent=None): return for index in range(inputSegmentIDs.GetNumberOfValues()): segmentID = inputSegmentIDs.GetValue(index) - self.showStatusMessage(f'Smoothing {segmentationNode.GetSegmentation().GetSegment(segmentID).GetName()}...') + self.showStatusMessage(_('Smoothing {segmentName}...').format(segmentationNode.GetSegmentation().GetSegment(segmentID).GetName())) self.scriptedEffect.parameterSetNode().SetSelectedSegmentID(segmentID) self.smoothSelectedSegment(maskImage, maskExtent) # restore segment selection @@ -365,7 +370,7 @@ def smoothSelectedSegment(self, maskImage=None, maskExtent=None): def smoothMultipleSegments(self, maskImage=None, maskExtent=None): import vtkSegmentationCorePython as vtkSegmentationCore - self.showStatusMessage(f'Joint smoothing ...') + self.showStatusMessage(_('Joint smoothing ...')) # Generate merged labelmap of all visible segments segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() visibleSegmentIds = vtk.vtkStringArray() @@ -449,8 +454,8 @@ def smoothMultipleSegments(self, maskImage=None, maskExtent=None): mergedImage.GetImageToWorldMatrix(imageToWorldMatrix) # TODO: Temporarily setting the overwrite mode to OverwriteVisibleSegments is an approach that should be change once additional - # layer control options have been implemented. Users may wish to keep segments on separate layers, and not allow them to be separated/merged automatically. - # This effect could leverage those options once they have been implemented. + # layer control options have been implemented. Users may wish to keep segments on separate layers, and not allow them to be + # separated/merged automatically. This effect could leverage those options once they have been implemented. oldOverwriteMode = self.scriptedEffect.parameterSetNode().GetOverwriteMode() self.scriptedEffect.parameterSetNode().SetOverwriteMode(slicer.vtkMRMLSegmentEditorNode.OverwriteVisibleSegments) for segmentId, labelValue in segmentLabelValues: @@ -472,7 +477,7 @@ def paintApply(self, viewWidget): if smoothingMethod == JOINT_TAUBIN: self.scriptedEffect.clearBrushes() self.scriptedEffect.forceRender(viewWidget) - slicer.util.messageBox("Smoothing brush is not available for 'joint smoothing' method.") + slicer.util.messageBox(_("Smoothing brush is not available for 'joint smoothing' method.")) return modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap() diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorThresholdEffect.py b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorThresholdEffect.py similarity index 92% rename from Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorThresholdEffect.py rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorThresholdEffect.py index c533ddf995b..7f0810eff1a 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorThresholdEffect.py +++ b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/SegmentEditorThresholdEffect.py @@ -8,6 +8,8 @@ import slicer from SegmentEditorEffects import * +from slicer.i18n import tr as _ + class SegmentEditorThresholdEffect(AbstractScriptedSegmentEditorEffect): """ ThresholdEffect is an Effect implementing the global threshold @@ -20,7 +22,8 @@ class SegmentEditorThresholdEffect(AbstractScriptedSegmentEditorEffect): def __init__(self, scriptedEffect): AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect) - scriptedEffect.name = 'Threshold' + scriptedEffect.name = 'Threshold' # no tr (don't translate it because modules find effects by this name) + scriptedEffect.title = _('Threshold') self.segment2DFillOpacity = None self.segment2DOutlineOpacity = None @@ -72,11 +75,11 @@ def icon(self): return qt.QIcon() def helpText(self): - return """Fill segment based on source volume intensity range
. Options:

+ return _("""Fill segment based on source volume intensity range
. Options:

""" +

""") def activate(self): self.setCurrentSegmentTransparent() @@ -145,8 +148,8 @@ def restorePreviewedSegmentTransparency(self): self.previewedSegmentID = None def setupOptionsFrame(self): - self.thresholdSliderLabel = qt.QLabel("Threshold Range:") - self.thresholdSliderLabel.setToolTip("Set the range of the background values that should be labeled.") + self.thresholdSliderLabel = qt.QLabel(_("Threshold Range:")) + self.thresholdSliderLabel.setToolTip(_("Set the range of the background values that should be labeled.")) self.scriptedEffect.addOptionsWidget(self.thresholdSliderLabel) self.thresholdSlider = ctk.ctkRangeWidget() @@ -155,45 +158,45 @@ def setupOptionsFrame(self): self.scriptedEffect.addOptionsWidget(self.thresholdSlider) self.autoThresholdModeSelectorComboBox = qt.QComboBox() - self.autoThresholdModeSelectorComboBox.addItem("threshold above", MODE_SET_LOWER_MAX) - self.autoThresholdModeSelectorComboBox.addItem("threshold below", MODE_SET_MIN_UPPER) - self.autoThresholdModeSelectorComboBox.addItem("set as lower value", MODE_SET_LOWER) - self.autoThresholdModeSelectorComboBox.addItem("set as upper value", MODE_SET_UPPER) - self.autoThresholdModeSelectorComboBox.setToolTip("How to set lower and upper values of the threshold range." - " Threshold above/below: sets the range from the computed value to maximum/minimum." - " Set as lower/upper value: only modifies one side of the threshold range.") + self.autoThresholdModeSelectorComboBox.addItem(_("threshold above"), MODE_SET_LOWER_MAX) + self.autoThresholdModeSelectorComboBox.addItem(_("threshold below"), MODE_SET_MIN_UPPER) + self.autoThresholdModeSelectorComboBox.addItem(_("set as lower value"), MODE_SET_LOWER) + self.autoThresholdModeSelectorComboBox.addItem(_("set as upper value"), MODE_SET_UPPER) + self.autoThresholdModeSelectorComboBox.setToolTip(_("How to set lower and upper values of the threshold range." + " Threshold above/below: sets the range from the computed value to maximum/minimum." + " Set as lower/upper value: only modifies one side of the threshold range.")) self.autoThresholdMethodSelectorComboBox = qt.QComboBox() - self.autoThresholdMethodSelectorComboBox.addItem("Otsu", METHOD_OTSU) - self.autoThresholdMethodSelectorComboBox.addItem("Huang", METHOD_HUANG) - self.autoThresholdMethodSelectorComboBox.addItem("IsoData", METHOD_ISO_DATA) + self.autoThresholdMethodSelectorComboBox.addItem(_("Otsu"), METHOD_OTSU) + self.autoThresholdMethodSelectorComboBox.addItem(_("Huang"), METHOD_HUANG) + self.autoThresholdMethodSelectorComboBox.addItem(_("IsoData"), METHOD_ISO_DATA) # Kittler-Illingworth sometimes fails with an exception, but it does not cause any major issue, # it just logs an error message and does not compute a new threshold value - self.autoThresholdMethodSelectorComboBox.addItem("Kittler-Illingworth", METHOD_KITTLER_ILLINGWORTH) + self.autoThresholdMethodSelectorComboBox.addItem(_("Kittler-Illingworth"), METHOD_KITTLER_ILLINGWORTH) # Li sometimes crashes (index out of range error in # ITK/Modules/Filtering/Thresholding/include/itkLiThresholdCalculator.hxx#L94) # We can add this method back when issue is fixed in ITK. # self.autoThresholdMethodSelectorComboBox.addItem("Li", METHOD_LI) - self.autoThresholdMethodSelectorComboBox.addItem("Maximum entropy", METHOD_MAXIMUM_ENTROPY) - self.autoThresholdMethodSelectorComboBox.addItem("Moments", METHOD_MOMENTS) - self.autoThresholdMethodSelectorComboBox.addItem("Renyi entropy", METHOD_RENYI_ENTROPY) - self.autoThresholdMethodSelectorComboBox.addItem("Shanbhag", METHOD_SHANBHAG) - self.autoThresholdMethodSelectorComboBox.addItem("Triangle", METHOD_TRIANGLE) - self.autoThresholdMethodSelectorComboBox.addItem("Yen", METHOD_YEN) - self.autoThresholdMethodSelectorComboBox.setToolTip("Select method to compute threshold value automatically.") + self.autoThresholdMethodSelectorComboBox.addItem(_("Maximum entropy"), METHOD_MAXIMUM_ENTROPY) + self.autoThresholdMethodSelectorComboBox.addItem(_("Moments"), METHOD_MOMENTS) + self.autoThresholdMethodSelectorComboBox.addItem(_("Renyi entropy"), METHOD_RENYI_ENTROPY) + self.autoThresholdMethodSelectorComboBox.addItem(_("Shanbhag"), METHOD_SHANBHAG) + self.autoThresholdMethodSelectorComboBox.addItem(_("Triangle"), METHOD_TRIANGLE) + self.autoThresholdMethodSelectorComboBox.addItem(_("Yen"), METHOD_YEN) + self.autoThresholdMethodSelectorComboBox.setToolTip(_("Select method to compute threshold value automatically.")) self.selectPreviousAutoThresholdButton = qt.QToolButton() self.selectPreviousAutoThresholdButton.text = "<" - self.selectPreviousAutoThresholdButton.setToolTip("Select previous thresholding method and set thresholds." - + " Useful for iterating through all available methods.") + self.selectPreviousAutoThresholdButton.setToolTip(_("Select previous thresholding method and set thresholds." + " Useful for iterating through all available methods.")) self.selectNextAutoThresholdButton = qt.QToolButton() self.selectNextAutoThresholdButton.text = ">" - self.selectNextAutoThresholdButton.setToolTip("Select next thresholding method and set thresholds." - + " Useful for iterating through all available methods.") + self.selectNextAutoThresholdButton.setToolTip(_("Select next thresholding method and set thresholds." + " Useful for iterating through all available methods.")) - self.setAutoThresholdButton = qt.QPushButton("Set") - self.setAutoThresholdButton.setToolTip("Set threshold using selected method.") + self.setAutoThresholdButton = qt.QPushButton(_("Set")) + self.setAutoThresholdButton.setToolTip(_("Set threshold using selected method.")) # qt.QSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Expanding) # fails on some systems, therefore set the policies using separate method calls qSize = qt.QSizePolicy() @@ -208,7 +211,7 @@ def setupOptionsFrame(self): autoThresholdFrame.addWidget(self.setAutoThresholdButton, 2, 0, 1, 3) autoThresholdGroupBox = ctk.ctkCollapsibleGroupBox() - autoThresholdGroupBox.setTitle("Automatic threshold") + autoThresholdGroupBox.setTitle(_("Automatic threshold")) autoThresholdGroupBox.setLayout(autoThresholdFrame) autoThresholdGroupBox.collapsed = True self.scriptedEffect.addOptionsWidget(autoThresholdGroupBox) @@ -218,35 +221,35 @@ def setupOptionsFrame(self): histogramBrushFrame = qt.QHBoxLayout() histogramFrame.addLayout(histogramBrushFrame) - self.regionLabel = qt.QLabel("Region shape:") + self.regionLabel = qt.QLabel(_("Region shape:")) histogramBrushFrame.addWidget(self.regionLabel) self.histogramBrushButtonGroup = qt.QButtonGroup() self.histogramBrushButtonGroup.setExclusive(True) self.boxROIButton = qt.QToolButton() - self.boxROIButton.setText("Box") + self.boxROIButton.setText(_("Box")) self.boxROIButton.setCheckable(True) self.boxROIButton.clicked.connect(self.updateMRMLFromGUI) histogramBrushFrame.addWidget(self.boxROIButton) self.histogramBrushButtonGroup.addButton(self.boxROIButton) self.circleROIButton = qt.QToolButton() - self.circleROIButton.setText("Circle") + self.circleROIButton.setText(_("Circle")) self.circleROIButton.setCheckable(True) self.circleROIButton.clicked.connect(self.updateMRMLFromGUI) histogramBrushFrame.addWidget(self.circleROIButton) self.histogramBrushButtonGroup.addButton(self.circleROIButton) self.drawROIButton = qt.QToolButton() - self.drawROIButton.setText("Draw") + self.drawROIButton.setText(_("Draw")) self.drawROIButton.setCheckable(True) self.drawROIButton.clicked.connect(self.updateMRMLFromGUI) histogramBrushFrame.addWidget(self.drawROIButton) self.histogramBrushButtonGroup.addButton(self.drawROIButton) self.lineROIButton = qt.QToolButton() - self.lineROIButton.setText("Line") + self.lineROIButton.setText(_("Line")) self.lineROIButton.setCheckable(True) self.lineROIButton.clicked.connect(self.updateMRMLFromGUI) histogramBrushFrame.addWidget(self.lineROIButton) @@ -307,7 +310,7 @@ def setupOptionsFrame(self): ### # Lower histogram threshold buttons - lowerGroupBox = qt.QGroupBox("Lower") + lowerGroupBox = qt.QGroupBox(_("Lower")) lowerHistogramLayout = qt.QHBoxLayout() lowerHistogramLayout.setContentsMargins(0, 3, 0, 3) lowerGroupBox.setLayout(lowerHistogramLayout) @@ -316,22 +319,22 @@ def setupOptionsFrame(self): self.histogramLowerMethodButtonGroup.setExclusive(True) self.histogramLowerThresholdMinimumButton = qt.QToolButton() - self.histogramLowerThresholdMinimumButton.setText("Min") - self.histogramLowerThresholdMinimumButton.setToolTip("Minimum") + self.histogramLowerThresholdMinimumButton.setText(_("Min")) + self.histogramLowerThresholdMinimumButton.setToolTip(_("Minimum")) self.histogramLowerThresholdMinimumButton.setCheckable(True) self.histogramLowerThresholdMinimumButton.clicked.connect(self.updateMRMLFromGUI) lowerHistogramLayout.addWidget(self.histogramLowerThresholdMinimumButton) self.histogramLowerMethodButtonGroup.addButton(self.histogramLowerThresholdMinimumButton) self.histogramLowerThresholdLowerButton = qt.QToolButton() - self.histogramLowerThresholdLowerButton.setText("Lower") + self.histogramLowerThresholdLowerButton.setText(_("Lower")) self.histogramLowerThresholdLowerButton.setCheckable(True) self.histogramLowerThresholdLowerButton.clicked.connect(self.updateMRMLFromGUI) lowerHistogramLayout.addWidget(self.histogramLowerThresholdLowerButton) self.histogramLowerMethodButtonGroup.addButton(self.histogramLowerThresholdLowerButton) self.histogramLowerThresholdAverageButton = qt.QToolButton() - self.histogramLowerThresholdAverageButton.setText("Mean") + self.histogramLowerThresholdAverageButton.setText(_("Mean")) self.histogramLowerThresholdAverageButton.setCheckable(True) self.histogramLowerThresholdAverageButton.clicked.connect(self.updateMRMLFromGUI) lowerHistogramLayout.addWidget(self.histogramLowerThresholdAverageButton) @@ -340,7 +343,7 @@ def setupOptionsFrame(self): ### # Upper histogram threshold buttons - upperGroupBox = qt.QGroupBox("Upper") + upperGroupBox = qt.QGroupBox(_("Upper")) upperHistogramLayout = qt.QHBoxLayout() upperHistogramLayout.setContentsMargins(0, 3, 0, 3) upperGroupBox.setLayout(upperHistogramLayout) @@ -349,40 +352,40 @@ def setupOptionsFrame(self): self.histogramUpperMethodButtonGroup.setExclusive(True) self.histogramUpperThresholdAverageButton = qt.QToolButton() - self.histogramUpperThresholdAverageButton.setText("Mean") + self.histogramUpperThresholdAverageButton.setText(_("Mean")) self.histogramUpperThresholdAverageButton.setCheckable(True) self.histogramUpperThresholdAverageButton.clicked.connect(self.updateMRMLFromGUI) upperHistogramLayout.addWidget(self.histogramUpperThresholdAverageButton) self.histogramUpperMethodButtonGroup.addButton(self.histogramUpperThresholdAverageButton) self.histogramUpperThresholdUpperButton = qt.QToolButton() - self.histogramUpperThresholdUpperButton.setText("Upper") + self.histogramUpperThresholdUpperButton.setText(_("Upper")) self.histogramUpperThresholdUpperButton.setCheckable(True) self.histogramUpperThresholdUpperButton.clicked.connect(self.updateMRMLFromGUI) upperHistogramLayout.addWidget(self.histogramUpperThresholdUpperButton) self.histogramUpperMethodButtonGroup.addButton(self.histogramUpperThresholdUpperButton) self.histogramUpperThresholdMaximumButton = qt.QToolButton() - self.histogramUpperThresholdMaximumButton.setText("Max") - self.histogramUpperThresholdMaximumButton.setToolTip("Maximum") + self.histogramUpperThresholdMaximumButton.setText(_("Max")) + self.histogramUpperThresholdMaximumButton.setToolTip(_("Maximum")) self.histogramUpperThresholdMaximumButton.setCheckable(True) self.histogramUpperThresholdMaximumButton.clicked.connect(self.updateMRMLFromGUI) upperHistogramLayout.addWidget(self.histogramUpperThresholdMaximumButton) self.histogramUpperMethodButtonGroup.addButton(self.histogramUpperThresholdMaximumButton) histogramGroupBox = ctk.ctkCollapsibleGroupBox() - histogramGroupBox.setTitle("Local histogram") + histogramGroupBox.setTitle(_("Local histogram")) histogramGroupBox.setLayout(histogramFrame) histogramGroupBox.collapsed = True self.scriptedEffect.addOptionsWidget(histogramGroupBox) - self.useForPaintButton = qt.QPushButton("Use for masking") - self.useForPaintButton.setToolTip("Use specified intensity range for masking and switch to Paint effect.") + self.useForPaintButton = qt.QPushButton(_("Use for masking")) + self.useForPaintButton.setToolTip(_("Use specified intensity range for masking and switch to Paint effect.")) self.scriptedEffect.addOptionsWidget(self.useForPaintButton) - self.applyButton = qt.QPushButton("Apply") + self.applyButton = qt.QPushButton(_("Apply")) self.applyButton.objectName = self.__class__.__name__ + 'Apply' - self.applyButton.setToolTip("Fill selected segment in regions that are in the specified intensity range.") + self.applyButton.setToolTip(_("Fill selected segment in regions that are in the specified intensity range.")) self.scriptedEffect.addOptionsWidget(self.applyButton) self.useForPaintButton.connect('clicked()', self.onUseForPaint) diff --git a/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects.__init__.py.in b/Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/__init__.py similarity index 100% rename from Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects.__init__.py.in rename to Modules/Loadable/Segmentations/EditorEffects/Python/SegmentEditorEffects/__init__.py diff --git a/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorAbstractEffect.cxx b/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorAbstractEffect.cxx index 4651d208069..94ffe10d9b7 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorAbstractEffect.cxx +++ b/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorAbstractEffect.cxx @@ -142,6 +142,30 @@ void qSlicerSegmentEditorAbstractEffect::setName(QString name) qCritical() << Q_FUNC_INFO << ": Cannot set effect name by method, only in constructor!"; } +//----------------------------------------------------------------------------- +QString qSlicerSegmentEditorAbstractEffect::title()const +{ + if (!this->m_Title.isEmpty()) + { + return this->m_Title; + } + else if (!this->m_Name.isEmpty()) + { + return this->m_Name; + } + else + { + qWarning() << Q_FUNC_INFO << ": Empty effect title!"; + return QString(); + } +} + +//----------------------------------------------------------------------------- +void qSlicerSegmentEditorAbstractEffect::setTitle(QString title) +{ + this->m_Title = title; +} + //----------------------------------------------------------------------------- bool qSlicerSegmentEditorAbstractEffect::perSegment()const { diff --git a/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorAbstractEffect.h b/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorAbstractEffect.h index bbacb3b2fc5..8d71873f37e 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorAbstractEffect.h +++ b/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorAbstractEffect.h @@ -71,6 +71,10 @@ class Q_SLICER_SEGMENTATIONS_EFFECTS_EXPORT qSlicerSegmentEditorAbstractEffect : /// \sa name(), \sa setName() Q_PROPERTY(QString name READ name WRITE setName) + /// This property stores the title of the effect + /// \sa title(), \sa setTitle() + Q_PROPERTY(QString title READ title WRITE setTitle) + /// This property stores the flag indicating whether effect operates on individual segments (true) /// or the whole segmentation (false). /// True by default. @@ -238,12 +242,21 @@ public slots: Q_INVOKABLE QWidget* addLabeledOptionsWidget(QString label, QWidget* newOptionsWidget); Q_INVOKABLE QWidget* addLabeledOptionsWidget(QString label, QLayout* newOptionsWidget); - /// Get name of effect + /// Get name of effect. + /// This name is used by various modules for accessing an effect. + /// This string is not displayed on the user interface and must not be translated. virtual QString name()const; - /// Set the name of the effect + /// Set the name of the effect. /// NOTE: name must be defined in constructor in C++ effects, this can only be used in python scripted ones virtual void setName(QString name); + /// Get title of effect. + /// This string is displayed on the application GUI and it is translated. + /// Returns the effect's name when the title is empty. + virtual QString title()const; + /// Set the title of the effect + virtual void setTitle(QString title); + /// Get flag indicating whether effect operates on segments (true) or the whole segmentation (false). virtual bool perSegment()const; /// Set flag indicating whether effect operates on segments (true) or the whole segmentation (false). @@ -427,9 +440,9 @@ public slots: Q_INVOKABLE bool segmentationDisplayableInView(vtkMRMLAbstractViewNode* viewNode); protected: - /// Name of the effect QString m_Name; bool m_Active{false}; + QString m_Title; /// Flag indicating whether effect operates on individual segments (true) or the whole segmentation (false). /// If the selected effect works on whole segmentation, selection of the segments does not trigger creation diff --git a/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorEraseEffect.cxx b/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorEraseEffect.cxx index 7c980362fa4..f237a95bb37 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorEraseEffect.cxx +++ b/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorEraseEffect.cxx @@ -53,6 +53,7 @@ qSlicerSegmentEditorEraseEffect::qSlicerSegmentEditorEraseEffect(QObject* parent , d_ptr( new qSlicerSegmentEditorEraseEffectPrivate(*this) ) { this->m_Name = QString(/*no tr*/"Erase"); + this->m_Title = tr("Erase"); this->m_AlwaysErase = true; this->m_ShowEffectCursorInThreeDView = true; } diff --git a/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorPaintEffect.cxx b/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorPaintEffect.cxx index 347a743f80d..9e35e1f971b 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorPaintEffect.cxx +++ b/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorPaintEffect.cxx @@ -970,6 +970,7 @@ qSlicerSegmentEditorPaintEffect::qSlicerSegmentEditorPaintEffect(QObject* parent , d_ptr( new qSlicerSegmentEditorPaintEffectPrivate(*this) ) { this->m_Name = QString(/*no tr*/"Paint"); + this->m_Title = tr("Paint"); this->m_AlwaysErase = false; this->m_Erase = false; this->m_ShowEffectCursorInThreeDView = true; diff --git a/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorScissorsEffect.cxx b/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorScissorsEffect.cxx index e49bcb46081..2f81c27bb51 100644 --- a/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorScissorsEffect.cxx +++ b/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorScissorsEffect.cxx @@ -1160,6 +1160,7 @@ qSlicerSegmentEditorScissorsEffect::qSlicerSegmentEditorScissorsEffect(QObject* , d_ptr( new qSlicerSegmentEditorScissorsEffectPrivate(*this) ) { this->m_Name = QString(/*no tr*/"Scissors"); + this->m_Title = tr("Scissors"); this->m_ShowEffectCursorInThreeDView = true; } diff --git a/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentEditorWidget.cxx b/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentEditorWidget.cxx index 70ce3766c78..299ce9fa292 100644 --- a/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentEditorWidget.cxx +++ b/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentEditorWidget.cxx @@ -1078,8 +1078,8 @@ void qMRMLSegmentEditorWidget::updateEffectList() effectButton->setCheckable(true); effectButton->setToolButtonStyle(d->EffectButtonStyle); effectButton->setIcon(effect->icon()); - effectButton->setText(effect->name()); - effectButton->setToolTip(effect->name()); + effectButton->setText(effect->title()); + effectButton->setToolTip(effect->title()); effectButton->setProperty("Effect", QVariant::fromValue(effect)); effectButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); // make all effect buttons the same width d->EffectButtonGroup.addButton(effectButton); @@ -1689,7 +1689,7 @@ void qMRMLSegmentEditorWidget::updateEffectsSectionFromMRML() // Activate newly selected effect activeEffect->activate(); d->OptionsGroupBox->show(); - d->OptionsGroupBox->setTitle(activeEffect->name()); + d->OptionsGroupBox->setTitle(activeEffect->title()); d->EffectHelpBrowser->setCollapsibleText(activeEffect->helpText()); d->MaskingGroupBox->show(); } diff --git a/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentationRepresentationsListView.cxx b/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentationRepresentationsListView.cxx index 6e86f889645..b9feb485e1b 100644 --- a/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentationRepresentationsListView.cxx +++ b/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentationRepresentationsListView.cxx @@ -313,7 +313,8 @@ void qMRMLSegmentationRepresentationsListView::createRepresentationDefault() QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); if (!d->SegmentationNode->GetSegmentation()->CreateRepresentation(representationName.toUtf8().constData())) { - QString message = QString("Failed to convert %1 to %2!\n\nProbably there is no valid conversion path between the source representation and %2").arg(d->SegmentationNode->GetName()).arg(representationName); + QString message = tr("Failed to convert %1 to %2!\n\nProbably there is no valid conversion path between the source representation and %2") + .arg(d->SegmentationNode->GetName()).arg(representationName); QMessageBox::warning(nullptr, tr("Conversion failed"), message); } QApplication::restoreOverrideCursor();