I could not reproduce any crash with the scripts above, so maybe the issue is how you fill the tables with real data. I’ve made a couple of fixes and improvement (fix order of views, fit plot to the view, adjust labeling, etc.) and this is the result:
The updated code:
def SetLayout(nChannels):
customLayout=(
"<layout type=\"horizontal\" split=\"true\">"
"<item>"
"<layout type=\"vertical\" split=\"true\">"
"<item>"
"<view class=\"vtkMRMLSliceNode\" singletontag=\"Red\">"
"<property name=\"orientation\" action=\"default\">Axis</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\">#EDD54C</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>"
"</layout>"
"</item>"
"<item>"
"<view class=\"vtkMRMLViewNode\" singletontag=\"1\">"
"<property name=\"viewlabel\" action=\"default\">1</property>"
"</view>"
"</item>"
"<item>"
"<layout type=\"vertical\">")
for i in range(nChannels):
customLayout+='<item><view class="vtkMRMLPlotViewNode" singletontag="SignalView'+'-{}"'.format(i)+'>'
customLayout+='<property name="viewlabel" action="default">'+'P{}'.format(i+1)+'</property>'
customLayout+='</view></item>'
customLayout+='</layout></item></layout>'
slicer.customLayout = customLayout
customLayoutId=501
layoutManager=slicer.app.layoutManager()
if not layoutManager.layoutLogic().GetLayoutNode().SetLayoutDescription(customLayoutId,customLayout):
layoutManager.layoutLogic().GetLayoutNode().AddLayoutDescription(customLayoutId,customLayout)
layoutManager.setLayout(customLayoutId)
for i in range(nChannels):
layoutManager.plotWidget(i).plotController().setVisible(False)
def PlotSignal():
import math
num=10
SetLayout(num)
maxNumOfPoints=100
layoutManager=slicer.app.layoutManager()
if layoutManager.plotViewCount!=num:
print("Plot view number({}) is not equal to signal channel number({})".format(layoutManager.plotViewCount,num))
return
for channel in range(num):
tableNode=slicer.mrmlScene.AddNewNodeByClass("vtkMRMLTableNode","Table{}".format(channel))
table=tableNode.GetTable()
table.Initialize()
arrX=vtk.vtkFloatArray()
arrX.SetName("Time(ms)")
table.AddColumn(arrX)
arrY=vtk.vtkFloatArray()
name="{0} mm".format(channel)
arrY.SetName(name)
table.AddColumn(arrY)
table.SetNumberOfRows(maxNumOfPoints)
for j in range(maxNumOfPoints):
table.SetValue(j,0,j)
table.SetValue(j,1,math.sin(j))
table.Modified()
#Create two plot series nodes
name="{0} mm".format(channel)
plotSeriesNode=slicer.mrmlScene.AddNewNodeByClass("vtkMRMLPlotSeriesNode","Signal@{0}".format(name))
plotSeriesNode.SetAndObserveTableNodeID(tableNode.GetID())
plotSeriesNode.SetXColumnName("Time(ms)")
plotSeriesNode.SetYColumnName(name)
plotSeriesNode.SetPlotType(slicer.vtkMRMLPlotSeriesNode.PlotTypeLine)
plotSeriesNode.SetUniqueColor()
plotChartNode=slicer.mrmlScene.AddNewNodeByClass("vtkMRMLPlotChartNode","Chart@{0}".format(name))
plotChartNode.AddAndObservePlotSeriesNodeID(plotSeriesNode.GetID())
plotChartNode.SetXAxisTitle('') # 'Time(ms)'
plotChartNode.SetYAxisTitle(f'uV @{name}')
plotChartNode.SetAxisTitleFontSize(9)
plotChartNode.SetAxisLabelFontSize(9)
plotChartNode.SetYAxisRangeAuto(True)
plotChartNode.SetXAxisRangeAuto(True)
plotChartNode.SetLegendVisibility(False)
plotViewNode=slicer.mrmlScene.GetSingletonNode(f'SignalView-{channel}','vtkMRMLPlotViewNode')
plotViewNode.SetPlotChartNodeID(plotChartNode.GetID())
for i in range(layoutManager.plotViewCount):
layoutManager.plotWidget(i).plotView().fitToContent()
PlotSignal()