First issue: Running getNode on a node whose name contains square brackets with a number inside.
I noticed that if a node’s name contains square brackets with a number or expression inside (for example [11]), I get the following error when using the Python Interactor. Let’s assume that when reading a DICOM RTDose file using the DICOM Module, I see a node with the name:
in the Data Module. Now, if I try to read it with getNode, the following error occurs:
>>> rtDose = getNode('2: RTDOSE: DOSIsoft:RTDOSE:Phase #1 Dosi Dosi 1: Beam setup 1 (GY) [11]: Beam setup 1');
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/sn/Slicer-5.0.2-linux-amd64/bin/Python/slicer/util.py", line 1436, in getNode
raise MRMLNodeNotFoundException("could not find nodes in the scene by name or id '%s'" % (pattern if (isinstance(pattern, str)) else ""))
slicer.util.MRMLNodeNotFoundException: could not find nodes in the scene by name or id '2: RTDOSE: DOSIsoft:RTDOSE:Phase #1 Dosi Dosi 1: Beam setup 1 (GY) [11]: Beam setup 1'
>>>
However, if I manually rename the node to (removing 11 from [11]):
Second issue: Filtering nodes in a ComboBox based on their type.
Is it possible to have a ComboBox where only nodes of the type RTDose are listed, and other nodes, such as those related to CT images, are not shown?
In the following figure, I have two nodes with the names of 2: Unnamed Series and 2: RTDOSE: DOSIsoft:RTDOSE:Phase #1 Dosi Dosi 1: Beam setup 1 (GY) []: Beam setup 1, both of which are of the type vtkMRMLScalarVolumeNode. However, for example, in the Isodose module, I can only see the node of type RTDOSE, not the other one.
I confirm that getNode does not work with square brackets in the latest. I think it used to work, because I regularly accessed beam nodes and they have these brackets in the name. This may be due to the new translation features? Not sure.
For the time being you can use slicer.mrmlScene.GetFirstNodeByName('MRHead [11]')
Second issue:
Please look at the comboboxes in Isodose or DoseVolumeHistogram modules in SlicerRT, they can filter to show only RTDose volumes.
Thank you very much for your answers and guidance. I understand that the Isodose or DoseVolumeHistogram modules have the filtering feature based on the type of node. I was wondering which property has been used in the RTDose type nodes that allows these modules to list only this type of node?
Unfortunately, I still haven’t been able to implement the UI that is similar to the comboboxes in the DoseVolumeHistogram or Isodose modules, where they can filter and only show RTDose volumes. However, I found some interesting points. One of these is the presence of an Attribute called DicomRtImport.DoseVolume with a value of 1 in RTDose volumes. Based on this, I can identify and separate the nodes with this attribute from the ones in the Data module using the following commands:
import slicer
listVolumeNodes = list(slicer.mrmlScene.GetNodesByClass('vtkMRMLScalarVolumeNode'))
strClass = 'vtkMRMLScalarVolumeNode'
for nodeNumber in range(len(listVolumeNodes)):
nodeID = strClass + str(nodeNumber+1)
print(nodeID)
slicer.mrmlScene.GetNodeByID(nodeID).GetAttributeNames()
strAttributeDoseVolume = 'DicomRtImport.DoseVolume'
for nodeNumber in range(len(listVolumeNodes)):
nodeID = strClass + str(nodeNumber+1)
if strAttributeDoseVolume == slicer.mrmlScene.GetNodeByID(nodeID).GetAttributeNames()[0]:
print('Attribute Node: ', slicer.mrmlScene.GetNodeByID(nodeID).GetAttributeNames())
nodeDoseVolume = slicer.mrmlScene.GetNodeByID(nodeID)
However, I couldn’t reach my goal in the UI, which is to only show RTDose Volume in the ComboBox. Still, the code I have written so far is as follows:
import os
As you can see from the commands above, I used various “Get” functions from slicer.mrmlScene, but none of them gave the desired result.
As you suggested, I studied the module code from DoseVolumeHistogram on GitHub. Although it seemed like the nodeType was assigned the value vtkMRMLScalarVolumeNode, I couldn’t figure out what other condition was added to make the combobox work correctly.
In any case, I will keep reading through this code to see if I can find a solution to my issue. I believe that when someone is looking for an answer to their problem on GitHub, StackOverflow, or in forums, they inevitably come across many other problems and solutions. Still, I would be happy if you could guide me.
getNode is only intended for quick access to nodes for testing and troubleshooting. For convenience, it uses searching with wildcards in filename format (e.g., you can ask getNode('something*')), but it also means that some special characters (such as *, square brackets, etc.) need to be escaped. It has many other limitations, such as it only returns the first node by that name (while in scene you often have several nodes by the exact same name). So, it is a convenient shorthand for simple cases, use it when it works for you.
If you have several nodes by the same name or the node name contain special characters, I would recommend to use:
getNode with the node ID as argument (it does not contain special character and it is unique within a scene)
getFirstNodeByClassByName function, which allows you to select the right node even when there are various other nodes of a different class by the same name. It also comes without filename search pattern matching, so you don’t need to worry about special characters. For example, try: getFirstNodeByClassByName("vtkMRMLScalarVolumeNode", "2: RTDOSE: DOSIsoft:RTDOSE:Phase #1 Dosi Dosi 1: Beam setup 1 (GY) [11]: Beam setup 1"
Filtering in node selector
As @cpinter suggested, you can have a look how it is done in DoseVolumeHistogram module:
In the future, please post unrelated questions as two separate topics. Thank you!
First of all, I would like to thank you for your excellent guidance and support. The point you mentioned at the end, I will definitely take into consideration.
I have learned some interesting points from the existing code, such as vtkSlicerRtCommon.cxx. For example, I learned how to determine whether a volume node is of type RTDose based on the attribute DicomRtImport.DoseVolume. To achieve this, I implemented a function called doseVolumeFilter:
I should mention, however, that after writing this function, I noticed that the IsDoseVolumeNode function in the vtkSlicerRtCommon.cxx file does the same thing.
Anyway, I am trying hard to write a combobox that only lists volumes of type RTDose. However, I haven’t been successful yet. As shown in the following lines:
import os
import unittest
from __main__ import vtk, qt, ctk, slicer
from slicer.ScriptedLoadableModule import *
import qt
parametersCollapsibleButton = ctk.ctkCollapsibleButton()
parametersCollapsibleButton.text = "Parameters"
parametersCollapsibleButton.show()
parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton)
inputSelector0 = slicer.qMRMLNodeComboBox()
inputSelector0.setMRMLScene( slicer.mrmlScene )
inputSelector0.enabled
inputSelector0.setToolTip( "Pick the input to the algorithm." )
parametersFormLayout.addRow("Dose Volume: ", inputSelector0)
The result of executing these commands is a combobox that lists different types of nodes. In these lines, the nodeTypes is not specified, as following figure:
Based on line 65 of the vtkSlicerRtCommon.cxx file, I thought perhaps an attribute named DICOMRTIMPORT_DOSE_VOLUME_IDENTIFIER_ATTRIBUTE_NAME with the value DicomRtImport.DoseVolume should be added to the combobox.
Accordingly, I executed the following code:
import os
import unittest
from __main__ import vtk, qt, ctk, slicer
from slicer.ScriptedLoadableModule import *
import qt
parametersCollapsibleButton = ctk.ctkCollapsibleButton()
parametersCollapsibleButton.text = "Parameters"
parametersCollapsibleButton.show()
parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton)
inputSelector0 = slicer.qMRMLNodeComboBox()
inputSelector0.nodeTypes = ["vtkMRMLScalarVolumeNode"]
inputSelector0.addAttribute("vtkMRMLScalarVolumeNode", "DICOMRTIMPORT_DOSE_VOLUME_IDENTIFIER_ATTRIBUTE_NAME", "DicomRtImport.DoseVolume")
inputSelector0.setMRMLScene( slicer.mrmlScene )
inputSelector0.enabled
inputSelector0.setToolTip( "Pick the input to the algorithm." )
parametersFormLayout.addRow("Dose Volume: ", inputSelector0)
In these commands, the nodeTypes and addAttribute methods were used. However, as you can see in the figure below, contrary to my expectation, the node list in the combobox is empty.
Based on line 271 of the qSlicerIsodoseModuleWidget.cxx file, it occurred to me that perhaps the filtering should be done through the inputSelector0.connect function, as dear Andras Lasso has pointed out.
I think the issue lies in the implementation of this function. In my module, named RTDoseSelector.py, I implemented this function as follows:
If possible, could you please guide me? In my opinion, acquiring the ability to filter nodes based on their type and attribute is very important. The attribute is particularly significant because the difference between RTDose and other volumeNode types lies in its attribute.
Excuse me, I think that in order for the combobox to have filtering capability (to pass volumes with the attribute equal to DicomRtImport.DoseVolume and reject other volumes, i.e., not display them in the list of combobox), it should use the concept of signal, slot, and connect. This understanding of mine is based on line 271 from the file qSlicerIsodoseModuleWidget.cxx.
Is my understanding correct?
Best regards.
Shahrokh
Connection of signals and slots are nor related to filtering. The issue in the code above is that DICOMRTIMPORT_DOSE_VOLUME_IDENTIFIER_ATTRIBUTE_NAME is not a string literal. It is a variable name.
Instead of using the variable name as input, you need to use the variable value.
Thank you very much for the support and assistance you are providing. You have guided me very well, but I was not able to understand the issue.
line corrected: