Search in sources :

Example 1 with TMAEntry

use of qupath.lib.gui.tma.TMAEntries.TMAEntry in project qupath by qupath.

the class TMASummaryViewer method updateSurvivalCurves.

private void updateSurvivalCurves() {
    String colID = null;
    String colScore = null;
    colCensored = null;
    for (String nameOrig : model.getAllNames()) {
        if (nameOrig.equals(TMACoreObject.KEY_UNIQUE_ID))
            colID = nameOrig;
        else // else if (nameOrig.equals(TMACoreObject.KEY_CENSORED))
        // colCensored = nameOrig;
        // else if (!Number.class.isAssignableFrom())
        // continue;
        {
            if (nameOrig.trim().length() == 0 || !model.getMeasurementNames().contains(nameOrig))
                continue;
            String name = nameOrig.toLowerCase();
            if (name.equals("h-score"))
                colScore = nameOrig;
            else if (name.equals("positive %") && colScore == null)
                colScore = nameOrig;
        }
    }
    // Check for a column with the exact requested name
    String colCensoredRequested = null;
    String colSurvival = getSurvivalColumn();
    if (colSurvival != null) {
        colCensoredRequested = getRequestedSurvivalCensoredColumn(colSurvival);
        if (model.getAllNames().contains(colCensoredRequested))
            colCensored = colCensoredRequested;
        else // Check for a general 'censored' column... less secure since it doesn't specify OS or RFS (but helps with backwards-compatibility)
        if (model.getAllNames().contains("Censored")) {
            logger.warn("Correct censored column for \"{}\" unavailable - should be \"{}\", but using \"Censored\" column instead", colSurvival, colCensoredRequested);
            colCensored = "Censored";
        }
    }
    if (colCensored == null && colSurvival != null) {
        logger.warn("Unable to find censored column - survival data will be uncensored");
    } else
        logger.info("Survival column: {}, Censored column: {}", colSurvival, colCensored);
    colScore = comboMainMeasurement.getSelectionModel().getSelectedItem();
    if (colID == null || colSurvival == null || colCensored == null) {
        // Adjust priority depending on whether we have any data at all..
        if (!model.getItems().isEmpty())
            logger.warn("No survival data found!");
        else
            logger.trace("No entries or survival data available");
        return;
    }
    // Generate a pseudo TMA core hierarchy
    Map<String, List<TMAEntry>> scoreMap = createScoresMap(model.getItems(), colScore, colID);
    // System.err.println("Score map size: " + scoreMap.size() + "\tEntries: " + model.getEntries().size());
    List<TMACoreObject> cores = new ArrayList<>(scoreMap.size());
    double[] scores = new double[15];
    for (Entry<String, List<TMAEntry>> entry : scoreMap.entrySet()) {
        TMACoreObject core = new TMACoreObject();
        core.setName("ID: " + entry.getKey());
        MeasurementList ml = core.getMeasurementList();
        Arrays.fill(scores, Double.POSITIVE_INFINITY);
        List<TMAEntry> list = entry.getValue();
        // Increase array size, if needed
        if (list.size() > scores.length)
            scores = new double[list.size()];
        for (int i = 0; i < list.size(); i++) {
            scores[i] = model.getNumericValue(list.get(i), colScore);
        // scores[i] = list.get(i).getMeasurement(colScore).doubleValue();
        }
        Arrays.sort(scores);
        int n = list.size();
        double score;
        if (n % 2 == 1)
            score = scores[n / 2];
        else
            score = (scores[n / 2 - 1] + scores[n / 2]) / 2;
        core.putMetadataValue(TMACoreObject.KEY_UNIQUE_ID, entry.getKey());
        // System.err.println("Putting: " + list.get(0).getMeasurement(colSurvival).doubleValue() + " LIST: " + list.size());
        ml.putMeasurement(colSurvival, list.get(0).getMeasurementAsDouble(colSurvival));
        ml.putMeasurement(colCensoredRequested, list.get(0).getMeasurementAsDouble(colCensored));
        if (colScore != null)
            ml.putMeasurement(colScore, score);
        cores.add(core);
    // logger.info(entry.getKey() + "\t" + score);
    }
    TMAGrid grid = DefaultTMAGrid.create(cores, 1);
    PathObjectHierarchy hierarchy = new PathObjectHierarchy();
    hierarchy.setTMAGrid(grid);
    kmDisplay.setHierarchy(hierarchy, colSurvival, colCensoredRequested);
    kmDisplay.setScoreColumn(comboMainMeasurement.getSelectionModel().getSelectedItem());
// new KaplanMeierPlotTMA.KaplanMeierDisplay(hierarchy, colScore).show(frame, colScore);
}
Also used : PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) TMACoreObject(qupath.lib.objects.TMACoreObject) MeasurementList(qupath.lib.measurements.MeasurementList) TMAEntry(qupath.lib.gui.tma.TMAEntries.TMAEntry) ArrayList(java.util.ArrayList) DefaultTMAGrid(qupath.lib.objects.hierarchy.DefaultTMAGrid) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) ObservableList(javafx.collections.ObservableList) ArrayList(java.util.ArrayList) MeasurementList(qupath.lib.measurements.MeasurementList) SortedList(javafx.collections.transformation.SortedList) FilteredList(javafx.collections.transformation.FilteredList) List(java.util.List)

Example 2 with TMAEntry

use of qupath.lib.gui.tma.TMAEntries.TMAEntry in project qupath by qupath.

the class TMASummaryViewer method setTMAEntries.

void setTMAEntries(final Collection<TMAEntry> newEntries) {
    // Turn off use-selected - can be crashy when replacing entries
    if (!newEntries.equals(entriesBase)) {
        useSelectedProperty.set(false);
        // Reset the cache
        imageCache.clear();
        // Try to load small images in a background thread
        List<TMAEntry> duplicateEntries = new ArrayList<>(newEntries);
        ExecutorService service = Executors.newSingleThreadExecutor();
        service.submit(() -> {
            duplicateEntries.parallelStream().forEach(entry -> {
                imageCache.getImage(entry, maxSmallWidth.get());
                imageCache.getOverlay(entry, maxSmallWidth.get());
            });
        });
        service.shutdown();
    }
    this.entriesBase.setAll(newEntries);
    // Store the names of any currently hidden columns
    lastHiddenColumns = table.getColumns().stream().filter(c -> !c.isVisible()).map(c -> c.getText()).collect(Collectors.toSet());
    // this.table.getColumns().clear();
    // // Useful for a paper, but not generally...
    // int count = 0;
    // int nCells = 0;
    // int nTumor = 0;
    // for (TMAEntry entry : entriesBase) {
    // if (!entry.isMissing() && (predicate.get() == null || predicate.get().test(entry))) {
    // count++;
    // nCells += (int)(entry.getMeasurement("Num Tumor").doubleValue() + entry.getMeasurement("Num Stroma").doubleValue());
    // nTumor += (int)(entry.getMeasurement("Num Tumor").doubleValue());
    // }
    // }
    // System.err.println(String.format("Num entries:\t%d\tNum tumor:\t%d\tNum cells:\t%d", count, nTumor, nCells));
    // Update measurement names
    Set<String> namesMeasurements = new LinkedHashSet<>();
    Set<String> namesMetadata = new LinkedHashSet<>();
    // boolean containsSummaries = false;
    for (TMAEntry entry : newEntries) {
        namesMeasurements.addAll(entry.getMeasurementNames());
        namesMetadata.addAll(entry.getMetadataNames());
    // containsSummaries = containsSummaries || entry instanceof TMASummaryEntry;
    }
    // Get the available survival columns
    String currentSurvival = getSurvivalColumn();
    survivalColumns.clear();
    if (namesMeasurements.contains(TMACoreObject.KEY_OVERALL_SURVIVAL))
        survivalColumns.add(TMACoreObject.KEY_OVERALL_SURVIVAL);
    if (namesMeasurements.contains(TMACoreObject.KEY_RECURRENCE_FREE_SURVIVAL))
        survivalColumns.add(TMACoreObject.KEY_RECURRENCE_FREE_SURVIVAL);
    if (currentSurvival != null && survivalColumns.contains(currentSurvival))
        comboSurvival.getSelectionModel().select(currentSurvival);
    else if (!survivalColumns.isEmpty())
        comboSurvival.getSelectionModel().select(survivalColumns.get(0));
    // // Add the count of non-missing cores if we are working with summaries
    // if (containsSummaries)
    namesMeasurements.add("Available cores");
    // Make sure there are no nulls or other unusable values
    namesMeasurements.remove(null);
    namesMeasurements.remove("");
    // measurementNames.clear();
    String selectedMainMeasurement = comboMainMeasurement.getSelectionModel().getSelectedItem();
    measurementNames.setAll(namesMeasurements);
    if (namesMeasurements.contains(selectedMainMeasurement))
        comboMainMeasurement.getSelectionModel().select(selectedMainMeasurement);
    else {
        namesMeasurements.remove(TMACoreObject.KEY_UNIQUE_ID);
        namesMeasurements.remove(TMACoreObject.KEY_OVERALL_SURVIVAL);
        namesMeasurements.remove(TMACoreObject.KEY_RECURRENCE_FREE_SURVIVAL);
        namesMeasurements.remove(TMACoreObject.KEY_OS_CENSORED);
        namesMeasurements.remove(TMACoreObject.KEY_RFS_CENSORED);
        // For historical reasons when there was only one censored column supported...
        namesMeasurements.remove("Censored");
        if (!namesMeasurements.isEmpty())
            comboMainMeasurement.getSelectionModel().select(0);
    }
    metadataNames.clear();
    metadataNames.addAll(namesMetadata);
    refreshTableData();
    // The next time the table is empty, show a different placeholder
    // from the original (which is for loading/import)
    table.setPlaceholder(new Text("No data"));
}
Also used : Arrays(java.util.Arrays) Change(javafx.collections.ListChangeListener.Change) ServerTools(qupath.lib.images.servers.ServerTools) ActionUtils(org.controlsfx.control.action.ActionUtils) HistogramDisplay(qupath.lib.gui.charts.HistogramDisplay) PathTableData(qupath.lib.gui.measure.PathTableData) MasterDetailPane(org.controlsfx.control.MasterDetailPane) ScrollPane(javafx.scene.control.ScrollPane) TabPane(javafx.scene.control.TabPane) ListChangeListener(javafx.collections.ListChangeListener) SpearmansCorrelation(org.apache.commons.math3.stat.correlation.SpearmansCorrelation) Map(java.util.Map) SimpleIntegerProperty(javafx.beans.property.SimpleIntegerProperty) ScriptException(javax.script.ScriptException) Set(java.util.Set) KeyEvent(javafx.scene.input.KeyEvent) Executors(java.util.concurrent.Executors) Platform(javafx.application.Platform) Separator(javafx.scene.control.Separator) BooleanProperty(javafx.beans.property.BooleanProperty) Project(qupath.lib.projects.Project) Clipboard(javafx.scene.input.Clipboard) ScrollBarPolicy(javafx.scene.control.ScrollPane.ScrollBarPolicy) CheckBoxTableCell(javafx.scene.control.cell.CheckBoxTableCell) SimpleDoubleProperty(javafx.beans.property.SimpleDoubleProperty) SummaryMeasurementTableCommand(qupath.lib.gui.commands.SummaryMeasurementTableCommand) ObservableList(javafx.collections.ObservableList) BorderPane(javafx.scene.layout.BorderPane) TMAObjectEntry(qupath.lib.gui.tma.TMAEntries.TMAObjectEntry) WeakChangeListener(javafx.beans.value.WeakChangeListener) TreeItem(javafx.scene.control.TreeItem) FXCollections(javafx.collections.FXCollections) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Bindings(javafx.beans.binding.Bindings) IntegerProperty(javafx.beans.property.IntegerProperty) ArrayList(java.util.ArrayList) MeasurementList(qupath.lib.measurements.MeasurementList) LinkedHashMap(java.util.LinkedHashMap) TabClosingPolicy(javafx.scene.control.TabPane.TabClosingPolicy) TextFields(org.controlsfx.control.textfield.TextFields) TreeTableView(javafx.scene.control.TreeTableView) TextAlignment(javafx.scene.text.TextAlignment) LinkedHashSet(java.util.LinkedHashSet) GridPane(javafx.scene.layout.GridPane) TitledPane(javafx.scene.control.TitledPane) ToolBar(javafx.scene.control.ToolBar) GeneralTools(qupath.lib.common.GeneralTools) Node(javafx.scene.Node) CheckBox(javafx.scene.control.CheckBox) IOException(java.io.IOException) ChartTools(qupath.lib.gui.charts.ChartTools) File(java.io.File) Menu(javafx.scene.control.Menu) DefaultTMAGrid(qupath.lib.objects.hierarchy.DefaultTMAGrid) KeyCodeCombination(javafx.scene.input.KeyCodeCombination) SelectionMode(javafx.scene.control.SelectionMode) TreeMap(java.util.TreeMap) SimpleObjectProperty(javafx.beans.property.SimpleObjectProperty) Tab(javafx.scene.control.Tab) ObservableValue(javafx.beans.value.ObservableValue) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) PathPrefs(qupath.lib.gui.prefs.PathPrefs) PaneTools(qupath.lib.gui.tools.PaneTools) PathIO(qupath.lib.io.PathIO) Button(javafx.scene.control.Button) Pos(javafx.geometry.Pos) LoggerFactory(org.slf4j.LoggerFactory) Scanner(java.util.Scanner) XYChart(javafx.scene.chart.XYChart) VBox(javafx.scene.layout.VBox) Side(javafx.geometry.Side) KeyCombination(javafx.scene.input.KeyCombination) ObservableMeasurementTableData(qupath.lib.gui.measure.ObservableMeasurementTableData) ComboBox(javafx.scene.control.ComboBox) ContextMenu(javafx.scene.control.ContextMenu) TableView(javafx.scene.control.TableView) QuPathGUI(qupath.lib.gui.QuPathGUI) SortedList(javafx.collections.transformation.SortedList) Pane(javafx.scene.layout.Pane) Orientation(javafx.geometry.Orientation) TextField(javafx.scene.control.TextField) MenuItem(javafx.scene.control.MenuItem) BufferedImage(java.awt.image.BufferedImage) Predicate(java.util.function.Predicate) Collection(java.util.Collection) FilteredList(javafx.collections.transformation.FilteredList) Collectors(java.util.stream.Collectors) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem) Text(javafx.scene.text.Text) SimpleBindings(javax.script.SimpleBindings) Priority(javafx.scene.layout.Priority) List(java.util.List) Entry(java.util.Map.Entry) Optional(java.util.Optional) NumberAxis(javafx.scene.chart.NumberAxis) Scene(javafx.scene.Scene) ListView(javafx.scene.control.ListView) ReadOnlyObjectProperty(javafx.beans.property.ReadOnlyObjectProperty) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) Action(org.controlsfx.control.action.Action) HashMap(java.util.HashMap) DoubleProperty(javafx.beans.property.DoubleProperty) TMAScoreImporter(qupath.lib.io.TMAScoreImporter) TreeTableRow(javafx.scene.control.TreeTableRow) TableColumn(javafx.scene.control.TableColumn) HashSet(java.util.HashSet) Dialogs(qupath.lib.gui.dialogs.Dialogs) ScatterChart(javafx.scene.chart.ScatterChart) Insets(javafx.geometry.Insets) Callback(javafx.util.Callback) Tooltip(javafx.scene.control.Tooltip) ExecutorService(java.util.concurrent.ExecutorService) ImageData(qupath.lib.images.ImageData) KeyCode(javafx.scene.input.KeyCode) ObjectProperty(javafx.beans.property.ObjectProperty) Logger(org.slf4j.Logger) Label(javafx.scene.control.Label) MenuBar(javafx.scene.control.MenuBar) CellDataFeatures(javafx.scene.control.TreeTableColumn.CellDataFeatures) ProjectImageEntry(qupath.lib.projects.ProjectImageEntry) ScriptEngineManager(javax.script.ScriptEngineManager) TMACoreObject(qupath.lib.objects.TMACoreObject) PearsonsCorrelation(org.apache.commons.math3.stat.correlation.PearsonsCorrelation) DropShadow(javafx.scene.effect.DropShadow) MenuTools(qupath.lib.gui.tools.MenuTools) ScriptContext(javax.script.ScriptContext) TMAEntry(qupath.lib.gui.tma.TMAEntries.TMAEntry) TreeTableColumn(javafx.scene.control.TreeTableColumn) SimpleBooleanProperty(javafx.beans.property.SimpleBooleanProperty) Stage(javafx.stage.Stage) ScriptEngine(javax.script.ScriptEngine) ChangeListener(javafx.beans.value.ChangeListener) Collections(java.util.Collections) LinkedHashSet(java.util.LinkedHashSet) TMAEntry(qupath.lib.gui.tma.TMAEntries.TMAEntry) ArrayList(java.util.ArrayList) ExecutorService(java.util.concurrent.ExecutorService) Text(javafx.scene.text.Text)

Example 3 with TMAEntry

use of qupath.lib.gui.tma.TMAEntries.TMAEntry in project qupath by qupath.

the class TMASummaryViewer method parseInputFile.

private void parseInputFile(File file, List<TMAEntry> entries) {
    int nEntries = entries.size();
    String serverPath = null;
    try {
        Scanner scanner = new Scanner(file);
        serverPath = scanner.nextLine().trim();
        scanner.close();
    } catch (Exception e) {
        logger.error("Error parsing input file", e);
    }
    if (serverPath == null) {
        // || !(new File(serverPath).exists())) {
        logger.error("Unable to find a server with path " + serverPath + " - cannot parse " + file.getAbsolutePath());
        return;
    }
    File dirData = new File(file.getAbsolutePath() + ".data");
    try {
        File fileResults = getTMAResultsFile(dirData);
        if (fileResults == null) {
            logger.error("No results file found for {}", dirData.getAbsolutePath());
            return;
        }
        Map<String, List<String>> csvData = TMAScoreImporter.readCSV(fileResults);
        if (csvData.isEmpty()) {
            logger.warn("Results file empty: {}", fileResults.getAbsolutePath());
            return;
        }
        // Identify metadata and numeric columns
        Map<String, List<String>> metadataColumns = new LinkedHashMap<>();
        Map<String, double[]> measurementColumns = new LinkedHashMap<>();
        List<String> idColumn = csvData.remove(TMACoreObject.KEY_UNIQUE_ID);
        if (idColumn != null) {
            metadataColumns.put(TMACoreObject.KEY_UNIQUE_ID, idColumn);
            // Make sure IDs are trimmed
            if (trimUniqueIDs) {
                for (int i = 0; i < idColumn.size(); i++) idColumn.set(i, idColumn.get(i) == null ? null : idColumn.get(i).trim());
            }
        }
        List<String> nameColumn = csvData.remove("Name");
        if (nameColumn == null)
            nameColumn = csvData.remove("Object");
        // Handle 'missing-ness' separately from general metadata
        List<String> missingColumn = csvData.remove(MISSING_COLUMN);
        // csvData.values().iterator().next().size();
        int n = idColumn == null ? 0 : idColumn.size();
        for (Entry<String, List<String>> entry : csvData.entrySet()) {
            List<String> list = entry.getValue();
            n = list.size();
            double[] values = TMAScoreImporter.parseNumeric(list, true);
            if (values == null || GeneralTools.numNaNs(values) == list.size())
                metadataColumns.put(entry.getKey(), list);
            else
                measurementColumns.put(entry.getKey(), values);
        }
        for (int i = 0; i < n; i++) {
            // Don't permit 'NaN' as an ID
            if (idColumn != null && "NaN".equals(idColumn.get(i)))
                continue;
            String name = nameColumn == null ? idColumn.get(i) : nameColumn.get(i);
            boolean missing = missingColumn != null && "True".equals(missingColumn.get(i));
            File fileImage = new File(dirData, name + ".jpg");
            File fileOverlayImage = new File(dirData, name + "-overlay.jpg");
            if (!fileOverlayImage.exists())
                fileOverlayImage = new File(dirData, name + "-overlay.png");
            TMAEntry entry = TMAEntries.createDefaultTMAEntry(serverPath, fileImage.getAbsolutePath(), fileOverlayImage.getAbsolutePath(), name, missing);
            for (Entry<String, List<String>> temp : metadataColumns.entrySet()) {
                entry.putMetadata(temp.getKey(), temp.getValue().get(i));
            }
            for (Entry<String, double[]> temp : measurementColumns.entrySet()) {
                entry.putMeasurement(temp.getKey(), temp.getValue()[i]);
            }
            entries.add(entry);
        }
    } catch (Exception e) {
        logger.error("Error parsing input file " + file, e);
    }
    logger.info("Parsed " + (entries.size() - nEntries) + " from " + file.getName() + " (" + entries.size() + " total)");
}
Also used : Scanner(java.util.Scanner) TMAEntry(qupath.lib.gui.tma.TMAEntries.TMAEntry) ScriptException(javax.script.ScriptException) IOException(java.io.IOException) LinkedHashMap(java.util.LinkedHashMap) ObservableList(javafx.collections.ObservableList) ArrayList(java.util.ArrayList) MeasurementList(qupath.lib.measurements.MeasurementList) SortedList(javafx.collections.transformation.SortedList) FilteredList(javafx.collections.transformation.FilteredList) List(java.util.List) File(java.io.File)

Example 4 with TMAEntry

use of qupath.lib.gui.tma.TMAEntries.TMAEntry in project qupath by qupath.

the class TMASummaryViewer method initialize.

private void initialize() {
    model = new TMATableModel();
    groupByIDProperty.addListener((v, o, n) -> refreshTableData());
    MenuBar menuBar = new MenuBar();
    Menu menuFile = new Menu("File");
    MenuItem miOpen = new MenuItem("Open...");
    miOpen.setAccelerator(new KeyCodeCombination(KeyCode.O, KeyCombination.SHORTCUT_DOWN));
    miOpen.setOnAction(e -> {
        File file = Dialogs.getChooser(stage).promptForFile(null, null, "TMA data files", new String[] { "qptma" });
        if (file == null)
            return;
        setInputFile(file);
    });
    MenuItem miSave = new MenuItem("Save As...");
    miSave.setAccelerator(new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN));
    miSave.setOnAction(e -> SummaryMeasurementTableCommand.saveTableModel(model, null, Collections.emptyList()));
    MenuItem miImportFromImage = new MenuItem("Import from current image...");
    miImportFromImage.setAccelerator(new KeyCodeCombination(KeyCode.I, KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN));
    miImportFromImage.setOnAction(e -> setTMAEntriesFromOpenImage());
    MenuItem miImportFromProject = new MenuItem("Import from current project... (experimental)");
    miImportFromProject.setAccelerator(new KeyCodeCombination(KeyCode.P, KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN));
    miImportFromProject.setOnAction(e -> setTMAEntriesFromOpenProject());
    MenuItem miImportClipboard = new MenuItem("Import from clipboard...");
    miImportClipboard.setOnAction(e -> {
        String text = Clipboard.getSystemClipboard().getString();
        if (text == null) {
            Dialogs.showErrorMessage("Import scores", "Clipboard is empty!");
            return;
        }
        int n = importScores(text);
        if (n > 0) {
            setTMAEntries(new ArrayList<>(entriesBase));
        }
        Dialogs.showMessageDialog("Import scores", "Number of scores imported: " + n);
    });
    Menu menuEdit = new Menu("Edit");
    MenuItem miCopy = new MenuItem("Copy table to clipboard");
    miCopy.setOnAction(e -> {
        SummaryMeasurementTableCommand.copyTableContentsToClipboard(model, Collections.emptyList());
    });
    combinedPredicate.addListener((v, o, n) -> {
        // We want any other changes triggered by this to have happened,
        // so that the data has already been updated
        Platform.runLater(() -> handleTableContentChange());
    });
    // Reset the scores for missing cores - this ensures they will be NaN and not influence subsequent results
    MenuItem miResetMissingScores = new MenuItem("Reset scores for missing cores");
    miResetMissingScores.setOnAction(e -> {
        int changes = 0;
        for (TMAEntry entry : entriesBase) {
            if (!entry.isMissing())
                continue;
            boolean changed = false;
            for (String m : entry.getMeasurementNames().toArray(new String[0])) {
                if (!TMASummaryEntry.isSurvivalColumn(m) && !Double.isNaN(entry.getMeasurementAsDouble(m))) {
                    entry.putMeasurement(m, null);
                    changed = true;
                }
            }
            if (changed)
                changes++;
        }
        if (changes == 0) {
            logger.info("No changes made when resetting scores for missing cores!");
            return;
        }
        logger.info("{} change(s) made when resetting scores for missing cores!", changes);
        table.refresh();
        updateSurvivalCurves();
        if (scatterPane != null)
            scatterPane.updateChart();
        if (histogramDisplay != null)
            histogramDisplay.refreshHistogram();
    });
    menuEdit.getItems().add(miResetMissingScores);
    MenuTools.addMenuItems(menuFile, miOpen, miSave, null, miImportClipboard, null, miImportFromImage, miImportFromProject);
    menuBar.getMenus().add(menuFile);
    menuEdit.getItems().add(miCopy);
    menuBar.getMenus().add(menuEdit);
    menuFile.setOnShowing(e -> {
        boolean imageDataAvailable = QuPathGUI.getInstance() != null && QuPathGUI.getInstance().getImageData() != null && QuPathGUI.getInstance().getImageData().getHierarchy().getTMAGrid() != null;
        miImportFromImage.setDisable(!imageDataAvailable);
        boolean projectAvailable = QuPathGUI.getInstance() != null && QuPathGUI.getInstance().getProject() != null && !QuPathGUI.getInstance().getProject().getImageList().isEmpty();
        miImportFromProject.setDisable(!projectAvailable);
    });
    // Double-clicking previously used for comments... but conflicts with tree table expansion
    // table.setOnMouseClicked(e -> {
    // if (!e.isPopupTrigger() && e.getClickCount() > 1)
    // promptForComment();
    // });
    table.setPlaceholder(new Text("Drag TMA data folder onto window, or choose File -> Open"));
    table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
    BorderPane pane = new BorderPane();
    pane.setTop(menuBar);
    menuBar.useSystemMenuBarProperty().bindBidirectional(PathPrefs.useSystemMenubarProperty());
    // menuBar.setUseSystemMenuBar(true);
    // Create options
    ToolBar toolbar = new ToolBar();
    Label labelMeasurementMethod = new Label("Combination method");
    labelMeasurementMethod.setLabelFor(comboMeasurementMethod);
    labelMeasurementMethod.setTooltip(new Tooltip("Method whereby measurements for multiple cores with the same " + TMACoreObject.KEY_UNIQUE_ID + " will be combined"));
    CheckBox cbHidePane = new CheckBox("Hide pane");
    cbHidePane.setSelected(hidePaneProperty.get());
    cbHidePane.selectedProperty().bindBidirectional(hidePaneProperty);
    CheckBox cbGroupByID = new CheckBox("Group by ID");
    entriesBase.addListener((Change<? extends TMAEntry> event) -> {
        if (!event.getList().stream().anyMatch(e -> e.getMetadataValue(TMACoreObject.KEY_UNIQUE_ID) != null)) {
            cbGroupByID.setSelected(false);
            cbGroupByID.setDisable(true);
        } else {
            cbGroupByID.setDisable(false);
        }
    });
    cbGroupByID.setSelected(groupByIDProperty.get());
    cbGroupByID.selectedProperty().bindBidirectional(groupByIDProperty);
    CheckBox cbUseSelected = new CheckBox("Use selection only");
    cbUseSelected.selectedProperty().bindBidirectional(useSelectedProperty);
    CheckBox cbSkipMissing = new CheckBox("Hide missing cores");
    cbSkipMissing.selectedProperty().bindBidirectional(skipMissingCoresProperty);
    skipMissingCoresProperty.addListener((v, o, n) -> {
        table.refresh();
        updateSurvivalCurves();
        if (histogramDisplay != null)
            histogramDisplay.refreshHistogram();
        updateSurvivalCurves();
        if (scatterPane != null)
            scatterPane.updateChart();
    });
    toolbar.getItems().addAll(labelMeasurementMethod, comboMeasurementMethod, new Separator(Orientation.VERTICAL), cbHidePane, new Separator(Orientation.VERTICAL), cbGroupByID, new Separator(Orientation.VERTICAL), cbUseSelected, new Separator(Orientation.VERTICAL), cbSkipMissing);
    comboMeasurementMethod.getItems().addAll(TMAEntries.MeasurementCombinationMethod.values());
    comboMeasurementMethod.getSelectionModel().select(TMAEntries.MeasurementCombinationMethod.MEDIAN);
    selectedMeasurementCombinationProperty.addListener((v, o, n) -> table.refresh());
    ContextMenu popup = new ContextMenu();
    MenuItem miSetMissing = new MenuItem("Set missing");
    miSetMissing.setOnAction(e -> setSelectedMissingStatus(true));
    MenuItem miSetAvailable = new MenuItem("Set available");
    miSetAvailable.setOnAction(e -> setSelectedMissingStatus(false));
    MenuItem miExpand = new MenuItem("Expand all");
    miExpand.setOnAction(e -> {
        if (table.getRoot() == null)
            return;
        for (TreeItem<?> item : table.getRoot().getChildren()) {
            item.setExpanded(true);
        }
    });
    MenuItem miCollapse = new MenuItem("Collapse all");
    miCollapse.setOnAction(e -> {
        if (table.getRoot() == null)
            return;
        for (TreeItem<?> item : table.getRoot().getChildren()) {
            item.setExpanded(false);
        }
    });
    popup.getItems().addAll(miSetMissing, miSetAvailable, new SeparatorMenuItem(), miExpand, miCollapse);
    table.setContextMenu(popup);
    table.setRowFactory(e -> {
        TreeTableRow<TMAEntry> row = new TreeTableRow<>();
        // // Make rows invisible if they don't pass the predicate
        // row.visibleProperty().bind(Bindings.createBooleanBinding(() -> {
        // TMAEntry entry = row.getItem();
        // if (entry == null || (entry.isMissing() && skipMissingCoresProperty.get()))
        // return false;
        // return entries.getPredicate() == null || entries.getPredicate().test(entry);
        // },
        // skipMissingCoresProperty,
        // entries.predicateProperty()));
        // Style rows according to what they contain
        row.styleProperty().bind(Bindings.createStringBinding(() -> {
            if (row.isSelected())
                return "";
            TMAEntry entry = row.getItem();
            if (entry == null || entry instanceof TMASummaryEntry)
                return "";
            else if (entry.isMissing())
                return "-fx-background-color:rgb(225,225,232)";
            else
                return "-fx-background-color:rgb(240,240,245)";
        }, row.itemProperty(), row.selectedProperty()));
        // });
        return row;
    });
    BorderPane paneTable = new BorderPane();
    paneTable.setTop(toolbar);
    paneTable.setCenter(table);
    MasterDetailPane mdTablePane = new MasterDetailPane(Side.RIGHT, paneTable, createSidePane(), true);
    mdTablePane.showDetailNodeProperty().bind(Bindings.createBooleanBinding(() -> !hidePaneProperty.get() && !entriesBase.isEmpty(), hidePaneProperty, entriesBase));
    mdTablePane.setDividerPosition(2.0 / 3.0);
    pane.setCenter(mdTablePane);
    model.getItems().addListener(new ListChangeListener<TMAEntry>() {

        @Override
        public void onChanged(ListChangeListener.Change<? extends TMAEntry> c) {
            if (histogramDisplay != null)
                histogramDisplay.refreshHistogram();
            updateSurvivalCurves();
            if (scatterPane != null)
                scatterPane.updateChart();
        }
    });
    Label labelPredicate = new Label();
    labelPredicate.setPadding(new Insets(5, 5, 5, 5));
    labelPredicate.setAlignment(Pos.CENTER);
    // labelPredicate.setStyle("-fx-background-color: rgba(20, 120, 20, 0.15);");
    labelPredicate.setStyle("-fx-background-color: rgba(120, 20, 20, 0.15);");
    labelPredicate.textProperty().addListener((v, o, n) -> {
        if (n.trim().length() > 0)
            pane.setBottom(labelPredicate);
        else
            pane.setBottom(null);
    });
    labelPredicate.setMaxWidth(Double.MAX_VALUE);
    labelPredicate.setMaxHeight(labelPredicate.getPrefHeight());
    labelPredicate.setTextAlignment(TextAlignment.CENTER);
    predicateMeasurements.addListener((v, o, n) -> {
        if (n == null)
            labelPredicate.setText("");
        else if (n instanceof TablePredicate) {
            TablePredicate tp = (TablePredicate) n;
            if (tp.getOriginalCommand().trim().isEmpty())
                labelPredicate.setText("");
            else
                labelPredicate.setText("Predicate: " + tp.getOriginalCommand());
        } else
            labelPredicate.setText("Predicate: " + n.toString());
    });
    // predicate.set(new TablePredicate("\"Tumor\" > 100"));
    scene = new Scene(pane);
    scene.addEventHandler(KeyEvent.KEY_PRESSED, e -> {
        KeyCode code = e.getCode();
        if ((code == KeyCode.SPACE || code == KeyCode.ENTER) && entrySelected != null) {
            promptForComment();
            return;
        }
    });
}
Also used : Arrays(java.util.Arrays) Change(javafx.collections.ListChangeListener.Change) ServerTools(qupath.lib.images.servers.ServerTools) ActionUtils(org.controlsfx.control.action.ActionUtils) HistogramDisplay(qupath.lib.gui.charts.HistogramDisplay) PathTableData(qupath.lib.gui.measure.PathTableData) MasterDetailPane(org.controlsfx.control.MasterDetailPane) ScrollPane(javafx.scene.control.ScrollPane) TabPane(javafx.scene.control.TabPane) ListChangeListener(javafx.collections.ListChangeListener) SpearmansCorrelation(org.apache.commons.math3.stat.correlation.SpearmansCorrelation) Map(java.util.Map) SimpleIntegerProperty(javafx.beans.property.SimpleIntegerProperty) ScriptException(javax.script.ScriptException) Set(java.util.Set) KeyEvent(javafx.scene.input.KeyEvent) Executors(java.util.concurrent.Executors) Platform(javafx.application.Platform) Separator(javafx.scene.control.Separator) BooleanProperty(javafx.beans.property.BooleanProperty) Project(qupath.lib.projects.Project) Clipboard(javafx.scene.input.Clipboard) ScrollBarPolicy(javafx.scene.control.ScrollPane.ScrollBarPolicy) CheckBoxTableCell(javafx.scene.control.cell.CheckBoxTableCell) SimpleDoubleProperty(javafx.beans.property.SimpleDoubleProperty) SummaryMeasurementTableCommand(qupath.lib.gui.commands.SummaryMeasurementTableCommand) ObservableList(javafx.collections.ObservableList) BorderPane(javafx.scene.layout.BorderPane) TMAObjectEntry(qupath.lib.gui.tma.TMAEntries.TMAObjectEntry) WeakChangeListener(javafx.beans.value.WeakChangeListener) TreeItem(javafx.scene.control.TreeItem) FXCollections(javafx.collections.FXCollections) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Bindings(javafx.beans.binding.Bindings) IntegerProperty(javafx.beans.property.IntegerProperty) ArrayList(java.util.ArrayList) MeasurementList(qupath.lib.measurements.MeasurementList) LinkedHashMap(java.util.LinkedHashMap) TabClosingPolicy(javafx.scene.control.TabPane.TabClosingPolicy) TextFields(org.controlsfx.control.textfield.TextFields) TreeTableView(javafx.scene.control.TreeTableView) TextAlignment(javafx.scene.text.TextAlignment) LinkedHashSet(java.util.LinkedHashSet) GridPane(javafx.scene.layout.GridPane) TitledPane(javafx.scene.control.TitledPane) ToolBar(javafx.scene.control.ToolBar) GeneralTools(qupath.lib.common.GeneralTools) Node(javafx.scene.Node) CheckBox(javafx.scene.control.CheckBox) IOException(java.io.IOException) ChartTools(qupath.lib.gui.charts.ChartTools) File(java.io.File) Menu(javafx.scene.control.Menu) DefaultTMAGrid(qupath.lib.objects.hierarchy.DefaultTMAGrid) KeyCodeCombination(javafx.scene.input.KeyCodeCombination) SelectionMode(javafx.scene.control.SelectionMode) TreeMap(java.util.TreeMap) SimpleObjectProperty(javafx.beans.property.SimpleObjectProperty) Tab(javafx.scene.control.Tab) ObservableValue(javafx.beans.value.ObservableValue) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) PathPrefs(qupath.lib.gui.prefs.PathPrefs) PaneTools(qupath.lib.gui.tools.PaneTools) PathIO(qupath.lib.io.PathIO) Button(javafx.scene.control.Button) Pos(javafx.geometry.Pos) LoggerFactory(org.slf4j.LoggerFactory) Scanner(java.util.Scanner) XYChart(javafx.scene.chart.XYChart) VBox(javafx.scene.layout.VBox) Side(javafx.geometry.Side) KeyCombination(javafx.scene.input.KeyCombination) ObservableMeasurementTableData(qupath.lib.gui.measure.ObservableMeasurementTableData) ComboBox(javafx.scene.control.ComboBox) ContextMenu(javafx.scene.control.ContextMenu) TableView(javafx.scene.control.TableView) QuPathGUI(qupath.lib.gui.QuPathGUI) SortedList(javafx.collections.transformation.SortedList) Pane(javafx.scene.layout.Pane) Orientation(javafx.geometry.Orientation) TextField(javafx.scene.control.TextField) MenuItem(javafx.scene.control.MenuItem) BufferedImage(java.awt.image.BufferedImage) Predicate(java.util.function.Predicate) Collection(java.util.Collection) FilteredList(javafx.collections.transformation.FilteredList) Collectors(java.util.stream.Collectors) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem) Text(javafx.scene.text.Text) SimpleBindings(javax.script.SimpleBindings) Priority(javafx.scene.layout.Priority) List(java.util.List) Entry(java.util.Map.Entry) Optional(java.util.Optional) NumberAxis(javafx.scene.chart.NumberAxis) Scene(javafx.scene.Scene) ListView(javafx.scene.control.ListView) ReadOnlyObjectProperty(javafx.beans.property.ReadOnlyObjectProperty) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) Action(org.controlsfx.control.action.Action) HashMap(java.util.HashMap) DoubleProperty(javafx.beans.property.DoubleProperty) TMAScoreImporter(qupath.lib.io.TMAScoreImporter) TreeTableRow(javafx.scene.control.TreeTableRow) TableColumn(javafx.scene.control.TableColumn) HashSet(java.util.HashSet) Dialogs(qupath.lib.gui.dialogs.Dialogs) ScatterChart(javafx.scene.chart.ScatterChart) Insets(javafx.geometry.Insets) Callback(javafx.util.Callback) Tooltip(javafx.scene.control.Tooltip) ExecutorService(java.util.concurrent.ExecutorService) ImageData(qupath.lib.images.ImageData) KeyCode(javafx.scene.input.KeyCode) ObjectProperty(javafx.beans.property.ObjectProperty) Logger(org.slf4j.Logger) Label(javafx.scene.control.Label) MenuBar(javafx.scene.control.MenuBar) CellDataFeatures(javafx.scene.control.TreeTableColumn.CellDataFeatures) ProjectImageEntry(qupath.lib.projects.ProjectImageEntry) ScriptEngineManager(javax.script.ScriptEngineManager) TMACoreObject(qupath.lib.objects.TMACoreObject) PearsonsCorrelation(org.apache.commons.math3.stat.correlation.PearsonsCorrelation) DropShadow(javafx.scene.effect.DropShadow) MenuTools(qupath.lib.gui.tools.MenuTools) ScriptContext(javax.script.ScriptContext) TMAEntry(qupath.lib.gui.tma.TMAEntries.TMAEntry) TreeTableColumn(javafx.scene.control.TreeTableColumn) SimpleBooleanProperty(javafx.beans.property.SimpleBooleanProperty) Stage(javafx.stage.Stage) ScriptEngine(javax.script.ScriptEngine) ChangeListener(javafx.beans.value.ChangeListener) Collections(java.util.Collections) BorderPane(javafx.scene.layout.BorderPane) Insets(javafx.geometry.Insets) MasterDetailPane(org.controlsfx.control.MasterDetailPane) Label(javafx.scene.control.Label) MenuBar(javafx.scene.control.MenuBar) ContextMenu(javafx.scene.control.ContextMenu) ListChangeListener(javafx.collections.ListChangeListener) KeyCode(javafx.scene.input.KeyCode) Menu(javafx.scene.control.Menu) ContextMenu(javafx.scene.control.ContextMenu) TreeTableRow(javafx.scene.control.TreeTableRow) TMAEntry(qupath.lib.gui.tma.TMAEntries.TMAEntry) Tooltip(javafx.scene.control.Tooltip) MenuItem(javafx.scene.control.MenuItem) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem) KeyCodeCombination(javafx.scene.input.KeyCodeCombination) Text(javafx.scene.text.Text) Change(javafx.collections.ListChangeListener.Change) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem) Scene(javafx.scene.Scene) CheckBox(javafx.scene.control.CheckBox) ToolBar(javafx.scene.control.ToolBar) File(java.io.File) Separator(javafx.scene.control.Separator)

Example 5 with TMAEntry

use of qupath.lib.gui.tma.TMAEntries.TMAEntry in project qupath by qupath.

the class TMAImageCache method getLargeCachedImage.

private Image getLargeCachedImage(final TMAEntry entry, final boolean isOverlay) {
    Map<TMAEntry, SoftReference<Image>> cache = isOverlay ? overlayLarge : imageLarge;
    SoftReference<Image> ref = cache.get(entry);
    Image img = ref == null ? null : ref.get();
    if (img == null) {
        img = isOverlay ? entry.getOverlay(-1) : entry.getImage(-1);
        if (img != null) {
            cache.put(entry, new SoftReference<>(img));
        }
    }
    return img;
}
Also used : SoftReference(java.lang.ref.SoftReference) TMAEntry(qupath.lib.gui.tma.TMAEntries.TMAEntry) Image(javafx.scene.image.Image)

Aggregations

TMAEntry (qupath.lib.gui.tma.TMAEntries.TMAEntry)22 ArrayList (java.util.ArrayList)12 IOException (java.io.IOException)8 MeasurementList (qupath.lib.measurements.MeasurementList)8 TMACoreObject (qupath.lib.objects.TMACoreObject)8 BufferedImage (java.awt.image.BufferedImage)7 File (java.io.File)7 List (java.util.List)7 ObservableList (javafx.collections.ObservableList)7 FilteredList (javafx.collections.transformation.FilteredList)7 SortedList (javafx.collections.transformation.SortedList)7 HashMap (java.util.HashMap)6 LinkedHashMap (java.util.LinkedHashMap)6 TMAGrid (qupath.lib.objects.hierarchy.TMAGrid)6 TreeMap (java.util.TreeMap)5 Arrays (java.util.Arrays)4 Collection (java.util.Collection)4 Collections (java.util.Collections)4 HashSet (java.util.HashSet)4 LinkedHashSet (java.util.LinkedHashSet)4