cybercyber

Controlling 3DSlicer

So there is this software called “3DSlicer”. It is an open source tool to work with medical images (CT, MRI).

It actually has a python-API that is documented very extensively — but which is not easy to understand if you have no idea what you want to do and only get “this is the way I always set everything up”. What settings in the GUI are where in the API?

So what I did was not very nice: I used the API to gain access to the Qt objects of the tools and just clicked all buttons in the right order…

Usually I would have added empty lines, but then the file would not load in 3DSlicer.

#!python
import os
import time
#
OUTPUT="output.nii"
#
def get_all_files(d):
  for f in os.listdir(d):
    if os.path.exists(os.path.join(d, f, OUTPUT)):
      continue
    yield os.path.join(d, f)
#
class Waiter(qt.QWidget):
  def __init__(self, parent=None):
    qt.QWidget.__init__(self, parent)
    self.timer = qt.QTimer()
    self.timer.setInterval(1000)
    self.timer.connect('timeout()', self.test)
    self.running = False
    self.gaf = get_all_files(r"F:\where_ever")
    self.timer.start()
  def test(self):
    # If the apply-Button is enabled, we can click it!
    if apply.enabled and not self.running:
      # set new
      self.dir = next(self.gaf)
      mprage_name = os.listdir(os.path.join(self.dir, "MPRAge"))[0]
      flair_name = os.listdir(os.path.join(self.dir, "3DFLAIR"))[0]
      mprage = os.path.join(self.dir, "MPRAge", mprage_name)
      flair = os.path.join(self.dir, "3DFLAIR", flair_name)
      mprage_name = os.path.splitext(mprage_name)[0]
      flair_name = os.path.splitext(flair_name)[0]
      print("Starting new...", self.dir)
      loadVolume(mprage)
      loadVolume(flair)
      c = slicer.mrmlScene.GetNodes()
      # find the two loaded files in the file-selection list
      mprage_node = [x for x in [c.GetItemAsObject(i) for i in range(c.GetNumberOfItems())] if x.GetName() == mprage_name][0]
      flair_node = [x for x in [c.GetItemAsObject(i) for i in range(c.GetNumberOfItems())] if x.GetName() == flair_name][0]
      # and select them
      fixed.setCurrentNode(mprage_node)
      moving.setCurrentNode(flair_node)
      # then click "Apply"
      apply.click()
      self.running = True
    elif apply.enabled and self.running:
      # if the button is pressable and we are waiting for it to finish, it must have just finished!
      c = slicer.mrmlScene.GetNodes()
      # find the output-file
      output = [x for x in [c.GetItemAsObject(i) for i in range(c.GetNumberOfItems())] if x.GetName() == "output"][0]
      # and save it
      saveNode(output, os.path.join(self.dir, OUTPUT))
      print("Finished.")
      self.running = False
    else:
      print("Waiting...")
#
# Get a handle to a loaded module
m = getModuleGui('BRAINSFit')
# find a handle of the input-file section of the GUI
b = m.children()[2].children()[0].children()[0].children()[1]
fixed = b.children()[4].children()[2]
moving = b.children()[4].children()[4]
#
# get a handle to the "Apply"-Button
apply = m.children()[-1]
# Enter the loop
w = Waiter()

Note: This was hacked together for a very specific task. You will most likely need to change at least some bits to use it. Also note, that this might break with new version of 3DSlicer or the BRAINSFit-module. This is tested on 3DSlicer v4.5.0-1.

Have fun!