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.
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 want to implement this example: http://docs.oracle.com/javafx/2/ui_controls/toggle-button.htm#
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class test extends Application
{
private void init(Stage primaryStage)
{
Group root = new Group();
primaryStage.setScene(new Scene(root));
String pillButtonCss = DX57DC.class.getResource("PillButton.css").toExternalForm();
// create 3 toggle buttons and a toogle group for them
ToggleButton tb1 = new ToggleButton("Left Button");
tb1.setId("pill-left");
ToggleButton tb2 = new ToggleButton("Center Button");
tb2.setId("pill-center");
ToggleButton tb3 = new ToggleButton("Right Button");
tb3.setId("pill-right");
final ToggleGroup group = new ToggleGroup();
tb1.setToggleGroup(group);
tb2.setToggleGroup(group);
tb3.setToggleGroup(group);
// select the first button to start with
group.selectToggle(tb1);
//////////////////////////////////////////
tb1.setUserData(Color.LIGHTGREEN);
tb2.setUserData(Color.LIGHTBLUE);
tb3.setUserData(Color.SALMON);
final Rectangle rect = new Rectangle(300, 300);
group.selectedToggleProperty().addListener(new ChangeListener<Toggle>()
{
#Override
public void changed(ObservableValue<? extends Toggle> ov,
Toggle toggle, Toggle new_toggle)
{
if (new_toggle == null)
{
rect.setFill(Color.WHITE);
}
else
{
rect.setFill(
(Color) group.getSelectedToggle().getUserData());
}
}
});
///////////////////////////////////////////
rect.setArcHeight(10);
rect.setArcWidth(10);
HBox hBox = new HBox();
hBox.getChildren().addAll(tb1, tb2, tb3);
hBox.setPadding(new Insets(20, 20, 260, 20));
hBox.getStylesheets().add(pillButtonCss);
VBox vbox = new VBox();
vbox.getChildren().add(hBox);
vbox.getChildren().add(rect);
root.getChildren().add(vbox);
}
#Override
public void start(Stage primaryStage) throws Exception
{
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
But for some reason I can see the buttons but the rectangle is not displayed when I try to switch the buttons. Can you help me to find where is my mistake? And also how I can implement this example with several rectangles holding different content?
You have to add rect to your scene graph :
root.getChildren().addAll(hBox, rect);
Also, think of using an appropriate layout for your root, BorderPane instead of Group for example.
Following the Oracle's example for a ListView, I have been able to get a ComboBox color rendering a list of color
My problem is that once a color is selected, ComboBox display the string name while I would like to display the color itself, with or without the color name aside.
How to change this code in order to get the selected color displayed?
Thanks all.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Callback;
public class ProvaComboRendering extends Application {
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
ComboBox<String> cb = new ComboBox<String>();
cb.setPrefSize(150, 20);
root.getChildren().add(cb);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
ObservableList<String> data = FXCollections.observableArrayList(
"chocolate", "salmon", "gold", "coral", "darkorchid",
"darkgoldenrod", "lightsalmon", "black", "rosybrown", "blue",
"blueviolet", "brown");
cb.setItems(data);
cb.setCellFactory(new Callback<ListView<String>, ListCell<String>>(){
#Override
public ListCell<String> call(ListView<String> list){
return new ColorRectCell();
}
});
}
static class ColorRectCell extends ListCell<String>{
#Override
public void updateItem(String item, boolean empty){
super.updateItem(item, empty);
Rectangle rect = new Rectangle(120,18);
if(item != null){
rect.setFill(Color.web(item));
setGraphic(rect);
}
}
}
public static void main(String[] args) {
launch(args);
}
}
Edit: Is it possibile to show a tooltip displaing the name of the color while the mouse pointer is over one of the color list?
Use ComboBox's button cell property. You can use the same cellfactory:
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
ComboBox<String> cb = new ComboBox<String>();
cb.setPrefSize(150, 20);
root.getChildren().add(cb);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
ObservableList<String> data = FXCollections.observableArrayList(
"chocolate", "salmon", "gold", "coral", "darkorchid",
"darkgoldenrod", "lightsalmon", "black", "rosybrown", "blue",
"blueviolet", "brown");
cb.setItems(data);
Callback<ListView<String>, ListCell<String>> factory = new Callback<ListView<String>, ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> list) {
return new ColorRectCell();
}
};
cb.setCellFactory(factory);
cb.setButtonCell(factory.call(null));
}
or define a new cell factory for button cell, like below which adds a tooltipablity :
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
ComboBox<String> cb = new ComboBox<String>();
cb.setPrefSize(150, 20);
root.getChildren().add(cb);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
ObservableList<String> data = FXCollections.observableArrayList(
"chocolate", "salmon", "gold", "coral", "darkorchid",
"darkgoldenrod", "lightsalmon", "black", "rosybrown", "blue",
"blueviolet", "brown");
cb.setItems(data);
Callback<ListView<String>, ListCell<String>> factory = new Callback<ListView<String>, ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> list) {
return new ColorRectCell();
}
};
cb.setCellFactory(factory);
Callback<ListView<String>, ListCell<String>> factoryTooltip = new Callback<ListView<String>, ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> list) {
return new ColorRectTooltipCell();
}
};
cb.setButtonCell(factoryTooltip.call(null));
}
static class ColorRectTooltipCell extends ColorRectCell {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
Tooltip.install(this.getParent(), new Tooltip(item));
}
}
}
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); }
}
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.