Accessing a custom loadable module's logic from python

Hello, I am working with an existing custom loadable module (written in C++) and was hoping to be able to access functions from the module’s logic in the python interface and/or within a scripted python module.

For the existing modules that come with Slicer, I typically do this as recommended here How to call a function from the logic - #2 by pieper

e.g.

slicer.modules.screencapture.widgetRepresentation().self().logic

However, for the loadable module that was developed in my group, the widgetRepresentation does not have a ‘self’. I was wondering if it is even possible to access the logic of C++ loadable modules in this way?

For Python scripted modules, you need to call self() to get the Python implementation of the generic C++ widget representation, an call logic because in scripted modules it makes dynamic reloading of the module much easier if the widget owns the logic. In C++ you don’t need any of these, so you can simply call logic() method of the module class, for example: slicer.modules.volumerendering.logic().

This and many other useful concepts are described in the PerkLab developer tutorial: Documentation/Nightly/Training - Slicer Wiki

This is the answer to a question I had after I went through the tutorials after the call on Tuesday=)
Thanks for the advice to revisit the PerkLab presentations. It really is an iterative process.

Thank you for your response! The distinction is clear - I had not seen this PerkLab tutorial before so thank you for the link.

I tried the slicer.modules.modulename.logic() route previously, but did not get access to any of the logic methods from the module, just standard ones (e.g. AddObserver(), BreakOnError(), etc.) so I thought it was the wrong approach. Instead, it seems there must be an implementation issue with the loadable module I am using such that it isn’t exposing these methods correctly.

I wanted to make sure that my assumption/understanding is correct as I try to hunt down the issue. If things are done correctly, should I be able to call all of the public methods of vtkSlicer[ModuleName]Logic from Python using the method we discussed of

logic = slicer.modules.modulename.logic()
logic.method() 

?

To be (hopefully) more clear, the setup is as follows:
There is a module which in the top-level directory has qSlicerUniversalRobotModule.cxx(/h) and qSlicerUniversalRobotModuleWidget.cxx(/h). The module has a createLogic() method which creates a new instance of vtkSlicerUniversalRobotLogic, which is derived from vtkSlicerModuleLogic. Basically, my questions i, if everything was set up correctly, should I be able to access the public methods of this vtkSlicerUniversalRobotLogic?

Thanks again!

Yes.

Yes.
The first thing to check is the class name that is displayed when you call slicer.modules.modulename.logic(). If that’s correct then you can call help(slicer.modules.modulename.logic()) to see what methods are visible from Python.

Thanks - for me it says the following when I call slicer.modules.modulename.logic():
vtkCommonCorePython.vtkSlicerModuleLogic.

I imagine if this was working correctly it would say vtkSlicerUniversalRobotModuleLogic? If so, I at least know my problem now :slight_smile:

There is either something unusual in the logic class header file (e.g., you have included some headers from a third-party library - those should be all just included in the implementation cxx file), or maybe there is something wrong with the export declaration (between class keyword and class name in the header file). You can make a verbatim copy of a loadable module from an extension and see if you see the logic methods. If yes, then you can start modifying it step by step and see what change causes failure of the Python wrapping.

There is either something unusual in the logic class header file (e.g., you have included some headers from a third-party library

This is exactly the problem, and it is done quite liberally. Many of the variables are of types defined by headers outside of Slicer (from internal code developed in our group). Basically, I think previous developers have baked in a lot of extraneous things into this logic code that should be elsewhere if done correctly. I don’t see a straightforward way of moving that over to the implementation, so unfortunately I will either have to do a large refactoring of this or find some other approach…

Thank you for you help - figuring out the issue here has been very helpful.

No redesign should be needed. You can use the private implementation (PIMPL) pattern, it is used many places (search for vtkInternal).

In general, logic should encapsulate libraries: classes from third-party libraries should not leak out to public interface of a logic class.

Thanks you Andras. This was very helpful. I was able to implement this and it seems to be working!

I just wanted to leave a note here in case someone runs into the same problem in the future. While I needed to do what was described above, I also found that there was the following line ‘DISABLE_WRAP_PYTHON’ in the CMakeLists in the Logic folder of the Loadable Module (see below). Unsurprisingly(!), if this is here, no matter how clean your logic headers are, the python wrapping will be skipped.

  NAME ${KIT}
  EXPORT_DIRECTIVE ${${KIT}_EXPORT_DIRECTIVE}
  INCLUDE_DIRECTORIES ${${KIT}_INCLUDE_DIRECTORIES}
  SRCS ${${KIT}_SRCS}
  TARGET_LIBRARIES ${${KIT}_TARGET_LIBRARIES}
  DISABLE_WRAP_PYTHON
  )

Thanks again!

1 Like