Search in sources :

Example 6 with QuPathViewer

use of qupath.lib.gui.viewer.QuPathViewer in project qupath by qupath.

the class SummaryMeasurementTableCommand method showTable.

/**
 * Show a measurement table for the specified image data.
 * @param imageData the image data
 * @param type the object type to show
 */
public void showTable(ImageData<BufferedImage> imageData, Class<? extends PathObject> type) {
    if (imageData == null) {
        Dialogs.showNoImageError("Show measurement table");
        return;
    }
    final PathObjectHierarchy hierarchy = imageData.getHierarchy();
    ObservableMeasurementTableData model = new ObservableMeasurementTableData();
    model.setImageData(imageData, imageData == null ? Collections.emptyList() : imageData.getHierarchy().getObjects(null, type));
    SplitPane splitPane = new SplitPane();
    HistogramDisplay histogramDisplay = new HistogramDisplay(model, true);
    // table.setTableMenuButtonVisible(true);
    TableView<PathObject> table = new TableView<>();
    table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
    table.getSelectionModel().getSelectedItems().addListener(new ListChangeListener<PathObject>() {

        @Override
        public void onChanged(ListChangeListener.Change<? extends PathObject> c) {
            synchronizeSelectionModelToTable(hierarchy, c, table);
        }
    });
    StringProperty displayedName = new SimpleStringProperty(ServerTools.getDisplayableImageName(imageData.getServer()));
    var title = Bindings.createStringBinding(() -> {
        if (type == null)
            return "Results " + displayedName.get();
        else
            return PathObjectTools.getSuitableName(type, false) + " results - " + displayedName.get();
    }, displayedName);
    // Handle double-click as a way to center on a ROI
    // var enter = new KeyCodeCombination(KeyCode.ENTER);
    table.setRowFactory(params -> {
        var row = new TableRow<PathObject>();
        row.setOnMouseClicked(e -> {
            if (e.getClickCount() == 2) {
                maybeCenterROI(row.getItem());
            }
        });
        // });
        return row;
    });
    // Create columns according to the table model
    // for (int i = 0; i < model.getColumnCount(); i++) {
    // // Add string column
    // if (model.getColumnClass(i).equals(String.class)) {
    // TableColumn<PathObject, String> col = null;
    // col = new TableColumn<>(model.getColumnName(i));
    // col.setCellValueFactory(new Callback<CellDataFeatures<PathObject, String>, ObservableValue<String>>() {
    // public ObservableValue<String> call(CellDataFeatures<PathObject, String> val) {
    // return new SimpleStringProperty(val.getValue().getDisplayedName());
    // }
    // });
    // col.setCellFactory(column -> new BasicTableCell<String>());
    // table.getColumns().add(col);
    // }
    // }
    boolean tmaCoreList = TMACoreObject.class.isAssignableFrom(type);
    // Add TMA core columns, if suitable
    if (tmaCoreList) {
        TableColumn<PathObject, ROI> col = new TableColumn<>("Image");
        col.setCellValueFactory(val -> new SimpleObjectProperty<>(val.getValue().getROI()));
        double maxWidth = maxDimForTMACore;
        double padding = 10;
        col.setCellFactory(column -> new TMACoreTableCell(table, imageData.getServer(), maxWidth, padding));
        col.widthProperty().addListener((v, o, n) -> table.refresh());
        col.setMaxWidth(maxWidth + padding * 2);
        table.getColumns().add(col);
        // While here, make sure we have fewer bins - don't usually have all that many cores
        histogramDisplay.setNumBins(10);
    }
    // Create numeric columns
    for (String columnName : model.getAllNames()) {
        // Add column
        if (model.isStringMeasurement(columnName)) {
            TableColumn<PathObject, String> col = new TableColumn<>(columnName);
            col.setCellValueFactory(column -> model.createStringMeasurement(column.getValue(), column.getTableColumn().getText()));
            col.setCellFactory(column -> new BasicTableCell<>());
            table.getColumns().add(col);
        } else {
            TableColumn<PathObject, Number> col = new TableColumn<>(columnName);
            col.setCellValueFactory(column -> model.createNumericMeasurement(column.getValue(), column.getTableColumn().getText()));
            col.setCellFactory(column -> new NumericTableCell<PathObject>(histogramDisplay));
            table.getColumns().add(col);
        }
    }
    // Set the PathObjects - need to deal with sorting, since a FilteredList won't handle it directly
    SortedList<PathObject> items = new SortedList<>(model.getItems());
    items.comparatorProperty().bind(table.comparatorProperty());
    table.setItems(items);
    List<ButtonBase> buttons = new ArrayList<>();
    ToggleButton btnHistogram = new ToggleButton("Show histograms");
    btnHistogram.selectedProperty().addListener((v, o, n) -> {
        if (n) {
            Pane paneHistograms = histogramDisplay.getPane();
            splitPane.getItems().add(paneHistograms);
        } else if (histogramDisplay != null)
            splitPane.getItems().remove(histogramDisplay.getPane());
    });
    buttons.add(btnHistogram);
    // Button btnScatterplot = new Button("Show scatterplots");
    // btnScatterplot.setOnAction(e -> {
    // SwingUtilities.invokeLater(() -> {
    // JDialog dialog = new ScatterplotDisplay(null, "Scatterplots: " + displayedName, model).getDialog();
    // dialog.setLocationRelativeTo(null);
    // dialog.setVisible(true);
    // });
    // });
    // buttons.add(btnScatterplot);
    Button btnCopy = new Button("Copy to clipboard");
    btnCopy.setOnAction(e -> {
        // TODO: Deal with repetition immediately below...
        Set<String> excludeColumns = new HashSet<>();
        for (TableColumn<?, ?> col : table.getColumns()) {
            if (!col.isVisible())
                excludeColumns.add(col.getText());
        }
        copyTableContentsToClipboard(model, excludeColumns);
    });
    buttons.add(btnCopy);
    Button btnSave = new Button("Save");
    btnSave.setOnAction(e -> {
        Set<String> excludeColumns = new HashSet<>();
        for (TableColumn<?, ?> col : table.getColumns()) {
            if (!col.isVisible())
                excludeColumns.add(col.getText());
        }
        File fileOutput = promptForOutputFile();
        if (fileOutput == null)
            return;
        if (saveTableModel(model, fileOutput, excludeColumns)) {
            WorkflowStep step;
            String includeColumns;
            if (excludeColumns.isEmpty())
                includeColumns = "";
            else {
                List<String> includeColumnList = new ArrayList<>(model.getAllNames());
                includeColumnList.removeAll(excludeColumns);
                includeColumns = ", " + includeColumnList.stream().map(s -> "'" + s + "'").collect(Collectors.joining(", "));
            }
            String path = qupath.getProject() == null ? fileOutput.toURI().getPath() : fileOutput.getParentFile().toURI().getPath();
            if (type == TMACoreObject.class) {
                step = new DefaultScriptableWorkflowStep("Save TMA measurements", String.format("saveTMAMeasurements('%s'%s)", path, includeColumns));
            } else if (type == PathAnnotationObject.class) {
                step = new DefaultScriptableWorkflowStep("Save annotation measurements", String.format("saveAnnotationMeasurements('%s\'%s)", path, includeColumns));
            } else if (type == PathDetectionObject.class) {
                step = new DefaultScriptableWorkflowStep("Save detection measurements", String.format("saveDetectionMeasurements('%s'%s)", path, includeColumns));
            } else {
                step = new DefaultScriptableWorkflowStep("Save measurements", String.format("saveMeasurements('%s', %s%s)", path, type == null ? null : type.getName(), includeColumns));
            }
            imageData.getHistoryWorkflow().addStep(step);
        }
    });
    buttons.add(btnSave);
    Stage frame = new Stage();
    frame.initOwner(qupath.getStage());
    frame.titleProperty().bind(title);
    BorderPane paneTable = new BorderPane();
    paneTable.setCenter(table);
    // Add text field to filter visible columns
    TextField tfColumnFilter = new TextField();
    GridPane paneFilter = new GridPane();
    paneFilter.add(new Label("Column filter"), 0, 0);
    paneFilter.add(tfColumnFilter, 1, 0);
    GridPane.setHgrow(tfColumnFilter, Priority.ALWAYS);
    paneFilter.setHgap(5);
    if (tmaCoreList) {
        CheckBox cbHideMissing = new CheckBox("Hide missing cores");
        paneFilter.add(cbHideMissing, 2, 0);
        cbHideMissing.selectedProperty().addListener((v, o, n) -> {
            if (n) {
                model.setPredicate(p -> (!(p instanceof TMACoreObject)) || !((TMACoreObject) p).isMissing());
            } else
                model.setPredicate(null);
        });
        cbHideMissing.setSelected(true);
    }
    paneFilter.setPadding(new Insets(2, 5, 2, 5));
    paneTable.setBottom(paneFilter);
    StringProperty columnFilter = tfColumnFilter.textProperty();
    columnFilter.addListener((v, o, n) -> {
        String val = n.toLowerCase().trim();
        if (val.isEmpty()) {
            for (TableColumn<?, ?> col : table.getColumns()) {
                if (!col.isVisible())
                    col.setVisible(true);
            }
            return;
        }
        for (TableColumn<?, ?> col : table.getColumns()) {
            col.setVisible(col.getText().toLowerCase().contains(val));
        }
    });
    BorderPane pane = new BorderPane();
    // pane.setCenter(table);
    splitPane.getItems().add(paneTable);
    pane.setCenter(splitPane);
    GridPane panelButtons = PaneTools.createColumnGridControls(buttons.toArray(new ButtonBase[0]));
    pane.setBottom(panelButtons);
    PathObjectHierarchyListener listener = new PathObjectHierarchyListener() {

        @Override
        public void hierarchyChanged(PathObjectHierarchyEvent event) {
            if (event.isChanging())
                return;
            if (!Platform.isFxApplicationThread()) {
                Platform.runLater(() -> hierarchyChanged(event));
                return;
            }
            if (imageData != null)
                displayedName.set(ServerTools.getDisplayableImageName(imageData.getServer()));
            model.refreshEntries();
            table.refresh();
            if (histogramDisplay != null)
                histogramDisplay.refreshHistogram();
        }
    };
    QuPathViewer viewer = qupath.getViewer();
    TableViewerListener tableViewerListener = new TableViewerListener(viewer, table);
    frame.setOnShowing(e -> {
        hierarchy.addPathObjectListener(listener);
        viewer.addViewerListener(tableViewerListener);
    });
    frame.setOnHiding(e -> {
        hierarchy.removePathObjectListener(listener);
        viewer.removeViewerListener(tableViewerListener);
    });
    Scene scene = new Scene(pane, 600, 500);
    frame.setScene(scene);
    frame.show();
    // Add ability to remove entries from table
    ContextMenu menu = new ContextMenu();
    Menu menuLimitClasses = new Menu("Show classes");
    menu.setOnShowing(e -> {
        Set<PathClass> representedClasses = model.getBackingListEntries().stream().map(p -> p.getPathClass() == null ? null : p.getPathClass().getBaseClass()).collect(Collectors.toCollection(() -> new HashSet<>()));
        representedClasses.remove(null);
        if (representedClasses.isEmpty()) {
            menuLimitClasses.setVisible(false);
        } else {
            menuLimitClasses.setVisible(true);
        }
        menuLimitClasses.getItems().clear();
        List<PathClass> sortedClasses = new ArrayList<>(representedClasses);
        Collections.sort(sortedClasses);
        MenuItem miClass = new MenuItem("All");
        miClass.setOnAction(e2 -> {
            model.setPredicate(null);
            histogramDisplay.refreshHistogram();
        });
        menuLimitClasses.getItems().add(miClass);
        for (PathClass pathClass : sortedClasses) {
            miClass = new MenuItem(pathClass.getName());
            miClass.setOnAction(e2 -> {
                model.setPredicate(p -> pathClass.isAncestorOf(p.getPathClass()));
                histogramDisplay.refreshHistogram();
            });
            menuLimitClasses.getItems().add(miClass);
        }
    });
    if (type != TMACoreObject.class) {
        menu.getItems().add(menuLimitClasses);
        table.setContextMenu(menu);
    }
}
Also used : Button(javafx.scene.control.Button) Pos(javafx.geometry.Pos) Arrays(java.util.Arrays) ImageServer(qupath.lib.images.servers.ImageServer) ServerTools(qupath.lib.images.servers.ServerTools) LoggerFactory(org.slf4j.LoggerFactory) HistogramDisplay(qupath.lib.gui.charts.HistogramDisplay) PathTableData(qupath.lib.gui.measure.PathTableData) MultipleSelectionModel(javafx.scene.control.MultipleSelectionModel) ObservableMeasurementTableData(qupath.lib.gui.measure.ObservableMeasurementTableData) ListChangeListener(javafx.collections.ListChangeListener) ContextMenu(javafx.scene.control.ContextMenu) PathObjectHierarchyEvent(qupath.lib.objects.hierarchy.events.PathObjectHierarchyEvent) Map(java.util.Map) TableView(javafx.scene.control.TableView) QuPathGUI(qupath.lib.gui.QuPathGUI) SortedList(javafx.collections.transformation.SortedList) Pane(javafx.scene.layout.Pane) Shape(java.awt.Shape) PrintWriter(java.io.PrintWriter) SplitPane(javafx.scene.control.SplitPane) TextField(javafx.scene.control.TextField) MenuItem(javafx.scene.control.MenuItem) BufferedImage(java.awt.image.BufferedImage) ButtonBase(javafx.scene.control.ButtonBase) Collection(java.util.Collection) Set(java.util.Set) Canvas(javafx.scene.canvas.Canvas) WorkflowStep(qupath.lib.plugins.workflow.WorkflowStep) QuPathViewerListener(qupath.lib.gui.viewer.QuPathViewerListener) Collectors(java.util.stream.Collectors) StandardCharsets(java.nio.charset.StandardCharsets) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) PathDetectionObject(qupath.lib.objects.PathDetectionObject) PathObject(qupath.lib.objects.PathObject) Platform(javafx.application.Platform) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer) Priority(javafx.scene.layout.Priority) List(java.util.List) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) ToggleButton(javafx.scene.control.ToggleButton) Clipboard(javafx.scene.input.Clipboard) GuiTools(qupath.lib.gui.tools.GuiTools) ClipboardContent(javafx.scene.input.ClipboardContent) BorderPane(javafx.scene.layout.BorderPane) StringProperty(javafx.beans.property.StringProperty) TableViewSelectionModel(javafx.scene.control.TableView.TableViewSelectionModel) Scene(javafx.scene.Scene) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Bindings(javafx.beans.binding.Bindings) ArrayList(java.util.ArrayList) TableColumn(javafx.scene.control.TableColumn) HashSet(java.util.HashSet) LinkedHashMap(java.util.LinkedHashMap) Dialogs(qupath.lib.gui.dialogs.Dialogs) TableCell(javafx.scene.control.TableCell) Insets(javafx.geometry.Insets) GridPane(javafx.scene.layout.GridPane) ImageData(qupath.lib.images.ImageData) Logger(org.slf4j.Logger) Label(javafx.scene.control.Label) GeneralTools(qupath.lib.common.GeneralTools) RegionRequest(qupath.lib.regions.RegionRequest) TableRow(javafx.scene.control.TableRow) CheckBox(javafx.scene.control.CheckBox) PathClass(qupath.lib.objects.classes.PathClass) IOException(java.io.IOException) TMACoreObject(qupath.lib.objects.TMACoreObject) PathObjectSelectionModel(qupath.lib.objects.hierarchy.events.PathObjectSelectionModel) File(java.io.File) PathObjectTools(qupath.lib.objects.PathObjectTools) Menu(javafx.scene.control.Menu) ROI(qupath.lib.roi.interfaces.ROI) SelectionMode(javafx.scene.control.SelectionMode) Stage(javafx.stage.Stage) SimpleObjectProperty(javafx.beans.property.SimpleObjectProperty) SwingFXUtils(javafx.embed.swing.SwingFXUtils) PathObjectHierarchyListener(qupath.lib.objects.hierarchy.events.PathObjectHierarchyListener) Collections(java.util.Collections) Image(javafx.scene.image.Image) PathPrefs(qupath.lib.gui.prefs.PathPrefs) ContentDisplay(javafx.scene.control.ContentDisplay) PaneTools(qupath.lib.gui.tools.PaneTools) WorkflowStep(qupath.lib.plugins.workflow.WorkflowStep) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) SortedList(javafx.collections.transformation.SortedList) ArrayList(java.util.ArrayList) Label(javafx.scene.control.Label) PathObjectHierarchyEvent(qupath.lib.objects.hierarchy.events.PathObjectHierarchyEvent) SplitPane(javafx.scene.control.SplitPane) ListChangeListener(javafx.collections.ListChangeListener) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) Button(javafx.scene.control.Button) ToggleButton(javafx.scene.control.ToggleButton) Stage(javafx.stage.Stage) TextField(javafx.scene.control.TextField) TableView(javafx.scene.control.TableView) HashSet(java.util.HashSet) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer) GridPane(javafx.scene.layout.GridPane) TMACoreObject(qupath.lib.objects.TMACoreObject) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) Scene(javafx.scene.Scene) ROI(qupath.lib.roi.interfaces.ROI) TableColumn(javafx.scene.control.TableColumn) Pane(javafx.scene.layout.Pane) SplitPane(javafx.scene.control.SplitPane) BorderPane(javafx.scene.layout.BorderPane) GridPane(javafx.scene.layout.GridPane) HistogramDisplay(qupath.lib.gui.charts.HistogramDisplay) PathObject(qupath.lib.objects.PathObject) CheckBox(javafx.scene.control.CheckBox) TableRow(javafx.scene.control.TableRow) File(java.io.File) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) BorderPane(javafx.scene.layout.BorderPane) Insets(javafx.geometry.Insets) PathObjectHierarchyListener(qupath.lib.objects.hierarchy.events.PathObjectHierarchyListener) StringProperty(javafx.beans.property.StringProperty) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) ContextMenu(javafx.scene.control.ContextMenu) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) PathClass(qupath.lib.objects.classes.PathClass) ContextMenu(javafx.scene.control.ContextMenu) Menu(javafx.scene.control.Menu) ToggleButton(javafx.scene.control.ToggleButton) MenuItem(javafx.scene.control.MenuItem) ButtonBase(javafx.scene.control.ButtonBase) ObservableMeasurementTableData(qupath.lib.gui.measure.ObservableMeasurementTableData)

Example 7 with QuPathViewer

use of qupath.lib.gui.viewer.QuPathViewer in project qupath by qupath.

the class PathClassifierPane method runClassifier.

void runClassifier() {
    QuPathViewer viewer = viewerValue.getValue();
    if (viewer == null)
        return;
    ImageData<BufferedImage> imageData = viewer.getImageData();
    if (classifier == null || imageData == null || !classifier.isValid())
        return;
    Collection<PathObject> pathObjects = imageData.getHierarchy().getDetectionObjects();
    var availableFeatures = PathClassifierTools.getAvailableFeatures(pathObjects);
    var requiredFeatures = classifier.getRequiredMeasurements();
    String missingFeatures = requiredFeatures.stream().filter(p -> !availableFeatures.contains(p)).collect(Collectors.joining("\n\t"));
    if (!missingFeatures.isEmpty())
        logger.warn("Detection objects are missing the following feature(s):\n\t" + missingFeatures + "\nWill proceed with classification anyway..");
    // SwingUtilities.getRoot(viewer).setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    try {
        PathClassifierTools.runClassifier(imageData.getHierarchy(), classifier);
        // Log the classifier
        if (pathClassifier != null) {
            imageData.getHistoryWorkflow().addStep(new RunSavedClassifierWorkflowStep(pathClassifier));
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // SwingUtilities.getRoot(viewer).setCursor(cursor);
        // Update displayed list - names may have changed - and classifier summary
        updateClassifierSummary();
    }
}
Also used : Button(javafx.scene.control.Button) ImageData(qupath.lib.images.ImageData) RunSavedClassifierWorkflowStep(qupath.lib.plugins.workflow.RunSavedClassifierWorkflowStep) PathClassifierTools(qupath.lib.classifiers.PathClassifierTools) Logger(org.slf4j.Logger) TextArea(javafx.scene.control.TextArea) BufferedImage(java.awt.image.BufferedImage) Date(java.util.Date) Collection(java.util.Collection) LoggerFactory(org.slf4j.LoggerFactory) SimpleDateFormat(java.text.SimpleDateFormat) Collectors(java.util.stream.Collectors) File(java.io.File) PathObject(qupath.lib.objects.PathObject) Dialogs(qupath.lib.gui.dialogs.Dialogs) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer) PathObjectClassifier(qupath.lib.classifiers.PathObjectClassifier) ObservableValue(javafx.beans.value.ObservableValue) BorderPane(javafx.scene.layout.BorderPane) GridPane(javafx.scene.layout.GridPane) Pane(javafx.scene.layout.Pane) PaneTools(qupath.lib.gui.tools.PaneTools) PathObject(qupath.lib.objects.PathObject) RunSavedClassifierWorkflowStep(qupath.lib.plugins.workflow.RunSavedClassifierWorkflowStep) BufferedImage(java.awt.image.BufferedImage) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer)

Example 8 with QuPathViewer

use of qupath.lib.gui.viewer.QuPathViewer in project qupath by qupath.

the class ScreenshotCommand method run.

@Override
public void run() {
    QuPathViewer viewer = qupath.getViewer();
    if (viewer == null)
        return;
    BufferedImage img = GuiTools.makeSnapshot(qupath, GuiTools.SnapshotType.VIEWER);
    // Try to start up & display ImageJ
    ImageJ ij = IJExtension.getImageJInstance();
    if (ij != null)
        ij.setVisible(true);
    String name = "QuPath screenshot";
    if (viewer.getServer() != null)
        name = WindowManager.getUniqueName("Screenshot - " + ServerTools.getDisplayableImageName(viewer.getServer()));
    ImagePlus imp = new ImagePlus(name, img);
    double pixelWidth = getDisplayedPixelWidthMicrons(viewer);
    double pixelHeight = getDisplayedPixelHeightMicrons(viewer);
    if (!Double.isNaN(pixelWidth + pixelHeight)) {
        Calibration cal = imp.getCalibration();
        cal.pixelWidth = pixelWidth;
        cal.pixelHeight = pixelHeight;
        cal.setUnit("um");
    // // TODO: Set the screenshot origin, if no rotations have been applied
    // cal.xOrigin = -viewer.componentXtoImageX(0) / viewer.getDownsampleFactor();
    // cal.yOrigin = -viewer.componentYtoImageY(0) / viewer.getDownsampleFactor();
    }
    // TODO: Put image path into screenshot images - so they can also be used as whole slide images later
    // Make sure we aren't in batch mode, so that image will display
    Interpreter.batchMode = false;
    SwingUtilities.invokeLater(() -> imp.show());
}
Also used : ImageJ(ij.ImageJ) Calibration(ij.measure.Calibration) ImagePlus(ij.ImagePlus) BufferedImage(java.awt.image.BufferedImage) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer)

Example 9 with QuPathViewer

use of qupath.lib.gui.viewer.QuPathViewer in project qupath by qupath.

the class UndoRedoManager method hierarchyChanged.

@Override
public void hierarchyChanged(PathObjectHierarchyEvent event) {
    // Try to avoid calling too often
    if (undoingOrRedoing || event.isChanging() || maxUndoHierarchySize.get() <= 0 || event.getChangedObjects().stream().allMatch(p -> p instanceof ParallelTileObject))
        return;
    // *Potentially* we might have the same hierarchy in multiple viewers
    // Since we don't have the viewer stored in the event, check to see what viewers are impacted
    QuPathViewer[] viewers = map.keySet().toArray(new QuPathViewer[map.size()]);
    PathObjectHierarchy hierarchy = event.getHierarchy();
    int maxSize = maxUndoHierarchySize.get();
    boolean sizeOK = hierarchy.nObjects() <= maxSize;
    for (QuPathViewer viewer : viewers) {
        if (viewer.getHierarchy() == hierarchy) {
            SerializableUndoRedoStack<PathObjectHierarchy> undoRedo = map.get(viewer);
            // If the size is ok, register the change for potential undo-ing
            if (sizeOK) {
                if (undoRedo == null)
                    map.put(viewer, new SerializableUndoRedoStack<>(hierarchy));
                else
                    undoRedo.addLatest(hierarchy, maxUndoLevels.get());
            } else {
                // If the hierarchy is too big turn off undo/redo
                map.put(viewer, (SerializableUndoRedoStack<PathObjectHierarchy>) null);
            }
        }
    }
    refreshProperties();
}
Also used : ByteArrayOutputStream(java.io.ByteArrayOutputStream) ReadOnlyObjectProperty(javafx.beans.property.ReadOnlyObjectProperty) ObjectInputStream(java.io.ObjectInputStream) LoggerFactory(org.slf4j.LoggerFactory) ReadOnlyBooleanProperty(javafx.beans.property.ReadOnlyBooleanProperty) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Deque(java.util.Deque) IntegerProperty(javafx.beans.property.IntegerProperty) ByteArrayInputStream(java.io.ByteArrayInputStream) QuPathViewerPlus(qupath.lib.gui.viewer.QuPathViewerPlus) PathObjectHierarchyEvent(qupath.lib.objects.hierarchy.events.PathObjectHierarchyEvent) Map(java.util.Map) ObjectOutputStream(java.io.ObjectOutputStream) WeakHashMap(java.util.WeakHashMap) Shape(java.awt.Shape) ImageData(qupath.lib.images.ImageData) Logger(org.slf4j.Logger) BufferedImage(java.awt.image.BufferedImage) GeneralTools(qupath.lib.common.GeneralTools) ParallelTileObject(qupath.lib.plugins.ParallelTileObject) IOException(java.io.IOException) QuPathViewerListener(qupath.lib.gui.viewer.QuPathViewerListener) PathObject(qupath.lib.objects.PathObject) Platform(javafx.application.Platform) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer) SimpleBooleanProperty(javafx.beans.property.SimpleBooleanProperty) ObservableValue(javafx.beans.value.ObservableValue) PathObjectHierarchyListener(qupath.lib.objects.hierarchy.events.PathObjectHierarchyListener) ArrayDeque(java.util.ArrayDeque) ChangeListener(javafx.beans.value.ChangeListener) PathPrefs(qupath.lib.gui.prefs.PathPrefs) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) ParallelTileObject(qupath.lib.plugins.ParallelTileObject) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer)

Example 10 with QuPathViewer

use of qupath.lib.gui.viewer.QuPathViewer in project qupath by qupath.

the class BrightnessContrastCommand method changed.

@Override
public void changed(ObservableValue<? extends ImageData<BufferedImage>> source, ImageData<BufferedImage> imageDataOld, ImageData<BufferedImage> imageDataNew) {
    // TODO: Consider different viewers but same ImageData
    if (imageDataOld == imageDataNew)
        return;
    QuPathViewer viewerNew = qupath.getViewer();
    if (viewer != viewerNew) {
        if (viewer != null)
            viewer.getView().removeEventHandler(KeyEvent.KEY_TYPED, keyListener);
        if (viewerNew != null)
            viewerNew.getView().addEventHandler(KeyEvent.KEY_TYPED, keyListener);
        viewer = viewerNew;
    }
    if (imageDisplay != null) {
        showGrayscale.unbindBidirectional(imageDisplay.useGrayscaleLutProperty());
        imageDisplay.useGrayscaleLutProperty().unbindBidirectional(showGrayscale);
    }
    imageDisplay = viewer == null ? null : viewer.getImageDisplay();
    if (imageDataOld != null)
        imageDataOld.removePropertyChangeListener(this);
    if (imageDataNew != null)
        imageDataNew.addPropertyChangeListener(this);
    updateTable();
    // updateHistogramMap();
    // Update if we aren't currently initializing
    updateHistogram();
    updateSliders();
}
Also used : QuPathViewer(qupath.lib.gui.viewer.QuPathViewer)

Aggregations

QuPathViewer (qupath.lib.gui.viewer.QuPathViewer)14 BufferedImage (java.awt.image.BufferedImage)8 PathObject (qupath.lib.objects.PathObject)7 Logger (org.slf4j.Logger)5 LoggerFactory (org.slf4j.LoggerFactory)5 File (java.io.File)4 Collection (java.util.Collection)4 Collectors (java.util.stream.Collectors)4 GeneralTools (qupath.lib.common.GeneralTools)4 ImageData (qupath.lib.images.ImageData)4 IOException (java.io.IOException)3 ArrayList (java.util.ArrayList)3 List (java.util.List)3 QuPathGUI (qupath.lib.gui.QuPathGUI)3 Dialogs (qupath.lib.gui.dialogs.Dialogs)3 PathObjectHierarchy (qupath.lib.objects.hierarchy.PathObjectHierarchy)3 ImageJ (ij.ImageJ)2 ImagePlus (ij.ImagePlus)2 Collections (java.util.Collections)2 IntegerProperty (javafx.beans.property.IntegerProperty)2