I am new to Java and JavaFX and have a question. I have an application with a single stage/scene in JavaFX. It has multiple tabs. In one of those tabs, I want the user to be able to input some values in text fields, press a submit button, and have a lineChart, on that same scene, be then displayed. I'm not having luck getting this to work.
An empty line Chart shows up when I run the app, but I cannot figure out how to get the series of data to appear.
Here is simple test code in the Controller class for the method that the code executes when the submit button is pressed:
#FXML
private void getEngDataPlot(ActionEvent event) {
lineChart.setTitle("Test charting");
//defining a series
XYChart.Series series = new XYChart.Series();
series.setName("My Test Data");
//populating the series with data
series.getData().add(new XYChart.Data(1, 23));
series.getData().add(new XYChart.Data(2, 14));
series.getData().add(new XYChart.Data(3, 15));
series.getData().add(new XYChart.Data(4, 24));
series.getData().add(new XYChart.Data(5, 34));
series.getData().add(new XYChart.Data(6, 36));
series.getData().add(new XYChart.Data(7, 22));
series.getData().add(new XYChart.Data(8, 45));
series.getData().add(new XYChart.Data(9, 43));
series.getData().add(new XYChart.Data(10, 17));
series.getData().add(new XYChart.Data(11, 29));
series.getData().add(new XYChart.Data(12, 25));
lineChart.getData().add(series);
}
must I 'do' something else in this method to get the chart on the scene to load?
I see show.stage(); in examples, but at the point I execute this method, my app is running and I already have the 'stage' shown - don't I? I'm obviously missing something fundamental to how this is supposed to hang together, but I don't know what.
thank you, and pardon my ignorance.
Most probably your chart axis are not set coorrectly so you can't see new data. Try to add next lines:
lineChart.getXAxis().setAutoRanging(true);
lineChart.getYAxis().setAutoRanging(true);
Or take a look at next example:
public class ChartUpdate extends Application {
#Override
public void start(Stage primaryStage) {
//random axis here
final LineChart lineChart = new LineChart(new NumberAxis(1, 22, 0.5), new NumberAxis(1, 22, 0.5));
lineChart.getXAxis().setAutoRanging(true);
lineChart.getYAxis().setAutoRanging(true);
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
lineChart.setTitle("Test charting");
//defining a series
XYChart.Series series = new XYChart.Series();
series.setName("My Test Data");
//populating the series with data
series.getData().add(new XYChart.Data(1, 23));
series.getData().add(new XYChart.Data(2, 14));
series.getData().add(new XYChart.Data(3, 15));
series.getData().add(new XYChart.Data(4, 24));
series.getData().add(new XYChart.Data(5, 34));
series.getData().add(new XYChart.Data(6, 36));
series.getData().add(new XYChart.Data(7, 22));
series.getData().add(new XYChart.Data(8, 45));
series.getData().add(new XYChart.Data(9, 43));
series.getData().add(new XYChart.Data(10, 17));
series.getData().add(new XYChart.Data(11, 29));
series.getData().add(new XYChart.Data(12, 25));
lineChart.getData().add(series);
}
});
VBox root = new VBox();
root.getChildren().addAll(btn, lineChart);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) { launch(); }
}
Maybe you already found the answer for your problem, but I belive this might help others in the future.
When creating a LineChart in Scene Builder, notice that there are two types of LineCharts: LineChart and LineChart (N x N). The first one has a category x-axis (where you can only define categories to show like a string). The second one has a number x-axis.
So if you want to display data vs. data, like in the example you show, you should use LineChart (N x N).
Hope it helps.
Related
I'd like to to be able to add a label to a chart that follows the cursor. I've already seen several examples of this on stack overflow already that attach labels to the values/nodes of a series but I want to be able to do this for the whole of the area chart. My problem comes when I want to add a label (ultimately a floating box of values). I'm struggling to figure out how I can do this and maintain the layout of the scene. When I add the chart to the scene as the dedicated item the chart renders as expected. But when I add a label to the scene it is shared and the chart no longer fills the entire canvas
Group root = new Group();
root.getChildren().addAll(sac,myLabel);
Scene scene = new Scene(root, 800, 600); // Now the Chart shares the scene with the label
I want to add the label so I can use it as the mouse moves over a chart and then simply translate it to the cursor position. I've no problem with the mouse events just that I can't seem to share a scene with objects that are transient. I believe this is down to a lack of understanding on how to manage this particular situation. It's entirely likely the solution to this problem is to dynamically generate this label and not have it as a static scene object. Any help gratefully received.
Full test case below (Java 8)
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedAreaChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
public class AreaChartTestCase extends Application {
final NumberAxis xAxis = new NumberAxis(1, 31, 1);
final NumberAxis yAxis = new NumberAxis();
final StackedAreaChart<Number, Number> sac
= new StackedAreaChart<Number, Number>(xAxis, yAxis);
Label myLabel = new Label();
#Override
public void start(Stage stage) {
stage.setTitle("Area Chart Sample");
sac.setTitle("Temperature Monitoring (in Degrees C)");
XYChart.Series<Number, Number> seriesApril
= new XYChart.Series<Number, Number>();
seriesApril.setName("April");
seriesApril.getData().add(new XYChart.Data(1, 4));
seriesApril.getData().add(new XYChart.Data(3, 10));
seriesApril.getData().add(new XYChart.Data(6, 15));
seriesApril.getData().add(new XYChart.Data(9, 8));
seriesApril.getData().add(new XYChart.Data(12, 5));
seriesApril.getData().add(new XYChart.Data(15, 18));
seriesApril.getData().add(new XYChart.Data(18, 15));
seriesApril.getData().add(new XYChart.Data(21, 13));
seriesApril.getData().add(new XYChart.Data(24, 19));
seriesApril.getData().add(new XYChart.Data(27, 21));
seriesApril.getData().add(new XYChart.Data(30, 21));
XYChart.Series<Number, Number> seriesMay
= new XYChart.Series<Number, Number>();
seriesMay.setName("May");
seriesMay.getData().add(new XYChart.Data(1, 20));
seriesMay.getData().add(new XYChart.Data(3, 15));
seriesMay.getData().add(new XYChart.Data(6, 13));
seriesMay.getData().add(new XYChart.Data(9, 12));
seriesMay.getData().add(new XYChart.Data(12, 14));
seriesMay.getData().add(new XYChart.Data(15, 18));
seriesMay.getData().add(new XYChart.Data(18, 25));
seriesMay.getData().add(new XYChart.Data(21, 25));
seriesMay.getData().add(new XYChart.Data(24, 23));
seriesMay.getData().add(new XYChart.Data(27, 26));
seriesMay.getData().add(new XYChart.Data(31, 26));
final Node chartPlotBackground = sac.lookup(".chart-plot-background");
chartPlotBackground.setOnMouseEntered((MouseEvent mouseEvent) -> {
myLabel.setVisible(true);
});
chartPlotBackground.setOnMouseExited((MouseEvent mouseEvent) -> {
myLabel.setVisible(false);
});
chartPlotBackground.setOnMouseMoved((MouseEvent mouseEvent) -> {
myLabel.setText("Location : X = " + mouseEvent.getSceneX() + " : Y = "+ mouseEvent.getSceneY());
myLabel.setTranslateX(mouseEvent.getSceneX());
myLabel.setTranslateY(mouseEvent.getSceneY());
});
Group root = new Group();
root.getChildren().addAll(sac, myLabel);
Scene scene = new Scene(root, 800, 600); // Now the Chart shares the scene with the label
sac.getData().addAll(seriesApril, seriesMay);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
So I think I've solved it. At least to a level that meets my requirements.
Group root = new Group();
root.setManaged(false);
root.getChildren().add(ac);
root.getChildren().add(myLabel);
Scene scene = new Scene(root);
scene.widthProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneWidth, Number newSceneWidth) {
ac.setPrefWidth((double) newSceneWidth);
}
});
scene.heightProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneHeight, Number newSceneHeight) {
ac.setPrefHeight((double) newSceneHeight);
}
});
By listening to changes in the width and height properties and manually managing the layout of the chart and label I was able to get the layout and functionality I required.I'm not 100% sure if this the optimal approach but it works.
I created this example of tabs which I want to close after I display confirm dialog and I click Yes button.
public static Tab testconfirmTabClose(Tab tab)
{
tab.setOnClosed(new EventHandler<Event>()
{
#Override
public void handle(Event t)
{
t.consume();
// Dialog Stage init
final Stage dialog = new Stage();
dialog.initModality(Modality.APPLICATION_MODAL);
Button btnYes = new Button("Yes");
btnYes.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent event)
{
dialog.close();
}
});
Button btnNo = new Button("No");
btnNo.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent event)
{
dialog.close();
}
});
// Layout for the Button
HBox hbox = new HBox();
hbox.setSpacing(10);
hbox.setAlignment(Pos.CENTER);
hbox.getChildren().add(btnYes);
hbox.getChildren().add(btnNo);
// Layout for the Label and hBox
VBox vbox = new VBox();
vbox.setAlignment(Pos.CENTER);
vbox.setSpacing(10);
// Text
Text tc = new Text();
tc.setText("Do you want to quit?");
// Layout for the Button
HBox thbox = new HBox();
thbox.setSpacing(10);
thbox.setPadding(new Insets(20, 20, 20, 90)); // Place the dialog text right
thbox.setAlignment(Pos.CENTER_LEFT);
thbox.getChildren().add(tc);
BorderPane bp = new BorderPane();
bp.setPadding(new Insets(15, 15, 10, 15));
bp.setTop(null);
bp.setLeft(vbox);
bp.setCenter(thbox);
bp.setRight(null);
bp.setBottom(hbox);
Scene scene = new Scene(bp, 500, 140);
dialog.setScene(scene);
dialog.show();
}
});
return tab;
}
I have this issue: When I click on the tab to close it the tab is closed and the confirm dialog is displayed. I cannot "freeze" the tab for the user response. Can you tell me how I can fix this problem?
I'm afraid there is no clean way to do this in JavaFX 2.2. JavaFX 8 will (probably) offer a method called Tab#setOnCloseRequest(...) that will do what you want. For 2.2, the only way I see right now is pulling the source from OpenJDK and creating your own adapted TabPane implementation - sorry :-/.
I want to add a context menu on right click of my Line Chart.
Its basically for re-sizing the chart.Is there any way i can achieve this?
Here is a code snippet to bring up a resizing context menu on a line chart when the line chart is right clicked.
final MenuItem resizeItem = new MenuItem("Resize");
resizeItem.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
System.out.println("Resize requested");
}
});
final ContextMenu menu = new ContextMenu(
resizeItem
);
lineChart.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent event) {
if (MouseButton.SECONDARY.equals(event.getButton())) {
menu.show(stage, event.getScreenX(), event.getScreenY());
}
}
});
Some complete sample code:
import javafx.application.Application;
import javafx.event.*;
import javafx.scene.Scene;
import javafx.scene.chart.*;
import javafx.scene.control.*;
import javafx.scene.input.*;
import javafx.stage.Stage;
public class LineChartWithContextMenu extends Application {
#Override public void start(final Stage stage) {
stage.setTitle("Line Chart Sample");
//defining the axes
final NumberAxis xAxis = new NumberAxis();
xAxis.setLabel("Number of Month");
final NumberAxis yAxis = new NumberAxis();
//creating the chart
final LineChart<Number,Number> lineChart =
new LineChart<>(xAxis,yAxis);
lineChart.setTitle("Stock Monitoring, 2010");
//defining a series
XYChart.Series series = new XYChart.Series();
series.setName("My portfolio");
//populating the series with data
series.getData().setAll(
new XYChart.Data(1, 23),
new XYChart.Data(2, 14),
new XYChart.Data(3, 15),
new XYChart.Data(4, 24),
new XYChart.Data(5, 34),
new XYChart.Data(6, 36),
new XYChart.Data(7, 22),
new XYChart.Data(8, 45),
new XYChart.Data(9, 43),
new XYChart.Data(10, 17),
new XYChart.Data(11, 29),
new XYChart.Data(12, 25)
);
lineChart.getData().add(series);
//adding a context menu item to the chart
final MenuItem resizeItem = new MenuItem("Resize");
resizeItem.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
System.out.println("Resize requested");
}
});
final ContextMenu menu = new ContextMenu(
resizeItem
);
lineChart.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent event) {
if (MouseButton.SECONDARY.equals(event.getButton())) {
menu.show(stage, event.getScreenX(), event.getScreenY());
}
}
});
stage.setScene(
new Scene(
lineChart,800,600
)
);
stage.show();
}
public static void main(String[] args) { launch(args); }
}
How can I set stroke (Color) for a BarChart? I always got yellow bars
Here is my code
import java.util.Set;
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class StackPaneTest extends Application {
#Override
public void start(Stage primaryStage) {
final CategoryAxis xAxis1 = new CategoryAxis();
final NumberAxis yAxis1 = new NumberAxis();
final BarChart<String, Number> barChart = new BarChart<String, Number>(xAxis1, yAxis1);
barChart.setAlternativeRowFillVisible(false);
barChart.setLegendVisible(false);
barChart.setAnimated(false);
XYChart.Series serie1 = new XYChart.Series();
serie1.getData().add(new XYChart.Data("Jan", 1));
serie1.getData().add(new XYChart.Data("Feb", 2));
serie1.getData().add(new XYChart.Data("Mar", 1.5));
serie1.getData().add(new XYChart.Data("Apr", 3));
serie1.getData().add(new XYChart.Data("May", 2.5));
serie1.getData().add(new XYChart.Data("Jun", 5));
serie1.getData().add(new XYChart.Data("Jul", 4));
serie1.getData().add(new XYChart.Data("Aug", 8));
serie1.getData().add(new XYChart.Data("Sep", 6.5));
serie1.getData().add(new XYChart.Data("Oct", 13));
serie1.getData().add(new XYChart.Data("Nov", 10));
serie1.getData().add(new XYChart.Data("Dec", 20));
barChart.getData().addAll(serie1);
Set<Node> barNode = barChart.lookupAll(".default-color0.chart-bar");
for(final Node bar : barNode){
bar.setStyle("-fx-stroke: GREEN");
}
StackPane stack = new StackPane();
stack.getChildren().addAll(barChart);
Scene scene = new Scene(stack, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If you want bar to be green, use setStyle("-fx-bar-fill: GREEN");
About stroke applying - it is a separate trouble, but it is not about style applying to the bar.
In common case, you may use caspian.css or modena.css files, to observe samples of style applying.
I am trying to display three images in sequence with some scale transformation to each of them(images of 1,2,3). I am using ScaleTransition on single instance of imageview, after each successive transformation the imageView height get scaled which is not resetting
My Code is
public void start(Stage primaryStage) {
final IntegerProperty intProperty = new SimpleIntegerProperty(0);
final Image[] array = {
(new Image(WaitEffect.class.getResource("1.png")
.toExternalForm())),
(new Image(WaitEffect.class.getResource("2.png")
.toExternalForm())),
(new Image(WaitEffect.class.getResource("3.png")
.toExternalForm())) };
Group root = new Group();
Scene scene = new Scene(root, 400, 400, Color.BLACK);
StackPane pane = StackPaneBuilder.create()
.layoutX(scene.getWidth() / 2).layoutY(scene.getHeight() / 2)
.build();
final ImageView imageView = new ImageView();
pane.getChildren().add(imageView);
Timeline timeline = new Timeline();
int count = 0;
KeyValue value = new KeyValue(intProperty, count++);
KeyFrame frame = new KeyFrame(Duration.millis(2000),
new EventHandler<ActionEvent>() {
int co = 0;
#Override
public void handle(ActionEvent paramT) {
imageView.setImage(array[co]);
ScaleTransition st = ScaleTransitionBuilder.create()
.byX(2).byY(2).node(imageView)
.duration(Duration.millis(500)).build();
st.play();
imageView.setFitHeight(150);
imageView.setFitWidth(150);
imageView.resize(150, 150);
co++;
}
}, value);
timeline.getKeyFrames().add(frame);
timeline.setCycleCount(3);
timeline.play();
root.getChildren().addAll(pane);
primaryStage.setScene(scene);
primaryStage.show();
}`
ImageView doesn't reset it's properties after setting new Image. You need to reset it yourself, e.g. by setting from properties of ScaleTransition:
ScaleTransition st = ScaleTransitionBuilder.create()
.fromX(1).fromY(1)
.byX(2).byY(2).node(imageView)
.duration(Duration.millis(500)).build();