# My QActions hangs the Slicer application

**URL:** <https://discourse.slicer.org/t/my-qactions-hangs-the-slicer-application/12152>\
**Category:** Development\
**Created:** [June 22, 2020, 11:21am UTC](https://discourse.slicer.org/t/my-qactions-hangs-the-slicer-application/12152 "2020-06-22T11:21:53Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![joachim](https://avatars.discourse-cdn.com/v4/letter/j/a87d85/32.png) [@joachim](https://discourse.slicer.org/u/joachim)\
**Post date:** [June 22, 2020, 11:21am UTC](https://discourse.slicer.org/t/my-qactions-hangs-the-slicer-application/12152/1 "2020-06-22T11:21:53Z")

</div>

I’m creating 2 layouts for manual segmentation. And I’m trying to create _QtAction_ s to trigger these layouts (_slicerrc.py_-file can be found [here](https://github.com/karamellpelle/SlicerRC/blob/965e571a12f9eab596626046a851095579c9c17e/slicerrc.py)):

```auto
# XML definitions (definitions omitted)
LAYOUTXML_MANUALSEGMENTATION_3D = "..."
LAYOUTXML_MANUALSEGMENATION = "..."
# out-of-the-blue values
LAYOUTID_MANUALSEGMENTATION_3D = 440
LAYOUTID_MANUALSEGMENTATION = 442

# switch layout
def setLayout(idx):
    slicer.app.layoutManager().setLayout( idx )

# global variables to access our layout actions
actionSegmentation3D = None
actionSegmentation = None

# callback for action
def triggerLayoutManualSegmentation():
    # set layout
    setLayout( LAYOUTID_MANUALSEGMENTATION )

# callback for action
def triggerLayoutManualSegmentation3D():
    # set layout
    setLayout( LAYOUTID_MANUALSEGMENTATION_3D )

# callback for shortcut
def toggleLayoutManualSegmentation():
    lm = slicer.app.layoutManager()
    if lm.layout == LAYOUTID_MANUALSEGMENTATION_3D:
        actionSegmentation.triggered()
    else: 
        actionSegmentation3D.triggered()

# create our custom layouts, add actions in layout menu, create shortcut
def createCustomLayouts():
    # create layouts
    slicer.app.layoutManager().layoutLogic().GetLayoutNode().AddLayoutDescription( LAYOUTID_MANUALSEGMENTATION_3D, LAYOUTXML_MANUALSEGMENTATION_3D )
    slicer.app.layoutManager().layoutLogic().GetLayoutNode().AddLayoutDescription( LAYOUTID_MANUALSEGMENTATION, LAYOUTXML_MANUALSEGMENTATION )
    # create menu entries
    global actionSegmentation3D
    global actionSegmentation
    actionSegmentation3D = mainWindow().findChild('QMenu', 'LayoutMenu').addAction( "Segmentation+3D" ) # TODO: create Icon: #.setIcon(qt.QIcon(':Icons/Go.png'))
    actionSegmentation3D.setToolTip("Manual Segmentation")
    actionSegmentation3D.connect('triggered()', lambda: triggerLayoutManualSegmentation3D() )
    actionSegmentation = mainWindow().findChild('QMenu', 'LayoutMenu').addAction( "Segmentation" ) # TODO: create Icon: #.setIcon(qt.QIcon(':Icons/Go.png'))
    actionSegmentation.setToolTip("Manual Segmentation, fullscreen")
    actionSegmentation.connect('triggered()', lambda: triggerLayoutManualSegmentation() )
    # create toggle shortcut
    shortcutToggleSeg = qt.QShortcut( mainWindow() )
    shortcutToggleSeg.setKey( qt.QKeySequence('g') )
    shortcutToggleSeg.connect( 'activated()', lambda: toggleLayoutManualSegmentation() )
    # set custom layout right now. this makes sure volumes are loaded into our custom Slice Views
    #actionSegmentation3D.triggered() # TODO: enable when slicerrc.py works

createCustomLayouts()

```

However, I can’t get this working. And if I do `actionSegmentation3D.triggered()` and then `actionSegmentation.triggered()` in the Python console, Slicer hangs and stops working. What am I doing wrong? I don’t know Python, so help would be appreciated.

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [June 22, 2020, 3:39pm UTC](https://discourse.slicer.org/t/my-qactions-hangs-the-slicer-application/12152/2 "2020-06-22T15:39:18Z")

</div>

The problem is that you missed the `layoutSwitchAction.setData(layoutId)` call. The [example code in the script repository](https://www.slicer.org/wiki/Documentation/Nightly/ScriptRepository#Customize_viewer_layout) was misleading because you don’t actually need to do anything else (no need to connect a custom function to the trigger). I’ve updated the example now.

---

<div class="post-metadata">

**Author:** ![joachim](https://avatars.discourse-cdn.com/v4/letter/j/a87d85/32.png) [@joachim](https://discourse.slicer.org/u/joachim)\
**Post date:** [June 23, 2020, 4:44pm UTC](https://discourse.slicer.org/t/my-qactions-hangs-the-slicer-application/12152/3 "2020-06-23T16:44:11Z")

</div>

Thank you, it worked!

I see you made [a fix for this](https://github.com/Slicer/Slicer/blob/21fa3d053e3eba4a18c50d7ba59a9495e3f266a9/Base/QTApp/qSlicerMainWindow.cxx#L1529). I guess you can remove the “find” code in `void qSlicerMainWindow::onLayoutActionTriggered(QAction* action)` since the `QAction` is contained in _LayoutMenu_ according to [`QMenu` documentation](https://doc.qt.io/qt-5/qmenu.html#triggered).

Also, connecting the submenus [`menuConventionalQuantitative`](https://github.com/Slicer/Slicer/blob/21fa3d053e3eba4a18c50d7ba59a9495e3f266a9/Base/QTApp/qSlicerMainWindow.cxx#L403) etc. to this slot can be removed since the parent menu (_LayoutMenu_) will handle the children. And in fact should be removed since it will set the layout twice. If I read the documentation right.

The example in Wiki has to be changed since a `QAction` not part of _LayoutMenu_ (i.e. _viewToolBar_) will not be able to switch layout. I suggest to only use LayoutMenu. This will also reflect the current layout by showing the corresponding icon/text in _LayoutMenu_:

```auto
# Add button to LayoutMenu
menu = mainWindow().findChild('QMenu', 'LayoutMenu')
action = menu.addAction('Custom layout') 
action.setData(layoutId) # 'layoutId' defines the number for your layout
action.setIcon(qt.QIcon(':Icons/Go.png'))
action.setToolTip('Switch to my custom layout')

```

🙂

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [June 23, 2020, 6:37pm UTC](https://discourse.slicer.org/t/my-qactions-hangs-the-slicer-application/12152/4 "2020-06-23T18:37:30Z")

</div>

> [@joachim](#):
>
> I guess you can remove the “find” code in `void`

Yes, it is just an extra check. Unfortunately, it is not described in code comments or commit comments why it was added. We can remove it when we make significant changes to that part of the code anyway (e.g., when implementing custom “favorite views”).

> [@joachim](#):
>
> Also, connecting the submenus [`menuConventionalQuantitative`](https://github.com/Slicer/Slicer/blob/21fa3d053e3eba4a18c50d7ba59a9495e3f266a9/Base/QTApp/qSlicerMainWindow.cxx#L403) etc. to this slot can be removed

Thank you for the suggestion, we have already removed these. The pull request has not been merged yet, as it was part of a larger effort that requires some more work ([https://github.com/Slicer/Slicer/pull/4904](https://github.com/Slicer/Slicer/pull/4904)).

> [@joachim](#):
>
> The example in Wiki has to be changed since a `QAction` not part of _LayoutMenu_ (i.e. _viewToolBar_ ) will not be able to switch layout

The example is for “You can use this code snippet to add a button to the layout selector toolbar” and the corresponding code is correct. We could add example to show how to define QActions in general, but I don’t think it is necessary.
