Difference between revisions of "Documentation/Nightly/Developers/Slicelets"

From Slicer Wiki
Jump to: navigation, search
m (Reverted edits by JChris.FillionR (talk) to last revision by Lasso)
 
Line 5: Line 5:
 
Slicer application user interface is very rich and complex, to allow free experimentation with all available tools. However, if Slicer is used for implementing a well-defined workflow, it is more efficient to develop a custom user interface, that only shows the required user interfaced, in a streamlined, simplified fashion.
 
Slicer application user interface is very rich and complex, to allow free experimentation with all available tools. However, if Slicer is used for implementing a well-defined workflow, it is more efficient to develop a custom user interface, that only shows the required user interfaced, in a streamlined, simplified fashion.
  
Slicelets are special Slicer modules that can provide full user interface, which can be used instead of Slicer's main application user interface.
+
"Slicelets" are special Slicer modules, which show a simplified user interface that is optimized for performing a specific workflow.
  
 
==Runnning a slicelet==
 
==Runnning a slicelet==
  
There are not too many differences between slicelets and regular module. In fact, any regular module can be run standalone, without the main application user interface. The ''--no-main-window'' command-line argument has to be specified to prevent showing the main application user interface and ''--python-code'' has to be provided to start the module.
+
Slicelets are regular modules, which hide user interface elements that are not necessary for the particular workflow and provide all required widgets on their module panel.
  
For example, to show the Command line module "Add", you could use (note: on Windows replace ''./Slicer'' by ''Slicer.exe''):
+
Hiding of user interface elements can be implemented in the setup method of the module widget class, using a combination of set...Visibility methods implemented in [https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/util.py#L1853 slicer.util].
  
  ./Slicer --no-main-window --python-code "slicer.modules.add.widgetRepresentation().show()"
+
To launch Slicer with a slicelet displayed (note: on Windows replace ''./Slicer'' by ''Slicer.exe''; MyModuleName is the name of the slicelet module):
  
.... to show a Loadable module, you could use:
+
  ./Slicer --python-code "slicer.util.selectModule('MyModuleName')"
  
  ./Slicer --no-main-window --python-code "slicer.modules.models.widgetRepresentation().show()"
+
The line may be too complex to enter each time to start a slicelet. To address this, a clone of the Slicer.exe launcher and SlicerLauncherSettings.ini settings file can be created with a different name (for example, MyModuleName.exe and bin/MyModuleNameLauncherSettings.ini):
  
In general, additional user interface elements need to be added if a module is used without the main application user interface, for example for loading data and saving the results. A simple example is the Label Statistics module, which can run as a regular module in Slicer but also can be started as a Slicelet. When it is started as a slicelet, it has buttons for loading input data:
+
MyModuleNameLauncherSettings.ini file content:
  
  ./Slicer --no-main-window --python-code "slicer.modules.labelstatistics.widgetRepresentation().show()"
+
<pre>
 
+
[General]
A slicelet implemented in python can be started from a custom location (the advantage is that the module does not have to be added to the Slicer module paths, but a disadvantage is that the module location has to be known):
+
additionalSettingsFilePath=<APPLAUNCHER_SETTINGS_DIR>/SlicerLauncherSettings.ini
 
+
additionalSettingsExcludeGroups=General,Application,ExtraApplicationToLaunch
  ./Slicer --no-main-window --python-script lib/Slicer-4.7/qt-scripted-modules/LabelStatistics.py
 
 
 
On Windows:
 
 
 
  Slicer.exe --no-main-window --python-script lib/Slicer-4.7/qt-scripted-modules/LabelStatistics.py
 
 
 
The line may be too complex to enter each time to start a slicelet. To solve this, one option is to create a batch (.bat) file that automatically finds Slicer.exe (based on the executable associated with scene .mrml files):
 
  
<pre>
+
launcherNoSplashScreen=false
@echo off
+
launcherSplashImagePath=<APPLAUNCHER_DIR>/bin/Slicer-SplashScreen.png
 +
launcherSplashScreenHideDelayMs=3000
 +
additionalLauncherHelpShortArgument=
 +
additionalLauncherHelpLongArgument=
 +
additionalLauncherNoSplashArguments=
  
REM Get Slicer executable in SlicerExecutable environment variable
+
[Application]
for /f "delims== tokens=2" %%a in ('ftype Slicer') do set SlicerExecutable=%%a
+
path=<APPLAUNCHER_DIR>/bin/SlicerApp-real.exe
set SlicerExecutable=%SlicerExecutable:~0,-5%
+
arguments=--python-code "slicer.util.selectModule('MyModuleNameLauncherSettings')"
 +
name=MyModuleNameLauncherSettings
 +
revision=
 +
organizationDomain=
 +
organizationName=
  
REM Go to Slicer directory
+
[ExtraApplicationToLaunch]
cd %SlicerExecutable%/..
 
  
Slicer.exe --no-main-window --python-script lib/Slicer-4.7/qt-scripted-modules/LabelStatistics.py
 
 
</pre>
 
</pre>
  
Alternatively, command-line arguments can be hardcoded in the Slicer application settings:
+
To use custom application settings, also clone SlicerApp-real.exe and rename to MyModuleNameApp-real.exe and use this executable name in application path in MyModuleNameLauncherSettings.ini.
 
 
Edit SlicerLauncherSettings.ini.
 
 
 
Before:
 
  ...
 
  [Application]
 
  path=<APPLAUNCHER_DIR>/bin/./SlicerApp-real
 
  arguments=
 
  ...
 
 
 
After:
 
  ...
 
  [Application]
 
  path=<APPLAUNCHER_DIR>/bin/./SlicerApp-real
 
  arguments=--no-main-window
 
  ...
 
 
 
 
 
Doing so, you wouldn't have to type the argument --no-main-window each time.
 
 
 
Similarly, you could also include the --python-code "..." arguments into the launcher settings file.
 
 
 
Alternatively, instead of adding the "--python-code" argument into the launcher settings, you could create a file named .slicerrc.py inside your home folder with the following content:
 
 
 
  modules = ["add", "models", "labelstatistics"]
 
  for module in modules:
 
    getattr(slicer.modules, module).widgetRepresentation().show()
 
  
 
==Slicelet examples==
 
==Slicelet examples==
[[image:SliceletSampleScreenshot.png|thumb|200px|SliceletTest example (slicelet with a single image viewer)]]
 
  
Simple example: [https://github.com/Slicer/Slicer/blob/master/Modules/Scripted/LabelStatistics/LabelStatistics.py Label Statistics module], see [[media:LabelStatisticsSlicelet.png|screenshot]]
+
Simple example: https://github.com/lassoan/SlicerSimpleWorkflows/tree/master/QuickSegment
  
Simple example with image viewer (see screenshot on the right): [https://www.assembla.com/code/slicerrt/subversion/nodes/1931/trunk/SlicerRt/sandbox/SliceletTest SliceletTest]
+
[[File:SliceletSimpleGUI.png|400px|thumb|left|alt text]]
  
More complex example: [https://www.assembla.com/code/Scoliosis/subversion/nodes/52/trunk/Scoliosis/src/ScoliosisMonitoring/ScoliosisMonitoring.py ScoliosisMonitoring]
+
==Making the slicelet more light-weight and customize it further==
  
==User interface examples==
+
===Optimizations without rebuilding Slicer===
  
Three modules are within a tab widget:
+
By default Slicer loads all modules when your slicelet is started to make sure you have access to all features. If some features are not needed then they can be disabled or removed to reduce the Slicer package size or startup time.
 
 
<pre>
 
import qt
 
import __main__
 
 
 
tabWidget = qt.QTabWidget()
 
 
 
modules = ["volumes", "models", "labelstatistics"]
 
for module in modules:
 
  tabWidget.addTab(getattr(slicer.modules, module).widgetRepresentation(), module)
 
 
 
tabWidget.show()
 
 
 
__main__.tabWidget = tabWidget # Keep track of the widget to avoid its premature destruction
 
</pre>
 
  
 +
====Reduce startup time====
  
Finally, to create a small UI including:
 
- a 3D view
 
- a button to load data
 
- a tab widget
 
- a module selector allowing to add any module to the tab widget
 
... the following could be done:
 
 
<pre>
 
import qt
 
import __main__
 
 
def onModuleSelected(modulename):
 
  global tabWidget
 
  scrollArea = qt.QScrollArea(tabWidget)
 
  scrollArea.setWidget(getattr(slicer.modules, modulename.lower()).widgetRepresentation())
 
  scrollArea.setHorizontalScrollBarPolicy(qt.Qt.ScrollBarAlwaysOff)
 
  scrollArea.setWidgetResizable(True)
 
  tabWidget.addTab(scrollArea, modulename)
 
 
 
# splitter between "layout" and "bottom frame"
 
splitter = qt.QSplitter()
 
splitter.orientation = qt.Qt.Vertical
 
vlayout.addWidget(splitter)
 
 
# layout manager
 
layoutManager = slicer.qSlicerLayoutManager()
 
slicer.app.setLayoutManager(layoutManager)
 
layoutManager.setMRMLScene(slicer.mrmlScene)
 
layoutManager.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpView)
 
 
# layout widget
 
layoutWidget = slicer.qMRMLLayoutWidget()
 
splitter.addWidget(layoutWidget)
 
layoutWidget.setLayoutManager(layoutManager)
 
 
# Bottom frame: including control buttons and tab widget
 
bottomFrame = qt.QFrame()
 
bottomVlayout = qt.QVBoxLayout()
 
bottomFrame.setLayout(bottomVlayout)
 
splitter.addWidget(bottomFrame)
 
 
# Horizontal layout: Load Data, Save Data, Module Selector
 
hlayout = qt.QHBoxLayout()
 
bottomVlayout.addLayout(hlayout)
 
 
loadDataButton = qt.QPushButton("Load Data")
 
hlayout.addWidget(loadDataButton)
 
loadDataButton.connect('clicked()', slicer.util.openAddVolumeDialog)
 
 
saveDataButton = qt.QPushButton("Save Data")
 
hlayout.addWidget(saveDataButton)
 
saveDataButton.connect('clicked()', slicer.util.openSaveDataDialog)
 
 
moduleSelector = slicer.qSlicerModuleSelectorToolBar()
 
moduleSelector.setModuleManager(slicer.app.moduleManager())
 
hlayout.addWidget(moduleSelector)
 
 
# Tab widget
 
tabWidget = qt.QTabWidget()
 
bottomVlayout.addWidget(tabWidget)
 
 
# Connections
 
moduleSelector.connect('moduleSelected(QString)', onModuleSelected)
 
 
# Initialize
 
modules = ["volumes", "models", "labelstatistics"]
 
for module in modules:
 
  onModuleSelected(module)
 
 
mainWidget.show()
 
 
 
__main__.mainWidget = mainWidget # Keep track of the widget to avoid its premature destruction
 
</pre>
 
 
==Making the slicelet more light-weight==
 
 
There are many ways of customizing 3D Slicer's user interface or feature set. See an overview in [http://www.na-mic.org/Wiki/images/b/b0/Slicelets2016.pdf this presentation].
 
 
By default Slicer loads all modules when your slicelet is started to make sure you have access to all features. If some features are not needed then they can be disabled or removed to reduce the Slicer package size or startup time.
 
 
===Reducing Slicer startup time===
 
 
Start Slicer, in the menu select Edit / Application settings. In the Modules section uncheck all modules in the module list that you do not need. There are dependencies between modules, therefore it may require some experimentation to find the minimum set of necessary modules.
 
Start Slicer, in the menu select Edit / Application settings. In the Modules section uncheck all modules in the module list that you do not need. There are dependencies between modules, therefore it may require some experimentation to find the minimum set of necessary modules.
  
 
Typical startup time on an average PC (i5 processor, 8GB RAM, SSD drive) is 6-8 seconds (15-20 seconds if it is the first time after starting the system). This can be reduced to 1 second if most modules are disabled.
 
Typical startup time on an average PC (i5 processor, 8GB RAM, SSD drive) is 6-8 seconds (15-20 seconds if it is the first time after starting the system). This can be reduced to 1 second if most modules are disabled.
  
===Reducing Slicer package size===
+
====Reduce package size====
 
 
If the size of installation footprint of Slicer is an issue then there are two main options to address this:
 
* Delete the shared library files (.dll on Windows, .so on Linux) of the modules that are not needed in your Slicer installation
 
* Disable options when you build Slicer (you can disable major components, such as SimpleITK ~150MB, EMSegment ~100MB, CLI modules ~100MB). You can then create your own installation package that contains only the reduced set of files.
 
 
 
Removing modules from the Slicer installation makes the disk footprint smaller and of course reduces the startup time as well.
 
 
 
==Additional steps to run the slicelet without a main window==
 
 
 
The default Slicer main window performs a couple of initialization operations that are missed when you start Slicer with the --no-main-window option. Therefore, for certain features, these initialization operations has to be performed in the slicelet.
 
 
 
=== Layout manager ===
 
 
 
You need to create a layout manager and set it in the main application to be able to use the Editor.
 
  
<pre>
+
If the size of installation footprint of Slicer is an issue then you may delete the shared library files (.dll on Windows, .so on Linux) of the modules that are not needed in your Slicer installation. Removing modules from the Slicer installation makes the disk footprint smaller and of course reduces the startup time as well.
...
 
mainWidget = qt.QWidget()
 
mainWidget.objectName = "qSlicerAppMainWindow"
 
vlayout = qt.QVBoxLayout()  
 
mainWidget.setLayout(vlayout)
 
 
 
layoutWidget = slicer.qMRMLLayoutWidget()
 
layoutManager = slicer.qSlicerLayoutManager()
 
layoutManager.setMRMLScene(slicer.mrmlScene)
 
layoutManager.setScriptedDisplayableManagerDirectory(slicer.app.slicerHome + "/bin/Python/mrmlDisplayableManager")
 
layoutWidget.setLayoutManager(layoutManager)
 
slicer.app.setLayoutManager(layoutManager)
 
layoutWidget.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpView)
 
vlayout.addWidget(layoutWidget)
 
...
 
</pre>
 
 
 
=== Logging ===
 
 
 
Set up Slicer's Python logger by calling:
 
 
 
<pre>
 
import slicer.util
 
import logging
 
</pre>
 
  
Then, messages can be logged as described in [[Documentation/{{documentation/version}}/Developers/Style_Guide#Logging]]
+
===Custom Slicer application===
  
=== Status bar ===
+
For full customization and branding options, reduction of package size, and bundling of all necessary modules in a single package, build a [https://github.com/KitwareMedical/SlicerCustomAppTemplate custom Slicer Application].
If your main window does not have a status bar and you use features that display information on the status bar then you may need to override ''slicer.util.showStatusMessage'' utility function.
 

Latest revision as of 21:48, 3 July 2019

Home < Documentation < Nightly < Developers < Slicelets


Slicelets

Slicer application user interface is very rich and complex, to allow free experimentation with all available tools. However, if Slicer is used for implementing a well-defined workflow, it is more efficient to develop a custom user interface, that only shows the required user interfaced, in a streamlined, simplified fashion.

"Slicelets" are special Slicer modules, which show a simplified user interface that is optimized for performing a specific workflow.

Runnning a slicelet

Slicelets are regular modules, which hide user interface elements that are not necessary for the particular workflow and provide all required widgets on their module panel.

Hiding of user interface elements can be implemented in the setup method of the module widget class, using a combination of set...Visibility methods implemented in slicer.util.

To launch Slicer with a slicelet displayed (note: on Windows replace ./Slicer by Slicer.exe; MyModuleName is the name of the slicelet module):

 ./Slicer --python-code "slicer.util.selectModule('MyModuleName')"

The line may be too complex to enter each time to start a slicelet. To address this, a clone of the Slicer.exe launcher and SlicerLauncherSettings.ini settings file can be created with a different name (for example, MyModuleName.exe and bin/MyModuleNameLauncherSettings.ini):

MyModuleNameLauncherSettings.ini file content:

[General]
additionalSettingsFilePath=<APPLAUNCHER_SETTINGS_DIR>/SlicerLauncherSettings.ini
additionalSettingsExcludeGroups=General,Application,ExtraApplicationToLaunch

launcherNoSplashScreen=false
launcherSplashImagePath=<APPLAUNCHER_DIR>/bin/Slicer-SplashScreen.png
launcherSplashScreenHideDelayMs=3000
additionalLauncherHelpShortArgument=
additionalLauncherHelpLongArgument=
additionalLauncherNoSplashArguments=

[Application]
path=<APPLAUNCHER_DIR>/bin/SlicerApp-real.exe
arguments=--python-code "slicer.util.selectModule('MyModuleNameLauncherSettings')"
name=MyModuleNameLauncherSettings
revision=
organizationDomain=
organizationName=

[ExtraApplicationToLaunch]

To use custom application settings, also clone SlicerApp-real.exe and rename to MyModuleNameApp-real.exe and use this executable name in application path in MyModuleNameLauncherSettings.ini.

Slicelet examples

Simple example: https://github.com/lassoan/SlicerSimpleWorkflows/tree/master/QuickSegment

alt text

Making the slicelet more light-weight and customize it further

Optimizations without rebuilding Slicer

By default Slicer loads all modules when your slicelet is started to make sure you have access to all features. If some features are not needed then they can be disabled or removed to reduce the Slicer package size or startup time.

Reduce startup time

Start Slicer, in the menu select Edit / Application settings. In the Modules section uncheck all modules in the module list that you do not need. There are dependencies between modules, therefore it may require some experimentation to find the minimum set of necessary modules.

Typical startup time on an average PC (i5 processor, 8GB RAM, SSD drive) is 6-8 seconds (15-20 seconds if it is the first time after starting the system). This can be reduced to 1 second if most modules are disabled.

Reduce package size

If the size of installation footprint of Slicer is an issue then you may delete the shared library files (.dll on Windows, .so on Linux) of the modules that are not needed in your Slicer installation. Removing modules from the Slicer installation makes the disk footprint smaller and of course reduces the startup time as well.

Custom Slicer application

For full customization and branding options, reduction of package size, and bundling of all necessary modules in a single package, build a custom Slicer Application.