I'm working on this code with Barchart and Piechart.
import java.sql.Timestamp;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableObjectValue;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.PieChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class MainApp extends Application
{
#Override
public void start(Stage stage) throws Exception
{
Scene scene = new Scene(initGeneralAgentsData(), 800, 800);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
launch(args);
}
private final StackPane stackPane = new StackPane();
private List<DownloadTrafficObj> obj = new LinkedList<>();
public StackPane initGeneralAgentsData() throws Exception
{
stackPane.setAlignment(Pos.TOP_RIGHT);
stackPane.setStyle("-fx-background-color: white;");
SQLSelect(30);
stackPane.getChildren().addAll(chartChoose());
return stackPane;
}
private List<DownloadTrafficObj> SQLSelect(int history_value)
{
for (int i = 0; i < history_value; i++)
{
obj.add(new DownloadTrafficObj(String.valueOf(randomDate()), Long.valueOf(randomNumber())));
}
return obj;
}
private Timestamp randomDate()
{
long offset = Timestamp.valueOf("2012-01-01 00:00:00").getTime();
long end = Timestamp.valueOf("2013-01-01 00:00:00").getTime();
long diff = end - offset + 1;
Timestamp rand = new Timestamp(offset + (long) (Math.random() * diff));
return rand;
}
private int randomNumber()
{
Random rand = new Random();
int n = rand.nextInt(50) + 1;
return n;
}
public StackPane chartChoose()
{
final ComboBox comboBox = new ComboBox();
comboBox.getItems().addAll("Bar Chart", "Pie Chart");
ComboBox cb = new ComboBox();
cb.getItems().addAll(10, 20, 30, 60);
cb.setValue(30);
final StackPane stack = new StackPane();
comboBox.getSelectionModel().selectedIndexProperty()
.addListener((ObservableValue<? extends Number> observable,
Number oldValue, Number newValue)
-> setVisibility(stack, comboBox)
);
cb.getSelectionModel().selectedIndexProperty()
.addListener((ObservableValue<? extends Number> observable,
Number oldValue, Number newValue)
->
{
SQLSelect((int) cb.getSelectionModel().getSelectedItem());
bc.getData().clear();
generateBarChartData();
}
);
stack.getChildren().add(generateBarChart());
stack.getChildren().add(generatePieChart());
// Placing it after adding rectangle to stack
// will trigger the changelistener to show default rectangle
comboBox.setValue("Bar Chart");
VBox vBox = new VBox();
vBox.setPadding(new Insets(10, 10, 10, 10));
vBox.setSpacing(5);
Label labelon = new Label("Chart type");
Label label = new Label("Days history");
HBox hBossx = new HBox(15, labelon, comboBox, label, cb);
hBossx.setAlignment(Pos.CENTER_RIGHT);
ProgressIndicator progress = new ProgressIndicator();
progress.setMaxSize(90, 90);
Task<ObservableList<DownloadTrafficObj>> task = new Task<ObservableList<DownloadTrafficObj>>()
{
#Override
protected ObservableList<DownloadTrafficObj> call() throws Exception
{
for (int i = 0; i < 99; i++)
{
Thread.sleep(20);
}
return (FXCollections.observableArrayList(obj));
}
};
progress.progressProperty().bind(task.progressProperty());
task.setOnSucceeded(ev ->
{
});
new Thread(task).start();
BorderPane bp = new BorderPane();
bp.centerProperty().bind(
Bindings
.when(task.runningProperty())
.then(progress)
.otherwise((ObservableObjectValue<ProgressIndicator>) stack));
vBox.getChildren().addAll(hBossx, bp);
StackPane root = new StackPane();
root.getChildren().add(vBox);
return root;
}
public void setVisibility(Pane pane, ComboBox comboBox)
{
// Make all children invisible
pane.getChildren().stream().forEach((node) ->
{
node.setVisible(false);
});
// make the selected rectangle visible
int selectedIndex = comboBox.getSelectionModel()
.selectedIndexProperty().getValue();
pane.getChildren().get(selectedIndex).setVisible(true);
}
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
final BarChart<String, Number> bc = new BarChart<>(xAxis, yAxis);
XYChart.Series series1 = new XYChart.Series();
public BarChart<String, Number> generateBarChart()
{
bc.setTitle("Network Download");
xAxis.setLabel("Groups");
yAxis.setLabel("Value");
series1.setName("Network Download");
generateBarChartData();
// TO DO... Very quick fix.
bc.widthProperty().addListener((obs, b, b1) ->
{
// Chart Bar column is not automatically resized. We need to wait for next JavaFX releases to fix this.
Platform.runLater(() -> setMaxBarWidth(bc, xAxis, 40, 10));
});
bc.getData().addAll(series1);
return bc;
}
private void generateBarChartData()
{
obj.stream().map((get) -> new XYChart.Data(get.getDate(), get.getDownloadTraffic())).map((data) ->
{
data.nodeProperty().addListener(new ChangeListener<Node>()
{
#Override
public void changed(ObservableValue<? extends Node> ov, Node oldNode, final Node node)
{
if (node != null)
{
//setNodeStyle(data);
displayLabelForData(data);
}
}
});
return data;
}).forEach((data) ->
{
series1.getData().add(data);
});
}
private void setMaxBarWidth(BarChart<String, Number> bc, CategoryAxis xAxis, double maxBarWidth, double minCategoryGap)
{
double barWidth = 0;
do
{
double catSpace = xAxis.getCategorySpacing();
double avilableBarSpace = catSpace - (bc.getCategoryGap() + bc.getBarGap());
barWidth = (avilableBarSpace / bc.getData().size()) - bc.getBarGap();
if (barWidth > maxBarWidth)
{
avilableBarSpace = (maxBarWidth + bc.getBarGap()) * bc.getData().size();
bc.setCategoryGap(catSpace - avilableBarSpace - bc.getBarGap());
}
}
while (barWidth > maxBarWidth);
do
{
double catSpace = xAxis.getCategorySpacing();
double avilableBarSpace = catSpace - (minCategoryGap + bc.getBarGap());
barWidth = Math.min(maxBarWidth, (avilableBarSpace / bc.getData().size()) - bc.getBarGap());
avilableBarSpace = (barWidth + bc.getBarGap()) * bc.getData().size();
bc.setCategoryGap(catSpace - avilableBarSpace - bc.getBarGap());
}
while (barWidth < maxBarWidth && bc.getCategoryGap() > minCategoryGap);
}
public PieChart generatePieChart()
{
ObservableList<PieChart.Data> pieChartData = FXCollections.observableArrayList();
obj.stream().forEach((activeAgentGroup) ->
{
pieChartData.add(new PieChart.Data(activeAgentGroup.getDate(), activeAgentGroup.getDownloadTraffic()));
});
final PieChart chart = new PieChart(pieChartData);
chart.setTitle("Label");
return chart;
}
private void displayLabelForData(XYChart.Data<String, Number> data)
{
final Node node = data.getNode();
final Text dataText = new Text(data.getYValue().toString());
node.parentProperty().addListener(new ChangeListener<Parent>()
{
#Override
public void changed(ObservableValue<? extends Parent> ov, Parent oldParent, Parent parent)
{
Group parentGroup = (Group) parent;
parentGroup.getChildren().add(dataText);
}
});
node.boundsInParentProperty().addListener(new ChangeListener<Bounds>()
{
#Override
public void changed(ObservableValue<? extends Bounds> ov, Bounds oldBounds, Bounds bounds)
{
dataText.setLayoutX(
Math.round(
bounds.getMinX() + bounds.getWidth() / 2 - dataText.prefWidth(-1) / 2
)
);
dataText.setLayoutY(
Math.round(
bounds.getMinY() - dataText.prefHeight(-1) * 0.5
)
);
}
});
}
}
I ant to add circular progress bar during switch of the charts and loading of the data. Usually it takes 2-3 seconds to load data from the database, so I need a way to display progress bay because the charts are stacked.
Also is there any much easy way to implement the switching of the charts?
Short and sweet:
Use a BorderPane as parent container for your charts.
Use Bindings, Task.runningProperty() and BorderPane.centerProperty()
For example something like this:
myBorderPane.centerProperty().bind(
Bindings
.when(myLongTask.runningProperty())
.then(myProggressIndicator)
.otherwise(myChart));
Related
How to obtain the (x; y) coordinate XYChart.Data(x, y) from a plotted
chart symbol by clicking on it or passing the mouse cursor above it?
A label has to receive the obtained coordinate if the mouse has selected it.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class GetChartCoord extends Application {
#Override
public void start(Stage stage) {
VBox vbox = new VBox();
// Creating a chart
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
LineChart<Number, Number> lineChart = new LineChart<Number, Number>(xAxis, yAxis);
XYChart.Series series = new XYChart.Series();
series.setName("Example 1");
for (int x = 0; x <= 100; x++) {
double y = Math.random()*100;
series.getData().add(new XYChart.Data(x, y));
}
lineChart.getData().add(series);
// This label should receive the coordinate (x; y) from the dot that is
// on the mouse cursor or very next to it
Label labelXY = new Label();
labelXY.setText("(x; y)");
vbox.getChildren().addAll(lineChart, labelXY);
Scene scene = new Scene(vbox, 800, 600);
stage.setScene(scene);
stage.show();
}
}
EDIT:
The answer for that question mentioned by Sedrick solved my problem, but I had to adapt to adapt the code. So I will answer my own question by posting my modified code
Chart.java
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
public class ChangeSymbolSize extends Application {
#Override
public void start(Stage stage) {
// Random chart
// Defining the Axis
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
// Creating the chart
LineChart<Number, Number> lineChart = new LineChart(xAxis, yAxis);
// Preparing the series
XYChart.Series series = new XYChart.Series();
series.setName("Grafico");
for (double x = 0; x <= 10; x++) {
double y = Math.random() * 100;
XYChart.Data chartData;
chartData = new XYChart.Data(x, y);
chartData.setNode(new ShowCoordinatesNode(x, y));
series.getData().add(chartData);
}
// Adding series to chart
lineChart.getData().add(series);
Scene scene = new Scene(lineChart, 800, 600);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
ShowCoordinatesNode.java
import java.text.DecimalFormat;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
public class ShowCoordinatesNode extends StackPane {
public ShowCoordinatesNode(double x, double y) {
final Label label = createDataThresholdLabel(x, y);
setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
setScaleX(1);
setScaleY(1);
getChildren().setAll(label);
setCursor(Cursor.NONE);
toFront();
}
});
setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
getChildren().clear();
setCursor(Cursor.CROSSHAIR);
}
});
}
private Label createDataThresholdLabel(double x, double y) {
DecimalFormat df = new DecimalFormat("0.##");
final Label label = new Label("(" + df.format(x) + "; " + df.format(y) + ")");
label.getStyleClass().addAll("default-color0", "chart-line-symbol", "chart-series-line");
label.setStyle("-fx-font-size: 10; -fx-font-weight: bold;");
label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
return label;
}
}
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());
}
I'm using JavaFX.
I've modified an example of a Zoom function on a LineChart. I get an exception when doing the zoom. I've figured out that there is a mismatch between Types on the xAxis, it wants Numbers and I'm using String Time stamps. How can I adapt this to work with time strings like "12:33:23" on xAxis?
package javafxapplication28;
import java.util.Collections;
import java.util.Random;
import static java.util.UUID.fromString;
import javafx.application.Application;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.beans.property.StringProperty;
import javafx.util.StringConverter;
public class JavaFXApplication28 extends Application {
private static final int NUM_DATA_POINTS = 1000 ;
#Override
public void start(Stage primaryStage) {
final LineChart<Number, Number> chart = createChart();
final StackPane chartContainer = new StackPane();
chartContainer.getChildren().add(chart);
final Rectangle zoomRect = new Rectangle();
zoomRect.setManaged(false);
zoomRect.setFill(Color.LIGHTSEAGREEN.deriveColor(0, 1, 1, 0.5));
chartContainer.getChildren().add(zoomRect);
setUpZooming(zoomRect, chart);
final HBox controls = new HBox(10);
controls.setPadding(new Insets(10));
controls.setAlignment(Pos.CENTER);
final Button zoomButton = new Button("Zoom");
final Button resetButton = new Button("Reset");
zoomButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
doZoom(zoomRect, chart);
}
});
resetButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
final NumberAxis xAxis = (NumberAxis)chart.getXAxis();
xAxis.setLowerBound(10);
xAxis.setUpperBound(200);
final NumberAxis yAxis = (NumberAxis)chart.getYAxis();
yAxis.setLowerBound(0);
yAxis.setUpperBound(200);
zoomRect.setWidth(0);
zoomRect.setHeight(0);
}
});
final BooleanBinding disableControls =
zoomRect.widthProperty().lessThan(5)
.or(zoomRect.heightProperty().lessThan(5));
zoomButton.disableProperty().bind(disableControls);
controls.getChildren().addAll(zoomButton, resetButton);
final BorderPane root = new BorderPane();
root.setCenter(chartContainer);
root.setBottom(controls);
final Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private LineChart<Number, Number> createChart() {
final NumberAxis xAxis = createAxis_x();
final NumberAxis yAxis = createAxis_y();
final LineChart<Number, Number> chart = new LineChart<>(xAxis, yAxis);
chart.setAnimated(false);
chart.setCreateSymbols(false);
chart.setData(generateChartData());
return chart ;
}
//Set range of x axis
private NumberAxis createAxis_x() {
final NumberAxis xAxis = new NumberAxis();
xAxis.setAutoRanging(false);
String xMin = "12:30:12";
String xMax = "13:21:01";
double xminDouble = Double.parseDouble(xMin);
double xmaxDouble = Double.parseDouble(xMax);
xAxis.setLowerBound(xminDouble);
xAxis.setUpperBound(xmaxDouble);
return xAxis;
}
//Set range of y axis
private NumberAxis createAxis_y() {
final NumberAxis yAxis = new NumberAxis();
yAxis.setAutoRanging(false);
String yMin = "40";
String yMax = "400";
double yminDouble = Double.parseDouble(yMin);
double ymaxDouble = Double.parseDouble(yMax);
yAxis.setLowerBound(yminDouble);
yAxis.setUpperBound(ymaxDouble);
return yAxis;
}
private ObservableList<Series<Number, Number>> generateChartData() {
final Series<Number, Number> series = new Series<>();
series.setName("Data");
final Random rng = new Random();
for (int i=0; i<NUM_DATA_POINTS; i++) {
Data<Number, Number> dataPoint = new Data<Number, Number>(i, rng.nextInt(1000));
series.getData().add(dataPoint);
}
return FXCollections.observableArrayList(Collections.singleton(series));
}
private void setUpZooming(final Rectangle rect, final Node zoomingNode) {
final ObjectProperty<Point2D> mouseAnchor = new SimpleObjectProperty<>();
zoomingNode.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
mouseAnchor.set(new Point2D(event.getX(), event.getY()));
rect.setWidth(0);
rect.setHeight(0);
}
});
zoomingNode.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
double x = event.getX();
double y = event.getY();
rect.setX(Math.min(x, mouseAnchor.get().getX()));
rect.setY(Math.min(y, mouseAnchor.get().getY()));
rect.setWidth(Math.abs(x - mouseAnchor.get().getX()));
rect.setHeight(Math.abs(y - mouseAnchor.get().getY()));
}
});
}
private void doZoom(Rectangle zoomRect, LineChart<Number, Number> chart) {
Point2D zoomTopLeft = new Point2D(zoomRect.getX(), zoomRect.getY());
Point2D zoomBottomRight = new Point2D(zoomRect.getX() + zoomRect.getWidth(), zoomRect.getY() + zoomRect.getHeight());
final NumberAxis yAxis = (NumberAxis) chart.getYAxis();
Point2D yAxisInScene = yAxis.localToScene(0, 0);
final NumberAxis xAxis = (NumberAxis) chart.getXAxis();
System.out.println(xAxis);
Point2D xAxisInScene = xAxis.localToScene(0, 0);
double xOffset = zoomTopLeft.getX() - yAxisInScene.getX() ;
double yOffset = zoomBottomRight.getY() - xAxisInScene.getY();
double xAxisScale = xAxis.getScale();
double yAxisScale = yAxis.getScale();
xAxis.setLowerBound(xAxis.getLowerBound() + xOffset / xAxisScale);
xAxis.setUpperBound(xAxis.getLowerBound() + zoomRect.getWidth() / xAxisScale);
yAxis.setLowerBound(yAxis.getLowerBound() + yOffset / yAxisScale);
yAxis.setUpperBound(yAxis.getLowerBound() - zoomRect.getHeight() / yAxisScale);
System.out.println(yAxis.getLowerBound() + " " + yAxis.getUpperBound());
zoomRect.setWidth(0);
zoomRect.setHeight(0);
}
public static void main(String[] args) {
launch(args);
}
}
It works like this. Draw a rectangle in the Linechart for the area to zoom in. Then press the zoom button.
The sample code above gives an exception due to the problem with time strings. To get it to work use this instead:
String xMin = "40";
String xMax = "300";
Thanks for ur help!
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.
I have a StackPane with charts (multiple axis chart). I want to set some action for yAxis. There is no result in GUI, but it is well for baseChart (isn't in StackPane). How can I set an action for Axis?
MCVE
mainclass with JFrame:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.function.Function;
public abstract class mainClass extends Application {
public static final int X_NUMBER = 20;
static StackChart chart;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
initAndShowGUI();
}
});
}
private static void initAndShowGUI() {
// This method is invoked on Swing thread
JFrame frame = new JFrame("FX");
final JFXPanel fxPanel = new JFXPanel();
frame.add(fxPanel);
frame.setVisible(true);
frame.setSize((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth(), (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight());
Platform.runLater(new Runnable() {
#Override
public void run() {
initFX(fxPanel);
}
});
}
private static void initFX(JFXPanel fxPanel) {
// This method is invoked on JavaFX thread
Scene scene = createScene();
fxPanel.setScene(scene);
}
private static Scene createScene() {
Group root = new Group();
Scene scene = new Scene(root, 1024, 600);
BorderPane borderPane = new BorderPane();
NumberAxis xAxis = new NumberAxis();
NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("Scale 1");
LineChart baseChart = new LineChart(xAxis, yAxis);
baseChart.getData().add(prepareSeries("Serie 1", (x) -> (double) x));
chart = new StackChart(baseChart, Color.RED);
chart.addSeries(prepareSeries("Serie 2", (x) -> (double) -2*x * x), Color.BLUE);
borderPane.setCenter(chart);
borderPane.setPrefSize(800, 600);
borderPane.setBottom(chart.getLegend());
root.getChildren().add(borderPane);
return scene;
}
private static XYChart.Series<Number, Number> prepareSeries(String name, Function<Integer, Double> function) {
XYChart.Series<Number, Number> series = new XYChart.Series<>();
series.setName(name);
for (int i = 0; i < X_NUMBER; i++) {
series.getData().add(new XYChart.Data<>(i, function.apply(i)));
}
return series;
}
}
StackChart class:
import javafx.beans.Observable;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
public class StackChart extends StackPane {
private final LineChart baseChart;
private final ObservableList<LineChart> backCharts = FXCollections.observableArrayList();
private final double yAxisWidth = 60;
private final AnchorPane details;
private final double yAxisSeparation = 20;
private double strokeWidth = 0.3;
public int _formatType = 1;
public StackChart(LineChart baseChart, Color lineColor) {
this(baseChart, lineColor, null);
}
public StackChart(LineChart baseChart, Color lineColor, Double strokeWidth) {
if (strokeWidth != null) {
this.strokeWidth = strokeWidth;
}
this.baseChart = baseChart;
baseChart.setCreateSymbols(false);
baseChart.setLegendVisible(false);
baseChart.getXAxis().setAutoRanging(false);
baseChart.getXAxis().setAnimated(false);
baseChart.getXAxis().setStyle("-fx-font-size:" + 18);
baseChart.getYAxis().setAnimated(false);
baseChart.getYAxis().setStyle("-fx-font-size:" + 18);
setStyle(baseChart, lineColor);
setFixedAxisWidth(baseChart);
setAlignment(Pos.CENTER_LEFT);
backCharts.addListener((Observable observable) -> rebuildChart());
details = new AnchorPane();
bindMouseEvents(baseChart, this.strokeWidth);
rebuildChart();
}
private void bindMouseEvents(LineChart baseChart, Double strokeWidth) {
getChildren().add(details);
details.prefHeightProperty().bind(heightProperty());
details.prefWidthProperty().bind(widthProperty());
details.setMouseTransparent(true);
setOnMouseMoved(null);
setMouseTransparent(false);
final Axis xAxis = baseChart.getXAxis();
final Axis yAxis = baseChart.getYAxis();
final Line xLine = new Line();
final Line yLine = new Line();
yLine.setFill(Color.GRAY);
xLine.setFill(Color.GRAY);
yLine.setStrokeWidth(strokeWidth/2);
xLine.setStrokeWidth(strokeWidth/2);
xLine.setVisible(false);
yLine.setVisible(false);
final Node chartBackground = baseChart.lookup(".chart-plot-background");
for (Node n: chartBackground.getParent().getChildrenUnmodifiable()) {
if (n != chartBackground && n != xAxis && n != yAxis) {
n.setMouseTransparent(true);
}
}
chartBackground.setCursor(Cursor.CROSSHAIR);
chartBackground.setOnMouseEntered((event) -> {
chartBackground.getOnMouseMoved().handle(event);
xLine.setVisible(true);
yLine.setVisible(true);
details.getChildren().addAll(xLine, yLine);
});
chartBackground.setOnMouseExited((event) -> {
xLine.setVisible(false);
yLine.setVisible(false);
details.getChildren().removeAll(xLine, yLine);
});
chartBackground.setOnMouseMoved(event -> {
double x = event.getX() + chartBackground.getLayoutX();
double y = event.getY() + chartBackground.getLayoutY();
xLine.setStartX(65);
xLine.setEndX(details.getWidth()-10);
xLine.setStartY(y+5);
xLine.setEndY(y+5);
yLine.setStartX(x+5);
yLine.setEndX(x+5);
yLine.setStartY(12);
yLine.setEndY(details.getHeight()-28);
});
}
private void setFixedAxisWidth(LineChart chart) {
chart.getYAxis().setPrefWidth(yAxisWidth);
chart.getYAxis().setMaxWidth(yAxisWidth);
}
private void rebuildChart() {
getChildren().clear();
getChildren().add(resizeBaseChart(baseChart));
for (LineChart lineChart : backCharts) {
getChildren().add(resizeBackgroundChart(lineChart));
}
getChildren().add(details);
}
private Node resizeBaseChart(LineChart lineChart) {
HBox hBox = new HBox(lineChart);
hBox.setAlignment(Pos.CENTER_LEFT);
hBox.prefHeightProperty().bind(heightProperty());
hBox.prefWidthProperty().bind(widthProperty());
lineChart.minWidthProperty().bind(widthProperty().subtract((yAxisWidth+yAxisSeparation)*backCharts.size()));
lineChart.prefWidthProperty().bind(widthProperty().subtract((yAxisWidth+yAxisSeparation)*backCharts.size()));
lineChart.maxWidthProperty().bind(widthProperty().subtract((yAxisWidth+yAxisSeparation)*backCharts.size()));
return lineChart;
}
private Node resizeBackgroundChart(LineChart lineChart) {
HBox hBox = new HBox(lineChart);
hBox.setAlignment(Pos.CENTER_LEFT);
hBox.prefHeightProperty().bind(heightProperty());
hBox.prefWidthProperty().bind(widthProperty());
hBox.setMouseTransparent(true);
lineChart.minWidthProperty().bind(widthProperty().subtract((yAxisWidth + yAxisSeparation) * backCharts.size()));
lineChart.prefWidthProperty().bind(widthProperty().subtract((yAxisWidth + yAxisSeparation) * backCharts.size()));
lineChart.maxWidthProperty().bind(widthProperty().subtract((yAxisWidth + yAxisSeparation) * backCharts.size()));
lineChart.translateXProperty().bind(baseChart.getYAxis().widthProperty());
lineChart.getYAxis().setTranslateX((yAxisWidth + yAxisSeparation) * backCharts.indexOf(lineChart));
return hBox;
}
public void addSeries(XYChart.Series series, Color lineColor) {
NumberAxis yAxis = new NumberAxis();
NumberAxis xAxis = new NumberAxis();
// xAxis
xAxis.setAutoRanging(false);
xAxis.setVisible(false);
xAxis.setOpacity(0.0);
xAxis.lowerBoundProperty().bind(((NumberAxis) baseChart.getXAxis()).lowerBoundProperty());
xAxis.upperBoundProperty().bind(((NumberAxis) baseChart.getXAxis()).upperBoundProperty());
xAxis.tickUnitProperty().bind(((NumberAxis) baseChart.getXAxis()).tickUnitProperty());
// yAxis
yAxis.setSide(Side.RIGHT);
yAxis.setLabel(series.getName());
// create chart
LineChart lineChart = new LineChart(xAxis, yAxis);
lineChart.setAnimated(false);
lineChart.setLegendVisible(false);
lineChart.getData().add(series);
//HERE
for (LineChart ch : backCharts) {
ch.getYAxis().setCursor(Cursor.CLOSED_HAND);
ch.getYAxis().setOnMousePressed(event -> {
ch.getYAxis().setLabel("123");
});
}
styleBackChart(lineChart, lineColor);
setFixedAxisWidth(lineChart);
backCharts.add(lineChart);
}
private void styleBackChart(LineChart lineChart, Color lineColor) {
setStyle(lineChart, lineColor);
Node contentBackground = lineChart.lookup(".chart-content").lookup(".chart-plot-background");
contentBackground.setStyle("-fx-background-color: transparent;");
lineChart.setVerticalZeroLineVisible(false);
lineChart.setHorizontalZeroLineVisible(false);
lineChart.setVerticalGridLinesVisible(false);
lineChart.setHorizontalGridLinesVisible(false);
lineChart.setCreateSymbols(false);
lineChart.getXAxis().setStyle("-fx-font-size:" + 18);
lineChart.getYAxis().setStyle("-fx-font-size:" + 18);
}
private String toRGBCode(Color color) {
return String.format("#%02X%02X%02X",
(int) (color.getRed() * 255),
(int) (color.getGreen() * 255),
(int) (color.getBlue() * 255));
}
private void setStyle(LineChart chart, Color lineColor) {
chart.getYAxis().lookup(".axis-label").setStyle("-fx-text-fill: " + toRGBCode(lineColor) + "; -fx-font-size: 24;");
Node seriesLine = chart.lookup(".chart-series-line");
seriesLine.setStyle("-fx-stroke: " + toRGBCode(lineColor) + "; -fx-stroke-width: " + strokeWidth + ";");
}
public Node getLegend() {
HBox hbox = new HBox();
final CheckBox baseChartCheckBox = new CheckBox(baseChart.getYAxis().getLabel());
baseChartCheckBox.setSelected(true);
baseChartCheckBox.setDisable(true);
baseChartCheckBox.getStyleClass().add("readonly-checkbox");
baseChartCheckBox.setOnAction(event -> baseChartCheckBox.setSelected(true));
hbox.getChildren().add(baseChartCheckBox);
for (final LineChart lineChart : backCharts) {
CheckBox checkBox = new CheckBox(lineChart.getYAxis().getLabel());
checkBox.setSelected(true);
checkBox.setOnAction(event -> {
if (backCharts.contains(lineChart)) {
backCharts.remove(lineChart);
} else {
backCharts.add(lineChart);
}
});
hbox.getChildren().add(checkBox);
}
hbox.setAlignment(Pos.CENTER);
hbox.setSpacing(20);
hbox.setStyle("-fx-padding: 0 10 20 10");
return hbox;
}
public int is_formatType() {
return _formatType;
}
public void set_formatType(int formatType) {
this._formatType = formatType;
this.requestLayout();
}
}
Regards,
Julia.