I've got this code right now.
public class ScalableChart extends VBox implements Initializable{
#FXML
private LineChart<Number, Number> chart;
#FXML
private Rectangle zoomRect;
#FXML
private StackPane pane;
private boolean selectionGestureStarted = false;
private ObjectProperty<javafx.geometry.Point2D> mouseAnchor = new SimpleObjectProperty<>();;
public ScalableChart() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(
"hello.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
zoomRect.setManaged(true);
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
#FXML
protected void reset(MouseEvent event) {
System.out.println("Reset");
chart.getXAxis().setAutoRanging(true);
chart.getYAxis().setAutoRanging(true);
final NumberAxis xAxis = (NumberAxis)chart.getXAxis();
xAxis.setLowerBound(0);
xAxis.invalidateRange(chart.getData().);
xAxis.setUpperBound(Collections.max(chart.getData().));
final NumberAxis yAxis = (NumberAxis)chart.getYAxis();
yAxis.setLowerBound(0);
yAxis.setUpperBound(1000);
zoomRect.setWidth(0);
zoomRect.setHeight(0);
}
#FXML
protected void startSettingZoom(MouseEvent event) {
setSelectionGestureStarted(true);
mouseAnchor.set(new javafx.geometry.Point2D(event.getX(), event.getY()));
zoomRect.setWidth(0);
zoomRect.setHeight(0);
}
#FXML
protected void changeZoomSettings(MouseEvent event) {
if (isSelectionGestureStarted()) {
zoomRect.setX(Math.min(event.getX(), mouseAnchor.get().getX()));
zoomRect.setY(Math.min(event.getY(), mouseAnchor.get().getY()));
zoomRect.setWidth(Math.abs(event.getX() - mouseAnchor.get().getX()));
zoomRect.setHeight(Math.abs(event.getY() - mouseAnchor.get().getY()));
}
}
#FXML
protected void stopSettingZoom(MouseEvent event) {
chart.getXAxis().setAutoRanging(false);
chart.getYAxis().setAutoRanging(false);
doZoom(zoomRect, chart);
zoomRect.setWidth(0);
zoomRect.setHeight(0);
setSelectionGestureStarted(false);
}
private void doZoom(javafx.scene.shape.Rectangle zoomRect, LineChart<Number, Number> chart) {
javafx.geometry.Point2D zoomTopLeft = new javafx.geometry.Point2D(zoomRect.getX(), zoomRect.getY());
javafx.geometry.Point2D zoomBottomRight = new javafx.geometry.Point2D(zoomRect.getX() + zoomRect.getWidth(), zoomRect.getY() + zoomRect.getHeight());
final NumberAxis yAxis = (NumberAxis) chart.getYAxis();
javafx.geometry.Point2D yAxisInScene = yAxis.localToScene(0, 0);
final NumberAxis xAxis = (NumberAxis) chart.getXAxis();
javafx.geometry.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);
}
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
chart.setTitle("Title");
LineChart.Series<Number, Number> series = new LineChart.Series<Number, Number>();
series.getData().add(new XYChart.Data<Number, Number>(10, 10));
series.getData().add(new XYChart.Data<Number, Number>(20, 20));
chart.getData().add(series);
}
public boolean isSelectionGestureStarted() {
return selectionGestureStarted;
}
public void setSelectionGestureStarted(boolean selectionGestureStarted) {
this.selectionGestureStarted = selectionGestureStarted;
}
}
Also right now I can't see selection rectangle when it's on chart. I'm using the stackpane and I tried to add rectangle before the chart and also after the chart. None of this is working.
Make a transparent rectangle with red border:
zoomRect.setStyle("-fx-fill:Transparent; -fx-stroke:RED");
Add it to the Scene root and call setX() adn seY() to place the upper left corner in the right position.
Related
I've create a small app which uses a live animated line chart to display the number of files in a certain folder every 2 seconds. All works well but the chart/yAxis keeps displaying double values after every update. The folder can contain between 0 and 30.000 files ...
#FXML
public LineChart<String, Integer> myChart;
#FXML
public CategoryAxis xAxis;
#FXML
public NumberAxis yAxis;
...
yAxis.setMinorTickVisible(false);
yAxis.setTickMarkVisible(false);
yAxis.setTickUnit(1);
How do I make sure the y-Axis only contains/uses integer values ... ?
The x,5 values/rows should never be used.
The tick unit is automatically set if autoranging is enabled (which it is by default). See the documentation.
So one way is to turn off autoranging, and set the range manually. This may need you to update the range when the data change, but the following demonstrates the idea:
public class ChartTest extends Application {
#Override
public void start(Stage stage) throws IOException {
CategoryAxis xAxis = new CategoryAxis();
NumberAxis yAxis = new NumberAxis();
yAxis.setTickUnit(1);
yAxis.setMinorTickVisible(false);
yAxis.setTickMarkVisible(false);
yAxis.setAutoRanging(false);
Random rng = new Random();
XYChart.Series<String, Integer> data = new XYChart.Series<>();
int max = Integer.MIN_VALUE;
for (String x : "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("")) {
int y = rng.nextInt(10);
if (y > max) max = y ;
data.getData().add(new XYChart.Data<>(x, y));
}
yAxis.setUpperBound(max + 1);
data.setName("Data");
LineChart<String, Integer> chart = new LineChart(xAxis, yAxis);
chart.getData().add(data);
Scene scene = new Scene(new BorderPane(chart), 600, 800);
stage.setTitle("Chart Test");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
If you want to retain autoranging, you can use a tick label formatter that returns an empty string for non-integer values:
public class ChartTest extends Application {
#Override
public void start(Stage stage) throws IOException {
CategoryAxis xAxis = new CategoryAxis();
NumberAxis yAxis = new NumberAxis();
yAxis.setMinorTickVisible(false);
yAxis.setTickMarkVisible(false);
yAxis.setTickLabelFormatter(new StringConverter<Number>() {
#Override
public String toString(Number number) {
double d = number.doubleValue();
if (d == Math.rint(d)) {
return Integer.toString(number.intValue());
}
return "" ;
}
// Not used
#Override
public Number fromString(String s) {
return null;
}
});
Random rng = new Random();
XYChart.Series<String, Integer> data = new XYChart.Series<>();
for (String x : "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("")) {
int y = rng.nextInt(10);
data.getData().add(new XYChart.Data<>(x, y));
}
data.setName("Data");
LineChart<String, Integer> chart = new LineChart(xAxis, yAxis);
chart.getData().add(data);
Scene scene = new Scene(new BorderPane(chart), 600, 800);
stage.setTitle("Chart Test");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I am trying to run a method in a controller class specified to a particular task, once a specified key is pressed using KeyListener. But i'm unable to detect the keypress and invoke the java.awt.event keyPressed method. My code is as follows :
public class POSController implements KeyListener {
#Override
public void keyPressed(java.awt.event.KeyEvent e) {
if (e.getKeyCode() == com.sun.glass.events.KeyEvent.VK_F1) {
try {
paymentAction();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
What could have gone wrong? Thanks in advance.
Here is the minimal executable example of the problem.
public class POSController implements KeyListener {
#FXML
private TableView<Product> productTableView;
#FXML
private TableView<Item> listTableView;
#FXML
private MenuItem logoutItem, profile;
#FXML
private javafx.scene.image.ImageView backImage;
#FXML
private MenuButton menuButton;
#FXML
private TableColumn<Item, String> itemColumn;
#FXML
private ComboBox<String> clientId, paymentMethod;
#FXML
private TableColumn<Item, Double> priceColumn, totalColumn, discountPercentageColumn, amountColumn;
#FXML
private TableColumn<Item, Integer> quantityColumn;
#FXML
private TableColumn<Product, String> productColumn;
#FXML
private TextField searchField,discountPercentage,productField,priceField,quantityField,vatPercentage,subTotalField,discountField,totalVatField,vatField,netPayableField,totalDiscountField;
#FXML
private TextField ;
#FXML
private TextField ;
#FXML
private TextField ;
#FXML
private TextField ;
#FXML
private TextArea descriptionArea;
#FXML
private Button addButton, removeButton, paymentButton, resetTableButton, resetButton;
#FXML
private Label quantityLabel, errorLabel, userName, backLabel;
#FXML
private ObservableList<Item> ITEMLIST;
public static Scene paymentScene;
private double xOffset = 0;
private double yOffset = 0;
public static double finalNetPayablePrice = 0.0;
public static double finalSubTotalPrice = 0.0;
public static double finalVat = 0.0;
public static double finalDiscount = 0.0;
public static String clientName = null;
public static String selectedPaymentMethod = null;
public static List<String> itemNames = new ArrayList<>();
public static List<Double> itemDiscounts = new ArrayList<>();
public static List<String> prices = new ArrayList<>();
public static List<String> quantities = new ArrayList<>();
public static List<String> subTotals = new ArrayList<>();
public static ObservableList<Item> itemList;
public static List<String> columnItemData = new ArrayList<>();
public static List<String> columnQuantityData = new ArrayList<>();
#FXML
private void initialize() throws SQLException, ClassNotFoundException, IOException {
ObservableList<Product> productsData = ProductDAO.searchGoodProducts(app.values.getProperty("STATUS_TYPE1"));
populateProducts(productsData);
}
#FXML
private void populateProducts(ObservableList<Product> productData) throws ClassNotFoundException {
productTableView.setItems(productData);
}
#Override
public void keyTyped(java.awt.event.KeyEvent e) {
}
#Override
public void keyPressed(java.awt.event.KeyEvent e) {
if (e.getKeyCode() == java.awt.event.KeyEvent.VK_F1) {
try {
paymentAction();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
#Override
public void keyReleased(java.awt.event.KeyEvent e) {
}
#FXML
public void paymentAction() throws Exception {
if (validateInputsForPayment()) {
Payment payment = new Payment();
FXMLLoader loader = new FXMLLoader((getClass().getResource(app.values.getProperty("INVOICE_VIEW_LOCATION"))));
Parent root = loader.load();
Stage stage = new Stage();
root.setOnMousePressed((MouseEvent e) -> {
xOffset = e.getSceneX();
yOffset = e.getSceneY();
});
root.setOnMouseDragged((MouseEvent e) -> {
stage.setX(e.getScreenX() - xOffset);
stage.setY(e.getScreenY() - yOffset);
});
Scene scene = new Scene(root);
stage.initModality(Modality.APPLICATION_MODAL);
stage.initStyle(StageStyle.UNDECORATED);
stage.setScene(scene);
this.paymentScene = scene;
stage.showAndWait();
}
}
You shouldn't be using java.awt.event.KeyListener for a JavaFX application. JavaFX has its own set of event API.
Assuming that POSController is a controller class for a particular FXML:
public class POSController {
#FXML private BorderPane root; // Or any other Node from FXML file
#FXML private void initialize() {
javafx.event.EventHandler<javafx.scene.input.KeyEvent> handler = event -> {
if (event.getCode() == javafx.scene.input.KeyCode.F1) {
try {
paymentAction();
} catch (Exception e1) {
e1.printStackTrace();
}
}
};
// I'm using root to get scene, but any node would be fine
if (root.getScene() != null) {
root.getScene().addEventHandler(javafx.scene.input.KeyEvent.KEY_PRESSED, handler);
}
else {
root.sceneProperty().addListener((obs, oldScene, newScene) -> {
if (newScene != null) {
root.getScene().addEventHandler(javafx.scene.input.KeyEvent.KEY_PRESSED, handler);
}
});
}
}
}
This will add the key event to the Scene. If you do not need to apply this event scene-wide, then you can add the event handler at other appropriate nodes.
Update
If there are any input controls in the scene, then you may need to use setEventFilter() instead of setEventHandler(). This is because those controls are probably going to consume the key event during the event bubbling phase.
I am creating a bar chart as you can see in photo. The problem is that the name of bars on XAxis are some how not match to the bar.
The tick label rotation is set to -45 in fxml. How can I shift them exactly under the bar?
public class MostComputerizedController {
#FXML
private BarChart<String, Number> barChart;
#FXML
private CategoryAxis orgNameAxis;
#FXML
private NumberAxis yAxis;
#FXML
private Label itsfField;
private ObservableList<String> orgNames = FXCollections
.observableArrayList();
private DataConstructor dc = new DataConstructor();
private int numberOfOrganizations;
private List<Double> sumOfOrgsITSF = new ArrayList<Double>();
/**
* sets the name of x axis, with the name of organizations
*/
#FXML
private void initialize() {
dc.findSortedAssignedOrg();
dc.orgFuncFuncType();
orgNames.addAll(dc.getOrgFuncFunctype().keySet());
orgNameAxis.setCategories(orgNames);
orgNameAxis.setLabel("Name of Organizations");
orgNameAxis.tickLabelFontProperty().set(Font.font(9));
yAxis.setLabel("Saturation");
numberOfOrganizations = dc.getSortedAssignedOrg().size();
}
/**
* sets organization and their saturation
*/
public void setOrgData() {
XYChart.Series<String, Number> seriesGreen = new XYChart.Series<>();
XYChart.Series<String, Number> seriesYellow = new XYChart.Series<>();
seriesGreen.setName("IT Saturation Satisfying");
seriesYellow.setName("IT Saturation not Satisfying");
for (Entry<String, List<Double>> entry : dc.getOrgFuncFunctype()
.entrySet()) {
sumOfOrgsITSF.add(entry.getValue().get(0));
if (entry.getValue().get(0) > 50) {
seriesGreen.getData().add(
new XYChart.Data<String, Number>(entry.getKey(), entry
.getValue().get(0)));
} else if ((entry.getValue().get(0) <= 50)) {
seriesYellow.getData().add(
new XYChart.Data<String, Number>(entry.getKey(), entry
.getValue().get(0)));
}
}
double value = sumOfOrgsITSF.stream().mapToDouble(Double::doubleValue)
.sum()
/ numberOfOrganizations;
itsfField.setText(String.format("%.0f", value) + "%");
barChart.setBarGap(1);
barChart.setCategoryGap(10);
barChart.getData().addAll(seriesGreen, seriesYellow);
}
Put all your data in a single series.
series1.getData().add(new Data<Number, String>(Integer1),(Title1)));
series1.getData().add(new Data<Number, String>(Integer2),(Title2)));
series1.getData().add(new Data<Number, String>(Integer3),(Title3)));
Results:
I have created a chart bar, and I wish to change the bar color according to the name of values. It has two series one shows number of green values which should be green and the other one yellow. I have also created a css file, but first problem is a get unknown property error for -fx-bar-fill in css. Beside I do not know how to call the appropriate color after creating the series? I face this warning at the time:
Mar 23, 2015 8:50:02 AM com.sun.javafx.css.parser.CSSParser parse
WARNING: CSS Error parsing file:/home/iman/workspace/AddNodeDynamicly /bin/application/chart.css: Expected LBRACE at [1,9]
Here I create the chart
public class MostComputerizedController {
#FXML
private BarChart<String, Number> barChart;
#FXML
private CategoryAxis orgNameAxis;
#FXML
private NumberAxis yAxis;
private ObservableList<String> orgNames = FXCollections
.observableArrayList();
private DataConstructor dc = new DataConstructor();
#FXML
private void initialize() {
orgNames.addAll(dc.getSortedAssignedOrg().values());
orgNameAxis.setCategories(orgNames);
orgNameAxis.setLabel("Name of Organizations");
orgNameAxis.tickLabelFontProperty().set(Font.font(10));
yAxis.setLabel("Saturation");
orgNameAxis.getStylesheets().add(
getClass().getResource("/application/application.css").toExternalForm());
}
/*
* *
* Sets the organization to show the statistics for.
*
* #param
*/
public void setPersonData() {
XYChart.Series<String, Number> series = new XYChart.Series<>();
XYChart.Series<String, Number> seriesy = new XYChart.Series<>();
series.setName("Green");
seriesy.setName("Yellow");
for (String entryOrg : dc.getSortedAssignedOrg().values()) {
for (List<String> entryfuncType : dc.getFuncTypeOrg().values()) {
if (entryOrg.equals(entryfuncType.get(5))
&& entryfuncType.contains("hasType")) {
int yellow = Collections.frequency(entryfuncType, "yellow");
int green = Collections.frequency(entryfuncType, "Green");
int typeNumber = Collections.frequency(entryfuncType,
"hasType");
series.getData().add(
new XYChart.Data<String, Number>(entryOrg, green));
seriesy.getData().add(
new XYChart.Data<String, Number>(entryOrg, yellow));
}
}
}
barChart.getData().addAll(series,seriesy);
}
}
In main I add it to stage:
public class Main extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
private Model model = new Model();
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("IT-Saturation");
initRootLayout();
showOverView();
}
private void showOverView() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("/view/OverView.fxml"));
loader.setController(new OverViewController(model));
AnchorPane overView = (AnchorPane) loader.load();
rootLayout.setCenter(overView);
} catch (IOException e) {
e.printStackTrace();
}
}
private void initRootLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("/view/RootLayout.fxml"));
loader.setController(new RootLayoutController(model));
rootLayout = (BorderPane) loader.load();
// show scene containing the root layout
Scene scene = new Scene(rootLayout);
scene.getStylesheets().add(
getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
RootLayoutController controller = loader.getController();
controller.setMainApp(this);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
public void showMostComputerizedStatistics() {
try {
// Load the fxml file and create a new stage for the popup.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class
.getResource("/view/MostComputerized.fxml"));
AnchorPane page = (AnchorPane) loader.load();
Stage dialogStage = new Stage();
dialogStage.setTitle("Saturation in Organizations");
dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.initOwner(primaryStage);
Scene scene = new Scene(page);
dialogStage.setScene(scene);
dialogStage.getScene().getStylesheets().add(
getClass().getResource("chart.css").toExternalForm());
MostComputerizedController controller = loader.getController();
controller.setPersonData();
dialogStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
and this is the css file:
#CHARSET "UTF-8";
.default-color0.chart-bar {
-fx-bar-fill: green;
}
.default-color1.chart-bar {
-fx-bar-fill: yellow;
}
If could get rid of the css warning by using -fx-background-color instead
.default-color0.chart-bar { -fx-background-color: rgb(146,208,80); }
I am making a school project in JAVAFX and I can't figure out how to collide with my 2 objects(bullet and enemy tank). Can someone tell me the right way to do it? I am trying more than 3 weeks... google everything but still not working.
public class TankGame extends Application {
private final static int WIDTH = 800;
private final static int HEIGHT = 600;
private final static Image BACKGROUND_IMAGE = new Image(TankGame.class.getResource("imgs/Tank_back.png").toString());
private final static Image PATRONA = new Image(TankGame.class.getResource("imgs/Tank_patrona.png").toString());
private Animation modelAnimacePatrony;
private Group patrona;
private double smerStrelyX, smerStrelyY;
private Otaceni otaceni = new Otaceni();
private TankHrac tankHrac = new TankHrac();
private TankProtivnik tankProtivnik = new TankProtivnik();
#Override
public void start(Stage primaryStage) {
final ImageView background = new ImageView(BACKGROUND_IMAGE);
final ImageView bullet = new ImageView(PATRONA);
patrona = new Group(bullet);
final Group root = new Group(background, tankHrac, tankProtivnik, patrona);//deti
patrona.setVisible(false);
Scene scene = new Scene(root, WIDTH, HEIGHT); //okno
tankHrac.setTranslateX(50);//defaultni vyskyt modelu
tankHrac.setTranslateY(50);//defaultni vyskyt modelu
tankProtivnik.setTranslateX(350);//defaultni vyskyt modeluProtivnika
tankProtivnik.setTranslateY(150);//defaultni vyskyt modeluProtivnika
smerStrelyX = tankHrac.getTranslateX();
smerStrelyY = tankHrac.getTranslateY()-250;
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent ke) {
/**
* Shooting
*/
if( ke.getCode() == KeyCode.SPACE ) {
if(!patrona.isVisible()){
//patrona.setVisible(true);
shooting(smerStrelyX,smerStrelyY, tankHrac);
}
}
}
});
primaryStage.setTitle("Tank 1.0");
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setResizable(false);
}
public void shooting(double smerStrelyX, double smerStrelyY, TankHrac jakyModelTanku){
patrona.setVisible(true);
modelAnimacePatrony = TranslateTransitionBuilder.create()
.node(patrona)
.fromX(jakyModelTanku.getTranslateX()+30)
.toX(smerStrelyX+30)
.fromY(jakyModelTanku.getTranslateY()+30)
.toY(smerStrelyY+30)
.duration(Duration.seconds(1))
.onFinished(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent t){
modelAnimacePatrony.stop();
patrona.setVisible(false);
}
})
.build();
modelAnimacePatrony.play();
}
here are all source files: https://www.dropbox.com/sh/1iq98jtxh8m06tt/7Y9LQSjfYs