Search in sources :

Example 21 with TMAEntry

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

the class TMASummaryViewer method refreshTableData.

private void refreshTableData() {
    // int nn = 0;
    // double nPositive = 0;
    // for (TMAEntry entry : entriesBase) {
    // if (entry.isMissing())
    // continue;
    // nPositive += entry.getMeasurementAsDouble("Num Positive");
    // nn++;
    // }
    // System.err.println(nPositive + " positive cells across " + nn + " tissue samples");
    Collection<? extends TMAEntry> entries = groupByIDProperty.get() ? createSummaryEntries(entriesBase) : entriesBase;
    // Ensure that we don't try to modify a filtered list
    List<TreeTableColumn<TMAEntry, ?>> columns = new ArrayList<>();
    // Add an empty column.
    // Its purpose is to provide the space needed for the little expansion arrows, to avoid
    // these stealing space from the first interesting column.
    // Note: there's nothing to prevent the user reordering it along with other columns...
    // but hopefully it looks 'right' enough where it is that few would try to do that
    TreeTableColumn<TMAEntry, String> columnEmpty = new TreeTableColumn<>("  ");
    columnEmpty.setCellValueFactory(new Callback<CellDataFeatures<TMAEntry, String>, ObservableValue<String>>() {

        @Override
        public ObservableValue<String> call(CellDataFeatures<TMAEntry, String> p) {
            return Bindings.createStringBinding(() -> "");
        }
    });
    columnEmpty.setSortable(false);
    columnEmpty.setResizable(false);
    columns.add(columnEmpty);
    // Check if we have any images or overlays
    boolean hasImages = entries.stream().anyMatch(e -> e.hasImage());
    boolean hasOverlay = entries.stream().anyMatch(e -> e.hasOverlay());
    // Add columns to show images, if we have them
    if (hasImages || hasOverlay) {
        TreeTableColumn<TMAEntry, TMAEntry> columnImage = hasImages ? new TreeTableColumn<>("Thumbnail") : null;
        TreeTableColumn<TMAEntry, TMAEntry> columnOverlay = hasOverlay ? new TreeTableColumn<>("Overlay") : null;
        if (hasImages) {
            columnImage.setCellValueFactory(new Callback<CellDataFeatures<TMAEntry, TMAEntry>, ObservableValue<TMAEntry>>() {

                @Override
                public ObservableValue<TMAEntry> call(CellDataFeatures<TMAEntry, TMAEntry> p) {
                    return p.getValue().valueProperty();
                }
            });
            columnImage.setCellFactory(c -> new ImageTableCell(imageCache, false));
            columnImage.maxWidthProperty().bind(maxSmallWidth);
            columnImage.widthProperty().addListener((v, o, n) -> {
                if (n.doubleValue() == columnImage.getPrefWidth())
                    return;
                if (hasOverlay)
                    columnOverlay.setPrefWidth(n.doubleValue());
                table.refresh();
            });
            columns.add(columnImage);
        }
        if (hasOverlay) {
            columnOverlay.setCellValueFactory(new Callback<CellDataFeatures<TMAEntry, TMAEntry>, ObservableValue<TMAEntry>>() {

                @Override
                public ObservableValue<TMAEntry> call(CellDataFeatures<TMAEntry, TMAEntry> p) {
                    return p.getValue().valueProperty();
                }
            });
            columnOverlay.setCellFactory(c -> new ImageTableCell(imageCache, true));
            columnOverlay.maxWidthProperty().bind(maxSmallWidth);
            columnOverlay.widthProperty().addListener((v, o, n) -> {
                if (n.doubleValue() == columnOverlay.getPrefWidth())
                    return;
                columnImage.setPrefWidth(n.doubleValue());
                if (hasImages)
                    table.refresh();
            });
            columns.add(columnOverlay);
        }
    }
    // Update image availability
    if (hasImages) {
        if (hasOverlay)
            imageAvailability.set(ImageAvailability.BOTH);
        else
            imageAvailability.set(ImageAvailability.IMAGE_ONLY);
    } else if (hasOverlay) {
        imageAvailability.set(ImageAvailability.OVERLAY_ONLY);
    } else
        imageAvailability.set(ImageAvailability.NONE);
    for (String name : model.getAllNames()) {
        if (model.getMeasurementNames().contains(name)) {
            TreeTableColumn<TMAEntry, Number> column = new TreeTableColumn<>(name);
            column.setCellValueFactory(new Callback<CellDataFeatures<TMAEntry, Number>, ObservableValue<Number>>() {

                @Override
                public ObservableValue<Number> call(CellDataFeatures<TMAEntry, Number> p) {
                    double value = p.getValue() == null ? Double.NaN : model.getNumericValue(p.getValue().getValue(), name);
                    return new SimpleDoubleProperty(value);
                }
            });
            column.setCellFactory(c -> new NumericTreeTableCell<>());
            columns.add(column);
        } else {
            TreeTableColumn<TMAEntry, Object> column = new TreeTableColumn<>(name);
            column.setCellValueFactory(new Callback<CellDataFeatures<TMAEntry, Object>, ObservableValue<Object>>() {

                @Override
                public ObservableValue<Object> call(CellDataFeatures<TMAEntry, Object> p) {
                    return new SimpleObjectProperty<>(p.getValue() == null ? null : model.getStringValue(p.getValue().getValue(), name));
                }
            });
            column.setCellFactory(c -> new CenteredTreeTableCell<>());
            columns.add(column);
        }
    }
    // Set the column visibility depending upon whether they were hidden previously
    columns.stream().forEach(c -> c.setVisible(!lastHiddenColumns.contains(c.getText())));
    // Set columns for table
    table.getColumns().setAll(columns);
    // Set new root for table
    TreeItem<TMAEntry> root = new RootTreeItem(entries, combinedPredicate);
    table.setShowRoot(false);
    table.setRoot(root);
    model.refreshList();
}
Also used : CellDataFeatures(javafx.scene.control.TreeTableColumn.CellDataFeatures) SimpleDoubleProperty(javafx.beans.property.SimpleDoubleProperty) ArrayList(java.util.ArrayList) ObservableValue(javafx.beans.value.ObservableValue) TMAEntry(qupath.lib.gui.tma.TMAEntries.TMAEntry) TreeTableColumn(javafx.scene.control.TreeTableColumn) TMACoreObject(qupath.lib.objects.TMACoreObject)

Example 22 with TMAEntry

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

the class TMASummaryViewer method getCustomizeTablePane.

private Pane getCustomizeTablePane() {
    TableView<TreeTableColumn<TMAEntry, ?>> tableColumns = new TableView<>();
    tableColumns.setPlaceholder(new Text("No columns available"));
    tableColumns.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
    tableColumns.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
    SortedList<TreeTableColumn<TMAEntry, ?>> sortedColumns = new SortedList<>(table.getColumns().filtered(p -> !p.getText().trim().isEmpty()));
    sortedColumns.setComparator((c1, c2) -> c1.getText().compareTo(c2.getText()));
    tableColumns.setItems(sortedColumns);
    sortedColumns.comparatorProperty().bind(tableColumns.comparatorProperty());
    // sortedColumns.comparatorProperty().bind(tableColumns.comparatorProperty());
    TableColumn<TreeTableColumn<TMAEntry, ?>, String> columnName = new TableColumn<>("Column");
    columnName.setCellValueFactory(v -> v.getValue().textProperty());
    TableColumn<TreeTableColumn<TMAEntry, ?>, Boolean> columnVisible = new TableColumn<>("Visible");
    columnVisible.setCellValueFactory(v -> v.getValue().visibleProperty());
    // columnVisible.setCellValueFactory(col -> {
    // SimpleBooleanProperty prop = new SimpleBooleanProperty(col.getValue().isVisible());
    // prop.addListener((v, o, n) -> col.getValue().setVisible(n));
    // return prop;
    // });
    tableColumns.setEditable(true);
    columnVisible.setCellFactory(v -> new CheckBoxTableCell<>());
    tableColumns.getColumns().add(columnName);
    tableColumns.getColumns().add(columnVisible);
    ContextMenu contextMenu = new ContextMenu();
    Action actionShowSelected = new Action("Show selected", e -> {
        for (TreeTableColumn<?, ?> col : tableColumns.getSelectionModel().getSelectedItems()) {
            if (col != null)
                col.setVisible(true);
            else {
                // Not sure why this happens...?
                logger.trace("Selected column is null!");
            }
        }
    });
    Action actionHideSelected = new Action("Hide selected", e -> {
        for (TreeTableColumn<?, ?> col : tableColumns.getSelectionModel().getSelectedItems()) {
            if (col != null)
                col.setVisible(false);
            else {
                // Not sure why this happens...?
                logger.trace("Selected column is null!");
            }
        }
    });
    contextMenu.getItems().addAll(ActionUtils.createMenuItem(actionShowSelected), ActionUtils.createMenuItem(actionHideSelected));
    tableColumns.setContextMenu(contextMenu);
    tableColumns.setTooltip(new Tooltip("Show or hide table columns - right-click to change multiple columns at once"));
    BorderPane paneColumns = new BorderPane(tableColumns);
    paneColumns.setBottom(PaneTools.createColumnGridControls(ActionUtils.createButton(actionShowSelected), ActionUtils.createButton(actionHideSelected)));
    VBox paneRows = new VBox();
    // Create a box to filter on some metadata text
    ComboBox<String> comboMetadata = new ComboBox<>();
    comboMetadata.setItems(metadataNames);
    comboMetadata.getSelectionModel().getSelectedItem();
    comboMetadata.setPromptText("Select column");
    TextField tfFilter = new TextField();
    CheckBox cbExact = new CheckBox("Exact");
    // Set listeners
    cbExact.selectedProperty().addListener((v, o, n) -> setMetadataTextPredicate(comboMetadata.getSelectionModel().getSelectedItem(), tfFilter.getText(), cbExact.isSelected(), !cbExact.isSelected()));
    tfFilter.textProperty().addListener((v, o, n) -> setMetadataTextPredicate(comboMetadata.getSelectionModel().getSelectedItem(), tfFilter.getText(), cbExact.isSelected(), !cbExact.isSelected()));
    comboMetadata.getSelectionModel().selectedItemProperty().addListener((v, o, n) -> setMetadataTextPredicate(comboMetadata.getSelectionModel().getSelectedItem(), tfFilter.getText(), cbExact.isSelected(), !cbExact.isSelected()));
    GridPane paneMetadata = new GridPane();
    paneMetadata.add(comboMetadata, 0, 0);
    paneMetadata.add(tfFilter, 1, 0);
    paneMetadata.add(cbExact, 2, 0);
    paneMetadata.setPadding(new Insets(10, 10, 10, 10));
    paneMetadata.setVgap(2);
    paneMetadata.setHgap(5);
    comboMetadata.setMaxWidth(Double.MAX_VALUE);
    GridPane.setHgrow(tfFilter, Priority.ALWAYS);
    GridPane.setFillWidth(comboMetadata, Boolean.TRUE);
    GridPane.setFillWidth(tfFilter, Boolean.TRUE);
    TitledPane tpMetadata = new TitledPane("Metadata filter", paneMetadata);
    tpMetadata.setExpanded(false);
    // tpMetadata.setCollapsible(false);
    Tooltip tooltipMetadata = new Tooltip("Enter text to filter entries according to a selected metadata column");
    Tooltip.install(paneMetadata, tooltipMetadata);
    tpMetadata.setTooltip(tooltipMetadata);
    paneRows.getChildren().add(tpMetadata);
    // Add measurement predicate
    TextField tfCommand = new TextField();
    tfCommand.setTooltip(new Tooltip("Predicate used to filter entries for inclusion"));
    TextFields.bindAutoCompletion(tfCommand, e -> {
        int ind = tfCommand.getText().lastIndexOf("\"");
        if (ind < 0)
            return Collections.emptyList();
        String part = tfCommand.getText().substring(ind + 1);
        return measurementNames.stream().filter(n -> n.startsWith(part)).map(n -> "\"" + n + "\" ").collect(Collectors.toList());
    });
    String instructions = "Enter a predicate to filter entries.\n" + "Only entries passing the test will be included in any results.\n" + "Examples of predicates include:\n" + "    \"Num Tumor\" > 200\n" + "    \"Num Tumor\" > 100 && \"Num Stroma\" < 1000";
    // labelInstructions.setTooltip(new Tooltip("Note: measurement names must be in \"inverted commands\" and\n" +
    // "&& indicates 'and', while || indicates 'or'."));
    BorderPane paneMeasurementFilter = new BorderPane(tfCommand);
    Label label = new Label("Predicate: ");
    label.setAlignment(Pos.CENTER);
    label.setMaxHeight(Double.MAX_VALUE);
    paneMeasurementFilter.setLeft(label);
    Button btnApply = new Button("Apply");
    btnApply.setOnAction(e -> {
        TablePredicate predicateNew = new TablePredicate(tfCommand.getText());
        if (predicateNew.isValid()) {
            predicateMeasurements.set(predicateNew);
        } else {
            Dialogs.showErrorMessage("Invalid predicate", "Current predicate '" + tfCommand.getText() + "' is invalid!");
        }
        e.consume();
    });
    TitledPane tpMeasurementFilter = new TitledPane("Measurement filter", paneMeasurementFilter);
    tpMeasurementFilter.setExpanded(false);
    Tooltip tooltipInstructions = new Tooltip(instructions);
    tpMeasurementFilter.setTooltip(tooltipInstructions);
    Tooltip.install(paneMeasurementFilter, tooltipInstructions);
    paneMeasurementFilter.setRight(btnApply);
    paneRows.getChildren().add(tpMeasurementFilter);
    logger.info("Predicate set to: {}", predicateMeasurements.get());
    VBox pane = new VBox();
    // TitledPane tpColumns = new TitledPane("Select column", paneColumns);
    // tpColumns.setMaxHeight(Double.MAX_VALUE);
    // tpColumns.setCollapsible(false);
    pane.getChildren().addAll(paneColumns, new Separator(), paneRows);
    VBox.setVgrow(paneColumns, Priority.ALWAYS);
    return pane;
}
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) Action(org.controlsfx.control.action.Action) BorderPane(javafx.scene.layout.BorderPane) Insets(javafx.geometry.Insets) SortedList(javafx.collections.transformation.SortedList) Label(javafx.scene.control.Label) ContextMenu(javafx.scene.control.ContextMenu) Button(javafx.scene.control.Button) TextField(javafx.scene.control.TextField) TreeTableView(javafx.scene.control.TreeTableView) TableView(javafx.scene.control.TableView) TitledPane(javafx.scene.control.TitledPane) GridPane(javafx.scene.layout.GridPane) ComboBox(javafx.scene.control.ComboBox) Tooltip(javafx.scene.control.Tooltip) Text(javafx.scene.text.Text) TableColumn(javafx.scene.control.TableColumn) TreeTableColumn(javafx.scene.control.TreeTableColumn) TreeTableColumn(javafx.scene.control.TreeTableColumn) CheckBox(javafx.scene.control.CheckBox) VBox(javafx.scene.layout.VBox) Separator(javafx.scene.control.Separator)

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