use of javafx.util.converter.DateTimeStringConverter in project qupath by qupath.
the class ViewTrackerAnalysisCommand method run.
@Override
public void run() {
if (dialog == null) {
dialog = new Stage();
dialog.sizeToScene();
dialog.initOwner(qupath.getStage());
dialog.setTitle("Recording analysis");
currentFrame.set(tracker.getFrame(0));
int nCols = nCols(tracker);
for (int i = 0; i < nCols; i++) {
final int col = i;
final String columnName = getColumnName(tracker, col);
TableColumn<ViewRecordingFrame, Object> column = new TableColumn<>(columnName);
column.setCellValueFactory(new Callback<CellDataFeatures<ViewRecordingFrame, Object>, ObservableValue<Object>>() {
@Override
public ObservableValue<Object> call(CellDataFeatures<ViewRecordingFrame, Object> frame) {
return new SimpleObjectProperty<>(getColumnValue(frame.getValue(), columnName));
}
});
table.getColumns().add(column);
}
table.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
table.getSelectionModel().selectedItemProperty().addListener((v, o, frame) -> {
if (frame != null)
currentFrame.set(frame);
});
refreshTracker();
playback.getCurrentFrame().addListener((v, o, frame) -> currentFrame.set(frame));
currentFrame.addListener((v, o, frame) -> {
if (frame == null)
return;
// Set viewer for frame
ViewTrackerPlayback.setViewerForFrame(viewer, frame);
// Set slide overview for frame
slideOverview.setVisibleShape(frame);
zSlider.setValue(frame.getZ());
tSlider.setValue(frame.getT());
timeSlider.setValue(frame.getTimestamp());
slideOverview.paintCanvas(o.getZ() != frame.getZ() || o.getT() != frame.getT(), false);
});
mainPane = new SplitPane();
mainPane.setDividerPositions(1.0);
BorderPane tablePane = new BorderPane();
tablePane.setCenter(table);
// ----------------------------------------------------------------------//
// ----------------- SLIDE OVERVIEW (TOP LEFT)------------------//
// ----------------------------------------------------------------------//
int z = server.getMetadata().getSizeZ();
int t = server.getMetadata().getSizeT();
tSlider = new Slider(0, t - 1, 0);
tSlider.setBlockIncrement(1);
tSlider.setValue(viewer.getTPosition());
tSlider.valueProperty().addListener((v, o, n) -> {
tSlider.setValue(n.intValue());
viewer.setTPosition(n.intValue());
slideOverview.paintCanvas();
});
zSlider = new Slider(0, z - 1, 0);
zSlider.setBlockIncrement(1);
zSlider.setMinorTickCount(0);
zSlider.setMajorTickUnit(1);
zSlider.setShowTickMarks(true);
zSlider.setValue(viewer.getZPosition());
zSlider.valueProperty().addListener((v, o, n) -> {
zSlider.setValue(n.intValue());
viewer.setZPosition(n.intValue());
slideOverview.paintCanvas();
});
zSlider.setOrientation(Orientation.VERTICAL);
var timeSliderLength = tracker.getLastTime() - tracker.getStartTime();
timeSlider = new Slider(0L, timeSliderLength, 0L);
timeSlider.setMinWidth(250.0);
if (timeSliderLength > 0) {
timeSlider.setMajorTickUnit(timeSliderLength / 4);
timeSlider.setMinorTickCount(0);
timeSlider.setShowTickMarks(true);
}
timeSlider.valueProperty().addListener((v, o, n) -> {
var frame = tracker.getFrameForTime(n.longValue());
currentFrame.set(frame);
if (table.getSelectionModel().getSelectedItem() != frame)
table.getSelectionModel().select(frame);
});
timeSlider.setOnMouseClicked(e -> {
playback.doStopPlayback();
});
long startTime = tracker.getStartTime();
long endTime = tracker.getLastTime();
Label timepointLabel = new Label();
timepointLabel.textProperty().bind(Bindings.createStringBinding(() -> "t=" + GeneralTools.formatNumber(tSlider.getValue(), 2), tSlider.valueProperty()));
Label zSliceLabel = new Label();
zSliceLabel.textProperty().bind(Bindings.createStringBinding(() -> "z=" + GeneralTools.formatNumber(zSlider.getValue(), 2), zSlider.valueProperty()));
Label timeLabelLeft = new Label();
timeLabelLeft.textProperty().bind(Bindings.createStringBinding(() -> ViewTrackerTools.getPrettyTimestamp(startTime, (long) timeSlider.getValue() + startTime), timeSlider.valueProperty()));
Label timeLabelRight = new Label();
timeLabelRight.textProperty().bind(Bindings.createStringBinding(() -> "-" + ViewTrackerTools.getPrettyTimestamp((long) timeSlider.getValue() + startTime, endTime), timeSlider.valueProperty()));
if (t == 1) {
tSlider.setVisible(false);
timepointLabel.setVisible(false);
}
if (z == 1) {
zSlider.setVisible(false);
zSliceLabel.setVisible(false);
}
Button btnPlay = new Button();
btnPlay.setGraphic(iconPlay);
btnPlay.setOnAction(e -> {
if (!playback.isPlaying()) {
// If it's not playing already, start playing
playback.setFirstFrame(currentFrame.get());
// Set the right slide overview image plane and visible shape for current frame
slideOverview.paintCanvas();
playback.doStartPlayback();
} else {
// If already playing, pause the playback where it currently is
playback.doStopPlayback();
}
});
Button btnStop = new Button();
btnStop.setGraphic(iconStop);
btnStop.setOnAction(e -> {
playback.doStopPlayback();
timeSlider.setValue(tracker.getFrame(0).getTimestamp());
});
// If we have a total of 0 or 1 frame in recording, disable playback
btnPlay.disableProperty().bind(new SimpleBooleanProperty(tracker.nFrames() <= 1));
btnStop.disableProperty().bind(new SimpleBooleanProperty(tracker.nFrames() <= 1));
playback.playingProperty().addListener((v, o, n) -> {
if (n) {
btnPlay.setGraphic(iconPause);
// btnPlay.setText("Pause");
zSlider.setDisable(true);
tSlider.setDisable(true);
} else {
btnPlay.setGraphic(iconPlay);
// btnPlay.setText("Play");
zSlider.setDisable(false);
tSlider.setDisable(false);
}
});
// --------------------------------------------------------------//
// ----------- DATA VISUALIZATION PANE ---------------//
// --------------------------------------------------------------//
visualizationCheckBox = new CheckBox("Enable data overlay");
// Label normalizedByLabel = new Label("Normalized by");
// Bound below in the Binding section
progressIndicator = new ProgressIndicator();
progressIndicator.setPrefSize(15, 15);
// ToggleGroup toggleGroup = new ToggleGroup();
// timeNormalizedRadio = new RadioButton("Time");
// timeNormalizedRadio.setSelected(true);
// timeNormalizedRadio.setToggleGroup(toggleGroup);
//
// downsampleNormalizedRadio = new RadioButton("Downsample");
// downsampleNormalizedRadio.setToggleGroup(toggleGroup);
// Create a color mapper ComboBox
colorMaps = FXCollections.observableArrayList(ColorMaps.getColorMaps().values());
ComboBox<ColorMap> colorMapCombo = new ComboBox<>(colorMaps);
colorMapCombo.setMinWidth(350);
colorMapCanvas = new ColorMapCanvas(10, colorMaps.get(0));
colorMapCombo.getSelectionModel().selectedItemProperty().addListener((v, o, n) -> colorMapCanvas.setColorMap(n));
if (colorMapCombo.getSelectionModel().isEmpty() && !colorMapCombo.getItems().isEmpty())
colorMapCombo.getSelectionModel().selectFirst();
colorMapCombo.setTooltip(new Tooltip("Select color map"));
// ------------------ TIME DISPLAYED RANGESLIDER ------------------//
Label timeDisplayedLeftLabel = new Label();
Label timeDisplayedRightLabel = new Label();
timeDisplayedSlider = new RangeSlider(0, timeSliderLength, 0, timeSliderLength);
timeDisplayedSlider.setMinWidth(250.0);
// Prompt input from user (min time)
timeDisplayedLeftLabel.setOnMouseClicked(e -> {
if (e.getButton().equals(MouseButton.PRIMARY) && e.getClickCount() == 2) {
GridPane gp = new GridPane();
TextField tf = new TextField();
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
try {
tf.setTextFormatter(new TextFormatter<>(new DateTimeStringConverter(format), format.parse("00:00:00")));
} catch (ParseException ex) {
logger.error("Error parsing the input time: ", ex.getLocalizedMessage());
}
gp.addRow(0, new Label("Enter time"), tf);
var response = Dialogs.showConfirmDialog("Set min time", gp);
if (response) {
long time = TimeUnit.HOURS.toMillis(Integer.parseInt(tf.getText(0, 2))) + TimeUnit.MINUTES.toMillis(Integer.parseInt(tf.getText(3, 5))) + TimeUnit.SECONDS.toMillis(Integer.parseInt(tf.getText(6, 8)));
timeDisplayedSlider.setLowValue(time);
updateOverlays();
}
}
});
// Prompt input from user (max time)
timeDisplayedRightLabel.setOnMouseClicked(e -> {
if (e.getButton().equals(MouseButton.PRIMARY) && e.getClickCount() == 2) {
GridPane gp = new GridPane();
TextField tf = new TextField();
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
try {
tf.setTextFormatter(new TextFormatter<>(new DateTimeStringConverter(format), format.parse(ViewTrackerTools.getPrettyTimestamp((long) timeDisplayedSlider.getHighValue()))));
} catch (ParseException ex) {
logger.error("Error parsing the input time: ", ex.getLocalizedMessage());
}
gp.addRow(0, new Label("Enter time"), tf);
var response = Dialogs.showConfirmDialog("Set max time", gp);
if (response) {
long time = ViewTrackerTools.getTimestampFromPrettyString(tf.getText());
timeDisplayedSlider.setHighValue(time);
updateOverlays();
}
}
});
timeDisplayedLeftLabel.textProperty().bind(Bindings.createStringBinding(() -> ViewTrackerTools.getPrettyTimestamp(startTime, (long) timeDisplayedSlider.getLowValue() + startTime), timeDisplayedSlider.lowValueProperty()));
timeDisplayedRightLabel.textProperty().bind(Bindings.createStringBinding(() -> ViewTrackerTools.getPrettyTimestamp(startTime, (long) timeDisplayedSlider.getHighValue() + startTime), timeDisplayedSlider.highValueProperty()));
// ------------------ DOWNSAMPLE RANGESLIDER ------------------//
List<Double> allFramesDownsamples = tracker.getAllFrames().stream().map(e -> e.getDownsampleFactor()).collect(Collectors.toList());
var minDownsample = allFramesDownsamples.stream().min(Comparator.naturalOrder()).get();
var maxDownsample = allFramesDownsamples.stream().max(Comparator.naturalOrder()).get();
downsampleSlider = new RangeSlider(minDownsample, maxDownsample, minDownsample, maxDownsample);
Label downsampleLeftLabel = new Label();
Label downsampleRightLabel = new Label();
// Prompt input from user (min downsample)
downsampleLeftLabel.setOnMouseClicked(e -> {
if (e.getButton().equals(MouseButton.PRIMARY) && e.getClickCount() == 2) {
ParameterList params = new ParameterList();
params.addDoubleParameter("downsampleFilterLow", "Enter downsample", downsampleSlider.getLowValue());
if (!Dialogs.showParameterDialog("Set min downsample", params))
return;
double downFactor = params.getDoubleParameterValue("downsampleFilterLow");
downsampleSlider.setLowValue(downFactor);
updateOverlays();
}
});
// Prompt input from user (max downsample)
downsampleRightLabel.setOnMouseClicked(e -> {
if (e.getButton().equals(MouseButton.PRIMARY) && e.getClickCount() == 2) {
ParameterList params = new ParameterList();
params.addDoubleParameter("downsampleFilterHigh", "Enter downsample", downsampleSlider.getHighValue());
if (!Dialogs.showParameterDialog("Set max downsample", params))
return;
double downFactor = params.getDoubleParameterValue("downsampleFilterHigh");
downsampleSlider.setHighValue(downFactor);
updateOverlays();
}
});
downsampleLeftLabel.textProperty().bind(Bindings.createStringBinding(() -> GeneralTools.formatNumber(downsampleSlider.getLowValue(), 2), downsampleSlider.lowValueProperty()));
downsampleRightLabel.textProperty().bind(Bindings.createStringBinding(() -> GeneralTools.formatNumber(downsampleSlider.getHighValue(), 2), downsampleSlider.highValueProperty()));
// TODO: if using keys to change sliders, nothing will trigger the overlay update
dataMaps = new ViewTrackerDataMaps(server, tracker);
timeDisplayedSlider.setOnMouseReleased(v -> updateOverlays());
downsampleSlider.setOnMouseReleased(v -> updateOverlays());
colorMapCanvas.colorMapProperty().addListener((v, o, n) -> updateOverlays());
progressIndicator.visibleProperty().bind(dataMaps.generatingOverlayProperty());
visualizationCheckBox.selectedProperty().addListener((v, o, n) -> {
if (n)
updateOverlays();
else {
viewer.getCustomOverlayLayers().clear();
slideOverview.setOverlay(null);
}
});
// timeNormalizedRadio.selectedProperty().addListener((v, o, n) -> updateOverlays());
// --------------------------------------------------------------//
// ---------- PUTTING EVERYTHING TOGETHER ---------//
// --------------------------------------------------------------//
GridPane mainLeftPane = new GridPane();
GridPane slideOverviewPane = new GridPane();
GridPane dataVisualizationPane = new GridPane();
mainLeftPane.setPadding(new Insets(5.0, 5.0, 5.0, 15.0));
slideOverviewPane.setPadding(new Insets(5.0, 5.0, 5.0, 5.0));
dataVisualizationPane.setPadding(new Insets(5.0, 5.0, 15.0, 5.0));
mainLeftPane.addRow(0, slideOverviewPane);
mainLeftPane.addRow(1, dataVisualizationPane);
GridPane canvasPane = new GridPane();
GridPane timelinePane = new GridPane();
GridPane playbackPane = new GridPane();
// GridPane normalizedByPane = new GridPane();
GridPane rangeSlidersPane = new GridPane();
GridPane downsamplePane = new GridPane();
Separator sep = new Separator();
slideOverviewPane.setHgap(10);
slideOverviewPane.setVgap(10);
dataVisualizationPane.setHgap(10);
dataVisualizationPane.setVgap(10);
playbackPane.setHgap(10.0);
// normalizedByPane.setHgap(10.0);
rangeSlidersPane.setHgap(10.0);
rangeSlidersPane.setVgap(10.0);
downsamplePane.setHgap(10.0);
// Some 'invisible' elements might shift the canvas to the right, so make sure this doesn't happen
if (zSlider.isVisible()) {
canvasPane.addRow(0, new HBox(), timepointLabel, tSlider);
canvasPane.addRow(1, zSliceLabel, zSlider, slideOverview.getCanvas());
zSlider.setTooltip(new Tooltip("Slide to change the visible z-slice"));
} else {
canvasPane.addRow(0, timepointLabel, tSlider, new HBox());
canvasPane.addRow(1, new HBox(), slideOverview.getCanvas());
}
if (tSlider.isVisible())
tSlider.setTooltip(new Tooltip("Slide to change the visible timepoint"));
// normalizedByPane.addRow(0, timeNormalizedRadio, downsampleNormalizedRadio, progressIndicator);
// normalizedByPane.addRow(0, progressIndicator);
timelinePane.addRow(0, timeLabelLeft, timeSlider, timeLabelRight);
playbackPane.addRow(0, btnPlay, btnStop);
timelinePane.setAlignment(Pos.CENTER);
playbackPane.setAlignment(Pos.CENTER);
canvasPane.setAlignment(Pos.CENTER);
btnPlay.setTooltip(new Tooltip("Play the recording"));
btnStop.setTooltip(new Tooltip("Rewind the recording"));
int row = 0;
PaneTools.addGridRow(slideOverviewPane, row++, 0, null, new HBox(), canvasPane);
PaneTools.addGridRow(slideOverviewPane, row++, 0, null, timeLabelLeft, timeSlider, timeLabelRight);
PaneTools.addGridRow(slideOverviewPane, row++, 0, "Playback options", new HBox(), playbackPane);
PaneTools.addGridRow(slideOverviewPane, row++, 0, null, sep, sep, sep, sep);
row = 0;
PaneTools.addGridRow(dataVisualizationPane, row++, 0, "Show the amount of time spent in each region of the image", visualizationCheckBox, progressIndicator);
PaneTools.addGridRow(dataVisualizationPane, row++, 0, "The data will only take into account the values recorded in-between this range", new Label("Time range"), timeDisplayedLeftLabel, timeDisplayedSlider, timeDisplayedRightLabel);
PaneTools.addGridRow(dataVisualizationPane, row++, 0, "The data will only take into account the values recorded in-between this range", new Label("Downsample range"), downsampleLeftLabel, downsampleSlider, downsampleRightLabel);
PaneTools.addGridRow(dataVisualizationPane, row++, 0, "Color map", new Label("Color map"), colorMapCombo, colorMapCombo, colorMapCombo);
PaneTools.addGridRow(dataVisualizationPane, row++, 0, null, colorMapCanvas, colorMapCanvas, colorMapCanvas, colorMapCanvas);
for (Node child : dataVisualizationPane.getChildren()) {
if (child == visualizationCheckBox)
continue;
if (child == downsampleSlider)
child.disableProperty().bind(visualizationCheckBox.selectedProperty().not().or(new SimpleBooleanProperty(minDownsample == maxDownsample)));
else
// else if (child == downsampleNormalizedRadio)
// child.disableProperty().bind(visualizationCheckBox.selectedProperty().not().or(new SimpleBooleanProperty(minDownsample == maxDownsample)));
child.disableProperty().bind(visualizationCheckBox.selectedProperty().not());
}
ColumnConstraints col1 = new ColumnConstraints();
ColumnConstraints col2 = new ColumnConstraints();
ColumnConstraints col3 = new ColumnConstraints();
ColumnConstraints col4 = new ColumnConstraints();
col2.setHgrow(Priority.ALWAYS);
slideOverviewPane.getColumnConstraints().addAll(col1, col2, col3, col4);
// --------------------- BOTTOM BUTTON PANE---------------------//
Button btnExpand = new Button("Show frames");
Button btnOpen = new Button("Open directory");
btnOpen.setDisable(tracker.getFile() == null);
List<Button> buttons = new ArrayList<>();
btnOpen.setOnAction(e -> GuiTools.browseDirectory(tracker.getFile()));
btnExpand.setOnAction(e -> {
if (btnExpand.getText().equals("Show frames")) {
dialog.setWidth(initialWidth + 700);
dialog.setResizable(true);
dialog.setMinWidth(initialWidth + 50);
dialog.setMinHeight(initialHeight);
mainLeftPane.setMinWidth(initialWidth);
mainLeftPane.setMaxWidth(initialWidth);
mainPane.getItems().add(tablePane);
mainPane.setDividerPositions(0.0);
btnExpand.setText("Hide frames");
} else {
mainPane.getItems().remove(tablePane);
btnExpand.setText("Show frames");
dialog.setWidth(initialWidth + 30);
dialog.setHeight(initialHeight);
dialog.setResizable(false);
}
});
buttons.add(btnOpen);
buttons.add(btnExpand);
GridPane panelButtons = PaneTools.createColumnGridControls(buttons.toArray(new ButtonBase[0]));
PaneTools.addGridRow(mainLeftPane, row++, 0, null, panelButtons, panelButtons, panelButtons);
mainPane.getItems().add(mainLeftPane);
dialog.setScene(new Scene(mainPane));
}
viewer.imageDataProperty().addListener((v, o, n) -> dialog.hide());
dialog.setOnHiding(e -> {
viewer.getCustomOverlayLayers().clear();
playback.doStopPlayback();
isOpenedProperty.set(false);
});
dialog.setResizable(false);
dialog.show();
initialWidth = dialog.getWidth();
initialHeight = dialog.getHeight();
}
Aggregations