Is there a way to view just one slice with one 3D view?

I see that the viewing options already include Side by side, but that only allows each viewport to show a (2D) slice, not a 3D view.
I feel that it would sometimes be useful to view just one slice and one 3D view, which displays each with more space than in the Four-Up view.

Three options I can think of to achieve this:

  • Provide “3D view” as one of the options in the drop-down menu at the top of a viewport (removing distinction between slice viewport and 3D viewport). image

  • Provide a new viewing configuration choice in the list of viewing options.

  • Allow the ‘struts’ (~transoms/mullions) separating each viewport to be draggable, so that the individual viewports are resizeable. [This would be a kind of workaround, as it might be applied to a Four-Up view, with two of the Slice viewports shrunk to a small size, making more room available for the other two.]

Or maybe there’s already some (convenient) way to do it that I just haven’t noticed?

—DIV

We provide a handful of layouts, but you can specify custom layouts (including a side-by-side 3D+slice view) by an XML string. This example in the script repository does almost exactly what you describe. You can change vertical to horizontal to have side-by-side instead of over-under.

The idea of making layout customization easier for clinicians or make layouts more dynamic comes up time-to-time (see the issues below) but overall there is not too many upvotes for these features:

You can upvote the issues that you find the most relevant. You may also find other feature request that are closer to what you want to achieve, or you can add a new feature request if you feel that none of them describe what you want.

2 Likes

This seems to work well. (One quirk: I had to toggle the crosshairs off and then back on again to be able to see them in the 3D viewport.)

I also tried the script for adding a button to the layout selector toolbar, which also works OK once layoutSwitchAction.setData(layoutId) is adjusted to layoutSwitchAction.setData(customLayoutId) to match the custom layout definition script.
Thanks.

Funnily enough this functionality is available already in the custom layout defined through the abovementioned script! Not sure why that happens, when I haven’t seen such (potentially useful) functionality in any of the other built-in layouts.

You can enable/disable the splitter (“struts”) by the split attribute. Slicer has lots of features that are not fully exposed to the users to keep things a bit simpler. I think that’s the main reason why the splitter is not used in every view layout.

Ah, yes. I see now that it is occasionally used in some built-in layouts too.
For example, the Conventional layout is the first on the list, and the properties can be viewed with

>>> layoutManager = slicer.app.layoutManager()
>>> layoutManager.layoutLogic().GetLayoutNode().GetLayoutDescription(1)

With a little reformatting the output is

<layout type="vertical" split="true" >
 <item splitSize="500">  <view class="vtkMRMLViewNode" singletontag="1" verticalStretch="0">    <property name="viewlabel" action="default">1</property>  </view> </item>
 <item splitSize="500">
  <layout type="horizontal">
   <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="Green">     <property name="orientation" action="default">Coronal</property>     <property name="viewlabel" action="default">G</property>     <property name="viewcolor" action="default">#6EB04B</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">#EDD54C</property>    </view>   </item>
  </layout>
 </item>
</layout>

layout type="vertical" creates vertically stacked panes separated by horizontal dividers. In the above case, for the horizontal divider the split is (explicitly) enabled, so it is draggable.

layout type="horizontal" creates horizontally stacked panes separated by vertical dividers. In the above case, for the vertical dividers the splits are (implicitly) disabled, so they are not draggable.

—DIV

1 Like

In case others want to be able to conveniently define the three distinct two-up views comprising 3D+Red, 3D+Yellow and 3D+Green, I have extended the existing code as follows.
It can be run (per session) from the “Python Interactor” (button on toolbar).

# https://slicer.readthedocs.io/en/latest/developer_guide/script_repository.html#customize-view-layout
# https://discourse.slicer.org/t/is-there-a-way-to-view-just-one-slice-with-one-3d-view/20541/2

# Adapted/extended by D.I.V.
# 2021 to 2022.
# CC-BY


### Initialise

tagTuple = ("Red", "Yellow", "Green")
orientationTuple = ("Axial", "Sagittal", "Coronal")
labelTuple = tuple( [x[0] for x in tagTuple] )
colourTuple = ("#F34A33", "#E9D14B", "#6CAD4A")

# Show a custom layout of a 3D view on top of the (yet-to-be) specified slice view
customLayoutTemplate = """
<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="{tag}">
    <property name="orientation" action="default">{ori}</property>
    <property name="viewlabel" action="default">{lab}</property>
    <property name="viewcolor" action="default">{col}</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.
customLayoutIdBase = 500

layoutManager = slicer.app.layoutManager()

### Iterate over new views

for i, (t, o, l, c) in enumerate(zip(tagTuple, orientationTuple, labelTuple, colourTuple), start=1):
    # Define new views
    customLayoutId = customLayoutIdBase + i
    customLayout = customLayoutTemplate.format(tag=t, ori=o, lab=l, col=c)
    layoutManager.layoutLogic().GetLayoutNode().AddLayoutDescription(customLayoutId, customLayout)
    # 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("Two-up (3D and " + t + ")") # add inside layout list
    layoutSwitchAction.setData(customLayoutId)
    layoutSwitchAction.setIcon(qt.QIcon(":Icons/Go.png"))
    layoutSwitchAction.setToolTip("3D and " + o + " slice view")


### Switch to the last new custom layout
layoutManager.setLayout(customLayoutId)
2 Likes

Thanks for sharing the script. Alternatively, you could add just one new layout, containing a “tabbed slice view” and “3D view” side by side. Something like this:

That’s good too. (Although I’m not sure how to make the tabs — never even saw/noticed them before on any other view.)


Actually, another way is to just make the one basic (untabbed) two-up view and then manually adjust the orientation from the drop-down menu.
image

That workflow is almost as efficient as defining three distinct two-up views: hover or click to display menu bar, click to display drop-down list, click to choose VERSUS click to display list of views, click to choose.
However, it breaks the correspondence between slice colour and slice orientation.


On reflection, the tabbed view maintains slice colour–orientation correspondence, and potentially has the most efficient workflow (fewest clicks) — depending on how the user likes to work. For instance, if the user were mostly using one of the two-up combinations, then just clicking the tab is a one-click process. Conversely, if the user were regularly swapping between (say) four-up and two-up views, then the tabbed set-up might actually add an extra click: e.g. select four-up; [do work]; select two-up (defaulting to Red slice), then click tab to get Green slice; [do work]; return to four-up; [do work]; select two-up (defaulting to Red or Green* slice), then click tab to get Yellow slice; [do work]; etc..

—DIV

*Presumably there could be a way of either setting one tab as a constant default, or else to display the most recently displayed tab (within a session, at least).