Creating a 3D Slicer extension with minimal programming skills

Operating system: Win64
Slicer version: 4.11

Hello,

I am an Engineering Student, with a background in CAD Softwares and Product Design. Recently, for an internship project, I started to use 3D Slicer to perform segmentation on CT scans and MRI DICOMs. For this project, I want to create a little extension to optimize my workflow, using only existing features of the software (or extensions like SegmentEditorExtraEffect). My goal is mainly to recreate a custom GUI where I have only the features I use, in the sequence I need to have something intuitive.

My problem is that I have a really REALLY low knowledge in programming, and since I don’t really want to add any function nor do I need to automatize existing features, I wonder if there is any way to create a GUI that uses existing tools without having to code that much… What do you think?

Thank you in advance for your help !

1 Like

Maybe somebody else here can chime in if they have been through this process recently. I suspect it’s a challenge for a new developer to be learning python and slicer (vtk, qt, etc) all at the same time. But if you are just creating buttons and rearranging widgets it’s certainly doable.

You can start with one of these tutorials and see how it fees:

https://www.slicer.org/wiki/Documentation/4.10/Training#Tutorials_for_software_developers

Thank you for your answer ! Effectively, I already saw this page and tried some tutorials. The hard thing for me is to understand what I’m doing… Because on some tutorials, it’s just a matter of copy pasting without much thinking, so I manage through the tutorial but can’t say that I learn that much. Other tutorials I don"t really understand because there are huge differences between the screenshots provided and what I actually see in the code. Maybe because things changed a bit (especially around the module extension wizard’s generated code).

That’s why I am a bit struggling (of course I will try other tutorials and do my best to learn as much as I can). I think that I still didn’t do a few tutorials on this page so I will do that, and I wanted to know if there’s maybe somewhere, even out of Slicer, where I can learn the basics to manage to understand the Slicer’s python code and modify a few things myself?

I appreciate any help and will keep the hard work to finally understand what I’m doing !

Creating simple modules which basically just use existing Slicer capabilities but organize them into a sensible workflow for a particular task is a lot of what I do in Slicer. In my experience, I think this would be a challenge without some basic programming knowledge. Designing your module GUI using QtDesigner is very intuitive and requires essentially no programming knowledge, but connecting the GUI to the functionality on the back end requires basic Python plus some familiarity with how Slicer does things (MRML nodes and things like that). You definitely do NOT need to be an expert programmer to do what you want, but you do need basic skills and a willingness to look through Slicer code examples and find how to do what you want.

This forum has lots of very helpful and responsive people on it and can be a great resource for when you inevitably get stuck on something.

1 Like

Yes that’s exactly what I want to do : for example understanding how to do a threshold button, or a watershed button, etc… I looked already a bit through QtDesigner and it’s indeed within my grasp. As for the code, I am really willing to dive in the code. The issue for now is that I don’t have a real “starting point” on which to start building some experience, and thus I’m a bit lost. If you have any particular place where I can learn the basics I will look through as much tutorials as I can !

One of the best single places to look for how to do things using Python in Slicer is the script repository: Documentation/Nightly/ScriptRepository - Slicer Wiki. There are many examples there of how to accomplish a wide variety of tasks. For example, modifying a volume by thresholding is shown here (note that this is not the same as creating a segment by thresholding; an example of that can be found in the links from here). Also, I think the extension wizard’s auto-generated code still has a complete example of having a threshold button, and connecting it to a callback, so examining that carefully will show you how to connect a button click to an action.

One other suggestion as you’re getting started: in the extension wizard auto-generated example code, there is a fair amount of code which manages a “parameter node”. This parameter node is primarily useful so that your module can save and restore its state with the scene, so that you could be partway through your workflow, save the scene, close Slicer, and at some point later, open Slicer, load the saved scene, and pick up exactly where you left off. If that functionality is not important to you, you don’t really need to understand the parameter node yet, and you can mostly just ignore it and comment out any line which gives you an error when trying to read or modify the parameter node. (When you remove stuff you don’t need from the auto-generated UI, whenever the existing code refers to those deleted UI elements to store stuff in the parameter node, there will be errors).

Good luck, and feel free to post questions when you get stuck.

I think there are two main approaches that you may try:

  • A. Try to develop a module in Slicer and learn everything at once along the way.
  • B. First learn most important technologies that Slicer is built on and once you are familiar with those then learn how to use them in Slicer.

It is up to you which works better for you.

If you choose option A, then you’ll get lost but you can ask specific questions here from time to time when you cannot figure out something yourself. The advantage of this method is that you don’t spend your time with working on some toy examples, but you are starting to go towards reaching your goal on day 1.

For option B, I could recommend materials listed in this post:

We’ll have our yearly 3-day bootcamp event where we teach our new students about basics of using and programming 3D Slicer. This year it will be a virtual event and the application is open to anyone (but we may need to prioritize applications if we get too many). If you are interested you can find the link to apply here.

This is great advice and I have also done that when trying to quickly develop a proof of concept. I did one time get bitten by a bug where somehow something I’d deleted made Slicer crash at startup for reasons I never figured out so I had to start fresh. It could be good for us to add some more templates that don’t aspire to be full working modules since they would be easier to understand.

Alright, I will take a look at all the links send here and try my best to figure out at least the amount I need. Indeed, I tried to have a dive in the generated code to understand the buttons connexion. I did the LineIntensityProfile tutorial, but for example, the buttons on the tutorial were built though coding, not QtDesigner, so I found myself with something a lot different that I couldn’t really understand through the tutorial. I guess I’ll just need time to figure things out through analysis.
Anyway I will try what you suggested !

Yes I agree very much on these 2 options. I would prefer to chose Option A but I know that at some point some Option B will be needed so I’ll try to combine both and get there aas fast as I can improving the little knowledge I have !

I will also check out the course ! It seems really interesting !

Great discussion!

Although the tutorials are made easy by providing the snippets you need to copy-paste, the goal of the tutorials are not to create simple useless modules, but to show how to make certain things possible. So a (maybe obvious) suggestion would be to think about what these copy-pasted snippets do exactly. If you cannot figure some of those out, please report back here in this thread, and I’ll update the tutorial with more comments. Many of the people here answering questions are seasoned programmers, so it’s quite hard to get into the skin of someone new to these things, so this kind of feedback would be actually useful to us.

If you could please point to these instances we’ll update those resources that have diverted a lot and are hard to apply. Typically Slicer documentation is created by the members of the community in their free time, so we know some of it is out of date. However it takes extra time to go through them and find if something needs updating, so again, once you already found some, please help us by letting us know.

We (at some of the commercial partners) are also thinking about Slicer programming training, but it will take time setting it up. We’ll make an announcement once we have a first event planned out. Until then another tutorial you could do (if you haven’t) is the PerkLab bootcamp one @lassoan mentioned: here is the programming tutorial. If at the end of your journey you have some suggestions please let us know.

Yes I totally agree with the first part ! I try to understand everytime but it’s a bit harder to understand “why” it works sometimes, and why it doesn’t the rest of the time. For example when I try to modify some code, it doesn’t work and I don’t know why because I do not fully understand yet the structure. I tried this tutorial for example : Developing extension in 3D Slicer - Google Slides
I manage to create the QtDesigner interface, and then I tried to connect the button based on the one already existing and doing the same thing (Input Volume Selection) and just mimicking the code of the first one doesn’t seem to work.

On the tutorial, there is this instructions for the creation :
image

But it doesn’t appear to be the same anymore in the generated code, I suspect that this part was replaced by QtDesigner’s GUI creation, which is great ! But as a consequence I find it hard to use this tutorial because I can’t adapt to the changes… I don’t know if I’m missing something obvious on the tutorial, but when I try to add the Second Volume Selector, I am not managing to have an actually functionnal ComboBox (the ComboBox remains grey, no selection possible). Since the code seem to have changed from when the tutorial was created, I can’t really refer to it so I tried the mimicking stuff I mentionned, without success yet

This particular tutorial is more out of date than usual I’d say - it is 7 years old now. The Annotations infrastructure is deprecated, and we do not use the type of scripted modules where the UI was created from code.

Can you please try the PerkLab bootcamp tutorial I linked in my previous post? It is updated each year.

Just to answer the specific issue, I think your combobox is greyed out because the scene connection is not made. In Designer, you go to events mode (F4) and drag&drop from the very edge of the widget to the combobox and select the signal/slot:
image

From code you need to call setMRMLScene(slicer.mrmlScene) as you can also see in the screenshot you provided.

Okay so that’s what I thought. i’ll stop using this one.

Yes I will anyway go through every link provided in this discussion, might take me several days (weeks? ^^) to do it but I will and let you know. Currently I am studying the first link sent by @pieper but I will go on others soon enough too.

Okay thank you for this specific answer, I will try it out !

Your advice worked on my selector problem, thanks ! I managed to do the first tutorial fairly entirely (not the sphere thing), except that I am not getting a functionnal auto update. I have been trying several things and I’m not getting what’s the problem. I would like to manage it since auto update is an interesting feature !

My function is as follows :
image
I don’t really get how the part I marked really works, like I tried to see self.ui.inputSelector.currentNode and it’s not a boolean so I don’t get how it can work as an “if” condition. Tryed commenting it, didn’t change anything, so I’m a bit blocked

First, you want to have the parentheses on the end of the self.ui.inputSelector.currentNode() in the if line as well. In Python, if you use the name of a function with parentheses, then you call that function, but if you use the name without parentheses, you just get a reference to the function (and the function is not actually called). Second, in an if statement, python treats almost anything as True, unless it is empty, None, zero, or explicitlly False (here’s a link that gives you the real details). The currentNode() function of node selectors returns the selected node (if one is selected) or None if there is no selected node. Since None counts as False and a node would count as True, it works to include this in the if.

The first line after the if stores the currently selected node in self.observedMarkupNode, so that it can be accessed later in other parts of the code (like in the previous if where the observer we’re about to create is removed). The next line creates an “observer” such that whenever the observed markups node is modified (by the user or by other code), another function (self.onMarkupsUpdated) is called. Since onMarkupsUpdated() just calls onApplyButton(), the ultimate effect is that anytime the user makes changes to the markups, it is like they followed that by clicking the Apply button as well.

Hello Mike, thank you for the answer, indeed the lack of parenthesis was weird. Fixed now. But it still isn’t working : when I move or add a markup, nothing happens, the displayed Center Of Mass doesn’t change. Can it be that since my “CenterOfMassValueLabel” is above the ApplyButton on the GUI, it doesn’t work because of that?
Also in QtDesigner I linked the CheckBox with the scene through a “toggle”, should I change for click?

@mikebind you said that a lot of what you do on Slicer is rearranging the GUI to create new workflows so I have a few questions :

I want to create some kind of workflow interface as follows :

  1. File loading manually (drag and drop would be cool)
  2. Selecting a segmentation scenario according to the type of image and anatomical parts to be treated
  3. Segmentation using a limited set of methods (I want to display only the needed methods for this particular scenario, not everything that is available)
  4. Exportation of the segments as STL files

So my questions are :

  1. I would like to not overcrowd the interface, so I am thinking of doing something like several interfaces that are displayed in the order of the phases and linked with a “previous/next” button, is it feasible? Do I need to have several QtDesigner files with the specific interface on each or is it one file with different pages?
  2. For the segmentation part especially, is there a way to fully reuse what is done in the segment editor but with just a limitation of the available effects? Or do I have to recreate the whole thing manually?
  3. I’ve seen in QtDesigner that there is a SegmentEditorWidget that basically displays the whole thing at once but I have not been able yet to understand how the connexion is done. It seems a bit different from simple buttons. And can I edit this widget? Because I haven’t found a way to access the internal features of the Widget…

Sorry for the amount of questions, I really appreciate any help !

I haven’t been through that tutorial or ever done auto-updating in response to markups changes, and the full code isn’t shown, so I’m not sure I can help debug that much further. You might take a look at this code from the script repository and see if that works for you and if you can adapt it to your purposes.

I don’t know how to do this, but I believe it is possible. Loading via the Add Data method has never seemed cumbersome enough to me to pursue it.

Automatically? Then you need to come up with the decision algorithm and implement it. Manually? Then just pick your favorite UI method (buttons, radio buttons, etc.)

I have used segment editor effects from my modules, but have not tried to put a limited set of interactive effects to be available to the user. However, it looks like this discussion has exactly what you want.

This is straightforward. You can use slicer.vtkSlicerSegmentationsModuleLogic.ExportSegmentsClosedSurfaceRepresentationToFiles() to export to STL or OBJ files.

The simple method used in many Slicer modules is to have sections with collapsible buttons. This allows areas which are not in current use to be collapsed and take up less room, allowing more screen space for the active area. You can set up your code to automatically collapse and expand sections at the proper times. For example, if I have a section in a collapsible button which is self.ui.CTRegistrationAreaCB I can collapse it with self.ui.CTRegistrationAreaCB.collapsed = True. For another approach, I believe Qt also allows tabbed layouts, but I’ve never tried that or looked into it so I can’t really help with that. Another possible approach would be to have multiple modules that you switch between (see this discussion). Also, I would highly recommend taking a look at Slicelets.

This discussion looks like it answers your question directly.

If you want to see an example of a working module which uses specific segment editor effects (but not the same way as they appear in the Segment Editor module) you could take a look at Heartbeat4D/PropagateSegToOtherPhases.py at main · mikebind/Heartbeat4D · GitHub The code there is old, written before Qt Designer became the default way of creating the UI, but it still works fine. It makes use of thresholding, grow from seeds, erode, close holes, masking, creating empty segments, and copying from one segmentation to another.