Hello, I have a doubt regarding accessing button icon using reload button in the 3d slicer. Here I created 3 buttons and set icon to each buttons. The button icons will change by clicking button each time. Everything working fine. Assume if I pressed first button. The icon of first button will change. when I clicked the reload button in the 3d slicer, it will reload the module and restore the changes made to the first button . if the module is reloaded, icon of first button changed and icon of other two buttons unchanged. How to implement this?. The below code I used for creating buttonn and set icon for each button.
class LockWidget(ScriptedLoadableModuleWidget, VTKObservationMixin):
"""Uses ScriptedLoadableModuleWidget base class, available at:
https://github.com/Slicer/Slicer/blob/main/Base/Python/slicer/ScriptedLoadableModule.py
"""
def __init__(self, parent=None) -> None:
"""
Called when the user opens the module the first time and the widget is initialized.
"""
ScriptedLoadableModuleWidget.__init__(self, parent)
def setup(self) -> None:
"""
Called when the user opens the module the first time and the widget is initialized.
"""
ScriptedLoadableModuleWidget.setup(self)
self.ASIS_R_layout, self.ASIS_R = self.create_layout(
label_name="ASIS_R", tooltip_text="Anterior Superior Iliac Spine Right"
)
self.PT_L_layout, self.PT_L= self.create_layout(
label_name="PT_L", tooltip_text="Pubic Tubercle Left"
)
self.PT_R_layout, self.PT_R = self.create_layout(
label_name="PT_R", tooltip_text="Pubic Tubercle Right"
)
self.fiducialVisibleButton = Button(
name="Visibility",
callback=self.on_invisibility_fiducial_point_button,
is_checkable=True
)
self.AlignBox = HBox(
self.fiducialVisibleButton
)
alignment_point_layout = VBox(
self.ASIS_R_layout,
self.PT_L_layout,
self.PT_R_layout,
self.AlignBox,
)
self.alignment_point_group_box = GroupBox(
title="Select points for volume alignment", layout=alignment_point_layout
)
self.layout.addWidget(self.alignment_point_group_box)
def updateParameterNodeFromGUI(self, caller=None, event=None):
"""
This method is called when the user makes any change in the GUI.
The changes are saved into the parameter node (so that they are restored when the scene is saved and loaded).
"""
if self._parameterNode is None or self._updatingGUIFromParameterNode:
return
wasModified = (
self._parameterNode.StartModify()
) # Modify all properties in a single batch
self._parameterNode.EndModify(wasModified)
def create_label(self, label_name, tooltip_text):
label = Label(label_name, None, tooltip_text)
return label
def create_node_selector(self, label_name, node_class="vtkMRMLMarkupsFiducialNode"):
node_selector = NodeSelector.create_node_selector(
self, label_name, node_class, call_back=self.updateParameterNodeFromGUI
)
return node_selector
def create_place_markup_button(
self, label_name, node_class="vtkMRMLMarkupsFiducialNode", transform=None
):
place_markup_button = qt.QPushButton("Place Markup")
place_markup_button.connect(
"clicked()",
lambda: self.add_markup_node_with_name_and_class(
name=label_name, node_class=node_class, transform=transform
),
)
return place_markup_button
def create_jump_button(self, label_name):
jump_button = qt.QPushButton("Jump")
jump_button.connect("clicked()", lambda: self.get_jump_callback(label_name))
return jump_button
def create_lock_button(self, label_name):
lock_button = qt.QPushButton()
lock_button.connect("clicked()", lambda: self.on_lock_button(label_name))
lock_button.setCheckable(True)
# Check if node exists before entering the lock status loop
try:
node = slicer.util.getNode(label_name)
if node and node.GetLocked():
lock_button.setIcon(Icons.locked_icon)
lock_button.setChecked(True)
else:
lock_button.setIcon(Icons.unlocked_icon)
except slicer.util.MRMLNodeNotFoundException:
lock_button.setIcon(Icons.unlocked_icon)
# Define a slot to change the icon when the button is clicked
def change_icon(checked):
node = slicer.util.getNode(label_name)
if node and node.GetLocked():
lock_button.setIcon(Icons.locked_icon)
lock_button.setChecked(True)
else:
lock_button.setIcon(Icons.unlocked_icon)
lock_button.clicked.connect(change_icon)
return lock_button
def on_lock_button(self, name: str) -> None:
"""
This function toggles the locked status of a fiducial node with the given name. If the node is currently locked, it will
be unlocked, and vice versa.
Args:
name(str): The name of the fiducial node to toggle the locked status for.
Returns:
None
"""
fiducial_node = slicer.util.getFirstNodeByName(name)
fiducial_node.SetLocked(not fiducial_node.GetLocked())
def on_invisibility_fiducial_point_button(self):
self.invisible_fiducial_points()
self.fiducialVisibleButton.clicked.connect(LockWidget.update_button(self))
"""
Calling the function update_button by clicking fiducialVisibleButton.
"""
self.fiducialVisibleButton.setCheckable(True)
def update_button(self):
"""
This function will set the icon of lock buttons to locked_icon.
Parameters:
ASIS_R
PT_L
PT_R
Returns:
None
"""
count0=self.ASIS_R_layout.count()
nodes = ["ASIS_R", "PT_L", "PT_R"]
layout=[self.ASIS_R_layout,self.PT_L_layout,self.PT_R_layout]
for i in range(count0):
if i==3:
widget=layout[0].itemAt(3).widget()
widget1=layout[1].itemAt(3).widget()
widget2=layout[2].itemAt(3).widget()
p=[widget,widget1,widget2]
#print(p)
# p[0].setIcon(Icons.locked_icon)
# self.layout.addWidget(p[0])
@staticmethod
def invisible_fiducial_points():
"""Toggle visibility of specified fiducial points in the scene.
The function gets a list of node names for fiducial points. It then checks if
each of these nodes exists in the current MRML scene. For every node that exists,
the visibility of the display node is toggled.
Parameters:
None
Returns:
None
"""
# Define a list of fiducial point node names
nodes = ["ASIS_R", "PT_L", "PT_R"]
# Check if each node exists in the scene
nodes_exist = [
node
for node in nodes
if slicer.mrmlScene.GetFirstNodeByName(node) is not None
]
# Toggle the visibility of each existing fiducial point node
for node in nodes_exist:
markups_node = slicer.util.getNode(node)
markups_node.SetDisplayVisibility(not markups_node.GetDisplayVisibility())
markups_node.SetLocked(markups_node.GetLocked())
def create_layout(
self,
label_name,
tooltip_text,
transform=None,
node_class="vtkMRMLMarkupsFiducialNode",
):
label = Label(label_name, tooltip_text=tooltip_text)
node_selector = self.create_node_selector(label_name, node_class)
place_markup_button = self.create_place_markup_button(
label_name, node_class, transform
)
jump_button = JumpButton(label_name)
lock_button = LockButton(label_name)
layout = HBox(
label, place_markup_button, jump_button, lock_button, node_selector
)
slicer.app.connect(
"mrmlSceneChanged(vtkMRMLScene*)",
node_selector,
"setMRMLScene(vtkMRMLScene*)",
)
return layout, node_selector
def add_markup_node_with_name_and_class(
self, name, node_class="vtkMRMLMarkupsFiducialNode", transform=None
):
existing_node = slicer.mrmlScene.GetFirstNodeByName(name)
if existing_node:
slicer.mrmlScene.RemoveNode(existing_node)
# Create a new markup node to store the point
markup_node = slicer.mrmlScene.AddNewNodeByClass(node_class, name)
# Create a markups place widget to allow the user to place a point
MarkupPlace(markup_node=markup_node)
if transform:
self.transform_node(
name,
transform_name="World_to_PelvicCS",
inverse_transform_name="World_to_PelvicCS_Inverse",
)
class NodeSelector:
"""
A class for creating various types of UI widgets.
Attributes:
None
"""
def create_node_selector(self, viewName: str, node_type: str, call_back=None):
"""
Creates a markup widget for selecting a fiducial node of a specified node type.
Args:
viewName (str): The name of the view.
node_type (str): The type of the node to select.
Returns:
slicer.qSlicerSimpleMarkupsWidget: A markup widget that can be used to select a fiducial node.
"""
node_selector = slicer.qSlicerSimpleMarkupsWidget()
node_selector.setSizePolicy(qt.QSizePolicy.Preferred, qt.QSizePolicy.Fixed)
node_selector.markupsSelectorComboBox().nodeTypes = [node_type]
node_selector.objectName = viewName
node_selector.toolTip = f"Select a fiducial to use as the {viewName} point."
node_selector.setNodeBaseName(viewName)
node_selector.defaultNodeColor = qt.QColor("#ff1472")
node_selector.tableWidget().hide()
node_selector.jumpToSliceEnabled = True
node_selector.optionsVisible = False
node_selector.markupsSelectorComboBox().noneEnabled = True
node_selector.markupsPlaceWidget().setPlaceMultipleMarkups(slicer.qSlicerMarkupsPlaceWidget.ForcePlaceSingleMarkup)
node_selector.markupsPlaceWidget().setButtonsVisible(False)
node_selector.markupsPlaceWidget().placeButton().show()
node_selector.setMRMLScene(slicer.mrmlScene)
node_selector.setStyleSheet("""
QFrame {
border: 2px solid black;
border-radius: 5px;
color: #FFFFFF;
font-family: Arial;
padding: 4px;
font-size: 16px;
background-color: #493F5E;
}
# QFrame:hover {
# color: #323232;
# }
""")
if call_back is not None:
node_selector.connect("currentNodeChanged(vtkMRMLNode*)", call_back)
return node_selector
class GroupBox(qt.QGroupBox):
def __init__(self, title=None, layout=None) -> None:
super().__init__()
self.setTitle(title)
self.setLayout(layout)
self.setSizePolicy(qt.QSizePolicy.Preferred, qt.QSizePolicy.Fixed)
self.setStyleSheet(self.set_style_sheet())
@staticmethod
def set_style_sheet():
return """
QGroupBox {
border: 2px solid #4285f4;
border-radius: 4px;
margin-top: 12px;
font-size: 16px;
}
QGroupBox::title {
subcontrol-origin: margin;
subcontrol-position: top center;
padding: 0 6px;
background-color: #F7EDE2;
color: #4285f4;
}
"""
class Button(qt.QPushButton):
def __init__(self, name:str = None, callback: Optional[callable] = None, is_checkable: bool = False, icon = None, enabled = True) -> qt.QPushButton:
super().__init__()
if name is not None:
self.setText(name)
self.setSizePolicy(qt.QSizePolicy.Preferred, qt.QSizePolicy.Fixed)
if callback is not None:
self.clicked.connect(callback)
self.setCheckable(is_checkable)
if icon is not None:
self.setIcon(icon)
if is_checkable:
self.setStyleSheet(self.set_style_sheet())
self.setEnabled(enabled)
@staticmethod
def set_style_sheet():
return """
QPushButton {
background-color: #3F51B5;
border: none;
color: white;
padding: 4px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 12px;
cursor: pointer;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
transition-duration: 0.3s;
border-style: outset;
border-width: 3px;
border-radius: 7px;
border-color: black;
}
QPushButton:hover {
background-color: #c83471;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
"""
class VBox(qt.QVBoxLayout):
"""
Creates a QHBoxLayout object and adds each widget to it.
Args:
*widgets: A variable-length argument list of QWidget objects.
Returns:
A QHBoxLayout object containing all the added widgets.
Example:
hbox = create_hbox(button1, button2, button3)
"""
def __init__(self, *layouts: qt.QLayout) -> qt.QVBoxLayout:
super().__init__()
for layout in layouts:
self.addLayout(layout)
class HBox(qt.QHBoxLayout):
"""
Creates a QHBoxLayout object and adds each widget to it.
Args:
*widgets: A variable-length argument list of QWidget objects.
Returns:
A QHBoxLayout object containing all the added widgets.
Example:
hbox = create_hbox(button1, button2, button3)
"""
def __init__(self, *widgets: qt.QWidget) -> qt.QHBoxLayout:
s=[]
super().__init__()
for widget in widgets:
self.addWidget(widget)
class LockButton(qt.QPushButton):
def __init__(self, name) -> None:
super().__init__()
self.setIcon(Icons.unlocked_icon)
self.connect("clicked()", lambda: self.on_lock_button(name))
self.setCheckable(True)
def on_lock_button(self, name):
try:
self.toggle_state(name)
self.change_icon(name)
except slicer.util.MRMLNodeNotFoundException:
self.setIcon(Icons.unlocked_icon)
def change_icon(self, name):
node = slicer.util.getNode(name)
if node and node.GetLocked():
self.setIcon(Icons.locked_icon)
self.setChecked(True)
else:
self.setIcon(Icons.unlocked_icon)
@staticmethod
def toggle_state(name: str) -> None:
"""
This function toggles the locked status of a fiducial node with the given name. If the node is currently locked, it will
be unlocked, and vice versa.
Args:
name(str): The name of the fiducial node to toggle the locked status for.
Returns:
None
"""
fiducial_node = slicer.util.getFirstNodeByName(name)
fiducial_node.SetLocked( not fiducial_node.GetLocked())
class JumpLogic:
@staticmethod
def get_jump_callback(name):
markup = slicer.util.getNode(name)
slicer.modules.markups.logic().JumpSlicesToNthPointInMarkup(markup.GetID(), 0)
position = [0.0, 0.0, 0.0]
markup.GetNthControlPointPositionWorld(0,position)
# Center slice views and cameras on this position
for sliceNode in slicer.util.getNodesByClass('vtkMRMLSliceNode'):
sliceNode.JumpSliceByCentering(*position)
for camera in slicer.util.getNodesByClass('vtkMRMLCameraNode'):
camera.SetFocalPoint(position)
class JumpButton(qt.QPushButton):
def __init__(self, name) -> None:
super().__init__()
self.text = "Jump"
self.connect("clicked()", lambda: JumpLogic.get_jump_callback(name))
class MarkupPlace:
def __init__(self, markup_node) -> None:
place_widget = slicer.qSlicerMarkupsPlaceWidget()
place_widget.setMRMLScene(slicer.mrmlScene)
place_widget.setCurrentNode(markup_node)
place_widget.setPlaceModeEnabled(True)
place_widget.setPlaceMultipleMarkups(slicer.qSlicerMarkupsPlaceWidget.ForcePlaceSingleMarkup)
# Set up a signal to be triggered when the point is placed
def on_point_placed():
place_widget.disconnect("markupPlaced(vtkMRMLNode*)", on_point_placed)
print("Markup point placed at:", markup_node.GetMarkupPointVector(0))
place_widget.connect("markupPlaced(vtkMRMLNode*)", on_point_placed)
class Label(qt.QLabel):
def __init__(self, text, size_policy=None, tooltip_text=None) -> None:
super().__init__()
self.setText(text)
if tooltip_text is not None:
self.setToolTip(tooltip_text)
self.setStyleSheet(self.set_style_sheet())
if size_policy is not None:
self.setSizePolicy(size_policy)
else:
self.setFixedSize(90, 25)
self.setAlignment(qt.Qt.AlignHCenter | qt.Qt.AlignVCenter)
@staticmethod
def set_style_sheet():
return """
QLabel {
background-color: #4285f4;
color: #ffffff;
border-radius: 4px;
font-size: 12px;
padding: 4px;
text-align: center;
}
"""
class Icons:
unlocked_icon = qt.QIcon(
os.path.join(os.path.dirname(__file__), "Widget", "Icons", "unlock.svg")
)
locked_icon = qt.QIcon(
os.path.join(os.path.dirname(__file__), "Widget", "Icons", "lock.svg")
)