use of qupath.lib.images.ImageData in project qupath by qupath.
the class ProjectImportImagesCommand method getThumbnailRGB.
// /**
// * Add a single ImageServer to a project, without considering sub-images.
// * <p>
// * This includes an optional attempt to request a thumbnail; if this fails, the image will not be added.
// *
// * @param project the project to which the entry should be added
// * @param server the server to add
// * @param type the ImageType that should be set for each entry being added
// * @return
// */
// public static ProjectImageEntry<BufferedImage> addSingleImageToProject(Project<BufferedImage> project, ImageServer<BufferedImage> server, ImageType type) {
// ProjectImageEntry<BufferedImage> entry = null;
// try {
// var img = getThumbnailRGB(server, null);
// entry = project.addImage(server);
// if (entry != null) {
// // Write a thumbnail if we can
// entry.setThumbnail(img);
// // Initialize an ImageData object with a type, if required
// if (type != null) {
// var imageData = new ImageData<>(server, type);
// entry.saveImageData(imageData);
// }
// }
// } catch (IOException e) {
// logger.warn("Error attempting to add " + server, e);
// }
// return entry;
// }
public static BufferedImage getThumbnailRGB(ImageServer<BufferedImage> server, ImageDisplay imageDisplay) throws IOException {
var img2 = server.getDefaultThumbnail(server.nZSlices() / 2, 0);
// Try to write RGB images directly
boolean success = false;
if (imageDisplay == null && (server.isRGB() || img2.getType() == BufferedImage.TYPE_BYTE_GRAY)) {
return resizeForThumbnail(img2);
}
if (!success) {
// Try with display transforms
if (imageDisplay == null) {
// By wrapping the thumbnail, we avoid slow z-stack/time series requests & determine brightness & contrast just from one plane
var wrappedServer = new WrappedBufferedImageServer("Dummy", img2, server.getMetadata().getChannels());
imageDisplay = new ImageDisplay(new ImageData<>(wrappedServer));
// imageDisplay = new ImageDisplay(new ImageData<>(server));
}
for (ChannelDisplayInfo info : imageDisplay.selectedChannels()) {
imageDisplay.autoSetDisplayRange(info);
}
img2 = imageDisplay.applyTransforms(img2, null);
return resizeForThumbnail(img2);
}
return img2;
}
use of qupath.lib.images.ImageData in project qupath by qupath.
the class Commands method requestShapeFeatures.
private static void requestShapeFeatures(ImageData<?> imageData, Collection<ShapeFeatures> features) {
if (imageData == null)
return;
var featureArray = features.toArray(ShapeFeatures[]::new);
if (featureArray.length == 0)
return;
Collection<PathObject> selected = imageData.getHierarchy().getSelectionModel().getSelectedObjects();
if (selected.isEmpty()) {
Dialogs.showWarningNotification("Shape features", "No objects selected!");
} else {
selected = new ArrayList<>(selected);
String featureString = Arrays.stream(featureArray).map(f -> "\"" + f.name() + "\"").collect(Collectors.joining(", "));
QP.addShapeMeasurements(imageData, selected, featureArray);
imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Add shape measurements", String.format("addShapeMeasurements(%s)", featureString)));
if (selected.size() == 1)
Dialogs.showInfoNotification("Shape features", "Shape features calculated for one object");
else
Dialogs.showInfoNotification("Shape features", "Shape features calculated for " + selected.size() + " objects");
}
}
use of qupath.lib.images.ImageData in project qupath by qupath.
the class TMACommands method promptToExportTMAData.
/**
* Prompt to export summary TMA data for a specific image to a directory.
* @param qupath
* @param imageData
*/
public static void promptToExportTMAData(QuPathGUI qupath, ImageData<BufferedImage> imageData) {
String title = "Export TMA data";
if (imageData == null) {
Dialogs.showNoImageError(title);
return;
}
PathObjectHierarchy hierarchy = imageData == null ? null : imageData.getHierarchy();
if (hierarchy == null || hierarchy.isEmpty() || hierarchy.getTMAGrid() == null || hierarchy.getTMAGrid().nCores() == 0) {
Dialogs.showErrorMessage(title, "No TMA data available!");
return;
}
var overlayOptions = qupath.getViewers().stream().filter(v -> v.getImageData() == imageData).map(v -> v.getOverlayOptions()).findFirst().orElse(qupath.getOverlayOptions());
String defaultName = ServerTools.getDisplayableImageName(imageData.getServer());
File file = Dialogs.promptToSaveFile(null, null, defaultName, "TMA data", ".qptma");
if (file != null) {
if (!file.getName().endsWith(".qptma"))
file = new File(file.getParentFile(), file.getName() + ".qptma");
double downsample = PathPrefs.tmaExportDownsampleProperty().get();
TMADataIO.writeTMAData(file, imageData, overlayOptions, downsample);
WorkflowStep step = new DefaultScriptableWorkflowStep("Export TMA data", "exportTMAData(\"" + GeneralTools.escapeFilePath(file.getParentFile().getAbsolutePath()) + "\", " + downsample + ")");
imageData.getHistoryWorkflow().addStep(step);
// PathAwtIO.writeTMAData(file, imageData, viewer.getOverlayOptions(), Double.NaN);
}
}
use of qupath.lib.images.ImageData in project qupath by qupath.
the class QuPathGUI method setViewerPopupMenu.
private void setViewerPopupMenu(final QuPathViewerPlus viewer) {
final ContextMenu popup = new ContextMenu();
MenuItem miAddRow = new MenuItem("Add row");
miAddRow.setOnAction(e -> viewerManager.addRow(viewer));
MenuItem miAddColumn = new MenuItem("Add column");
miAddColumn.setOnAction(e -> viewerManager.addColumn(viewer));
MenuItem miRemoveRow = new MenuItem("Remove row");
miRemoveRow.setOnAction(e -> viewerManager.removeViewerRow(viewer));
MenuItem miRemoveColumn = new MenuItem("Remove column");
miRemoveColumn.setOnAction(e -> viewerManager.removeViewerColumn(viewer));
MenuItem miCloseViewer = new MenuItem("Close viewer");
miCloseViewer.setOnAction(e -> {
viewerManager.closeViewer(viewer);
// viewerManager.removeViewer(viewer);
});
MenuItem miResizeGrid = new MenuItem("Reset grid size");
miResizeGrid.setOnAction(e -> {
viewerManager.resetGridSize();
});
MenuItem miToggleSync = ActionTools.createCheckMenuItem(defaultActions.TOGGLE_SYNCHRONIZE_VIEWERS, null);
MenuItem miMatchResolutions = ActionTools.createMenuItem(defaultActions.MATCH_VIEWER_RESOLUTIONS);
Menu menuMultiview = MenuTools.createMenu("Multi-view", miToggleSync, miMatchResolutions, miCloseViewer, null, miResizeGrid, null, // null,
miAddRow, miAddColumn, null, miRemoveRow, miRemoveColumn);
Menu menuView = MenuTools.createMenu("Display", ActionTools.createCheckMenuItem(defaultActions.SHOW_ANALYSIS_PANE, null), defaultActions.BRIGHTNESS_CONTRAST, null, ActionTools.createAction(() -> Commands.setViewerDownsample(viewer, 0.25), "400%"), ActionTools.createAction(() -> Commands.setViewerDownsample(viewer, 1), "100%"), ActionTools.createAction(() -> Commands.setViewerDownsample(viewer, 2), "50%"), ActionTools.createAction(() -> Commands.setViewerDownsample(viewer, 10), "10%"), ActionTools.createAction(() -> Commands.setViewerDownsample(viewer, 100), "1%"));
ToggleGroup groupTools = new ToggleGroup();
Menu menuTools = MenuTools.createMenu("Set tool", ActionTools.createCheckMenuItem(defaultActions.MOVE_TOOL, groupTools), ActionTools.createCheckMenuItem(defaultActions.RECTANGLE_TOOL, groupTools), ActionTools.createCheckMenuItem(defaultActions.ELLIPSE_TOOL, groupTools), ActionTools.createCheckMenuItem(defaultActions.LINE_TOOL, groupTools), ActionTools.createCheckMenuItem(defaultActions.POLYGON_TOOL, groupTools), ActionTools.createCheckMenuItem(defaultActions.POLYLINE_TOOL, groupTools), ActionTools.createCheckMenuItem(defaultActions.BRUSH_TOOL, groupTools), ActionTools.createCheckMenuItem(defaultActions.POINTS_TOOL, groupTools), null, ActionTools.createCheckMenuItem(defaultActions.SELECTION_MODE));
// Handle awkward 'TMA core missing' option
CheckMenuItem miTMAValid = new CheckMenuItem("Set core valid");
miTMAValid.setOnAction(e -> setTMACoreMissing(viewer.getHierarchy(), false));
CheckMenuItem miTMAMissing = new CheckMenuItem("Set core missing");
miTMAMissing.setOnAction(e -> setTMACoreMissing(viewer.getHierarchy(), true));
Menu menuTMA = new Menu("TMA");
MenuTools.addMenuItems(menuTMA, miTMAValid, miTMAMissing, null, defaultActions.TMA_ADD_NOTE, null, MenuTools.createMenu("Add", createImageDataAction(imageData -> TMACommands.promptToAddRowBeforeSelected(imageData), "Add TMA row before"), createImageDataAction(imageData -> TMACommands.promptToAddRowAfterSelected(imageData), "Add TMA row after"), createImageDataAction(imageData -> TMACommands.promptToAddColumnBeforeSelected(imageData), "Add TMA column before"), createImageDataAction(imageData -> TMACommands.promptToAddColumnAfterSelected(imageData), "Add TMA column after")), MenuTools.createMenu("Remove", createImageDataAction(imageData -> TMACommands.promptToDeleteTMAGridRow(imageData), "Remove TMA row"), createImageDataAction(imageData -> TMACommands.promptToDeleteTMAGridColumn(imageData), "column")));
// Create an empty placeholder menu
Menu menuSetClass = MenuTools.createMenu("Set class");
// CheckMenuItem miTMAValid = new CheckMenuItem("Set core valid");
// miTMAValid.setOnAction(e -> setTMACoreMissing(viewer.getHierarchy(), false));
// CheckMenuItem miTMAMissing = new CheckMenuItem("Set core missing");
// miTMAMissing.setOnAction(e -> setTMACoreMissing(viewer.getHierarchy(), true));
Menu menuCells = MenuTools.createMenu("Cells", ActionTools.createCheckMenuItem(defaultActions.SHOW_CELL_BOUNDARIES_AND_NUCLEI, null), ActionTools.createCheckMenuItem(defaultActions.SHOW_CELL_NUCLEI, null), ActionTools.createCheckMenuItem(defaultActions.SHOW_CELL_BOUNDARIES, null), ActionTools.createCheckMenuItem(defaultActions.SHOW_CELL_CENTROIDS, null));
MenuItem miClearSelectedObjects = new MenuItem("Delete object");
miClearSelectedObjects.setOnAction(e -> {
PathObjectHierarchy hierarchy = viewer.getHierarchy();
if (hierarchy == null)
return;
if (hierarchy.getSelectionModel().singleSelection()) {
GuiTools.promptToRemoveSelectedObject(hierarchy.getSelectionModel().getSelectedObject(), hierarchy);
} else {
GuiTools.promptToClearAllSelectedObjects(viewer.getImageData());
}
});
// Create a standard annotations menu
Menu menuAnnotations = GuiTools.populateAnnotationsMenu(this, new Menu("Annotations"));
SeparatorMenuItem topSeparator = new SeparatorMenuItem();
popup.setOnShowing(e -> {
// Check if we have any cells
ImageData<?> imageData = viewer.getImageData();
if (imageData == null)
menuCells.setVisible(false);
else
menuCells.setVisible(!imageData.getHierarchy().getDetectionObjects().isEmpty());
// Check what to show for TMA cores or annotations
Collection<PathObject> selectedObjects = viewer.getAllSelectedObjects();
PathObject pathObject = viewer.getSelectedObject();
menuTMA.setVisible(false);
if (pathObject instanceof TMACoreObject) {
boolean isMissing = ((TMACoreObject) pathObject).isMissing();
miTMAValid.setSelected(!isMissing);
miTMAMissing.setSelected(isMissing);
menuTMA.setVisible(true);
}
// Add clear objects option if we have more than one non-TMA object
if (imageData == null || imageData.getHierarchy().getSelectionModel().noSelection() || imageData.getHierarchy().getSelectionModel().getSelectedObject() instanceof TMACoreObject)
miClearSelectedObjects.setVisible(false);
else {
if (imageData.getHierarchy().getSelectionModel().singleSelection()) {
miClearSelectedObjects.setText("Delete object");
miClearSelectedObjects.setVisible(true);
} else {
miClearSelectedObjects.setText("Delete objects");
miClearSelectedObjects.setVisible(true);
}
}
boolean hasAnnotations = pathObject instanceof PathAnnotationObject || (!selectedObjects.isEmpty() && selectedObjects.stream().allMatch(p -> p.isAnnotation()));
updateSetAnnotationPathClassMenu(menuSetClass, viewer);
menuAnnotations.setVisible(hasAnnotations);
topSeparator.setVisible(hasAnnotations || pathObject instanceof TMACoreObject);
// Occasionally, the newly-visible top part of a popup menu can have the wrong size?
popup.setWidth(popup.getPrefWidth());
});
// popup.add(menuClassify);
popup.getItems().addAll(miClearSelectedObjects, menuTMA, menuSetClass, menuAnnotations, topSeparator, menuMultiview, menuCells, menuView, menuTools);
popup.setAutoHide(true);
// Enable circle pop-up for quick classification on right-click
CirclePopupMenu circlePopup = new CirclePopupMenu(viewer.getView(), null);
viewer.getView().addEventHandler(MouseEvent.MOUSE_PRESSED, e -> {
if ((e.isPopupTrigger() || e.isSecondaryButtonDown()) && e.isShiftDown() && !getAvailablePathClasses().isEmpty()) {
circlePopup.setAnimationDuration(Duration.millis(200));
updateSetAnnotationPathClassMenu(circlePopup, viewer);
circlePopup.show(e.getScreenX(), e.getScreenY());
e.consume();
return;
} else if (circlePopup.isShown())
circlePopup.hide();
if (e.isPopupTrigger() || e.isSecondaryButtonDown()) {
popup.show(viewer.getView().getScene().getWindow(), e.getScreenX(), e.getScreenY());
e.consume();
}
});
// // It's necessary to make the Window the owner, since otherwise the context menu does not disappear when clicking elsewhere on the viewer
// viewer.getView().setOnContextMenuRequested(e -> {
// popup.show(viewer.getView().getScene().getWindow(), e.getScreenX(), e.getScreenY());
// // popup.show(viewer.getView(), e.getScreenX(), e.getScreenY());
// });
}
use of qupath.lib.images.ImageData in project qupath by qupath.
the class BrightnessContrastCommand method createDialog.
protected Stage createDialog() {
if (!isInitialized())
initializeSliders();
initializePopup();
changed(null, null, qupath.getImageData());
BorderPane pane = new BorderPane();
GridPane box = new GridPane();
String blank = " ";
Label labelMin = new Label("Min display");
// labelMin.setTooltip(new Tooltip("Set minimum lookup table value - double-click to edit manually"));
Label labelMinValue = new Label(blank);
labelMinValue.setTooltip(new Tooltip("Set minimum lookup table value - double-click to edit manually"));
labelMinValue.textProperty().bind(Bindings.createStringBinding(() -> {
// If value < 10, return 2 dp, otherwise 1 dp. Should be more useful for 16-/32-bit images.
return sliderMin.getValue() < 10 ? String.format("%.2f", sliderMin.getValue()) : String.format("%.1f", sliderMin.getValue());
}, table.getSelectionModel().selectedItemProperty(), sliderMin.valueProperty()));
box.add(labelMin, 0, 0);
box.add(sliderMin, 1, 0);
box.add(labelMinValue, 2, 0);
Label labelMax = new Label("Max display");
labelMax.setTooltip(new Tooltip("Set maximum lookup table value - double-click to edit manually"));
Label labelMaxValue = new Label(blank);
labelMaxValue.setTooltip(new Tooltip("Set maximum lookup table value - double-click to edit manually"));
labelMaxValue.textProperty().bind(Bindings.createStringBinding(() -> {
// If value < 10, return 2 dp, otherwise 1 dp. Should be more useful for 16-/32-bit images.
return sliderMax.getValue() < 10 ? String.format("%.2f", sliderMax.getValue()) : String.format("%.1f", sliderMax.getValue());
}, table.getSelectionModel().selectedItemProperty(), sliderMax.valueProperty()));
box.add(labelMax, 0, 1);
box.add(sliderMax, 1, 1);
box.add(labelMaxValue, 2, 1);
box.setVgap(5);
GridPane.setFillWidth(sliderMin, Boolean.TRUE);
GridPane.setFillWidth(sliderMax, Boolean.TRUE);
box.prefWidthProperty().bind(pane.widthProperty());
box.setPadding(new Insets(5, 0, 5, 0));
GridPane.setHgrow(sliderMin, Priority.ALWAYS);
GridPane.setHgrow(sliderMax, Priority.ALWAYS);
// In the absence of a better way, make it possible to enter display range values
// manually by double-clicking on the corresponding label
labelMinValue.setOnMouseClicked(e -> {
if (e.getClickCount() == 2) {
ChannelDisplayInfo infoVisible = getCurrentInfo();
if (infoVisible == null)
return;
Double value = Dialogs.showInputDialog("Display range", "Set display range minimum", (double) infoVisible.getMinDisplay());
if (value != null && !Double.isNaN(value)) {
sliderMin.setValue(value);
// Update display directly if out of slider range
if (value < sliderMin.getMin() || value > sliderMin.getMax()) {
imageDisplay.setMinMaxDisplay(infoVisible, (float) value.floatValue(), (float) infoVisible.getMaxDisplay());
// infoVisible.setMinDisplay(value.floatValue());
// viewer.updateThumbnail();
updateSliders();
viewer.repaintEntireImage();
}
}
}
});
labelMaxValue.setOnMouseClicked(e -> {
if (e.getClickCount() == 2) {
ChannelDisplayInfo infoVisible = getCurrentInfo();
if (infoVisible == null)
return;
Double value = Dialogs.showInputDialog("Display range", "Set display range maximum", (double) infoVisible.getMaxDisplay());
if (value != null && !Double.isNaN(value)) {
sliderMax.setValue(value);
// Update display directly if out of slider range
if (value < sliderMax.getMin() || value > sliderMax.getMax()) {
imageDisplay.setMinMaxDisplay(infoVisible, (float) infoVisible.getMinDisplay(), (float) value.floatValue());
// infoVisible.setMaxDisplay(value.floatValue());
// viewer.updateThumbnail();
updateSliders();
viewer.repaintEntireImage();
}
}
}
});
Button btnAuto = new Button("Auto");
btnAuto.setOnAction(e -> {
if (imageDisplay == null)
return;
// if (histogram == null)
// return;
// // setSliders((float)histogram.getEdgeMin(), (float)histogram.getEdgeMax());
ChannelDisplayInfo info = getCurrentInfo();
double saturation = PathPrefs.autoBrightnessContrastSaturationPercentProperty().get() / 100.0;
imageDisplay.autoSetDisplayRange(info, saturation);
for (ChannelDisplayInfo info2 : table.getSelectionModel().getSelectedItems()) {
imageDisplay.autoSetDisplayRange(info2, saturation);
}
updateSliders();
handleSliderChange();
});
Button btnReset = new Button("Reset");
btnReset.setOnAction(e -> {
for (ChannelDisplayInfo info : table.getSelectionModel().getSelectedItems()) {
imageDisplay.setMinMaxDisplay(info, info.getMinAllowed(), info.getMaxAllowed());
}
sliderMin.setValue(sliderMin.getMin());
sliderMax.setValue(sliderMax.getMax());
});
Stage dialog = new Stage();
dialog.initOwner(qupath.getStage());
dialog.setTitle("Brightness & contrast");
// Create color/channel display table
table = new TableView<>(imageDisplay == null ? FXCollections.observableArrayList() : imageDisplay.availableChannels());
table.setPlaceholder(new Text("No channels available"));
table.addEventHandler(KeyEvent.KEY_PRESSED, new CopyTableListener());
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
table.getSelectionModel().selectedItemProperty().addListener((v, o, n) -> {
updateHistogram();
updateSliders();
});
TableColumn<ChannelDisplayInfo, ChannelDisplayInfo> col1 = new TableColumn<>("Color");
col1.setCellValueFactory(new Callback<CellDataFeatures<ChannelDisplayInfo, ChannelDisplayInfo>, ObservableValue<ChannelDisplayInfo>>() {
@Override
public ObservableValue<ChannelDisplayInfo> call(CellDataFeatures<ChannelDisplayInfo, ChannelDisplayInfo> p) {
return new SimpleObjectProperty<ChannelDisplayInfo>(p.getValue());
}
});
col1.setCellFactory(column -> {
return new TableCell<ChannelDisplayInfo, ChannelDisplayInfo>() {
@Override
protected void updateItem(ChannelDisplayInfo item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setGraphic(null);
return;
}
setText(item.getName());
Rectangle square = new Rectangle(0, 0, 10, 10);
Integer rgb = item.getColor();
if (rgb == null)
square.setFill(Color.TRANSPARENT);
else
square.setFill(ColorToolsFX.getCachedColor(rgb));
setGraphic(square);
}
};
});
col1.setSortable(false);
TableColumn<ChannelDisplayInfo, Boolean> col2 = new TableColumn<>("Selected");
col2.setCellValueFactory(new Callback<CellDataFeatures<ChannelDisplayInfo, Boolean>, ObservableValue<Boolean>>() {
@Override
public ObservableValue<Boolean> call(CellDataFeatures<ChannelDisplayInfo, Boolean> item) {
SimpleBooleanProperty property = new SimpleBooleanProperty(imageDisplay.selectedChannels().contains(item.getValue()));
// Remove repaint code here - now handled by table selection changes
property.addListener((v, o, n) -> {
imageDisplay.setChannelSelected(item.getValue(), n);
table.refresh();
Platform.runLater(() -> viewer.repaintEntireImage());
});
return property;
}
});
col2.setCellFactory(column -> {
CheckBoxTableCell<ChannelDisplayInfo, Boolean> cell = new CheckBoxTableCell<>();
// Select cells when clicked - means a click anywhere within the row forces selection.
// Previously, clicking within the checkbox didn't select the row.
cell.addEventFilter(MouseEvent.MOUSE_CLICKED, e -> {
if (e.isPopupTrigger())
return;
int ind = cell.getIndex();
var tableView = cell.getTableView();
if (ind < tableView.getItems().size()) {
if (e.isShiftDown())
tableView.getSelectionModel().select(ind);
else
tableView.getSelectionModel().clearAndSelect(ind);
var channel = cell.getTableRow().getItem();
// Handle clicks within the cell but outside the checkbox
if (e.getTarget() == cell && channel != null && imageDisplay != null) {
updateDisplay(channel, !imageDisplay.selectedChannels().contains(channel));
}
e.consume();
}
});
return cell;
});
col2.setSortable(false);
col2.setEditable(true);
col2.setResizable(false);
// Handle color change requests when an appropriate row is double-clicked
table.setRowFactory(tableView -> {
TableRow<ChannelDisplayInfo> row = new TableRow<>();
row.setOnMouseClicked(e -> {
if (e.getClickCount() == 2) {
ChannelDisplayInfo info = row.getItem();
var imageData = viewer.getImageData();
if (info instanceof DirectServerChannelInfo && imageData != null) {
DirectServerChannelInfo multiInfo = (DirectServerChannelInfo) info;
int c = multiInfo.getChannel();
var channel = imageData.getServer().getMetadata().getChannel(c);
Color color = ColorToolsFX.getCachedColor(multiInfo.getColor());
picker.setValue(color);
Dialog<ButtonType> colorDialog = new Dialog<>();
colorDialog.setTitle("Channel properties");
colorDialog.getDialogPane().getButtonTypes().setAll(ButtonType.APPLY, ButtonType.CANCEL);
var paneColor = new GridPane();
int r = 0;
var labelName = new Label("Channel name");
var tfName = new TextField(channel.getName());
labelName.setLabelFor(tfName);
PaneTools.addGridRow(paneColor, r++, 0, "Enter a name for the current channel", labelName, tfName);
var labelColor = new Label("Channel color");
labelColor.setLabelFor(picker);
PaneTools.addGridRow(paneColor, r++, 0, "Choose the color for the current channel", labelColor, picker);
paneColor.setVgap(5.0);
paneColor.setHgap(5.0);
colorDialog.getDialogPane().setContent(paneColor);
Optional<ButtonType> result = colorDialog.showAndWait();
if (result.orElse(ButtonType.CANCEL) == ButtonType.APPLY) {
// if (!DisplayHelpers.showMessageDialog("Choose channel color", picker))
// return;
String name = tfName.getText().trim();
if (name.isEmpty()) {
Dialogs.showErrorMessage("Set channel name", "The channel name must not be empty!");
return;
}
Color color2 = picker.getValue();
if (color == color2 && name.equals(channel.getName()))
return;
// Update the server metadata
int colorUpdated = ColorToolsFX.getRGB(color2);
if (imageData != null) {
var server = imageData.getServer();
var metadata = server.getMetadata();
var channels = new ArrayList<>(metadata.getChannels());
channels.set(c, ImageChannel.getInstance(name, colorUpdated));
var metadata2 = new ImageServerMetadata.Builder(metadata).channels(channels).build();
imageData.updateServerMetadata(metadata2);
}
// Update the display
multiInfo.setLUTColor((int) (color2.getRed() * 255), (int) (color2.getGreen() * 255), (int) (color2.getBlue() * 255));
// Add color property
imageDisplay.saveChannelColorProperties();
viewer.repaintEntireImage();
updateHistogram();
table.refresh();
}
}
}
});
return row;
});
table.getColumns().add(col1);
table.getColumns().add(col2);
table.setEditable(true);
table.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);
// Hack... space for a scrollbar
col1.prefWidthProperty().bind(table.widthProperty().subtract(col2.widthProperty()).subtract(25));
// col2.setResizable(false);
// col2.setMaxWidth(100);
// col2.setPrefWidth(50);
// col2.setResizable(false);
// col1.prefWidthProperty().bind(table.widthProperty().multiply(0.7));
// col2.prefWidthProperty().bind(table.widthProperty().multiply(0.2));
// table.getColumnModel().getColumn(0).setCellRenderer(new ChannelCellRenderer());
// table.getColumnModel().getColumn(1).setMaxWidth(table.getColumnModel().getColumn(1).getPreferredWidth());
BorderPane panelColor = new BorderPane();
// panelColor.setBorder(BorderFactory.createTitledBorder("Color display"));
BorderPane paneTableAndFilter = new BorderPane(table);
TextField tfFilter = new TextField("");
tfFilter.textProperty().bindBidirectional(filterText);
tfFilter.setTooltip(new Tooltip("Enter text to find specific channels by name"));
tfFilter.setPromptText("Filter channels by name");
paneTableAndFilter.setBottom(tfFilter);
predicate.addListener((v, o, n) -> updatePredicate());
panelColor.setCenter(paneTableAndFilter);
CheckBox cbShowGrayscale = new CheckBox("Show grayscale");
cbShowGrayscale.selectedProperty().bindBidirectional(showGrayscale);
cbShowGrayscale.setTooltip(new Tooltip("Show single channel with grayscale lookup table"));
if (imageDisplay != null)
cbShowGrayscale.setSelected(!imageDisplay.useColorLUTs());
showGrayscale.addListener(o -> {
if (imageDisplay == null)
return;
Platform.runLater(() -> viewer.repaintEntireImage());
table.refresh();
});
CheckBox cbKeepDisplaySettings = new CheckBox("Keep settings");
cbKeepDisplaySettings.selectedProperty().bindBidirectional(PathPrefs.keepDisplaySettingsProperty());
cbKeepDisplaySettings.setTooltip(new Tooltip("Retain same display settings where possible when opening similar images"));
FlowPane paneCheck = new FlowPane();
paneCheck.getChildren().add(cbShowGrayscale);
paneCheck.getChildren().add(cbKeepDisplaySettings);
paneCheck.setHgap(10);
paneCheck.setPadding(new Insets(5, 0, 0, 0));
panelColor.setBottom(paneCheck);
pane.setCenter(panelColor);
// Create brightness/contrast panel
BorderPane panelSliders = new BorderPane();
panelSliders.setTop(box);
GridPane panelButtons = PaneTools.createColumnGridControls(btnAuto, btnReset);
panelSliders.setBottom(panelButtons);
panelSliders.setPadding(new Insets(5, 0, 5, 0));
BorderPane panelMinMax = new BorderPane();
// panelMinMax.setBorder(BorderFactory.createTitledBorder("Brightness/Contrast"));
panelMinMax.setTop(panelSliders);
histogramPanel.setShowTickLabels(false);
histogramPanel.getChart().setAnimated(false);
// histogramPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
// panelMinMax.setCenter(histogramPanel.getChart());
panelMinMax.setCenter(chartWrapper.getPane());
chartWrapper.getPane().setPrefSize(200, 200);
// histogramPanel.getChart().setPrefSize(200, 200);
// histogramPanel.setPreferredSize(new Dimension(200, 120));
pane.setBottom(panelMinMax);
pane.setPadding(new Insets(10, 10, 10, 10));
Scene scene = new Scene(pane, 350, 500);
dialog.setScene(scene);
dialog.setMinWidth(300);
dialog.setMinHeight(400);
dialog.setMaxWidth(600);
dialog.setMaxHeight(800);
updateTable();
if (!table.getItems().isEmpty())
table.getSelectionModel().select(0);
updateDisplay(getCurrentInfo(), true);
updateHistogram();
updateSliders();
// Update sliders when receiving focus - in case the display has been updated elsewhere
dialog.focusedProperty().addListener((v, o, n) -> {
if (n)
updateSliders();
});
return dialog;
}
Aggregations