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)
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))
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)
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()
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()
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)
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)
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)
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)
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])
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()
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)
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)
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}
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()
def _getTileWidth(self): key, default = self._getTileWidthConfigKeyDefault() tile_width = preferences.get("ImageScene2D", key, default=default) return tile_width
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)