Custom file reader without 'Add data into scene' dialog

I’m writing a Python Scripted Module and I want to create a custom data loading functionality.

What I want to achieve is to drag a folder into the scene and for Slicer to load the content automatically, according to some logic that I would specify. The folder has some predefined structure of subfolders and files, which is always the same.

I want to do this to save time, so I don’t have to select descriptions in the ‘Add data into scene’ dialog and then click load.

I tried to use a custom file reader and writer, as shown here, but this didn’t help me. As far as I understood, those two classes only allow you to write custom code to load/save a specific file type - but I want to automatically load files that are dropped into the scene, without opening the dialog.

The holding of ‘shift’ while selecting the description as described here, doesn’t help me as I have three file types to load - volumes, segmentations and displacement fields.

Is this possible?

I figured it out - I created a custom drop widget - when I drop a folder in a specified area, my custom code gets triggered

here’s a simplified extension that does that

import os
import vtk
import qt
import ctk
import slicer
from slicer.ScriptedLoadableModule import *
import logging


class baselineLoader(ScriptedLoadableModule):
    def __init__(self, parent):
        ScriptedLoadableModule.__init__(self, parent)
        self.parent.title = "Custom Data Loader"
        self.parent.categories = ["Utilities"]
        self.parent.dependencies = []
        self.parent.contributors = ["Your Name"]
        self.parent.helpText = """
            Module for automatic loading of data from dropped folders.
            """
        self.parent.acknowledgementText = """
            Developed for custom data loading workflow.
            """


class DropWidget(qt.QFrame):
    def __init__(self, parent=None):
        # Get the widget's layout widget as the parent
        if parent is not None:
            parent_widget = parent.parent
        else:
            parent_widget = None
        qt.QFrame.__init__(self, parent_widget)

        self.setAcceptDrops(True)
        self.setStyleSheet(
            "QFrame { border: 2px dashed #999; border-radius: 5px; }")
        self.setMinimumHeight(100)

        # Create layout
        layout = qt.QVBoxLayout(self)
        label = qt.QLabel("Drop folder here")
        label.setAlignment(qt.Qt.AlignCenter)
        layout.addWidget(label)

        # Store reference to parent widget for accessing configuration
        self.moduleWidget = parent

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
            self.setStyleSheet(
                "QFrame { border: 2px dashed #44A; border-radius: 5px; background: #EEF; }")
        else:
            event.ignore()

    def dragLeaveEvent(self, event):
        self.setStyleSheet(
            "QFrame { border: 2px dashed #999; border-radius: 5px; }")

    def dropEvent(self, event):
        self.setStyleSheet(
            "QFrame { border: 2px dashed #999; border-radius: 5px; }")
        paths = []
        for url in event.mimeData().urls():
            path = url.toLocalFile()
            if os.path.isdir(path):
                paths.append(path)

        if paths and self.moduleWidget:
            # todo LOAD HERE
            pass


class baselineLoaderWidget(ScriptedLoadableModuleWidget):
    def __init__(self, parent=None):
        ScriptedLoadableModuleWidget.__init__(self, parent)
        self.logic = baselineLoaderLogic()

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

        # Add drop zone directly to main layout
        self.dropWidget = DropWidget(self)
        self.layout.addWidget(self.dropWidget)
        self.layout.addStretch(1)
2 Likes

Hi,

I am also very interested in a custom load function. I tried running your code by saving it as a .py file and creating a module from it so I could import it, but I didn’t succeed. How can I use your code?

Many thanks.

you still have to create an extension first, then you can use the code

  1. go to extension wizard
  2. click on ‘Create Extension’ and follow the GUI
  3. click on ‘Add Module to Extension’ - the name of the module has to match the name in the code, so in this case ‘baselineLoader’
  4. now replace the code from <module_name>.py with the code from my previous answer

Many thanks, it works but if I restart slicer and then try to open the module again, then
imagen
Do you know why this happens?

I made an error. you have to delete this line

@koeglfryderyk Adding a dropwidget is a neat idea if you want to directly load and select a file in a particular widget. I’m just wondering what was the issue with the Add Data dialog. Was the problem that you had to confirm loading in the add data window (single mouse click or hitting Enter key)? Or the problem was that you had to change what description was selected by default?

In recent Slicer versions, you can make sure that always the correct description is selected by adding a custom file reader that provides high confidence value.

I think it’s about hanging protocols and simplification of data organized in a standard way.

Btw I’ll need to override drag&drop very soon on the main window in one of the applications as well, in this particular case because the app loads a huge stack of TIFFs and the default loader does not support progress reporting, which is a must (may be 200+GB).

If you are working with TIFF, and particularly that large, be aware of this potential problem, which hopefully will be fixed soon.

it was actually both the description and the click/enter. I just needed a fast way to load data organized in a standard way.

and to return a confidence value, do I just have to add this function to my reader?

def canLoadFileConfidence(self, filePath)

Could I just return ‘1’? the few example that I saw always returned something smaller than 1

See a complete example here:

Confidence value can be any positive number. Default readers use values around 0.5. Slicer core and uses confidence values <= 1 and I would recommend this for all publicly distributed extensions (so that custom applications can be sure that when they use a > 1 value then that is higher than all other values).

1 Like