Slicer crashes when adding 2 custom layouts in startup script

I want to configure and enable two additional custom layouts that are available in all my Slicer instances. I’m doing this by adding the attached script to my .slicerrc file.
However when I start Slicer and select one of the custom views from the layout menu, Slicer crashes.

This only seems to be a problem with two or more custom views. If I only configure and add one layout, there are no issues.

I know it’s not good practice to have object and object2 but that’s me trying to debug why this issue is happening

# Python commands in this file are executed on Slicer startup

# Examples:
#
# Load a scene file
# slicer.util.loadScene('c:/Users/SomeUser/Documents/SlicerScenes/SomeScene.mrb')
#
# Open a module (overrides default startup module in application settings / modules)
# slicer.util.mainWindow().moduleSelector().selectModule('SegmentEditor')
#

# Set up 3-slice view for 2D viz.
customLayout = """
<layout type="horizontal" split="true">
	<item>
		<view class="vtkMRMLSliceNode" singletontag="Red">
			<property name="orientation" action="default">Axial</property>
			<property name="viewlabel" action="default">R</property>
			<property name="viewcolor" action="default">#F34A33</property>
		</view>
	</item>
	<item>
		<view class="vtkMRMLSliceNode" singletontag="Yellow">
			<property name="orientation" action="default">Sagittal</property>
			<property name="viewlabel" action="default">Y</property>
			<property name="viewcolor" action="default">#F34A33</property>
		</view>
	</item>
	<item>
		<view class="vtkMRMLSliceNode" singletontag="Green">
			<property name="orientation" action="default">Coronal</property>
			<property name="viewlabel" action="default">G</property>
			<property name="viewcolor" action="default">#F34A33</property>
		</view>
	</item>
</layout>
"""
# Built-in layout IDs are all below 100, so you can choose any large random number
# for your custom layout ID.
twoDLayoutID=100

layoutManager = slicer.app.layoutManager()
layoutManager.layoutLogic().GetLayoutNode().AddLayoutDescription(twoDLayoutID, customLayout)                                         

## Switch to the new custom layout 
layoutManager.setLayout(twoDLayoutID)

## Add button to layout selector toolbar for this custom layout
viewToolBar = mainWindow().findChild('QToolBar', 'ViewToolBar')
layoutMenu = viewToolBar.widgetForAction(viewToolBar.actions()[0]).menu()
layoutSwitchActionParent = layoutMenu  # use `layoutMenu` to add inside layout list, use `viewToolBar` to add next the standard layout list
layoutSwitchAction = layoutSwitchActionParent.addAction("All three 2D Views") # add inside layout list
layoutSwitchAction.setIcon(qt.QIcon(':Icons/Go.png'))
layoutSwitchAction.setToolTip('All three 2D Views')
layoutSwitchAction.connect('triggered()', lambda layoutId = twoDLayoutID: slicer.app.layoutManager().setLayout(layoutId))

# Set up volume renderings for 3D viz.
customLayout2 = """
<layout type="horizontal" split="true">
	<item>
		<view class="vtkMRMLViewNode" singletontag="1">
			<property name="viewlabel" action="default">1</property>
		</view>
	</item>
	<item>
		<view class="vtkMRMLSliceNode" singletontag="Yellow">
			<property name="orientation" action="default">Sagittal</property>
			<property name="viewlabel" action="default">Y</property>
			<property name="viewcolor" action="default">#F34A33</property>
		</view>
	</item>
	<item>
		<view class="vtkMRMLViewNode" singletontag="2">
			<property name="viewlabel" action="default">2</property>
		</view>
	</item>
</layout>
"""
# Built-in layout IDs are all below 100, so you can choose any large random number
# for your custom layout ID.
threeDlayoutID=200

layoutManager2 = slicer.app.layoutManager()
layoutManager2.layoutLogic().GetLayoutNode().AddLayoutDescription(threeDlayoutID, customLayout2)                                         

## Switch to the new custom layout 
layoutManager2.setLayout(threeDlayoutID)

# Add button to layout selector toolbar for this custom layout
viewToolBar2 = mainWindow().findChild('QToolBar', 'ViewToolBar')
layoutMenu2 = viewToolBar2.widgetForAction(viewToolBar2.actions()[0]).menu()
layoutSwitchActionParent2 = layoutMenu2  # use `layoutMenu2` to add inside layout list, use `viewToolBar2` to add next the standard layout list
layoutSwitchAction2 = layoutSwitchActionParent2.addAction("Custom 3D and Slice") # add inside layout list
layoutSwitchAction2.setIcon(qt.QIcon(':Icons/Go.png'))
layoutSwitchAction2.setToolTip('Double 3D and slice view')
layoutSwitchAction2.connect('triggered()', lambda layoutId = threeDlayoutID: slicer.app.layoutManager().setLayout(layoutId))

You need to set the layout ID as custom data in the layout switch action. Add these two lines:

layoutSwitchAction.setData(twoDLayoutID)
layoutSwitchAction2.setData(threeDlayoutID)
1 Like

Thanks, works perfectly now!

1 Like

How would I access the variables layoutManager, twoDLayoutID, and others defined in .slicerrc in a scripted module? I’ve tried using global or globals() in my scripted module file but I can’t seem to access them.

Thr easiest is to put all “global” variables in an easily accessible namespace, such as slicer: slicer.myVar=555

1 Like