# Import order in a scripted loadable module

**URL:** <https://discourse.slicer.org/t/import-order-in-a-scripted-loadable-module/21814>\
**Category:** Development\
**Tags:** python\
**Created:** [February 5, 2022, 6:23pm UTC](https://discourse.slicer.org/t/import-order-in-a-scripted-loadable-module/21814 "2022-02-05T18:23:24Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![CsatiZoltan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/csatizoltan/32/13807_2.png) [@CsatiZoltan](https://discourse.slicer.org/u/CsatiZoltan)\
**Post date:** [February 5, 2022, 6:23pm UTC](https://discourse.slicer.org/t/import-order-in-a-scripted-loadable-module/21814/1 "2022-02-05T18:23:24Z")

</div>

### Problem

What is the order of the module loading in a scripted loadable module? I realized that I cannot access the `slicer` module in a class attribute. For example, the code

```python
class materWidget(ScriptedLoadableModuleWidget, VTKObservationMixin):
    pointSet = qt.Signal(slicer.vtkMRMLMarkupsFiducialNode)
    ...
    def __init__ (self, parent=None):
        ScriptedLoadableModuleWidget. __init__ (self, parent)
        ...

    def setup(self):
        ScriptedLoadableModuleWidget.setup(self)
        ...

```

fails on the line where the class attribute is defined with the following error:

```python
AttributeError: module 'slicer' has no attribute 'vtkMRMLMarkupsFiducialNode'

```

On the other hand, if I make `pointSet` an instance variable (by putting it into ` __init__ ` for instance), it works. Note that `import slicer` is already present at the top of the file. I must be missing something in the Python import logic… How could I use the `slicer` module in a class attribute?

* * *

### Background information

I want to emit a Qt signal once a specific MRML event has been triggered:

```python
mrml_event = slicer.vtkMRMLMarkupsFiducialNode.PointPositionDefinedEvent

def onPointSet(observer, eventid):
    self.pointSet.emit(observer)

id = self.markup_node.AddObserver(mrml_event, onPointSet)

@qt.Slot(slicer.vtkMRMLMarkupsFiducialNode)
def test_slot(markup_node):
    print(markup_node.GetName())

self.pointSet.connect(test_slot)

```

Why do I complicate my life by connecting a Qt signal to an MRML event callback? Because I want to connect multiple slots to the same event. In this case, when a control point is added to the Markups node, I want to perform multiple tasks:

- change the color of a button (widget-related)
- execute a computation (logic-raleted)
- … (features that will come in the future)

The signal-slot mechanism allows me to completely separate the logic from the GUI.  
A PyQt signal must be a defined as a class attribute, that’s why this problem with `pointSet` came up.

---

<div class="post-metadata">

**Author:** ![ungi](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/ungi/32/78573_2.png) [@ungi](https://discourse.slicer.org/u/ungi)\
**Post date:** [February 6, 2022, 2:36pm UTC](https://discourse.slicer.org/t/import-order-in-a-scripted-loadable-module/21814/2 "2022-02-06T14:36:14Z")

</div>

Hi, that’s not the right way to connect MRML events to Qt functions. MRML nodes are VTK-based objects, so they don’t have Qt signals. Typically, you would add a VTK-style observer function to your module widget class. That observes events from your Markups node. The observer function is in your module widget class, so it has direct access to Qt objects. It can update button colors directly using code like self.ui.myButton.setStyleSheet(…).

There are probably lots of examples if you search in the source code of other modules. E.g. look at this example module: [https://github.com/PerkLab/PerkLabBootcamp/blob/master/Examples/CampTutorial2/CampTutorial2.py](https://github.com/PerkLab/PerkLabBootcamp/blob/master/Examples/CampTutorial2/CampTutorial2.py)  
This module is just an example for a tutorial, it doesn’t do exactly what you need. It updates a MRML node (sphere model) when a Markups node is modified. But it’s a good example for how to observe a Markups node.

---

<div class="post-metadata">

**Author:** ![CsatiZoltan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/csatizoltan/32/13807_2.png) [@CsatiZoltan](https://discourse.slicer.org/u/CsatiZoltan)\
**Post date:** [February 6, 2022, 4:59pm UTC](https://discourse.slicer.org/t/import-order-in-a-scripted-loadable-module/21814/3 "2022-02-06T16:59:08Z")

</div>

Hi @ungi . I considered that design choice too. However, it requires that the observation (`AddObserver`) is defined in the widget class. But I want my application to work without the GUI too, meaning that I must allocate the Markups node in the logic class. That class does not know about the widget class. That is why I included an intermediate step (VTK event → Qt event → perform action): this way the VTK event on the Markups node can be observed by methods (slots) of both the widget class and the logic class.

---

<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:** [February 6, 2022, 5:49pm UTC](https://discourse.slicer.org/t/import-order-in-a-scripted-loadable-module/21814/4 "2022-02-06T17:49:28Z")

</div>

When a module class is instantiated, it is still the module discovery phase (the application scans what modules are available, builds a dependency tree, determines in which order the modules will be initialized). Slicer application or other modules are not available yet. If you want to do something at application startup then you can connect a method to the application’s `startupCompleted()` signal:

> Create logic object on startupCompleted() signal and store it in the module class. This is only necessary if only one logic instance is allowed in the application. For example, this is the case when the logic observes the scene and reacts to changes, modifies nodes automatically

(source: [PerkLab bootcamp, Slicer programming tutorial](https://github.com/PerkLab/PerkLabBootcamp/blob/master/Doc/day3_2_SlicerProgramming.pptx), slide 11)

See the scripted module template for an example of using this signal:

> <https://github.com/Slicer/Slicer/blob/4d756f0ef57832d654df7a8dc1a8b10e38b8fba9/Utilities/Templates/Modules/Scripted/TemplateKey.py#L35>

---

<div class="post-metadata">

**Author:** ![CsatiZoltan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/csatizoltan/32/13807_2.png) [@CsatiZoltan](https://discourse.slicer.org/u/CsatiZoltan)\
**Post date:** [February 6, 2022, 6:55pm UTC](https://discourse.slicer.org/t/import-order-in-a-scripted-loadable-module/21814/5 "2022-02-06T18:55:10Z")

</div>

Thank you, this was very valuable!  
I read the bootcamp tutorials before, but skipped the details that I did not understand then; time to reread them with my current, deeper, understanding…  
I found [`startupCompleted`](https://apidocs.slicer.org/master/classqSlicerApplication.html#a97b2b5f43e708bfc9d874871a7e3260f) in the API docs. One question though: how could I know that `slicer.app.startupCompleted()` is the Python wrapper of the corresponding C++ method `qSlicerApplication::startupCompleted()`? I found it by trial and error. Is there a systematic way to discover it (the debugger doesn’t work for me and I don’t have autocompletion for the `slicer` module in my IDE)?

---

<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:** [February 6, 2022, 7:05pm UTC](https://discourse.slicer.org/t/import-order-in-a-scripted-loadable-module/21814/6 "2022-02-06T19:05:33Z")

</div>

> [@CsatiZoltan](#):
>
> how could I know that `slicer.app.startupCompleted()` is the Python wrapper of the corresponding C++ method `qSlicerApplication::startupCompleted()` ?

You can find [here](https://slicer.readthedocs.io/en/latest/developer_guide/api.html#doxygen-style-documentation) a set of rules how C++ classes, methods, properties are accessible in Python.

As with most APIs, probably the most effective ways of learning examples, tutorials, and trial and error. API documentation is sometimes useful, too. If none of these help then you can get all the answers from the source code or by asking here.

> [@CsatiZoltan](#):
>
> the debugger doesn’t work for me and I don’t have autocompletion for the `slicer` module in my IDE

Multiple debuggers can be used. Feel free to create a new topic if you have trouble setting up a Python debugger.
