Search in sources :

Example 56 with PathClass

use of qupath.lib.objects.classes.PathClass in project qupath by qupath.

the class PathClassPane method createClassPane.

private Pane createClassPane() {
    listClasses = new ListView<>();
    var filteredList = qupath.getAvailablePathClasses().filtered(createPredicate(null));
    listClasses.setItems(filteredList);
    listClasses.setTooltip(new Tooltip("Annotation classes available (right-click to add or remove)"));
    listClasses.getSelectionModel().selectedItemProperty().addListener((v, o, n) -> updateAutoSetPathClassProperty());
    listClasses.setCellFactory(v -> new PathClassListCell(qupath));
    listClasses.getSelectionModel().select(0);
    listClasses.setPrefSize(100, 200);
    listClasses.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
    var copyCombo = new KeyCodeCombination(KeyCode.C, KeyCodeCombination.SHORTCUT_DOWN);
    var pasteCombo = new KeyCodeCombination(KeyCode.V, KeyCodeCombination.SHORTCUT_DOWN);
    // Intercept space presses because we handle them elsewhere
    listClasses.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
        if (e.getCode() == KeyCode.SPACE || e.getCode() == KeyCode.T) {
            toggleSelectedClassesVisibility();
            e.consume();
            return;
        } else if (e.getCode() == KeyCode.S) {
            setSelectedClassesVisibility(true);
            e.consume();
            return;
        } else if (e.getCode() == KeyCode.H) {
            setSelectedClassesVisibility(false);
            e.consume();
            return;
        }
    });
    listClasses.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
        if (e.isConsumed())
            return;
        if (e.getCode() == KeyCode.BACK_SPACE) {
            promptToRemoveSelectedClasses();
            e.consume();
            return;
        } else if (e.getCode() == KeyCode.ENTER) {
            promptToEditSelectedClass();
            e.consume();
            return;
        } else if (copyCombo.match(e)) {
            // Copy the list if needed
            String s = listClasses.getSelectionModel().getSelectedItems().stream().map(p -> p.toString()).collect(Collectors.joining(System.lineSeparator()));
            if (!s.isBlank()) {
                Clipboard.getSystemClipboard().setContent(Map.of(DataFormat.PLAIN_TEXT, s));
            }
            e.consume();
            return;
        } else if (pasteCombo.match(e)) {
            logger.debug("Paste not implemented for classification list!");
            e.consume();
            return;
        }
    });
    listClasses.setOnMouseClicked(e -> {
        if (!e.isPopupTrigger() && e.getClickCount() == 2)
            promptToEditSelectedClass();
    });
    ContextMenu menuClasses = createClassesMenu();
    listClasses.setContextMenu(menuClasses);
    // Add the class list
    BorderPane paneClasses = new BorderPane();
    paneClasses.setCenter(listClasses);
    Action setSelectedObjectClassAction = new Action("Set class", e -> {
        var hierarchy = getHierarchy();
        if (hierarchy == null)
            return;
        PathClass pathClass = getSelectedPathClass();
        if (pathClass == PathClassFactory.getPathClassUnclassified())
            pathClass = null;
        var pathObjects = new ArrayList<>(hierarchy.getSelectionModel().getSelectedObjects());
        List<PathObject> changed = new ArrayList<>();
        for (PathObject pathObject : pathObjects) {
            if (pathObject.isTMACore())
                continue;
            if (pathObject.getPathClass() == pathClass)
                continue;
            pathObject.setPathClass(pathClass);
            changed.add(pathObject);
        }
        if (!changed.isEmpty()) {
            hierarchy.fireObjectClassificationsChangedEvent(this, changed);
            GuiTools.refreshList(listClasses);
        }
    });
    setSelectedObjectClassAction.setLongText("Set the class of the currently-selected annotation(s)");
    Action autoClassifyAnnotationsAction = new Action("Auto set");
    autoClassifyAnnotationsAction.setLongText("Automatically set all new annotations to the selected class");
    autoClassifyAnnotationsAction.selectedProperty().bindBidirectional(doAutoSetPathClass);
    doAutoSetPathClass.addListener((e, f, g) -> updateAutoSetPathClassProperty());
    Button btnSetClass = ActionUtils.createButton(setSelectedObjectClassAction);
    ToggleButton btnAutoClass = ActionUtils.createToggleButton(autoClassifyAnnotationsAction);
    // Create a button to show context menu (makes it more obvious to the user that it exists)
    Button btnMore = GuiTools.createMoreButton(menuClasses, Side.RIGHT);
    GridPane paneClassButtons = new GridPane();
    paneClassButtons.add(btnSetClass, 0, 0);
    paneClassButtons.add(btnAutoClass, 1, 0);
    paneClassButtons.add(btnMore, 2, 0);
    GridPane.setHgrow(btnSetClass, Priority.ALWAYS);
    GridPane.setHgrow(btnAutoClass, Priority.ALWAYS);
    var tfFilter = new TextField();
    tfFilter.setTooltip(new Tooltip("Type to filter classifications in list"));
    tfFilter.setPromptText("Filter classifications in list");
    filterText.bind(tfFilter.textProperty());
    filterText.addListener((v, o, n) -> filteredList.setPredicate(createPredicate(n)));
    var paneBottom = PaneTools.createRowGrid(tfFilter, paneClassButtons);
    PaneTools.setMaxWidth(Double.MAX_VALUE, btnSetClass, btnAutoClass, tfFilter);
    paneClasses.setBottom(paneBottom);
    return paneClasses;
}
Also used : Button(javafx.scene.control.Button) Arrays(java.util.Arrays) ImageServer(qupath.lib.images.servers.ImageServer) ListCell(javafx.scene.control.ListCell) ActionUtils(org.controlsfx.control.action.ActionUtils) LoggerFactory(org.slf4j.LoggerFactory) Side(javafx.geometry.Side) ContextMenu(javafx.scene.control.ContextMenu) Map(java.util.Map) StandardPathClasses(qupath.lib.objects.classes.PathClassFactory.StandardPathClasses) QuPathGUI(qupath.lib.gui.QuPathGUI) Pane(javafx.scene.layout.Pane) TextField(javafx.scene.control.TextField) MenuItem(javafx.scene.control.MenuItem) BufferedImage(java.awt.image.BufferedImage) Predicate(java.util.function.Predicate) Set(java.util.Set) Rectangle(javafx.scene.shape.Rectangle) KeyEvent(javafx.scene.input.KeyEvent) Collectors(java.util.stream.Collectors) PathObject(qupath.lib.objects.PathObject) Platform(javafx.application.Platform) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer) Priority(javafx.scene.layout.Priority) List(java.util.List) BooleanProperty(javafx.beans.property.BooleanProperty) Project(qupath.lib.projects.Project) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) ProjectIO(qupath.lib.projects.ProjectIO) ToggleButton(javafx.scene.control.ToggleButton) Clipboard(javafx.scene.input.Clipboard) DataFormat(javafx.scene.input.DataFormat) GuiTools(qupath.lib.gui.tools.GuiTools) ColorToolsFX(qupath.lib.gui.tools.ColorToolsFX) ObservableList(javafx.collections.ObservableList) BorderPane(javafx.scene.layout.BorderPane) StringProperty(javafx.beans.property.StringProperty) ListView(javafx.scene.control.ListView) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) Action(org.controlsfx.control.action.Action) PathClassFactory(qupath.lib.objects.classes.PathClassFactory) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Bindings(javafx.beans.binding.Bindings) ArrayList(java.util.ArrayList) Dialogs(qupath.lib.gui.dialogs.Dialogs) Insets(javafx.geometry.Insets) Tooltip(javafx.scene.control.Tooltip) GridPane(javafx.scene.layout.GridPane) ColorPicker(javafx.scene.control.ColorPicker) ImageData(qupath.lib.images.ImageData) KeyCode(javafx.scene.input.KeyCode) Color(javafx.scene.paint.Color) Logger(org.slf4j.Logger) Label(javafx.scene.control.Label) PathClass(qupath.lib.objects.classes.PathClass) MenuTools(qupath.lib.gui.tools.MenuTools) File(java.io.File) OverlayOptions(qupath.lib.gui.viewer.OverlayOptions) Menu(javafx.scene.control.Menu) KeyCodeCombination(javafx.scene.input.KeyCodeCombination) SimpleBooleanProperty(javafx.beans.property.SimpleBooleanProperty) SelectionMode(javafx.scene.control.SelectionMode) QP(qupath.lib.scripting.QP) Collections(java.util.Collections) PathPrefs(qupath.lib.gui.prefs.PathPrefs) DialogButton(qupath.lib.gui.dialogs.Dialogs.DialogButton) PaneTools(qupath.lib.gui.tools.PaneTools) BorderPane(javafx.scene.layout.BorderPane) Action(org.controlsfx.control.action.Action) ToggleButton(javafx.scene.control.ToggleButton) GridPane(javafx.scene.layout.GridPane) Tooltip(javafx.scene.control.Tooltip) ArrayList(java.util.ArrayList) KeyCodeCombination(javafx.scene.input.KeyCodeCombination) ContextMenu(javafx.scene.control.ContextMenu) PathClass(qupath.lib.objects.classes.PathClass) PathObject(qupath.lib.objects.PathObject) Button(javafx.scene.control.Button) ToggleButton(javafx.scene.control.ToggleButton) DialogButton(qupath.lib.gui.dialogs.Dialogs.DialogButton) TextField(javafx.scene.control.TextField)

Example 57 with PathClass

use of qupath.lib.objects.classes.PathClass in project qupath by qupath.

the class PathClassPane method selectObjectsByClassification.

/**
 * Select objects by classification, logging the step (if performed) in the history workflow.
 * @param imageData the {@link ImageData} containing objects to be selected
 * @param pathClasses classifications that will result in an object being selected
 * @return true if a selection command was run, false otherwise (e.g. if no pathClasses were specified)
 */
public static boolean selectObjectsByClassification(ImageData<?> imageData, PathClass... pathClasses) {
    var hierarchy = imageData.getHierarchy();
    if (pathClasses.length == 0) {
        logger.warn("Cannot select objects by classification - no classifications selected!");
        return false;
    }
    QP.selectObjectsByPathClass(hierarchy, pathClasses);
    var s = Arrays.stream(pathClasses).map(p -> p == null || p == PathClassFactory.getPathClassUnclassified() ? "null" : "\"" + p.toString() + "\"").collect(Collectors.joining(", "));
    imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Select objects by classification", "selectObjectsByClassification(" + s + ");"));
    return true;
}
Also used : Button(javafx.scene.control.Button) Arrays(java.util.Arrays) ImageServer(qupath.lib.images.servers.ImageServer) ListCell(javafx.scene.control.ListCell) ActionUtils(org.controlsfx.control.action.ActionUtils) LoggerFactory(org.slf4j.LoggerFactory) Side(javafx.geometry.Side) ContextMenu(javafx.scene.control.ContextMenu) Map(java.util.Map) StandardPathClasses(qupath.lib.objects.classes.PathClassFactory.StandardPathClasses) QuPathGUI(qupath.lib.gui.QuPathGUI) Pane(javafx.scene.layout.Pane) TextField(javafx.scene.control.TextField) MenuItem(javafx.scene.control.MenuItem) BufferedImage(java.awt.image.BufferedImage) Predicate(java.util.function.Predicate) Set(java.util.Set) Rectangle(javafx.scene.shape.Rectangle) KeyEvent(javafx.scene.input.KeyEvent) Collectors(java.util.stream.Collectors) PathObject(qupath.lib.objects.PathObject) Platform(javafx.application.Platform) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer) Priority(javafx.scene.layout.Priority) List(java.util.List) BooleanProperty(javafx.beans.property.BooleanProperty) Project(qupath.lib.projects.Project) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) ProjectIO(qupath.lib.projects.ProjectIO) ToggleButton(javafx.scene.control.ToggleButton) Clipboard(javafx.scene.input.Clipboard) DataFormat(javafx.scene.input.DataFormat) GuiTools(qupath.lib.gui.tools.GuiTools) ColorToolsFX(qupath.lib.gui.tools.ColorToolsFX) ObservableList(javafx.collections.ObservableList) BorderPane(javafx.scene.layout.BorderPane) StringProperty(javafx.beans.property.StringProperty) ListView(javafx.scene.control.ListView) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) Action(org.controlsfx.control.action.Action) PathClassFactory(qupath.lib.objects.classes.PathClassFactory) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Bindings(javafx.beans.binding.Bindings) ArrayList(java.util.ArrayList) Dialogs(qupath.lib.gui.dialogs.Dialogs) Insets(javafx.geometry.Insets) Tooltip(javafx.scene.control.Tooltip) GridPane(javafx.scene.layout.GridPane) ColorPicker(javafx.scene.control.ColorPicker) ImageData(qupath.lib.images.ImageData) KeyCode(javafx.scene.input.KeyCode) Color(javafx.scene.paint.Color) Logger(org.slf4j.Logger) Label(javafx.scene.control.Label) PathClass(qupath.lib.objects.classes.PathClass) MenuTools(qupath.lib.gui.tools.MenuTools) File(java.io.File) OverlayOptions(qupath.lib.gui.viewer.OverlayOptions) Menu(javafx.scene.control.Menu) KeyCodeCombination(javafx.scene.input.KeyCodeCombination) SimpleBooleanProperty(javafx.beans.property.SimpleBooleanProperty) SelectionMode(javafx.scene.control.SelectionMode) QP(qupath.lib.scripting.QP) Collections(java.util.Collections) PathPrefs(qupath.lib.gui.prefs.PathPrefs) DialogButton(qupath.lib.gui.dialogs.Dialogs.DialogButton) PaneTools(qupath.lib.gui.tools.PaneTools) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep)

Example 58 with PathClass

use of qupath.lib.objects.classes.PathClass in project qupath by qupath.

the class PathClassPane method promptToImportClasses.

/**
 * Prompt to import available class list from another project.
 * @return true if the class list was changed, false otherwise.
 */
boolean promptToImportClasses() {
    File file = Dialogs.promptForFile("Import classifications", null, "QuPath project", ProjectIO.getProjectExtension());
    if (file == null)
        return false;
    if (!file.getAbsolutePath().toLowerCase().endsWith(ProjectIO.getProjectExtension())) {
        Dialogs.showErrorMessage("Import PathClasses", file.getName() + " is not a project file!");
        return false;
    }
    try {
        Project<?> project = ProjectIO.loadProject(file, BufferedImage.class);
        List<PathClass> pathClasses = project.getPathClasses();
        if (pathClasses.isEmpty()) {
            Dialogs.showErrorMessage("Import PathClasses", "No classes found in " + file.getName());
            return false;
        }
        ObservableList<PathClass> availableClasses = qupath.getAvailablePathClasses();
        if (pathClasses.size() == availableClasses.size() && availableClasses.containsAll(pathClasses)) {
            Dialogs.showInfoNotification("Import PathClasses", file.getName() + " contains same classifications - no changes to make");
            return false;
        }
        availableClasses.setAll(pathClasses);
        return true;
    } catch (Exception ex) {
        Dialogs.showErrorMessage("Error reading project", ex);
        return false;
    }
}
Also used : PathClass(qupath.lib.objects.classes.PathClass) File(java.io.File)

Example 59 with PathClass

use of qupath.lib.objects.classes.PathClass in project qupath by qupath.

the class PathClassPane method createClassesMenu.

ContextMenu createClassesMenu() {
    ContextMenu menu = new ContextMenu();
    Action actionAddClass = new Action("Add class", e -> promptToAddClass());
    Action actionRemoveClass = new Action("Remove class", e -> promptToRemoveSelectedClasses());
    Action actionResetClasses = new Action("Reset to default classes", e -> promptToResetClasses());
    Action actionImportClasses = new Action("Import classes from project", e -> promptToImportClasses());
    actionRemoveClass.disabledProperty().bind(Bindings.createBooleanBinding(() -> {
        PathClass item = listClasses.getSelectionModel().getSelectedItem();
        return item == null || PathClassFactory.getPathClassUnclassified() == item;
    }, listClasses.getSelectionModel().selectedItemProperty()));
    MenuItem miRemoveClass = ActionUtils.createMenuItem(actionRemoveClass);
    MenuItem miAddClass = ActionUtils.createMenuItem(actionAddClass);
    MenuItem miResetAllClasses = ActionUtils.createMenuItem(actionResetClasses);
    // MenuItem miPopulateFromImage = ActionUtils.createMenuItem(actionPopulateFromImage);
    // MenuItem miPopulateFromImageBase = ActionUtils.createMenuItem(actionPopulateFromImageBase);
    // MenuItem miClearAllClasses = new MenuItem("Clear all classes");
    // miClearAllClasses.setOnAction(e -> promptToClearClasses());
    MenuItem miPopulateFromImage = new MenuItem("All classes (including sub-classes)");
    miPopulateFromImage.setOnAction(e -> promptToPopulateFromImage(false));
    MenuItem miPopulateFromImageBase = new MenuItem("Base classes only");
    miPopulateFromImageBase.setOnAction(e -> promptToPopulateFromImage(true));
    MenuItem miPopulateFromChannels = new MenuItem("Populate from image channels");
    miPopulateFromChannels.setOnAction(e -> promptToPopulateFromChannels());
    Menu menuPopulate = new Menu("Populate from existing objects");
    menuPopulate.getItems().addAll(miPopulateFromImage, miPopulateFromImageBase);
    MenuItem miSelectObjects = new MenuItem("Select objects by classification");
    miSelectObjects.disableProperty().bind(Bindings.createBooleanBinding(() -> {
        var item = listClasses.getSelectionModel().getSelectedItem();
        return item == null;
    }, listClasses.getSelectionModel().selectedItemProperty()));
    miSelectObjects.setOnAction(e -> {
        var imageData = qupath.getImageData();
        if (imageData == null)
            return;
        selectObjectsByClassification(imageData, getSelectedPathClasses().toArray(PathClass[]::new));
    });
    MenuItem miSetHidden = new MenuItem("Hide classes in viewer");
    miSetHidden.setOnAction(e -> setSelectedClassesVisibility(false));
    MenuItem miSetVisible = new MenuItem("Show classes in viewer");
    miSetVisible.setOnAction(e -> setSelectedClassesVisibility(true));
    // MenuItem miToggleClassVisible = new MenuItem("Toggle display class");
    // miToggleClassVisible.setOnAction(e -> {
    // OverlayOptions overlayOptions = qupath.getViewer().getOverlayOptions();
    // for (var pathClass : getSelectedPathClasses()) {
    // if (pathClass == null || pathClass == PathClassFactory.getPathClassUnclassified())
    // continue;
    // overlayOptions.setPathClassHidden(pathClass, !overlayOptions.isPathClassHidden(pathClass));
    // }
    // listClasses.refresh();
    // });
    menu.setOnShowing(e -> {
        var hierarchy = getHierarchy();
        menuPopulate.setDisable(hierarchy == null);
        miPopulateFromImage.setDisable(hierarchy == null);
        miPopulateFromImageBase.setDisable(hierarchy == null);
        miPopulateFromChannels.setDisable(qupath.getImageData() == null);
        var selected = getSelectedPathClasses();
        boolean hasClasses = !selected.isEmpty();
        // boolean hasClasses = selected.size() > 1 ||
        // (selected.size() == 1 && selected.get(0) != null && selected.get(0) != PathClassFactory.getPathClassUnclassified());
        miSetVisible.setDisable(!hasClasses);
        miSetHidden.setDisable(!hasClasses);
    // miRemoveClass.setDisable(!hasClasses);
    });
    MenuItem miImportFromProject = ActionUtils.createMenuItem(actionImportClasses);
    menu.getItems().addAll(MenuTools.createMenu("Add/Remove...", miAddClass, miRemoveClass), menuPopulate, miPopulateFromChannels, miResetAllClasses, miImportFromProject, new SeparatorMenuItem(), MenuTools.createMenu("Show/Hide...", miSetVisible, miSetHidden), // new SeparatorMenuItem(),
    miSelectObjects);
    return menu;
}
Also used : PathClass(qupath.lib.objects.classes.PathClass) Action(org.controlsfx.control.action.Action) ContextMenu(javafx.scene.control.ContextMenu) MenuItem(javafx.scene.control.MenuItem) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem) ContextMenu(javafx.scene.control.ContextMenu) Menu(javafx.scene.control.Menu) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem)

Example 60 with PathClass

use of qupath.lib.objects.classes.PathClass in project qupath by qupath.

the class PathClassPane method promptToEditSelectedClass.

/**
 * Prompt to edit the selected classification.
 * @return true if changes were made, false otherwise
 */
boolean promptToEditSelectedClass() {
    PathClass pathClassSelected = getSelectedPathClass();
    if (promptToEditClass(pathClassSelected)) {
        // listModelPathClasses.fireListDataChangedEvent();
        GuiTools.refreshList(listClasses);
        var project = qupath.getProject();
        // Make sure we have updated the classes in the project
        if (project != null) {
            project.setPathClasses(listClasses.getItems());
        }
        qupath.repaintViewers();
        // TODO: Considering the only thing that can change is the color, firing an event should be unnecessary?
        // In any case, it doesn't make sense to do the current image only... should do all or none
        var hierarchy = getHierarchy();
        if (hierarchy != null)
            hierarchy.fireHierarchyChangedEvent(listClasses);
        return true;
    }
    return false;
}
Also used : PathClass(qupath.lib.objects.classes.PathClass)

Aggregations

PathClass (qupath.lib.objects.classes.PathClass)66 ArrayList (java.util.ArrayList)42 PathObject (qupath.lib.objects.PathObject)34 List (java.util.List)29 Map (java.util.Map)25 IOException (java.io.IOException)21 Logger (org.slf4j.Logger)20 LoggerFactory (org.slf4j.LoggerFactory)20 Collections (java.util.Collections)17 Collectors (java.util.stream.Collectors)17 BufferedImage (java.awt.image.BufferedImage)16 LinkedHashMap (java.util.LinkedHashMap)16 ROI (qupath.lib.roi.interfaces.ROI)16 HashMap (java.util.HashMap)15 ImageData (qupath.lib.images.ImageData)15 PathClassFactory (qupath.lib.objects.classes.PathClassFactory)15 PathObjectHierarchy (qupath.lib.objects.hierarchy.PathObjectHierarchy)15 ParameterList (qupath.lib.plugins.parameters.ParameterList)15 Collection (java.util.Collection)14 TreeMap (java.util.TreeMap)11