Esempio n. 1
0
    def addDvidVolume(self, roleIndex, laneIndex):
        group = "DataSelection"
        recent_hosts_key = "Recent DVID Hosts"
        recent_hosts = preferences.get(group, recent_hosts_key)
        if not recent_hosts:
            recent_hosts = ["localhost:8000"]
        recent_hosts = [
            h for h in recent_hosts if h
        ]  # There used to be a bug where empty strings could be saved. Filter those out.

        recent_nodes_key = "Recent DVID Nodes"
        recent_nodes = preferences.get(group, recent_nodes_key) or {}

        from .dvidDataSelectionBrowser import DvidDataSelectionBrowser

        browser = DvidDataSelectionBrowser(recent_hosts,
                                           recent_nodes,
                                           parent=self)
        if browser.exec_() == DvidDataSelectionBrowser.Rejected:
            return

        if None in browser.get_selection():
            QMessageBox.critical("Couldn't use your selection.")
            return

        rois = None
        hostname, repo_uuid, volume_name, node_uuid, typename = browser.get_selection(
        )
        dvid_url = f"http://{hostname}/api/node/{node_uuid}/{volume_name}"
        subvolume_roi = browser.get_subvolume_roi()

        # Relocate host to top of 'recent' list, and limit list to 10 items.
        try:
            i = recent_hosts.index(hostname)
            del recent_hosts[i]
        except ValueError:
            pass
        finally:
            recent_hosts.insert(0, hostname)
            recent_hosts = recent_hosts[:10]

        # Save pref
        preferences.set(group, recent_hosts_key, recent_hosts)

        recent_nodes[hostname] = node_uuid
        preferences.set(group, recent_nodes_key, recent_nodes)

        self.addLanes(
            [UrlDatasetInfo(url=dvid_url, subvolume_roi=subvolume_roi)],
            roleIndex)
Esempio n. 2
0
    def _chooseDirectory(self):
        # Find the directory of the most recently opened image file
        mostRecentStackDirectory = preferences.get("DataSelection",
                                                   "recent stack directory")
        if mostRecentStackDirectory is not None:
            defaultDirectory = os.path.split(mostRecentStackDirectory)[0]
        else:
            defaultDirectory = os.path.expanduser("~")

        options = QFileDialog.Options(QFileDialog.ShowDirsOnly)
        if ilastik.config.cfg.getboolean("ilastik", "debug"):
            options |= QFileDialog.DontUseNativeDialog

        # Launch the "Open File" dialog
        directory = QFileDialog.getExistingDirectory(self,
                                                     "Image Stack Directory",
                                                     defaultDirectory,
                                                     options=options)

        if not directory:
            # User cancelled
            return

        preferences.set("DataSelection", "recent stack directory", directory)

        self.directoryEdit.setText(directory)
        try:
            globstring = self._getGlobString(directory)
            self.patternEdit.setText(globstring)
            self._applyPattern()
        except StackFileSelectionWidget.DetermineStackError as e:
            QMessageBox.warning(self, "Invalid selection", str(e))
Esempio n. 3
0
    def _exportAllObjectMeshes(self):
        """
        Export all objects in the project as separate .obj files, stored to a user-specified directory.
        """
        mst = self.topLevelOperatorView.MST.value
        if not list(mst.object_lut.keys()):
            QMessageBox.critical(
                self, "Can't Export",
                "You have no saved objets, so there are no meshes to export.")
            return

        recent_dir = preferences.get("carving", "recent export mesh directory")
        if recent_dir is None:
            defaultPath = os.path.join(os.path.expanduser("~"))
        else:
            defaultPath = os.path.join(recent_dir)
        export_dir = QFileDialog.getExistingDirectory(
            self, "Select export directory for mesh files", defaultPath)
        if not export_dir:
            return
        export_dir = str(export_dir)
        preferences.set("carving", "recent export mesh directory", export_dir)

        # Get the list of all object names
        object_names = []
        obj_filepaths = []
        for object_name in list(mst.object_lut.keys()):
            object_names.append(object_name)
            obj_filepaths.append(
                os.path.join(export_dir, "{}.obj".format(object_name)))

        if object_names:
            self._exportMeshes(object_names, obj_filepaths)
Esempio n. 4
0
    def addModelClicked(self):
        """
        When AddModel button is clicked.
        """
        # open dialog in recent model folder if possible
        folder = preferences.get("DataSelection", "recent model")
        if folder is None:
            folder = os.path.expanduser("~")

        # get folder from user
        filename = self.getModelToOpen(self, folder)

        if filename:
            projectManager = self.parentApplet._StandardApplet__workflow._shell.projectManager
            # save whole project (specifically important here: the labels)
            if not projectManager.currentProjectIsReadOnly:
                projectManager.saveProject()

            with open(filename, "rb") as modelFile:
                modelBytes = modelFile.read()

            self._uploadModel(modelBytes)

            preferences.set("DataSelection", "recent model", filename)
            self.parentApplet.appletStateUpdateRequested()
Esempio n. 5
0
 def _load_from_preferences(self):
     """
     Read previously-saved preferences file and return the dict of shortcut keys -> targets (a 'reversemap').
     Called during initialization only.
     """
     return preferences.get(self.PreferencesGroup,
                            "all_shortcuts",
                            default={})
        def handleImportLabelsAction():
            # Find the directory of the most recently opened image file
            mostRecentImageFile = preferences.get("DataSelection",
                                                  "recent image")
            if mostRecentImageFile is not None:
                defaultDirectory = os.path.split(mostRecentImageFile)[0]
            else:
                defaultDirectory = os.path.expanduser("~")
            fileNames = DataSelectionGui.getImageFileNamesToOpen(
                self, defaultDirectory)
            fileNames = list(map(str, fileNames))

            # For now, we require a single hdf5 file
            if len(fileNames) > 1:
                QMessageBox.critical(
                    self, "Too many files",
                    "Labels must be contained in a single hdf5 volume.")
                return
            if len(fileNames) == 0:
                # user cancelled
                return

            file_path = fileNames[0]
            internal_paths = DataSelectionGui.getPossibleInternalPaths(
                file_path)
            if len(internal_paths) == 0:
                QMessageBox.critical(
                    self, "No volumes in file",
                    "Couldn't find a suitable dataset in your hdf5 file.")
                return
            if len(internal_paths) == 1:
                internal_path = internal_paths[0]
            else:
                dlg = SubvolumeSelectionDlg(internal_paths, self)
                if dlg.exec_() == QDialog.Rejected:
                    return
                selected_index = dlg.combo.currentIndex()
                internal_path = str(internal_paths[selected_index])

            path_components = PathComponents(file_path)
            path_components.internalPath = str(internal_path)

            try:
                top_op = self.topLevelOperatorView
                opReader = OpInputDataReader(parent=top_op.parent)
                opReader.FilePath.setValue(path_components.totalPath())

                # Reorder the axes
                op5 = OpReorderAxes(parent=top_op.parent)
                op5.AxisOrder.setValue(top_op.LabelInputs.meta.getAxisKeys())
                op5.Input.connect(opReader.Output)

                # Finally, import the labels
                top_op.importLabels(top_op.current_view_index(), op5.Output)

            finally:
                op5.cleanUp()
                opReader.cleanUp()
Esempio n. 7
0
    def addModels(self):
        """
        When AddModels button is clicked.
        """
        mostRecentImageFile = preferences.get("DataSelection", "recent models")
        mostRecentImageFile = str(mostRecentImageFile)
        if mostRecentImageFile is not None:
            defaultDirectory = os.path.split(mostRecentImageFile)[0]
        else:
            defaultDirectory = os.path.expanduser("~")

        fileNames = self.getImageFileNamesToOpen(self, defaultDirectory)

        if len(fileNames) > 0:
            self.add_NN_classifiers(fileNames)
    def __init__(self, view):
        """
        Constructor.

        :param view: The parent widget -> ImageView2D
        """
        super(WysiwygExportOptionsDlg, self).__init__(view)
        uic.loadUi(os.path.splitext(__file__)[0] + ".ui", self)

        self.view = view

        # indicators for ok button
        self._pattern_ok = False
        self._directory_ok = False
        self.fileExt = ""

        # properties
        self.along = self.view.scene()._along
        reverse = -1 if self.view.scene().is_swapped else 1
        self.inputAxes = ["t", "x", "y", "z", "c"]
        self.shape = self.view.scene()._posModel.shape5D
        self.sliceAxes = [
            i for i in range(len(self.inputAxes)) if not i in self.along
        ][::reverse]
        self.sliceCoords = "".join([
            a for i, a in enumerate(self.inputAxes) if not i in self.along
        ])[::reverse]

        # Init child widgets
        self._initSubregionWidget()
        self._initFileOptionsWidget()
        self._initExportInfoWidget()

        # disable OK button if file path/pattern are invalid
        self._updateOkButton()

        # See self.eventFilter()
        self.installEventFilter(self)

        default_location = preferences.get("WYSIWYG",
                                           "export directory",
                                           default=os.path.expanduser("~"))
        self.directoryEdit.setText(default_location)

        # hide stack tiffs if Wand is not installed
        if wand is None:
            self.stack_tiffs_checkbox.setVisible(False)
Esempio n. 9
0
    def _loadModelButtonClicked(self):
        """
        When Load Model button is clicked.
        """
        mostRecentModelFile = preferences.get("DataSelection",
                                              "recent neural net")
        if mostRecentModelFile is not None:
            defaultDirectory = os.path.split(mostRecentModelFile)[0]
        else:
            defaultDirectory = os.path.expanduser("~")

        fileName = self.getModelFileNameToOpen(self, defaultDirectory)

        if fileName is not None:
            self._setClassifierModel(fileName)
            preferences.set("DataSelection", "recent neural net", fileName)
            self._setModelGuiVisible(True)
Esempio n. 10
0
    def __init__(self, parent=None):
        GLViewWidget.__init__(self, parent)
        self.setBackgroundColor(
            preferences.get("GLView",
                            "backgroundColor",
                            default=[255, 255, 255]))
        self._shape = (1, 1, 1)
        self._slice_planes = SlicingPlanes(self)
        self._arrows = AxesSymbols(self)

        self._mouse_pos = None

        self._meshes = {}
        # Make sure the layout stays the same no matter if the 3D widget is on/off
        size_policy = self.sizePolicy()
        size_policy.setRetainSizeWhenHidden(True)
        self.setSizePolicy(size_policy)
Esempio n. 11
0
    def initFeatureDlg(self):
        """
        Initialize the feature selection widget.
        """
        self.featureDlg = FeatureDlg(parent=self)
        self.featureDlg.setWindowTitle("Features")
        try:
            size = preferences.get("featureSelection", "dialog size")
            self.featureDlg.resize(*size)
        except TypeError:
            pass

        def saveSize():
            size = self.featureDlg.size()
            s = (size.width(), size.height())
            preferences.set("featureSelection", "dialog size", s)

        self.featureDlg.accepted.connect(saveSize)
        self.featureDlg.setImageToPreView(None)
        self.featureDlg.accepted.connect(self.onNewFeaturesFromFeatureDlg)
Esempio n. 12
0
    def _onContextMenuExportMesh(self, _name):
        """
        Export a single object mesh to a user-specified filename.
        """
        recent_dir = preferences.get("carving", "recent export mesh directory")
        if recent_dir is None:
            defaultPath = os.path.join(os.path.expanduser("~"),
                                       "{}obj".format(_name))
        else:
            defaultPath = os.path.join(recent_dir, "{}.obj".format(_name))
        filepath, _filter = QFileDialog.getSaveFileName(
            self, "Save meshes for object '{}'".format(_name), defaultPath,
            "OBJ Files (*.obj)")
        if not filepath:
            return
        obj_filepath = str(filepath)
        preferences.set("carving", "recent export mesh directory",
                        os.path.split(obj_filepath)[0])

        self._exportMeshes([_name], [obj_filepath])
Esempio n. 13
0
def prompt_export_settings_and_export_layer(layer: Layer, parent_widget=None) -> None:
    """
    Prompt the user for layer export settings, and perform the layer export.
    """
    opExport = get_export_operator(layer)

    export_dir = preferences.get("layer", "export-dir", default=os.path.expanduser("~"))
    opExport.OutputFilenameFormat.setValue(os.path.join(export_dir, layer.name))

    # Use this dialog to populate the operator's slot settings
    settingsDlg = DataExportOptionsDlg(parent_widget, opExport)

    # If user didn't cancel, run the export now.
    if settingsDlg.exec_() == DataExportOptionsDlg.Accepted:
        export_dir = PathComponents(opExport.ExportPath.value).externalDirectory
        preferences.set("layer", "export-dir", export_dir)

        helper = ExportHelper(parent_widget)
        helper.run(opExport)

    # Clean up our temporary operators
    opExport.cleanUp()
Esempio n. 14
0
    def __init__(
        self,
        parent_window,
        preferences_group: str = "DataSelection",
        preferences_setting: str = "recent image",
    ):
        self.preferences_group = preferences_group
        self.preferences_setting = preferences_setting

        ext_str = " ".join(f"*.{ext}"
                           for ext in OpDataSelection.SupportedExtensions)
        filters = f"Image files ({ext_str})"
        # empty QFileDialog.Options() need to be provided, otherwise native dialog is not shown
        super().__init__(
            parent_window,
            caption="Select Images",
            directory=str(
                Path(
                    preferences.get(preferences_group, preferences_setting,
                                    Path.home()))),
            filter=filters,
            options=QFileDialog.Options(),
        )
        self.setFileMode(QFileDialog.ExistingFiles)
Esempio n. 15
0
    def _initLabelUic(self, drawerUiPath):
        _labelControlUi = uic.loadUi(drawerUiPath)

        # We own the applet bar ui
        self._labelControlUi = _labelControlUi

        # Initialize the label list model
        model = LabelListModel()
        _labelControlUi.labelListView.setModel(model)
        _labelControlUi.labelListModel = model
        _labelControlUi.labelListModel.rowsRemoved.connect(
            self._onLabelRemoved)
        _labelControlUi.labelListModel.elementSelected.connect(
            self._onLabelSelected)

        def handleClearRequested(row, name):
            selection = QMessageBox.warning(
                self,
                "Clear labels?",
                "All '{}' brush strokes will be erased.  Are you sure?".format(
                    name),
                QMessageBox.Ok | QMessageBox.Cancel,
            )
            if selection != QMessageBox.Ok:
                return

            # This only works if the top-level operator has a 'clearLabel' function.
            self.topLevelOperatorView.clearLabel(row + 1)

        _labelControlUi.labelListView.clearRequested.connect(
            handleClearRequested)

        def handleLabelMergeRequested(from_row, from_name, into_row,
                                      into_name):
            from_label = from_row + 1
            into_label = into_row + 1
            selection = QMessageBox.warning(
                self,
                "Merge labels?",
                "All '{}' brush strokes will be converted to '{}'.  Are you sure?"
                .format(from_name, into_name),
                QMessageBox.Ok | QMessageBox.Cancel,
            )
            if selection != QMessageBox.Ok:
                return

            # This only works if the top-level operator has a 'mergeLabels' function.
            self.topLevelOperatorView.mergeLabels(from_label, into_label)

            names = list(self._labelingSlots.labelNames.value)
            names.pop(from_label - 1)
            self._labelingSlots.labelNames.setValue(names)

        _labelControlUi.labelListView.mergeRequested.connect(
            handleLabelMergeRequested)

        # Connect Applet GUI to our event handlers
        if hasattr(_labelControlUi, "AddLabelButton"):
            _labelControlUi.AddLabelButton.setIcon(QIcon(ilastikIcons.AddSel))
            _labelControlUi.AddLabelButton.clicked.connect(
                bind(self._addNewLabel))
        _labelControlUi.labelListModel.dataChanged.connect(
            self.onLabelListDataChanged)

        # Initialize the arrow tool button with an icon and handler
        iconPath = os.path.split(__file__)[0] + "/icons/arrow.png"
        arrowIcon = QIcon(iconPath)
        _labelControlUi.arrowToolButton.setIcon(arrowIcon)
        _labelControlUi.arrowToolButton.setCheckable(True)
        _labelControlUi.arrowToolButton.clicked.connect(
            lambda checked: self._handleToolButtonClicked(
                checked, Tool.Navigation))

        # Initialize the paint tool button with an icon and handler
        paintBrushIconPath = os.path.split(
            __file__)[0] + "/icons/paintbrush.png"
        paintBrushIcon = QIcon(paintBrushIconPath)
        _labelControlUi.paintToolButton.setIcon(paintBrushIcon)
        _labelControlUi.paintToolButton.setCheckable(True)
        _labelControlUi.paintToolButton.clicked.connect(
            lambda checked: self._handleToolButtonClicked(checked, Tool.Paint))

        # Initialize the erase tool button with an icon and handler
        eraserIconPath = os.path.split(__file__)[0] + "/icons/eraser.png"
        eraserIcon = QIcon(eraserIconPath)
        _labelControlUi.eraserToolButton.setIcon(eraserIcon)
        _labelControlUi.eraserToolButton.setCheckable(True)
        _labelControlUi.eraserToolButton.clicked.connect(
            lambda checked: self._handleToolButtonClicked(checked, Tool.Erase))

        # Initialize the thresholding tool
        if hasattr(_labelControlUi, "thresToolButton"):
            thresholdIconPath = os.path.split(
                __file__)[0] + "/icons/threshold.png"
            thresholdIcon = QIcon(thresholdIconPath)
            _labelControlUi.thresToolButton.setIcon(thresholdIcon)
            _labelControlUi.thresToolButton.setCheckable(True)
            _labelControlUi.thresToolButton.clicked.connect(
                lambda checked: self._handleToolButtonClicked(
                    checked, Tool.Threshold))

        # This maps tool types to the buttons that enable them
        if hasattr(_labelControlUi, "thresToolButton"):
            self.toolButtons = {
                Tool.Navigation: _labelControlUi.arrowToolButton,
                Tool.Paint: _labelControlUi.paintToolButton,
                Tool.Erase: _labelControlUi.eraserToolButton,
                Tool.Threshold: _labelControlUi.thresToolButton,
            }
        else:
            self.toolButtons = {
                Tool.Navigation: _labelControlUi.arrowToolButton,
                Tool.Paint: _labelControlUi.paintToolButton,
                Tool.Erase: _labelControlUi.eraserToolButton,
            }

        self.brushSizes = [1, 3, 5, 7, 11, 23, 31, 61]

        for size in self.brushSizes:
            _labelControlUi.brushSizeComboBox.addItem(str(size))

        _labelControlUi.brushSizeComboBox.currentIndexChanged.connect(
            self._onBrushSizeChange)

        self.paintBrushSizeIndex = preferences.get("labeling",
                                                   "paint brush size",
                                                   default=0)
        self.eraserSizeIndex = preferences.get("labeling",
                                               "eraser brush size",
                                               default=4)
Esempio n. 16
0
 def _load_from_preferences(self) -> Dict[Tuple[str, str], str]:
     shortcuts = preferences.get(self.PreferencesGroup,
                                 self.PreferencesSetting,
                                 default=[])
     return {(s["group"], s["name"]): s["keyseq"] for s in shortcuts}
Esempio n. 17
0
    def __init__(self,
                 posModel,
                 along,
                 preemptive_fetch_number=5,
                 parent=None,
                 name="Unnamed Scene",
                 swapped_default=False):
        """
        * preemptive_fetch_number -- number of prefetched slices; 0 turns the feature off
        * swapped_default -- whether axes should be swapped by default.

        """
        QGraphicsScene.__init__(self, parent=parent)

        self._along = along
        self._posModel = posModel

        # QGraphicsItems can change this if they are in a state that should temporarily forbid brushing
        # (For example, when the slice intersection marker is in 'draggable' state.)
        self.allow_brushing = True

        self._dataShape = (0, 0)
        self._dataRectItem = None  # A QGraphicsRectItem (or None)
        self._offsetX = 0
        self._offsetY = 0
        self.name = name
        self._tileWidth = preferences.get("ImageScene2D",
                                          "tileWidth",
                                          default=512)

        self._stackedImageSources = StackedImageSources(LayerStackModel())
        self._showTileOutlines = False

        # FIXME: We don't show the red 'progress pies' because they look terrible.
        #        If we could fix their timing, maybe it would be worth it.
        self._showTileProgress = False

        self._tileProvider = None
        self._dirtyIndicator = None
        self._prefetching_enabled = False

        self._swappedDefault = swapped_default
        self.reset()

        # BowWave preemptive caching
        self.setPreemptiveFetchNumber(preemptive_fetch_number)
        self._course = (1, 1)  # (along, pos or neg direction)
        self._time = self._posModel.time
        self._channel = self._posModel.channel
        self._posModel.timeChanged.connect(self._onTimeChanged)
        self._posModel.channelChanged.connect(self._onChannelChanged)
        self._posModel.slicingPositionChanged.connect(
            self._onSlicingPositionChanged)

        self._allTilesCompleteEvent = threading.Event()
        self.dirty = False

        # We manually keep track of the tile-wise QGraphicsItems that
        # we've added to the scene in this dict, otherwise we would need
        # to use O(N) lookups for every tile by calling QGraphicsScene.items()
        self.tile_graphicsitems = defaultdict(
            set)  # [Tile.id] -> set(QGraphicsItems)

        self.last_drag_pos = None  # See mouseMoveEvent()
Esempio n. 18
0
 def _getTileWidth(self):
     key, default = self._getTileWidthConfigKeyDefault()
     tile_width = preferences.get("ImageScene2D", key, default=default)
     return tile_width
Esempio n. 19
0
    def _selectFiles(self):
        # Find the directory of the most recently opened image file
        mostRecentStackDirectory = preferences.get("DataSelection",
                                                   "recent stack directory")
        if mostRecentStackDirectory is not None:
            defaultDirectory = os.path.split(mostRecentStackDirectory)[0]
        else:
            defaultDirectory = os.path.expanduser("~")

        options = QFileDialog.Options(QFileDialog.ShowDirsOnly)
        if ilastik.config.cfg.getboolean("ilastik", "debug"):
            options |= QFileDialog.DontUseNativeDialog

        h5exts = [x.lstrip(".") for x in OpStreamingH5N5SequenceReaderM.H5EXTS]
        # Launch the "Open File" dialog
        extensions = vigra.impex.listExtensions().split()
        extensions.extend(h5exts)
        extensions.extend(OpInputDataReader.n5Selection)
        filt = "Image files (" + " ".join("*." + x for x in extensions) + ")"
        options = QFileDialog.Options()
        if ilastik.config.cfg.getboolean("ilastik", "debug"):
            options |= QFileDialog.DontUseNativeDialog
        fileNames, _filter = QFileDialog.getOpenFileNames(
            self,
            "Select Images for Stack",
            defaultDirectory,
            filt,
            options=options)

        # For the n5 extension, the attributes.json file has to be selected in the file dialog.
        # However we need just the n5 directory-file.
        for i in range(len(fileNames)):
            if os.path.join("n5", "attributes.json") in fileNames[i]:
                fileNames[i] = fileNames[i].replace(
                    os.path.sep + "attributes.json", "")

        msg = ""
        if len(fileNames) == 0:
            return

        pathComponents = PathComponents(fileNames[0])

        if ((len(fileNames) == 1) and pathComponents.extension
                not in OpStreamingH5N5SequenceReaderM.H5EXTS +
                OpStreamingH5N5SequenceReaderM.N5EXTS):
            msg += "Cannot create stack: You only chose a single file.  "
            msg += "If your stack is contained in a single file (e.g. a multi-page tiff) "
            msg += 'please use the "Add File" button.'
            QMessageBox.warning(self, "Invalid selection", msg)
            return None

        directory = pathComponents.externalPath
        preferences.set("DataSelection", "recent stack directory", directory)

        if (pathComponents.extension in OpStreamingH5N5SequenceReaderM.H5EXTS
                or pathComponents.extension
                in OpStreamingH5N5SequenceReaderM.N5EXTS):
            if len(fileNames) == 1:
                # open the dialog for globbing:
                file_name = fileNames[0]
                dlg = H5N5StackingDlg(
                    parent=self,
                    list_of_paths=self._findInternalStacks(file_name))
                if dlg.exec_() == QDialog.Accepted:
                    globstring = "{}/{}".format(file_name,
                                                dlg.get_globstring())
                    self.patternEdit.setText(globstring)
                    self._applyPattern()
                    return None
                else:
                    return None
            else:
                # check for internal paths
                internal_paths = self._h5N5FindCommonInternal(fileNames)

                if len(internal_paths) == 0:
                    msg += "Could not find a unique common internal path in"
                    msg += directory + "\n"
                    QMessageBox.warning(self, "Invalid selection", msg)
                    return None
                elif len(internal_paths) == 1:
                    fileNames = [
                        "{}/{}".format(fn, internal_paths[0])
                        for fn in fileNames
                    ]
                else:
                    # Ask the user which dataset to choose
                    dlg = SubvolumeSelectionDlg(internal_paths, self)
                    if dlg.exec_() == QDialog.Accepted:
                        selected_index = dlg.combo.currentIndex()
                        selected_dataset = str(internal_paths[selected_index])
                        fileNames = [
                            "{}/{}".format(fn, selected_dataset)
                            for fn in fileNames
                        ]
                    else:
                        msg = "No valid internal path selected."
                        QMessageBox.warning(self, "Invalid selection", msg)
                        return None
        self._updateFileList(fileNames)