Understanding UI Editing and Python File Generation in 3DSlicer with Qt Designer

Hello Dear Developers and Users,

In the implementation of the module, I am using the Extension Wizard module. This module utilizes a fixed template, the result of which ultimately creates a .ui file and its corresponding Python file. For example, let’s assume that the name of this module is developModuleTry01. After switching to the developModuleTry01 module, I can run the Qt Designer within 3DSlicer and add or remove the module’s widgets. After saving and exiting Qt Designer, I click on Reload and Test to apply the changes. However, I noticed that the Python file does not change when I edit the UI file. I also checked other options of the developModuleTry01 module, including Reload and Restart Slicer, but I observed that the Python file still doesn’t change when I edit the module’s UI.

Is it a correct and reasonable expectation that the Python file should also change when I edit the UI through Qt Designer?

I know that, in general, to generate Python code from a UI file created with Qt Designer, tools like or pyuic or pyuic5 should be used. Similarly, in 3DSlicer, a tool similar to pyuic5 should be used, and it is expected that when using Edit UI and clicking on Reload and Test, two things should happen:
one, the graphical environment of the module is reloaded, and
two, a tool like pyuic5 is executed to regenerate the Python file.

Is my assumption incorrect?
Please guide me.
Best regards.
Shahrokh

This is expected. The UI file is an xml file in the Resources/UI subdirectory that is used to build the widgets for the module GUI. The default python module script from the template reads this file with a line like uiWidget = slicer.util.loadUI(self.resourcePath("UI<module>.ui")) so the python code doesn’t need to change with you edit the ui file.

Thank you very much for your response, but perhaps I should explain the reason for asking this question so that, if possible, you may guide me further.

Perhaps you have already read the question I raised in my previous message with the title of Implementing Widgets for Selecting Nodes of Type Segment and Beam. In general, I want to familiarize myself with the process of developing modules in 3DSlicer. Therefore, I want to learn how I can develop the functionality of widgets so that I can select specific nodes from the Data module and process them. For example, with one widget, I should be able to select only nodes of type Segment, with another widget, only nodes of type Model, and with yet another widget, only nodes of type Beam.

Based on this, the roadmap I set for myself was to first create a UI that contains a widget of type qMRMLSegmentSelectorWidget to select only nodes of type Segment. The reason I chose this node is that I found out this widget is part of the Slicer [MRML Widgets] in Widget Box of Qt Designer, and it seems to have been specifically added to Qt Designer by the 3D Slicer development team for module implementation. (At this point, I also had a problem, as I couldn’t find other widgets in the list that, based on their names, I could guess might be useful for selecting nodes of specific types.)

By selecting this particular widget, I expected that the related Python file would be much shorter so I could analyze and understand its components, and ultimately develop its function so that it would select only nodes of type Segment from the Data module. As I mentioned in my message, the Python file that was generated was default and contains 403 lines.

Best regards.
Shahrokh

It sounds like what you are looking for is a qMRMLNodeComboBox. You can use the nodeTypes property and other api options to control what gets displayed. These are used all over Slicer, so looking at the code will give you ideas how to use it.

Thank you very much for the guidance you provided. I will definitely implement the point you mentioned in my module.

The plans I have in mind are to implement modules with names such as VolumeSelector, ModelSelector, BeamSelector, etc., separately, in order to expand my experience in module development.

However, if you pay attention to the following figure, you will notice that the parent class used is of type qMRMLWidget. Interestingly, the qMRMLSegmentSelectorWidget widget does not have the nodeType property.

But as mentioned you, if a widget of type qMRMLNodeComboBox is selected, it does have the nodeType property, and I think I should set it to vtkMRMLScalarVolumeNode or vtkMRMLSegmentationNode in python code.

Best regards.
Shahrokh

Thank you very much for the helpful guidance regarding the property nodeTypes. Based on your response and the answer that Csaba Pinter provided to the question raised by Thomas Dutrey titled Input segments in new module, I was able to implement two separate modules named VolumeSelector, SegmentSelector and RTBeamSelector.

As mentioned in the response, the key lines in the corresponding Python code files, VolumeSelector.py, SegmentSelector.py and RTBeamSelector.py, are the following lines, which determine the type of node in the Selector:

In the VolumeSelector.py file:

    # input volume selector
    #
    self.inputSelector = slicer.qMRMLNodeComboBox()
    self.inputSelector.nodeTypes = ( ("vtkMRMLScalarVolumeNode"), "" )
    #self.inputSelector.addAttribute( "vtkMRMLScalarVolumeNode", "LabelMap", 0 )
    self.inputSelector.selectNodeUponCreation = True
    self.inputSelector.addEnabled = False
    self.inputSelector.removeEnabled = False
    self.inputSelector.noneEnabled = False
    self.inputSelector.showHidden = False
    self.inputSelector.showChildNodeTypes = False
    self.inputSelector.setMRMLScene( slicer.mrmlScene )
    self.inputSelector.setToolTip( "Pick the input to the algorithm." )
    parametersFormLayout.addRow("Input Volume: ", self.inputSelector)

In the SegmentSelector.py file:

    # input segment selector
    #
    self.inputSelector = slicer.qMRMLNodeComboBox()
    self.inputSelector.nodeTypes = ( ("vtkMRMLSegmentationNode"), "" )
    #self.inputSelector.addAttribute( "vtkMRMLSegmentationNode", "Segment", 0 )
    self.inputSelector.selectNodeUponCreation = True
    self.inputSelector.addEnabled = False
    self.inputSelector.removeEnabled = False
    self.inputSelector.noneEnabled = False
    self.inputSelector.showHidden = False
    self.inputSelector.showChildNodeTypes = False
    self.inputSelector.setMRMLScene( slicer.mrmlScene )
    self.inputSelector.setToolTip( "Pick the input to the algorithm." )
    parametersFormLayout.addRow("Segment: ", self.inputSelector)

In the RTBeamSelector.py file:

    # input RT beam selector
    #
    self.inputSelector = slicer.qMRMLNodeComboBox()
    self.inputSelector.nodeTypes = ( ("vtkMRMLRTBeamNode"), "" )
    #self.inputSelector.addAttribute( "vtkMRMLRTBeamNode", "RTBeam", 0 )
    self.inputSelector.selectNodeUponCreation = True
    self.inputSelector.addEnabled = False
    self.inputSelector.removeEnabled = False
    self.inputSelector.noneEnabled = False
    self.inputSelector.showHidden = False
    self.inputSelector.showChildNodeTypes = False
    self.inputSelector.setMRMLScene( slicer.mrmlScene )
    self.inputSelector.setToolTip( "Pick the input to the algorithm." )
    parametersFormLayout.addRow("Beam: ", self.inputSelector)

Best regards.
Shahrokh

1 Like