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();
}
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;
}
Aggregations