High refreshing rate in JavaFX - javafx

I'm trying to write a program with an equalizer, a frequency analyzer and a sound level meter. The model part seems to work very well but I'm experimenting some bugs with the IHM.
My last bug is with the level meter. After a while (from few milliseconds to few seconds), it freezes and don't update anymore. So, here is a (simplified) version of it. I added the runnable part to test and reproduce the bug. Of course, this bug appears sooner when I add other graphical components which also need to refresh very frequently. For example, the frequency analyze is represented by a line-chart with something like 1000 points.
public class LevelMeter2 extends Parent implements Runnable {
private IntegerProperty levelMeterHeight = new SimpleIntegerProperty();
private Rectangle led;
private IntegerProperty height = new SimpleIntegerProperty();
private IntegerProperty width = new SimpleIntegerProperty();
private DoubleProperty linearValue = new SimpleDoubleProperty();
private Color backgroundColor=Color.BLACK;
private double minLinearValue, maxLinearValue;
public LevelMeter2 (int height2, int width2) {
this.height.set(height2);
this.levelMeterHeight.bind(height.multiply(0.9));
this.width.set(width2);
linearValue.set(1.0);
minLinearValue = Math.pow(10, -60.0/100);
maxLinearValue = Math.pow(10, 3.0/100)-minLinearValue;
Rectangle levelMeterShape = new Rectangle();
levelMeterShape.widthProperty().bind(width);
levelMeterShape.heightProperty().bind(height);
levelMeterShape.setStroke(backgroundColor);
this.getChildren().add(levelMeterShape);
led = new Rectangle();
led.widthProperty().bind(width.multiply(0.8));
led.translateXProperty().bind(width.multiply(0.1));
led.heightProperty().bind(levelMeterHeight.multiply(linearValue));
led.setFill(Color.AQUA);
Rotate rotate = new Rotate();
rotate.pivotXProperty().bind(width.multiply(0.8).divide(2));
rotate.pivotYProperty().bind(height.divide(2));
rotate.setAngle(180);
led.getTransforms().add(rotate);
this.getChildren().add(led);
}
public double convertdBToLinearValue (double dB) {
return ((double)Math.round(100 * ((Math.pow(10, dB/100)-minLinearValue)/maxLinearValue)) ) /100 ;
//return (Math.pow(10, dB/100)-minLinearValue)/maxLinearValue;
}
public double convertLinearValueTodB (double linearValue) {
return 100*Math.log10(linearValue*maxLinearValue+minLinearValue);
}
public void setValue (double dB) {
if (dB>3) {
dB=3;
}
linearValue.setValue(convertdBToLinearValue(dB));
}
#Override
public void run() {
int i = 0;
double value=-20;
while (i<1000) {
setValue(value);
value = (Math.random()-0.5)*10+value;
if (value>3) {
value=3;
}
if (value<-60) {
value=-60;
}
i++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("END OF WHILE");
}
}
And a "Main" to test it :
public class MainGraph extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
HBox pane = new HBox();
LevelMeter2 levelMeter = new LevelMeter2(300,30);
Thread t = new Thread(levelMeter);
pane.getChildren().add(levelMeter);
t.start();
Scene scene = new Scene(pane, 300, 300);
primaryStage.setScene(scene);
primaryStage.setTitle("Test IHM");
primaryStage.setOnCloseRequest( event -> {
System.out.println("FIN");
System.exit(0);
});
primaryStage.show();
}
}
What's wrong with my code ? How can I write a more robust code that will allow me high refresh rates of my IHM ? Or how can I prevent from freezing ?
Thank you for you help.

I would suggest you move away from Threads and use something from JavaFX Animation package. In this example Timeline is used. This code is set to run at a rate of about 60 fps. You can adjust that using Duration.millis().
Main
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication342 extends Application
{
#Override
public void start(Stage primaryStage)
{
LevelMeter2 levelMeter = new LevelMeter2(300, 30);
Button button = new Button("Start");
button.setOnAction((event) -> {
switch (button.getText()) {
case "Start":
levelMeter.startAnimation();
button.setText("Stop");
break;
case "Stop":
levelMeter.stopAnimation();
button.setText("Start");
break;
}
});
HBox pane = new HBox(levelMeter, button);
Scene scene = new Scene(pane, 300, 300);
primaryStage.setScene(scene);
primaryStage.setTitle("Test IHM");
primaryStage.setOnCloseRequest(event -> {
System.out.println("FIN");
System.exit(0);
});
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
LevelMeter2
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Parent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Rotate;
import javafx.util.Duration;
public final class LevelMeter2 extends Parent
{
private final IntegerProperty levelMeterHeight = new SimpleIntegerProperty();
Timeline timeline;
double value = -20;
private final Rectangle led;
private final IntegerProperty height = new SimpleIntegerProperty();
private final IntegerProperty width = new SimpleIntegerProperty();
private final DoubleProperty linearValue = new SimpleDoubleProperty();
private final Color backgroundColor = Color.BLACK;
private final double minLinearValue;
private final double maxLinearValue;
public LevelMeter2(int height2, int width2)
{
this.height.set(height2);
this.levelMeterHeight.bind(height.multiply(0.9));
this.width.set(width2);
linearValue.set(1.0);
minLinearValue = Math.pow(10, -60.0 / 100);
maxLinearValue = Math.pow(10, 3.0 / 100) - minLinearValue;
Rectangle levelMeterShape = new Rectangle();
levelMeterShape.widthProperty().bind(width);
levelMeterShape.heightProperty().bind(height);
levelMeterShape.setStroke(backgroundColor);
this.getChildren().add(levelMeterShape);
led = new Rectangle();
led.widthProperty().bind(width.multiply(0.8));
led.translateXProperty().bind(width.multiply(0.1));
led.heightProperty().bind(levelMeterHeight.multiply(linearValue));
led.setFill(Color.AQUA);
Rotate rotate = new Rotate();
rotate.pivotXProperty().bind(width.multiply(0.8).divide(2));
rotate.pivotYProperty().bind(height.divide(2));
rotate.setAngle(180);
led.getTransforms().add(rotate);
getChildren().add(led);
timeline = new Timeline(new KeyFrame(Duration.millis(16), (event) -> {
setValue(value);
value = (Math.random() - 0.5) * 10 + value;
if (value > 3) {
value = 3;
}
if (value < -60) {
value = -60;
}
}));
timeline.setCycleCount(Timeline.INDEFINITE);
}
public double convertdBToLinearValue(double dB)
{
return ((double) Math.round(100 * ((Math.pow(10, dB / 100) - minLinearValue) / maxLinearValue))) / 100;
}
public double convertLinearValueTodB(double linearValue)
{
return 100 * Math.log10(linearValue * maxLinearValue + minLinearValue);
}
public void setValue(double dB)
{
if (dB > 3) {
dB = 3;
}
linearValue.setValue(convertdBToLinearValue(dB));
}
public void startAnimation()
{
timeline.play();
}
public void stopAnimation()
{
timeline.stop();
}
}
Multiple LevelMeters Example:
Main
import java.util.ArrayList;
import java.util.List;
import javafx.animation.ParallelTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication342 extends Application
{
#Override
public void start(Stage primaryStage)
{
List<LevelMeter2> levelMeter2s = new ArrayList();
List<Timeline> metersTimelines = new ArrayList();
for (int i = 0; i < 9; i++) {
LevelMeter2 levelMeter2 = new LevelMeter2(300, 30);
levelMeter2s.add(levelMeter2);
metersTimelines.add(levelMeter2.getTimeline());
}
ParallelTransition parallelTransition = new ParallelTransition();
parallelTransition.getChildren().addAll(metersTimelines);
Button button = new Button("Start");
button.setOnAction((event) -> {
switch (button.getText()) {
case "Start":
parallelTransition.play();
button.setText("Stop");
break;
case "Stop":
parallelTransition.stop();
button.setText("Start");
break;
}
});
HBox hBox = new HBox();
hBox.getChildren().addAll(levelMeter2s);
VBox vBox = new VBox(hBox, new StackPane(button));
Scene scene = new Scene(vBox, 300, 350);
primaryStage.setScene(scene);
primaryStage.setTitle("Test IHM");
primaryStage.setOnCloseRequest(event -> {
System.out.println("FIN");
System.exit(0);
});
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
LevelMeter2
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Parent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Rotate;
import javafx.util.Duration;
public final class LevelMeter2 extends Parent
{
private final IntegerProperty levelMeterHeight = new SimpleIntegerProperty();
Timeline timeline;
double value = -20;
private final Rectangle led;
private final IntegerProperty height = new SimpleIntegerProperty();
private final IntegerProperty width = new SimpleIntegerProperty();
private final DoubleProperty linearValue = new SimpleDoubleProperty();
private final Color backgroundColor = Color.BLACK;
private final double minLinearValue;
private final double maxLinearValue;
public LevelMeter2(int height2, int width2)
{
this.height.set(height2);
this.levelMeterHeight.bind(height.multiply(0.9));
this.width.set(width2);
linearValue.set(1.0);
minLinearValue = Math.pow(10, -60.0 / 100);
maxLinearValue = Math.pow(10, 3.0 / 100) - minLinearValue;
Rectangle levelMeterShape = new Rectangle();
levelMeterShape.widthProperty().bind(width);
levelMeterShape.heightProperty().bind(height);
levelMeterShape.setStroke(backgroundColor);
this.getChildren().add(levelMeterShape);
led = new Rectangle();
led.widthProperty().bind(width.multiply(0.8));
led.translateXProperty().bind(width.multiply(0.1));
led.heightProperty().bind(levelMeterHeight.multiply(linearValue));
led.setFill(Color.AQUA);
Rotate rotate = new Rotate();
rotate.pivotXProperty().bind(width.multiply(0.8).divide(2));
rotate.pivotYProperty().bind(height.divide(2));
rotate.setAngle(180);
led.getTransforms().add(rotate);
getChildren().add(led);
timeline = new Timeline(new KeyFrame(Duration.millis(25), (event) -> {
setValue(value);
value = (Math.random() - 0.5) * 10 + value;
if (value > 3) {
value = 3;
}
if (value < -60) {
value = -60;
}
}));
timeline.setCycleCount(Timeline.INDEFINITE);
}
public double convertdBToLinearValue(double dB)
{
return ((double) Math.round(100 * ((Math.pow(10, dB / 100) - minLinearValue) / maxLinearValue))) / 100;
}
public double convertLinearValueTodB(double linearValue)
{
return 100 * Math.log10(linearValue * maxLinearValue + minLinearValue);
}
public void setValue(double dB)
{
if (dB > 3) {
dB = 3;
}
linearValue.setValue(convertdBToLinearValue(dB));
}
public void startAnimation()
{
timeline.play();
}
public void stopAnimation()
{
timeline.stop();
}
public Timeline getTimeline()
{
return timeline;
}
}

Your implementation of run() appears to be updating the scene graph from a background thread. As discussed in Concurrency in JavaFX:
The JavaFX scene graph…is not thread-safe and can only be accessed and modified from the UI thread also known as the JavaFX Application thread. Implementing long-running tasks on the JavaFX Application thread inevitably makes an application UI unresponsive."
Instead, use a Task, illustrated here and here. Your implementation of call() can collect data asynchronously and notify the GUI of the current state via updateValue(). Your valueProperty() listener can then invoke setValue() safely. Because "Updates are coalesced to prevent saturation of the FX event queue," your application will perform satisfactorily even on older hardware.
Alternatively, if your audio source is one of the supported Media types, AudioBarChartApp, also seen here, updates the data model of a BarChart in an AudioSpectrumListener registered with the corresponding MediaPlayer. The image below displays pink noise.
private XYChart.Data<String, Number>[] series1Data;
…
audioSpectrumListener = (double timestamp, double duration,
float[] magnitudes, float[] phases) -> {
for (int i = 0; i < series1Data.length; i++) {
series1Data[i].setYValue(magnitudes[i] + 60);
}
};

Related

javaFX: MediaPlayer's seek method hangs the player without error message oder Status change

I have a programm with 8 mediaplayer, which are controlled like one big video with a single set of controls.
I have one Slider to control the time, aka I call all MediaPlayer's seek methods in onMouseReleased of the slider. My Problem is, the mediaplayer hang all the time, without changing their status or calling onError . When I put every seek in a new Thread, these problems disappear msot of the time, but not always and I'm gettign a lot of concurrency issues.
Does anybody know the reason why the player hangs?
EDIT:
Ok, here a minimal reproducible example:
Class MediaControlMinimal:
package org.example;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Slider;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.stage.Window;
import javafx.util.Duration;
import java.util.ArrayList;
import java.util.List;
public class MediaControlMinimal extends BorderPane {
private final List<MediaPlayer> mpList;
private Duration duration;
private boolean shouldPlay;
private int setupComplete;
private final Slider timeSlider;
private final HBox mediaBar;
private final Button playButton;
private final MediaPlayer controlMediaPlayer;
public MediaControlMinimal(List<MediaPlayer> mpList, int videoRows) {
this.mpList = mpList;
List<MediaView> mvList = new ArrayList<>(mpList.size());
Pane mvPane = new Pane() {
};
for (MediaPlayer mp : mpList) {
MediaView mediaView = new MediaView(mp);
mvList.add(mediaView);
mvPane.getChildren().add(mediaView);
}
mvPane.setStyle("-fx-background-color: black;");
setCenter(mvPane);
mediaBar = new HBox(); // 5 als param für spacing = 5, sieh zeile 247
mediaBar.setAlignment(Pos.CENTER);
mediaBar.setPadding(new Insets(5, 10, 5, 10));
BorderPane.setAlignment(mediaBar, Pos.CENTER);
playButton = new Button();
playButton.setOnAction(e -> {
shouldPlay = !shouldPlay;
if (shouldPlay) {
playAll();
} else {
pauseAll();
}
});
// Add time slider
timeSlider = new Slider();
HBox.setHgrow(timeSlider, Priority.ALWAYS);
timeSlider.setMinWidth(50);
timeSlider.setMaxWidth(Double.MAX_VALUE);
timeSlider.setOnMouseReleased(event -> {
for (MediaPlayer mp : mpList) {
mp.seek(Duration.millis((event.getX() / timeSlider.getWidth()) * timeSlider.getMax());
}
});
controlMediaPlayer = mpList.get(1);
controlMediaPlayer.currentTimeProperty().addListener(observable -> {
updateValues(controlMediaPlayer);
});
for (MediaPlayer mp : mpList) {
mp.setOnReady(() -> {
int videosPerRow = mpList.size() / videoRows;
if (setupComplete == 0) {
duration = mp.getMedia().getDuration();
timeSlider.setMax(duration.toMillis());
updateValues(mp);
final Window window = mvPane.getScene().getWindow();
final double titleHeight = window.getHeight() - mvPane.getScene().getHeight();
double windowHeight = videoRows * mp.getMedia().getHeight() + titleHeight;
if (!Main.isTransDesign) {
windowHeight += mediaBar.getHeight();
}
window.setHeight(windowHeight);
window.setWidth(videosPerRow * mp.getMedia().getWidth());
}
if (setupComplete < mpList.size()) {
final Node mpNode = mvPane.getChildren().get(mpList.indexOf(mp));
if (mpList.indexOf(mp) != 0 && mpNode.getLayoutX() == 0 && mpNode.getLayoutY() == 0) {
//fenster höhe
double xRelocate = mp.getMedia().getWidth() * (mpList.indexOf(mp) % videosPerRow);
double yRelocate = mp.getMedia().getHeight() * Math.floorDiv(mpList.indexOf(mp), videosPerRow);
mpNode.relocate(xRelocate, yRelocate);
}
++setupComplete;
}
});
mp.setCycleCount(MediaPlayer.INDEFINITE);
}
mediaBar.getChildren().add(playButton);
mediaBar.getChildren().add(timeSlider);
setBottom(mediaBar);
}
private void playAll() {
for (MediaPlayer mp : mpList) {
mp.play();
}
}
private void pauseAll() {
for (MediaPlayer mp : mpList) {
mp.pause();
}
}
protected void updateValues(MediaPlayer mp) {
if (timeSlider != null) {
Platform.runLater(() -> {
Duration currentTime = mp.getCurrentTime();
timeSlider.setDisable(duration.isUnknown());
if (!timeSlider.isDisabled() && duration.greaterThan(Duration.ZERO) && !timeSlider.isValueChanging()) {
timeSlider.setValue(currentTime.toMillis());
}
});
}
}
}
Class EmbeddedMediaPlayer
package org.example;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
public class EmbeddedMediaPlayer extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle(Main.WINDOW_TITLE);
Group root = new Group();
Scene scene = new Scene(root, 1586, 900);
List<MediaPlayer> mediaPlayerList = new ArrayList<>();
// create media player
for(String s : Main.MEDIA_URL){
MediaPlayer mediaPlayer = new MediaPlayer(new Media(s));
mediaPlayer.setAutoPlay(false);
mediaPlayerList.add(mediaPlayer);
}
MediaControlMinimal mediaControl = new MediaControlMinimal(mediaPlayerList, Main.VIDEO_ROWS);
scene.setRoot(mediaControl);
primaryStage.setScene(scene);
primaryStage.show();
}
#Override
public void stop(){
System.out.println("Stage is closing");
System.exit(0);
}
/**
* The main() method is ignored in correctly deployed JavaFX application.
* main() serves only as fallback in case the application can not be
* launched through deployment artifacts, e.g., in IDEs with limited FX
* support. NetBeans ignores main().
*
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Class Main
package org.example;
public class Main {
public static final String[] MEDIA_URL = {
"https://video.fogodosamba.de/media/SambaReggae_Sticks.mp4",
"https://video.fogodosamba.de/media/SambaReggae_Fundo1.mp4",
"https://video.fogodosamba.de/media/SambaReggae_Dobra.mp4",
"https://video.fogodosamba.de/media/SambaReggae_Fundo2.mp4",
"https://video.fogodosamba.de/media/SambaReggae_Ansage.mp4",
"https://video.fogodosamba.de/media/SambaReggae_Timbal.mp4",
"https://video.fogodosamba.de/media/SambaReggae_Caixa.mp4",
"https://video.fogodosamba.de/media/SambaReggae_Repi.mp4"
};
public static final int VIDEO_ROWS = 2;
public static final String WINDOW_TITLE = "";
public static void main(String[] args) {
EmbeddedMediaPlayer.main(args);
}
}
EDIT 2:
Sometimes a video hsngs for a while, then starts up again, but plays in slow motion. No error message in onerror, no state change and getCurrentRate = 1.

JavaFX position dialog and stage in center of screen

The following codes demonstrates centering of a dialog and the stage in the center of the screen. The dialog is supposed to be displayed first for the user to enter the login credentials. After successful login, the main window (stage) is then displayed. I found the solution of centering the dialog and stage from this web site, but it doesn't seem very ideal. For both the dialog and stage, they have to be displayed first before we can calculate the coordinates and then positioning them in the center. This means that we can see the dialog and the main window moving to the center after they are displayed. Is there a better way? Ideally, they should be positioned in the center before they are displayed.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.Window;
public class Demo extends Application {
private Stage primaryStage;
private Dialog<String> dialog;
private Button createUserButton = new Button("Create User");
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
Text usersLabel = new Text("Current Users:");
TableColumn<User, String> indexColumn = new TableColumn<User, String>("No.");
indexColumn.setMaxWidth(1f * Integer.MAX_VALUE * 10);
indexColumn.setCellValueFactory(p -> p.getValue().indexProperty());
TableColumn<User, String> userNameColumn = new TableColumn<User, String>("User Name");
userNameColumn.setMaxWidth(1f * Integer.MAX_VALUE * 60);
userNameColumn.setCellValueFactory(p -> p.getValue().userNameProperty());
TableColumn<User, String> roleColumn = new TableColumn<User, String>("Role");
roleColumn.setMaxWidth(1f * Integer.MAX_VALUE * 30);
roleColumn.setCellValueFactory(p -> p.getValue().roleProperty());
TableView<User> tableView = new TableView<User>();
tableView.getColumns().add(indexColumn);
tableView.getColumns().add(userNameColumn);
tableView.getColumns().add(roleColumn);
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
Text dummyLabel = new Text("");
VBox leftPane = new VBox(5);
leftPane.getChildren().addAll(usersLabel, tableView);
VBox rightPane = new VBox(20);
rightPane.setFillWidth(true);
rightPane.getChildren().addAll(dummyLabel, createUserButton);
GridPane mainPane = new GridPane();
mainPane.setPadding(new Insets(10, 0, 0, 10));
mainPane.setHgap(20);
mainPane.add(leftPane, 0, 0);
mainPane.add(rightPane, 1, 0);
Scene scene = new Scene(mainPane);
primaryStage.setScene(scene);
primaryStage.setResizable(false);
showDialog();
}
private void showDialog() {
dialog = new Dialog<>();
dialog.setTitle("Login");
dialog.setHeaderText("Please enter User Name and Password to login.");
dialog.setResizable(false);
Label userNameLabel = new Label("User Name:");
Label passwordLabel = new Label("Password:");
TextField userNameField = new TextField();
PasswordField passwordField = new PasswordField();
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(20, 35, 20, 35));
grid.add(userNameLabel, 1, 1);
grid.add(userNameField, 2, 1);
grid.add(passwordLabel, 1, 2);
grid.add(passwordField, 2, 2);
dialog.getDialogPane().setContent(grid);
dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
Button okButton = (Button) dialog.getDialogPane().lookupButton(ButtonType.OK);
okButton.addEventFilter(ActionEvent.ACTION, event -> {
createUser(userNameField.getText().trim(), passwordField.getText());
event.consume();
});
dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
Platform.runLater(() -> {
Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
Window window = dialog.getDialogPane().getScene().getWindow();
window.setX((screenBounds.getWidth() - window.getWidth()) / 2);
window.setY((screenBounds.getHeight() - window.getHeight()) / 2);
});
dialog.showAndWait();
}
private void createUser(String userName, String password) {
dialog.getDialogPane().setDisable(true);
dialog.getDialogPane().getScene().setCursor(Cursor.WAIT);
Task<Boolean> task = new Task<Boolean>() {
#Override
public Boolean call() {
try {
Thread.sleep(100);
} catch (InterruptedException exception) {
}
return Boolean.TRUE;
}
};
task.setOnSucceeded(e -> {
Boolean success = task.getValue();
dialog.getDialogPane().setDisable(false);
dialog.getDialogPane().getScene().setCursor(Cursor.DEFAULT);
if (success.booleanValue()) {
Platform.runLater(() -> {
dialog.close();
primaryStage.show();
Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
primaryStage.setX((screenBounds.getWidth() - primaryStage.getWidth()) / 2);
primaryStage.setY((screenBounds.getHeight() - primaryStage.getHeight()) / 2);
});
} else {
Alert alert = new Alert(AlertType.ERROR);
alert.setTitle("Login Error");
alert.setHeaderText("Unable to login.");
alert.showAndWait();
}
});
new Thread(task).start();
}
public static void main(String[] arguments) {
Application.launch(arguments);
}
}
class User {
private StringProperty index;
private StringProperty userName;
private StringProperty role;
public String getIndex() {
return indexProperty().get();
}
public StringProperty indexProperty() {
if (index == null) {
index = new SimpleStringProperty(this, "index");
}
return index;
}
public void setIndex(String index) {
indexProperty().set(index);
}
public String getUserName() {
return userNameProperty().get();
}
public StringProperty userNameProperty() {
if (userName == null) {
userName = new SimpleStringProperty(this, "userName");
}
return userName;
}
public void setUserName(String userName) {
userNameProperty().set(userName);
}
public String getRole() {
return roleProperty().get();
}
public StringProperty roleProperty() {
if (role == null) {
role = new SimpleStringProperty(this, "role");
}
return role;
}
public void setRole(String role) {
roleProperty().set(role);
}
}
Below is solution by setting custom dimensions to stage and dialog. It works for the stage but it doesn't work for the dialog.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
public class Demo extends Application {
private Stage primaryStage;
private Dialog<String> dialog;
private Button createUserButton = new Button("Create User");
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
Text usersLabel = new Text("Current Users:");
TableColumn<User, String> indexColumn = new TableColumn<User, String>("No.");
indexColumn.setMaxWidth(1f * Integer.MAX_VALUE * 10);
indexColumn.setCellValueFactory(p -> p.getValue().indexProperty());
TableColumn<User, String> userNameColumn = new TableColumn<User, String>("User Name");
userNameColumn.setMaxWidth(1f * Integer.MAX_VALUE * 60);
userNameColumn.setCellValueFactory(p -> p.getValue().userNameProperty());
TableColumn<User, String> roleColumn = new TableColumn<User, String>("Role");
roleColumn.setMaxWidth(1f * Integer.MAX_VALUE * 30);
roleColumn.setCellValueFactory(p -> p.getValue().roleProperty());
TableView<User> tableView = new TableView<User>();
tableView.getColumns().add(indexColumn);
tableView.getColumns().add(userNameColumn);
tableView.getColumns().add(roleColumn);
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
Text dummyLabel = new Text("");
VBox leftPane = new VBox(5);
leftPane.getChildren().addAll(usersLabel, tableView);
VBox rightPane = new VBox(20);
rightPane.setFillWidth(true);
rightPane.getChildren().addAll(dummyLabel, createUserButton);
GridPane mainPane = new GridPane();
mainPane.setPadding(new Insets(10, 0, 0, 10));
mainPane.setHgap(20);
mainPane.add(leftPane, 0, 0);
mainPane.add(rightPane, 1, 0);
float width = 372f;
float height = 470f;
Scene scene = new Scene(mainPane, width, height);
primaryStage.setScene(scene);
primaryStage.setResizable(false);
Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
primaryStage.setX((screenBounds.getWidth() - width) / 2);
primaryStage.setY((screenBounds.getHeight() - height) / 2);
showDialog();
}
private void showDialog() {
dialog = new Dialog<>();
dialog.setTitle("Login");
dialog.setHeaderText("Please enter User Name and Password to login.");
dialog.setResizable(false);
Label userNameLabel = new Label("User Name:");
Label passwordLabel = new Label("Password:");
TextField userNameField = new TextField();
PasswordField passwordField = new PasswordField();
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(20, 35, 20, 35));
grid.add(userNameLabel, 1, 1);
grid.add(userNameField, 2, 1);
grid.add(passwordLabel, 1, 2);
grid.add(passwordField, 2, 2);
dialog.getDialogPane().setContent(grid);
dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
Button okButton = (Button) dialog.getDialogPane().lookupButton(ButtonType.OK);
okButton.addEventFilter(ActionEvent.ACTION, event -> {
login(userNameField.getText().trim(), passwordField.getText());
event.consume();
});
dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
float width = 509f;
float height = 168f;
dialog.setWidth(width);
dialog.setHeight(height);
Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
dialog.setX((screenBounds.getWidth() - width) / 2);
dialog.setY((screenBounds.getHeight() - height) / 2);
dialog.showAndWait();
}
private void login(String userName, String password) {
dialog.getDialogPane().setDisable(true);
dialog.getDialogPane().getScene().setCursor(Cursor.WAIT);
Task<Boolean> task = new Task<Boolean>() {
#Override
public Boolean call() {
try {
Thread.sleep(100);
} catch (InterruptedException exception) {
}
return Boolean.TRUE;
}
};
task.setOnSucceeded(e -> {
Boolean success = task.getValue();
dialog.getDialogPane().setDisable(false);
dialog.getDialogPane().getScene().setCursor(Cursor.DEFAULT);
if (success.booleanValue()) {
Platform.runLater(() -> {
primaryStage.show();
});
} else {
Alert alert = new Alert(AlertType.ERROR);
alert.setTitle("Login Error");
alert.setHeaderText("Unable to login.");
alert.showAndWait();
}
});
new Thread(task).start();
}
public static void main(String[] arguments) {
Application.launch(arguments);
}
}
class User {
private StringProperty index;
private StringProperty userName;
private StringProperty role;
public String getIndex() {
return indexProperty().get();
}
public StringProperty indexProperty() {
if (index == null) {
index = new SimpleStringProperty(this, "index");
}
return index;
}
public void setIndex(String index) {
indexProperty().set(index);
}
public String getUserName() {
return userNameProperty().get();
}
public StringProperty userNameProperty() {
if (userName == null) {
userName = new SimpleStringProperty(this, "userName");
}
return userName;
}
public void setUserName(String userName) {
userNameProperty().set(userName);
}
public String getRole() {
return roleProperty().get();
}
public StringProperty roleProperty() {
if (role == null) {
role = new SimpleStringProperty(this, "role");
}
return role;
}
public void setRole(String role) {
roleProperty().set(role);
}
}
JKostikiadis's solution:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class TestApp extends Application {
private static final double WIDTH = 316.0;
private static final double HEIGHT = 339.0;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
HBox pane = new HBox();
pane.setAlignment(Pos.CENTER);
Button b = new Button("click me");
b.setOnAction(e -> {
showDialog();
});
pane.getChildren().add(b);
Scene scene = new Scene(pane, 300, 300);
stage.setScene(scene);
centerStage(stage, WIDTH, HEIGHT);
stage.show();
}
private void showDialog() {
Alert dialog = new Alert(AlertType.ERROR);
dialog.setTitle("Error Dialog");
dialog.setHeaderText("Look, an Error Dialog");
dialog.setContentText("Ooops, there was an error!\nOoops, there was an error!");
Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow();
centerStage(stage, -10000, -10000);
dialog.show();
System.out.println(stage.getWidth() + " " + stage.getHeight());
dialog.hide();
centerStage(stage, stage.getWidth(), stage.getHeight());
dialog.showAndWait();
}
private void centerStage(Stage stage, double width, double height) {
Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
stage.setX((screenBounds.getWidth() - width) / 2);
stage.setY((screenBounds.getHeight() - height) / 2);
}
}
Unfortunately, you have to wait for the width/height of the Window (or Dialog) to be computed as well as for the Window to be shown. Since the Window is visible you will always notice the window moving when updating the xy-position.
Doing the update when the WindowEvent.WINDOW_SHOWN event is fired might provide a better result:
final Window window = dialog.getDialogPane().getScene().getWindow();
window.addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
window.setX((screenBounds.getWidth() - window.getWidth()) / 2);
window.setY((screenBounds.getHeight() - window.getHeight()) / 2);
}
});
And for the primaryStage
primaryStage.addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
primaryStage.setX((screenBounds.getWidth() - primaryStage.getWidth()) / 2);
primaryStage.setY((screenBounds.getHeight() - primaryStage.getHeight()) / 2);
}
});
primaryStage.show();
But as mentioned by JKostikiadis, a better and proper solution might be to compute your own dimension with respect to the current screen size.
Here is the small improvement I can see.
When running your demo on my machine, the movement is erratic:
I can see a small improvement when using WindowEvent.WINDOW_SHOWN (without usingPlatform.runLater for the first Dialog):
Anyway, I don't think using Platform.runLater for displaying the first window is ideal as there is no guarantee that showAndWait() will always be executed before the Runnable
You can center a stage on another stage before rendering it by applying the css which will provide you with the width/height.
For example.
From where you create the stage:
WindowHelper.centerChildWindowOnStage(stage, primaryStage); //assuming primary is the stage you want to center on
stage.show();
below is the code to center the unshown window (assume this is on a WindowHelper class to be reused in the app).
public static void centerChildWindowOnStage(Stage stage, Stage primaryStage ) {
if(primaryStage == null){
return;
}
double x = stage.getX();
double y = stage.getY();
// Firstly we need to force CSS and layout to happen, as the dialogPane
// may not have been shown yet (so it has no dimensions)
stage.getScene().getRoot().applyCss();
stage.getScene().getRoot().layout();
final Scene ownerScene = primaryStage.getScene();
final double titleBarHeight = ownerScene.getY();
// because Stage does not seem to centre itself over its owner, we
// do it here.
// then we can get the dimensions and position the dialog appropriately.
final double dialogWidth = stage.getScene().getRoot().prefWidth(-1);
final double dialogHeight = stage.getScene().getRoot().prefHeight(dialogWidth);
final double ownerWidth = primaryStage.getScene().getRoot().prefWidth(-1);
final double ownerHeight = primaryStage.getScene().getRoot().prefHeight(ownerWidth);
if(dialogWidth < ownerWidth){
x = primaryStage.getX() + (ownerScene.getWidth() / 2.0) - (dialogWidth / 2.0);
}else {
x = primaryStage.getX();
stage.setWidth(dialogWidth);
}
if(dialogHeight < ownerHeight){
y = primaryStage.getY() + titleBarHeight / 2.0 + (ownerScene.getHeight() / 2.0) - (dialogHeight / 2.0);
}else {
y = primaryStage.getY();
}
stage.setX(x);
stage.setY(y);
}
Well because you ask me in the commends I am going to provide an example of setting the Stage ( main application or dialog ) to the center of the screen by early initialization of their dimensions.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class TestApp extends Application {
private static final double WIDTH = 316.0;
private static final double HEIGHT = 339.0;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
HBox pane = new HBox();
pane.setAlignment(Pos.CENTER);
Button b = new Button("click me");
b.setOnAction(e -> {
showDialog();
});
pane.getChildren().add(b);
Scene scene = new Scene(pane, 300, 300);
stage.setScene(scene);
centerStage(stage, WIDTH, HEIGHT);
stage.show();
System.out.println(stage.getWidth() + " " + stage.getHeight());
}
private void showDialog() {
Alert dialog = new Alert(AlertType.ERROR);
dialog.setTitle("Error Dialog");
dialog.setHeaderText("Look, an Error Dialog");
dialog.setContentText("Ooops, there was an error!");
Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow();
centerStage(stage, 366, 175);
dialog.showAndWait();
}
private void centerStage(Stage stage, double width, double height) {
Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
stage.setX((screenBounds.getWidth() - width) / 2);
stage.setY((screenBounds.getHeight() - height) / 2);
}
}
In the example above you will see that I have specify the application dimensions to 300,300 but I am using for width = 316.0 and height = 339.0 and you might wondering why. It's because the stage size will always be a little bigger than the Scene ( borders + Title bar etc ) so in order to find the real width and height of the Stage you will have to print the dimensions of the stage after you show it. The same logic is happening to the Dialog.
Important : Of course you could forget all about the above and just do :
stage.setWidth(300); // or a variable here
stage.setHeight(300);
But this will affect your internal components cause if previously the scene's components had a size of 300,300 now they are going to be squeezed to something less in order to make the stage to fix the size of 300,300 so in that case yes it might affect the way your application looks like.
In the past I was searching for a way to find the dimension of a label before I show it. I found out that it was possible to get it's dimensions by adding it to the Scene and then call
labe.impl_processCSS(true);
System.out.println(labe.prefWidth(-1) + "/" + labe.prefHeight(-1));
Now If I try to do the same for the main pane in the above application it shows 59/25 which are the dimensions of the button itself so this approach is not going to work in case of someone wondering about it.
Edit :
I don't really want to show this "hack" cause I find it stupid and i am sure there is a better way, but until I find it here you go :
private void showDialog() {
Alert dialog = new Alert(AlertType.ERROR);
dialog.setTitle("Error Dialog");
dialog.setHeaderText("Look, an Error Dialog");
dialog.setContentText("Ooops, there was an error!\nOoops, there was an error!");
Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow();
centerStage(stage, -10000, -10000);
dialog.show();
centerStage(stage, stage.getWidth(), stage.getHeight());
}

Textarea scrollpane hold in text append

It is my test code of textarea append text,
public class TextAreaScrollHold extends Application {
TextArea area = new TextArea();
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
root.getChildren().add(area);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
addTextInTextArea();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public void addTextInTextArea() {
for (int i = 0; i < 15; i++) {
area.appendText("Hello World " + i + "\n");
}
Task<Void> task = new Task() {
#Override
protected Void call() throws Exception {
for (int i = 15; i < 100; i++) {
area.appendText("Hello World " + i + "\n");
Thread.sleep(1000);
}
return null;
}
};
new Thread(task).start();
}
}
It my code data will update in thread. i need how to hold in scroll bar when data update in textarea. I have ref JavaFX TextArea and autoscroll and Access to TextArea's Scroll Pane or Scroll Bars but how solve this problems.
I need
When data update in textarea, i will scroll the text area scrollbar the bar will hold.
textArea.scrollTopProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
textArea.setScrollTop(100);
}
});
I have used this code but scroll bar in not moved bar will fixed in pixel 100 positions
You can use getCaretPostion and postionCaret (yes, that setter's method name is awkward for Java).
I quickly drafted up some code for you, use the scroll lock button to enable/disable scrolling:
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ConsoleDemo extends Application {
Console console = new Console();
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
root.getChildren().add(console);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Console Demo");
primaryStage.setScene(scene);
primaryStage.show();
addTextInTextArea();
}
/**
* #param args
* the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public void addTextInTextArea() {
for (int i = 0; i < 15; i++) {
console.log("Hello World " + i);
}
Task<Void> task = new Task() {
#Override
protected Void call() throws Exception {
for (int i = 15; i < 100; i++) {
console.log("Hello World " + i);
Thread.sleep(1000);
}
return null;
}
};
new Thread(task).start();
}
public class Console extends BorderPane {
TextArea textArea = new TextArea();
int scrollLockPos = -1;
public Console() {
HBox toolbar = new HBox();
ToggleButton scrollLockButton = new ToggleButton("Scroll Lock");
scrollLockButton.setOnAction(e -> {
if (scrollLockButton.isSelected()) {
scrollLockPos = textArea.getCaretPosition();
} else {
scrollLockPos = -1;
}
});
HBox.setMargin(scrollLockButton, new Insets(5, 5, 5, 5));
toolbar.getChildren().add(scrollLockButton);
setCenter(textArea);
setTop(toolbar);
}
public void log(String text) {
textArea.appendText(text + "\n");
if (scrollLockPos != -1) {
textArea.positionCaret(scrollLockPos);
}
}
}
}
Not the nicest solution, but unless you want to use selection in the textarea while it's scrolling is locked, this one works. For a proper solution you'd need access to the skin / scrollpane / scrollbars and with the upcoming Java 9 version and its modularization you don't know what you will have access to since access to them is currently flagged as "restricted".
Edit:
Here's an alternate solution which uses the Range, console component only. With this version you can select text and keep the selection while the Scroll Lock button is down:
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.IndexRange;
import javafx.scene.control.TextArea;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
/**
* Console which provides a mechanism to lock scrolling. Selecting text and copying it works while scrolling is locked.
*/
public class Console extends BorderPane {
TextArea textArea = new TextArea();
ToggleButton scrollLockButton;
IndexRange range;
public Console() {
initComponents();
}
private void initComponents() {
// toolbar
HBox toolbar = new HBox();
toolbar.setAlignment(Pos.CENTER_RIGHT);
// clear
Button clearButton = new Button("Clear");
clearButton.setOnAction(e -> {
textArea.clear();
});
// scroll lock
scrollLockButton = new ToggleButton("Scroll Lock");
// button positions & layout
Insets insets = new Insets(5, 5, 5, 5);
HBox.setMargin(clearButton, insets);
HBox.setMargin(scrollLockButton, insets);
toolbar.getChildren().addAll(clearButton,scrollLockButton);
// component layout
setCenter(textArea);
setTop(toolbar);
}
public void log(String text) {
if (scrollLockButton.isSelected()) {
range = textArea.getSelection();
}
textArea.appendText(text + "\n");
if (scrollLockButton.isSelected()) {
textArea.selectRange(range.getStart(), range.getEnd());
}
}
}

How to set Menubutton always on top of other component like HBox (which is draggable)

I have a parent VBox which holds a menu button and a draggable HBox. When I drag the HBox, the menu button is not responding (because the HBox is set over the menu button). How do I always set the menu button on top of the HBox if it is draggable?
HBoxandVBoxExampleupdated.java:
import DraggableNode;
import javafx.scene.chart.NumberAxis;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class HBoxandVBoxExampleupdated extends Application
{
static Pane pane = new Pane();
static DraggableNode node = new DraggableNode();
static NumberAxis noaxis = new NumberAxis();
static String ref = "HHHHHelllelelelellelellelelellelelelaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
static HBox nobox = new HBox();
static NumberAxis lineXAxis;
static String Style = "-fx-border-color: blue;\n"
+ "-fx-border-insets: 5;\n"
+ "-fx-border-width: 3;\n"
+ "-fx-border-style: dashed;\n";
static String Style1 = "-fx-border-color: red;\n"
+ "-fx-border-insets: 5;\n"
+ "-fx-border-width: 3;\n"
+ "-fx-border-style: dashed;\n";
#Override
public void start(Stage primaryStage) throws Exception
{
pane.setStyle(Style);
node.setStyle(Style1);
VBox mainbox = new VBox(80);
mainbox.setAlignment(Pos.CENTER);
mainbox.setPadding(new Insets(50, 30, 100, 50));
VBox hbox = new VBox(60);
hbox.setAlignment(Pos.CENTER); // default TOP_LEFT
HBox vbox1 = new HBox();
HBox vbox2 = new HBox(10);
HBox vbox3 = new HBox(20);
Button close = new Button("X");
Button close1 = new Button("X");
MenuButton vcfmenu = new MenuButton("Vcf");
vcfmenu.getItems().add(new CheckMenuItem("About This Track"));
vcfmenu.getItems().add(new CheckMenuItem("Ping To Tap"));
vcfmenu.getItems().add(new CheckMenuItem("Edit Config"));
vcfmenu.getItems().add(new CheckMenuItem("Delete Track"));
vcfmenu.getItems().add(new CheckMenuItem("Save Track Data"));
vcfmenu.getItems().add(new CheckMenuItem("Show Labels"));
vcfmenu.getItems().add(new CheckMenuItem("Hides Sites Passing All Filters"));
vcfmenu.getItems().add(new CheckMenuItem("Hides Sites not Passing All Filters"));
vbox2.getChildren().add(vcfmenu);
for (String s : ref.split("")) {
Label l = new Label(s);
l.setBorder(new Border(
new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT)));
l.setBackground(
new Background(
new BackgroundFill(
(s.equals("N") ? Color.web("#DDDDDD")
: (s.equals("A") ? Color.web("#00BF00")
: (s.equals("C") ? Color.web("#0099FF")
: (s.equals("T") ? Color.web("#F00")
: Color.web("#D5BB04"))))),
CornerRadii.EMPTY, Insets.EMPTY)));
l.setAlignment(Pos.CENTER);
l.setPadding(new Insets(1, 4, 1, 4));
vbox1.getChildren().add(l);
}
lineXAxis = new NumberAxis(1, ref.length(), 4);
nobox.getChildren().add(lineXAxis);
nobox.setHgrow(lineXAxis, Priority.ALWAYS);
mainbox.getChildren().addAll(vbox2);
hbox.getChildren().addAll(nobox, vbox1);
node.getChildren().add(hbox);
pane.getChildren().addAll(node, mainbox);
Scene scene = new Scene(pane, 1150, 250);
primaryStage.setTitle("HBox and VBox Example");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
DraggableNode.java:
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
public class DraggableNode extends StackPane {
private double x = 0;
private double y = 0;
private double mousex = 0;
private double mousey = 0;
private Node view;
private boolean dragging = false;
private boolean moveToFront = true;
private double size = 0;
private double newSize = 0;
public DraggableNode() {
init();
}
public DraggableNode(Node view) {
this.view = view;
getChildren().add(view);
setMouseTransparent(true);
init();
}
private void init() {
onMousePressedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
getScene().setCursor(Cursor.HAND);
// record the current mouse X and Y position on Node
mousex = event.getSceneX();
mousey = event.getSceneY();
x = getLayoutX();
y = getLayoutY();
if (isMoveToFront()) {
toFront();
}
}
});
onMouseDraggedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
double offsetX = event.getSceneX() - mousex;
x += offsetX;
double scaledX = x;
System.out.println(" : " + scaledX);
if (scaledX > 0)
{
return;
}
setLayoutX(scaledX);
dragging = false;
mousex = event.getSceneX();
event.consume();
}
});
onMouseClickedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
dragging = false;
}
});
}
/**
* #return the dragging
*/
protected boolean isDragging() {
return dragging;
}
/**
* #return the view
*/
public Node getView() {
return view;
}
/**
* #param moveToFront
* the moveToFront to set
*/
public void setMoveToFront(boolean moveToFront) {
this.moveToFront = moveToFront;
}
/**
* #return the moveToFront
*/
public boolean isMoveToFront() {
return moveToFront;
}
public void removeNode(Node n) {
getChildren().remove(n);
}
}
You set the DraggableNode to the front in the EventHandler for onMousePressedProperty. This puts DraggableNode on top of its sibling nodes and prevents the menu button from receiving mouse inputs.
To prevent this, I see two options:
don't set the DraggableNode to the front by setting moveToFront = false or, if that isn't possible,
set the DraggableNode to the back again after dragging by adding
onMouseReleasedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
toBack();
}
});
to the init method of DraggableNode.
For a more general solution, you could add a property to DraggableNode
private BooleanProperty dragInProcessProperty = new SimpleBooleanProperty(false);
public BooleanProperty dragInProcessProperty() {
return this.dragInProcessProperty;
}
Set the property to true while dragging in onMouseDraggedProperty and to false when the mouse is released
onMouseReleasedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
dragInProcessProperty.set(false);
}
});
and add a ChangeListener to the dragInProgressProperty in HBoxandVBoxExampleupdated
node.dragInProcessProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (!newValue.booleanValue()) {
mainbox.toFront();
}
}
});
to set the node to the front whenever dragging is finished.

JavaFX stopwatch timer

This is a class for a simple stopwatch for JavaFX, style the Label object as desired
package aaa;
import java.text.SimpleDateFormat;
import java.util.Date;
import javafx.beans.property.SimpleStringProperty;
/**
*
* #author D07114915
*/
public class KTimer extends Thread {
private Thread thread = null;
private SimpleDateFormat sdf = new SimpleDateFormat("mm:ss:S");
private String[] split;
private SimpleStringProperty min, sec, millis, sspTime;
private long time;
public static void main(String[] args) {
KTimer t = new KTimer();
t.startTimer(00);
}
public KTimer() {
min = new SimpleStringProperty("00");
sec = new SimpleStringProperty("00");
millis = new SimpleStringProperty("00");
sspTime = new SimpleStringProperty("00:00:00");
}
public void startTimer(long time) {
this.time = time;
thread = new Thread(this);
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
}
public void stopTimer(long time) {
if (thread != null) {
thread.interrupt();
}
this.time = time;
setTime(time);
}
public void setTime(long time) {
this.time = time;
split = sdf.format(new Date(time)).split(":");
min.set(split[0]);
sec.set(split[1]);
if (split[2].length() == 1) {
split[2] = "0" + split[2];
}
millis.set(split[2].substring(0, 2));
sspTime.set(min.get() + ":" + sec.get() + ":" + millis.get());
}
public long getTime() {
return time;
}
public SimpleStringProperty getSspTime() {
return sspTime;
}
#Override
public void run() {
try {
while (!thread.isInterrupted()) {
setTime(time);
sleep(10);
time = time + 10;
}
} catch (Exception e) {
}
}
}//end of class
Now just get a listener on the property for your GUI
Add vars
KTimer ktimer;
Label timeLabel;
in your class initialize the vars
//Clock
ktimer = new KTimer();
timeLabel = new Label(ktimer.getSspTime().get());
ktimer.getSspTime().addListener(new InvalidationListener() {
#Override
public void invalidated(Observable observable) {
timeLabel.setText(ktimer.getSspTime().get());
}
});
then call the method to start and stop wherever you need to
Stop and reset is
ktimer.stopTimer(0);
Start and Pause timer is
ktimer.startTimer(ktimer.getTime());
Any improvements appreciated as the class is a bit CPU hungry..., but you can adjust the run thread and setTime(time) functions to suit the application
Here's a slightly different version (maybe better) and I'm not sure the synchronized methods are really necessary
package aaa;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import javafx.beans.property.SimpleStringProperty;
/**
*
* #author D07114915
*/
public class KTimer {
private SimpleDateFormat sdf = new SimpleDateFormat("mm:ss:S");
private String[] split;
private SimpleStringProperty sspTime;
private long time;
private Timer t = new Timer("Metronome", true);
private TimerTask tt;
boolean timing = false;
public KTimer() {
sspTime = new SimpleStringProperty("00:00:00");
}
public void startTimer(final long time) {
this.time = time;
timing = true;
tt = new TimerTask() {
#Override
public void run() {
if (!timing) {
try {
tt.cancel();
} catch (Exception e) {
e.printStackTrace();
}
} else {
updateTime();
}
}
};
t.scheduleAtFixedRate(tt, 10, 10);
}
public synchronized void stopTimer() {
timing = false;
}
public synchronized void updateTime() {
this.time = this.time + 10;
split = sdf.format(new Date(this.time)).split(":");
sspTime.set(split[0] + ":" + split[1] + ":" + (split[2].length() == 1 ? "0" + split[2] : split[2].substring(0, 2)));
}
public synchronized void moveToTime(long time) {
stopTimer();
this.time = time;
split = sdf.format(new Date(time)).split(":");
sspTime.set(split[0] + ":" + split[1] + ":" + (split[2].length() == 1 ? "0" + split[2] : split[2].substring(0, 2)));
}
public synchronized long getTime() {
return time;
}
public synchronized SimpleStringProperty getSspTime() {
return sspTime;
}
}
Building on KEV's answer and various demos I found across the internet, here's a simple "count up" timer with 100ms precision:
package fxtimer;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;
public class FXTimer extends Application {
private Timeline timeline;
private Label timerLabel = new Label(), splitTimerLabel = new Label();
private DoubleProperty timeSeconds = new SimpleDoubleProperty(),
splitTimeSeconds = new SimpleDoubleProperty();
private Duration time = Duration.ZERO, splitTime = Duration.ZERO;
#Override
public void start(Stage primaryStage) {
// Configure the Label
// Bind the timerLabel text property to the timeSeconds property
timerLabel.textProperty().bind(timeSeconds.asString());
timerLabel.setTextFill(Color.RED);
timerLabel.setStyle("-fx-font-size: 4em;");
splitTimerLabel.textProperty().bind(splitTimeSeconds.asString());
splitTimerLabel.setTextFill(Color.BLUE);
splitTimerLabel.setStyle("-fx-font-size: 4em;");
// Create and configure the Button
Button button = new Button();
button.setText("Start / Split");
button.setOnAction(new EventHandler() {
#Override
public void handle(Event event) {
if (timeline != null) {
splitTime = Duration.ZERO;
splitTimeSeconds.set(splitTime.toSeconds());
} else {
timeline = new Timeline(
new KeyFrame(Duration.millis(100),
new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
Duration duration = ((KeyFrame)t.getSource()).getTime();
time = time.add(duration);
splitTime = splitTime.add(duration);
timeSeconds.set(time.toSeconds());
splitTimeSeconds.set(splitTime.toSeconds());
}
})
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
}
});
// Setup the Stage and the Scene (the scene graph)
StackPane root = new StackPane();
Scene scene = new Scene(root, 300, 250);
// Create and configure VBox
// gap between components is 20
VBox vb = new VBox(20);
// center the components within VBox
vb.setAlignment(Pos.CENTER);
// Make it as wide as the application frame (scene)
vb.setPrefWidth(scene.getWidth());
// Move the VBox down a bit
vb.setLayoutY(30);
// Add the button and timerLabel to the VBox
vb.getChildren().addAll(button, timerLabel, splitTimerLabel);
// Add the VBox to the root component
root.getChildren().add(vb);
primaryStage.setTitle("FX Timer");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}

Resources