Skip to content

Commit f60ff75

Browse files
committed
Add more helper functions
- showSliceViewAnnotations: show/hide slice view annotations - displayViewLightbox: show lightbox image - installExtensions: install a list of extensions - AppWindow: display VNC window inside a notebook cell
1 parent fd473df commit f60ff75

File tree

4 files changed

+285
-138
lines changed

4 files changed

+285
-138
lines changed
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# TODO: separate widgets (can be inserted into layouts) from functions (providing data)
22

33
# views (widgets and rendering utils)
4-
from .display import displayViews, displaySliceView, display3DView, showVolumeRendering, reset3DView, setViewLayout
4+
from .display import displayViews, displaySliceView, display3DView, displayViewLightbox
5+
from .display import setViewLayout, showSliceViewAnnotations, showVolumeRendering, reset3DView
56

67
# mrml (convert MRML nodes to data types that has nice visualization in notebooks)
78
from .display import displayModel, displayTable, displayMarkups, displayTransform, displayNode
@@ -10,13 +11,13 @@
1011
from .cli import cliRunSync
1112

1213
# util (file management, useful widgets)
13-
from .files import downloadFromURL, localPath, notebookPath
14+
from .files import downloadFromURL, localPath, notebookPath, installExtensions
1415

1516
# widgets
1617
try:
1718
import ipywidgets
1819
except ImportError:
1920
pass
2021
else:
21-
from .widgets import SliceViewWidget, ThreeDViewWidget, FileUploadWidget
22+
from .widgets import SliceViewWidget, SliceViewBaseWidget, ThreeDViewWidget, FileUploadWidget, AppWindow
2223
from .interactive_view_widget import InteractiveViewWidget

JupyterNotebooks/JupyterNotebooksLib/display.py

Lines changed: 87 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import ctk, qt, slicer, vtk
22

3-
class displayModel():
3+
class displayModel(object):
44
def __init__(self, modelNode, imageSize=None, orientation=None, zoom=None, showFeatureEdges=False):
55
self.modelNode = modelNode
66
# rollPitchYawDeg
@@ -90,7 +90,7 @@ def displayMarkups(markupsNode):
9090
"""Display markups node by converting to pandas dataframe object"""
9191
return slicer.util.dataframeFromMarkups(markupsNode)
9292

93-
class displayTransform():
93+
class displayTransform(object):
9494
"""This class displays information about a transform in a Jupyter notebook cell.
9595
"""
9696
def __init__(self, transform):
@@ -130,7 +130,7 @@ def displayNode(node):
130130
return None
131131

132132

133-
class displayViews:
133+
class displayViews(object):
134134
"""This class captures current views and makes it available
135135
for display in the output of a Jupyter notebook cell.
136136
:param viewLayout: FourUp, Conventional, OneUp3D, OneUpRedSlice,
@@ -167,7 +167,7 @@ def _repr_mimebundle_(self, include=None, exclude=None):
167167
dataType = "image/png"
168168
return { dataType: dataValue }
169169

170-
class displaySliceView():
170+
class displaySliceView(object):
171171
"""This class captures a slice view and makes it available
172172
for display in the output of a Jupyter notebook cell.
173173
"""
@@ -198,7 +198,7 @@ def _repr_mimebundle_(self, include=None, exclude=None):
198198
dataType = "image/jpeg"
199199
return { dataType: dataValue }
200200

201-
class display3DView():
201+
class display3DView(object):
202202
"""This class captures a 3D view and makes it available
203203
for display in the output of a Jupyter notebook cell.
204204
"""
@@ -237,19 +237,28 @@ def _repr_mimebundle_(self, include=None, exclude=None):
237237
dataType = "image/jpeg"
238238
return { dataType: dataValue }
239239

240-
def showVolumeRendering(volumeNode, presetName=None):
240+
def showVolumeRendering(volumeNode, show=True, presetName=None):
241241
volRenLogic = slicer.modules.volumerendering.logic()
242-
displayNode = volRenLogic.CreateDefaultVolumeRenderingNodes(volumeNode)
243-
displayNode.SetVisibility(True)
244-
scalarRange = volumeNode.GetImageData().GetScalarRange()
245-
if not presetName:
246-
if scalarRange[1]-scalarRange[0] < 1500:
247-
# small dynamic range, probably MRI
248-
presetName = 'MR-Default'
249-
else:
250-
# larger dynamic range, probably CT
251-
presetName = 'CT-Chest-Contrast-Enhanced'
252-
displayNode.GetVolumePropertyNode().Copy(volRenLogic.GetPresetByName(presetName))
242+
if show:
243+
displayNode = volRenLogic.GetFirstVolumeRenderingDisplayNode(volumeNode)
244+
if not displayNode:
245+
displayNode = volRenLogic.CreateDefaultVolumeRenderingNodes(volumeNode)
246+
displayNode.SetVisibility(True)
247+
scalarRange = volumeNode.GetImageData().GetScalarRange()
248+
if not presetName:
249+
if scalarRange[1]-scalarRange[0] < 1500:
250+
# small dynamic range, probably MRI
251+
presetName = 'MR-Default'
252+
else:
253+
# larger dynamic range, probably CT
254+
presetName = 'CT-Chest-Contrast-Enhanced'
255+
displayNode.GetVolumePropertyNode().Copy(volRenLogic.GetPresetByName(presetName))
256+
else:
257+
# hide
258+
volRenLogic = slicer.modules.volumerendering.logic()
259+
displayNode = volRenLogic.GetFirstVolumeRenderingDisplayNode(volumeNode)
260+
if displayNode:
261+
displayNode.SetVisibility(False)
253262

254263
def reset3DView(viewID=0):
255264
threeDWidget = slicer.app.layoutManager().threeDWidget(viewID)
@@ -259,3 +268,64 @@ def reset3DView(viewID=0):
259268
def setViewLayout(layoutName):
260269
layoutId = eval("slicer.vtkMRMLLayoutNode.SlicerLayout"+layoutName+"View")
261270
slicer.app.layoutManager().setLayout(layoutId)
271+
272+
def showSliceViewAnnotations(show):
273+
# Disable slice annotations immediately
274+
slicer.modules.DataProbeInstance.infoWidget.sliceAnnotations.sliceViewAnnotationsEnabled=show
275+
slicer.modules.DataProbeInstance.infoWidget.sliceAnnotations.updateSliceViewFromGUI()
276+
# Disable slice annotations persistently (after Slicer restarts)
277+
settings = qt.QSettings()
278+
settings.setValue('DataProbe/sliceViewAnnotations.enabled', 1 if show else 0)
279+
280+
class displayViewLightbox(object):
281+
282+
def __init__(self, viewName=None, rows=None, columns=None, filename=None, positionRange=None, rangeShrink=None):
283+
viewName = viewName if viewName else "Red"
284+
rows = rows if rows else 4
285+
columns = columns if columns else 6
286+
287+
sliceWidget = slicer.app.layoutManager().sliceWidget(viewName)
288+
289+
if positionRange is None:
290+
sliceBounds = [0,0,0,0,0,0]
291+
sliceWidget.sliceLogic().GetLowestVolumeSliceBounds(sliceBounds)
292+
slicePositionRange = [sliceBounds[0], sliceBounds[1]]
293+
else:
294+
slicePositionRange = [positionRange[0], positionRange[1]]
295+
296+
if rangeShrink:
297+
slicePositionRange[0] += rangeShrink[0]
298+
slicePositionRange[1] -= rangeShrink[1]
299+
300+
# Capture red slice view, 30 images, from position -125.0 to 75.0
301+
# into current folder, with name image_00001.png, image_00002.png, ...
302+
import ScreenCapture
303+
screenCaptureLogic = ScreenCapture.ScreenCaptureLogic()
304+
viewNodeID = 'vtkMRMLSliceNodeRed'
305+
destinationFolder = 'outputs/Capture-SliceSweep'
306+
numberOfFrames = rows*columns
307+
filenamePattern = "_lightbox_tmp_image_%05d.png"
308+
viewNode = sliceWidget.mrmlSliceNode()
309+
# Suppress log messages
310+
def noLog(msg):
311+
pass
312+
screenCaptureLogic.addLog=noLog
313+
# Capture images
314+
screenCaptureLogic.captureSliceSweep(viewNode, slicePositionRange[0], slicePositionRange[1],
315+
numberOfFrames, destinationFolder, filenamePattern)
316+
# Create lightbox image
317+
resultImageFilename = filename if filename else filenamePattern % numberOfFrames
318+
screenCaptureLogic.createLightboxImage(columns, destinationFolder, filenamePattern, numberOfFrames, resultImageFilename)
319+
320+
# Save result
321+
with open(destinationFolder+"/"+resultImageFilename, "rb") as file:
322+
self.dataValue = file.read()
323+
self.dataType = "image/png"
324+
# This could be used to create an image widget: img = Image(value=image, format='png')
325+
326+
# Clean up
327+
screenCaptureLogic.deleteTemporaryFiles(destinationFolder, filenamePattern, numberOfFrames if filename else numberOfFrames+1)
328+
329+
def _repr_mimebundle_(self, include=None, exclude=None):
330+
import base64
331+
return { self.dataType: base64.b64encode(self.dataValue) }

0 commit comments

Comments
 (0)