Display problem when adding Swing node in JavaFX application - java

I have a JavaFX application for plotting a heatmap. The app is developed with Oracle's JDK 8 on a Windows 10 machine.
I'm using xchart-3.8.0.jar library (https://knowm.org/downloads/xchart/xchart-3.8.0.zip) for plotting the data through a swing node.
Here is the code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.layout.VBox;
import javafx.scene.layout.Priority;
import javafx.embed.swing.SwingNode;
import org.knowm.xchart.HeatMapChart;
import org.knowm.xchart.HeatMapChartBuilder;
import org.knowm.xchart.XChartPanel;
import org.knowm.xchart.style.Styler;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
public class XChartExample extends Application {
#Override public void start(Stage stage) {
VBox vBox = new VBox();
stage.setTitle("Heatmap");
HeatMapChart chart =
new HeatMapChartBuilder().xAxisTitle("X").yAxisTitle("Y").theme(Styler.ChartTheme.Matlab).build();
chart.getStyler().setShowWithinAreaPoint(true);
chart.getStyler().setLegendPosition(Styler.LegendPosition.OutsideS);
chart.getStyler().setLegendLayout(Styler.LegendLayout.Horizontal);
chart.getStyler().setPlotContentSize(1);
chart.getStyler().setShowValue(true);
int[] xData = new int[5];
xData[0] = 10;
xData[1] = 20;
xData[2] = 30;
xData[3] = 40;
xData[4] = 50;
int[] yData = new int[5];
yData[0] = 10;
yData[1] = 20;
yData[2] = 30;
yData[3] = 40;
yData[4] = 50;
int[][] heatData = new int[5][5];
for (int i = 0; i < 5; ++i) {
for (int j = 0; j < 5; ++j) {
heatData[i][j] = i + j;
}
}
chart.addSeries("Basic HeatMap", xData, yData, heatData);
SwingNode swingNode = new SwingNode();
createAndSetSwingContent(swingNode, chart);
vBox.getChildren().add(swingNode);
VBox.setVgrow(swingNode, Priority.ALWAYS);
Scene scene = new Scene(vBox, 500, 350);
stage.setTitle("Demo");
stage.setScene(scene);
stage.show();
}
private void createAndSetSwingContent(SwingNode swingNode, HeatMapChart chart) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JPanel chartPanel = new XChartPanel<>(chart);
swingNode.setContent(chartPanel);
}
});
}
public static void main(String[] args) {
launch(args);
}
}
On running the application I usually get the following black window:
On slightly dragging the window I get the window with the chart plotted:
In some cases, on launching the app the plot is displayed at once without the black screen. In this case when I resize/move the window the black screen may appear again and I have to drag the window again and so on.
The problem does not appear when the app runs on a Virtual Machine (Windows 10) with total video memory 4 MB without enabling 3D support feature. In this case the app runs correctly with no displaying problems.
Is there a way to avoid this behavior?

Related

how to make all cells in GridPane visible and same size?

I've written the code below and that's the output. Is it possible to make all of the rows and columns(cells) visible and to make all of them certain size?
The result I want is to have some sort of "net", so I can put in something into a cell and be sure that the size of all table will remain the same.
GridPane.setVgap() and GridPane.setMinSize() is not what I'm looking for.
enter image description here
package sample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.scene.control.Button;
public class Main extends Application {
Stage window;
#Override
public void start(Stage primaryStage) throws Exception{
window = primaryStage;
GridPane grid = new GridPane();
//buttons
Button btn = new Button("8,5");
GridPane.setConstraints(btn, 8,5);
Button btn2 = new Button("1,3");
GridPane.setConstraints(btn2, 1,3);
grid.getChildren().addAll(btn, btn2);
grid.setGridLinesVisible(true);
Scene scene = new Scene(grid, 600,400);
window.setScene(scene);
window.show();
}
public static void main(String[] args) {
launch(args);
}
}
Thanks in advance!
You cannot span an infinite grid with GridPane. That's just not what the layout is designed for. The gridLinesVisible feature exists for debugging purposes only anyways.
You can achieve cells with the same sizes though, if you know the number of rows/columns though: Use RowConstraints/ColumnConstraints with percentHeight/Width to give every row/column the same size:
int rowCount = 6;
int columnCount = 9;
RowConstraints rc = new RowConstraints();
rc.setPercentHeight(100d / rowCount);
for (int i = 0; i < rowCount; i++) {
grid.getRowConstraints().add(rc);
}
ColumnConstraints cc = new ColumnConstraints();
cc.setPercentWidth(100d / columnCount);
for (int i = 0; i < columnCount; i++) {
grid.getColumnConstraints().add(cc);
}

How do I insert all my shapes into an array?

I am Trying to build a cinema booker, where I have to select the seats. I was thinking about make placing all my rectangles in an array so i could use it for later, when clicking on a seat it should check if left and right seats are booked. Here the array index should help me. However I cant figure out how to get to this stage. (See picture.)
Take this scenario:
You click on a rectangle (representing a seat). It changes color only it is not Red colored. So Seats[][].checkNeighbourColor or something like that.See picture
package sample;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.awt.*;
import java.util.ArrayList;
public class Main extends Application {
private int seats = 12;
private int rows = 8;
private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); //gets screen resolution data
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("");
Group root = new Group();
Scene scene = new Scene(root, screenSize.getWidth()/4, screenSize.getHeight()/3, Color.WHITE);
for (int i = 0; i<= seats; i++)
{
Rectangle r = new Rectangle();
r.setFill(Color.GREEN);
r.setX(scene.getWidth()/5+i*30);
r.setY(scene.getHeight()/5);
r.setWidth(screenSize.getHeight()/80);
r.setHeight(screenSize.getHeight()/80);
root.getChildren().add(r);
for (int q = 0; q<=rows; q++)
{
Rectangle s = new Rectangle();
s.setFill(Color.GREEN);
s.setX(scene.getWidth()/5+i*30);
s.setY(scene.getHeight()/5+q*30);
s.setWidth(screenSize.getHeight()/80);
s.setHeight(screenSize.getHeight()/80);
root.getChildren().add(s);
s.setOnMouseClicked(event ->{
s.setFill(Color.BLACK);
});
}
}
primaryStage.setScene(scene);
primaryStage.show();
}
}
It's not entirely clear to me what you're asking, but can't you just do
private Rectangle[][] rectangles = new Rectangle[seats][rows];
and then just do
rectangles[i][q] = s ;
For your listener you can do
final int row = i ;
final int seat = q ;
s.setOnMouseClicked(event -> {
// check rectangles[row-1][seat] and rectangles[row+1][seat] as needed,
// checking for range of row first
});
Aside: don't mix AWT and JavaFX. Use the Screen API to get the dimension of the physical screen(s) in JavaFX.
Okay I found my mistake. It was a simple ArrayOutOfBound (The statement in my for-loops was wrong), but thank you all for your help :)

I am trying to connect circles with each other with a line from the its centers [duplicate]

This question already has an answer here:
JavaFX: How to connect two Nodes by a Line?
(1 answer)
Closed 7 years ago.
Trying to connect circles together in the picture with a line from the center of the circle to the other circle. The line should be shown will the mouse is being dragged.And when I release the mouse than it should not show any line at all.
This is the code.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.StrokeType;
import javafx.stage.Stage;
public class Projekt extends Application {
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
//Inserting Circles
for(int i=0;i<10;i++)
{
for(int j=0;j<10;j++)
{
Circle c = new Circle(i*60+10,j*60+10,10);
c.setFill(Color.WHITE);
c.setStroke(Color.BLACK);
c.setStrokeWidth(2);
c.setStrokeType(StrokeType.OUTSIDE);
c.setOnMouseDragged((MouseEvent e)->{
Line line = new Line(c.getCenterX(),c.getCenterY(),e.getX(),e.getY());
});
pane.getChildren().add(c);
}
}
Scene scene = new Scene(pane,600,600);
primaryStage.setScene(scene);
primaryStage.setTitle("Hello World!");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
There are several ways to do this.
1-st of all you need to add your line to the pane. Because there is line creation in your code, but nothing adds this line to the pane. And you probably don't want to create new line each time, so just create some line (maybe even static one) and change coords with line.setStartX(x); line.setStartY(y); and line.setEndX(e.getX()); line.setEndY(e.getY()); on each mouse press and mouse drag events.
I think you will not be able to do this with just 1 setOnMouseDragged listener, you have to use at least some other events to set your line visible and invisible.
Here is starter code for you to play with:
public class Main extends Application {
private Line line = new Line();
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
line.setVisible(false);
pane.getChildren().add(line);
//Inserting Circles
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
double x = i * 60 + 10;
double y = j * 60 + 10;
Circle c = new Circle(x, y, 10);
c.setFill(Color.WHITE);
c.setStroke(Color.BLACK);
c.setStrokeWidth(2);
c.setStrokeType(StrokeType.OUTSIDE);
c.setOnMousePressed((MouseEvent e) -> {
line.setStartX(x);
line.setStartY(y);
});
c.setOnMouseReleased((MouseEvent e) -> {
line.setVisible(false);
});
c.setOnMouseDragged((MouseEvent e) -> {
line.setEndX(e.getX());
line.setEndY(e.getY());
line.setVisible(true);
});
pane.getChildren().add(c);
}
}

Javafx: How to transform a Rectangle keeping it in the center?

I have to create a "slot machine effect": I have a root layer an on it I have 3 Rectangles, each one in a TilePane cell. i tryed to add an event handler that should modify the rect (resizing it and rotating it) in order to change the figure that it displays. Unfortunately, my figure is never at the center fo its cell. How can I fix it?
package test;
import java.util.Random;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.TilePane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
/**
*
* #author bog
*/
public class Test extends Application {
StackPane root = new StackPane();
TilePane tp = new TilePane(Orientation.HORIZONTAL);
#Override
public void start(Stage primaryStage) {
//root.getChildren().add(btn);
for(int i = 0; i < 3 ; i++){
final Rectangle r = new Rectangle(100, 100, Color.GREY);
r.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
Random rnd = new Random();
int n = rnd.nextInt(5);
if(n == 0){ // horizontal line
r.setWidth(100);
r.setHeight(5);
System.out.println("Linea orizontale");
}
if(n == 1){ // vertical line
r.setWidth(5);
r.setHeight(100);
System.out.println("Linea verticale");
}
if(n == 2){ // rombo
r.getTransforms().add(new Rotate(45,50,50));
System.out.println("rombo");
}
if(n == 3){ // back-slash line
r.setWidth(5);
r.setHeight(100);
r.getTransforms().add(new Rotate(45,50,50));
System.out.println("Linea /");
}
if(n == 4){ // slash line
r.setWidth(100);
r.setHeight(5);
r.getTransforms().add(new Rotate(45,50,50));
System.out.println("Linea \\");
}
}
});
final StackPane sp = new StackPane();
sp.getChildren().add(r);
tp.getChildren().add(sp);
}
tp.setAlignment(Pos.CENTER);
root.getChildren().add(tp);
Scene scene = new Scene(root, 500, 450);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
i am not quite sure what is your problem , and right now i cant test your code , but almost every time i got alignment issues i fix them with scene builder, where i create the static components by hand , which will be rendered allright , and inside them i place the dynamic components. So your pane could be made from scene builder starting with a basic anchor pane and inside it place 3 panes with your default width, and inside them dynamically add the images you wish each time.
hope it helps...

How to force Java FX scene refresh?

I have an Java FX scene with a start button and several rectangles which represent the tiles of a map. I also have drawn a sphere which represents my explorer (it has to explore the map), but I am having difficulties with running the animation.
In my OnMouseClicked handler for the start button, I start an algorithm for exploring the map which changes the position of the sphere and the colors of the tiles which have been visited. The problem is that the scene won't update itself while the algorithm is running, so I only get to see how the final scene will look like (after the algorithm has stopped running). How can I force a scene update so I can see all the color changes sequentially?
Later edit:
import javafx.application.Application;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
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 static final double boxOuterSize = 50;
private static final double boxInnerSize = 48;
private static final double boxCornerRadius = 20;
private Stage applicationStage;
private Scene applicationScene;
private static double sceneWidth = 1024;
private static double sceneHeight = 800;
private static HBox container = new HBox();
private static Group root = new Group();
private Rectangle[] rectangles = new Rectangle[10];
#Override
public void start(Stage mainStage) throws Exception {
applicationStage = mainStage;
container.setSpacing(10);
container.setPadding(new Insets(10, 10, 10, 10));
try {
applicationScene = new Scene(container, sceneWidth, sceneHeight);
applicationScene.addEventHandler(EventType.ROOT,(EventHandler<? super Event>)this);
applicationScene.setFill(Color.WHITE);
} catch (Exception exception) {
System.out.println ("exception : "+exception.getMessage());
}
applicationStage.setTitle("HurtLockerRobot - Tema 3 IA");
applicationStage.getIcons().add(new Image("icon.png"));
applicationStage.setScene(applicationScene);
for(int i=0; i<10; i++) {
Rectangle r = new Rectangle();
r.setFill(Color.BLUE);
r.setX(i * boxOuterSize);
r.setY(0);
r.setWidth(boxInnerSize);
r.setHeight(boxInnerSize);
r.setArcHeight(boxCornerRadius);
r.setArcWidth(boxCornerRadius);
r.setSmooth(true);
rectangles[i] = r;
root.getChildren().add(rectangles[i]);
}
container.getChildren().add(root);
Button startButton = new Button("Start");
startButton.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event arg0) {
for(int i=0; i<10; i++) {
rectangles[i].setFill(Color.RED);
// TODO: some kind of scene refresh here
}
}
});
container.getChildren().add(startButton);
applicationStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Initially all the rectangles are blue. The behavior I want to obtain here is to see the rectangles changing colors sequentially. The problem is that I only get to see the end result (all the rectangles change their color at the same time).
This is an old question and it caught my eye since this is a very general issue faced by people new to JavaFX.
The problem that OP is facing is because he updates all the rectangles at once, without waiting.
OP can wait by either creating a new Thread, put the thread on sleep for an estimated seconds for every iteration of the loop and then update the color of the rectangle on JavaFX application thread by using Platform.runLater.
#Override
public void handle(Event arg0) {
new Thread(() -> {
for(int i=0; i<10; i++) {
try {
Thread.sleep(1000); // Wait for 1 sec before updating the color
} catch (InterruptedException e) {
e.printStackTrace();
}
int finalI = i;
Platform.runLater(() -> rectangles[finalI].setFill(Color.RED));// Update on JavaFX Application Thread
}
}).start();
The above snippet is more of a traditional way of doing things. If we want to use the "JavaFX" ways of doing things, we can achieve the same by using an Animation.
Below is a code snippet which will wait for x-seconds before changing the color of the rectangle. It doesn't need any extra thread since the wait is handled by PauseTransition applied for each rectangle.
startButton.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event arg0) {
for(int i=0; i<10; i++) {
PauseTransition pauseTransition = new PauseTransition(Duration.seconds(i));
int finalI = i;
pauseTransition.setOnFinished(event -> rectangles[finalI].setFill(Color.RED));
pauseTransition.play();
}
}
});
It creates a PauseTransition for each rectangle and depending on its index in the array rectangles, it waits for the same number of seconds before updating the color.
This is because of :
exception : Test cannot be cast to javafx.event.EventHandler
Well, I have no idea how Class cast exception came up.
Otherwise, to delay, you can use Thread.sleep().
UPDATE:
Its good to use AnimationTimer to create an animation, you don't need to refresh anything.
Here, I have done a short EG to show color rect using FillTransition.
CODE:
import javafx.animation.FillTransition;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class NewFXMain1 extends Application {
private static final double boxOuterSize = 50;
private static final double boxInnerSize = 48;
private static final double boxCornerRadius = 20;
private Rectangle rectangles = new Rectangle();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("rect");
Button btn = new Button();
StackPane root = new StackPane();
Rectangle r = new Rectangle();
r.setFill(Color.BLUE);
r.setX(2 * boxOuterSize);
r.setY(0);
r.setWidth(boxInnerSize);
r.setHeight(boxInnerSize);
r.setArcHeight(boxCornerRadius);
r.setArcWidth(boxCornerRadius);
r.setSmooth(true);
r.localToScene(boxOuterSize, boxOuterSize);
rectangles = r;
root.getChildren().add(rectangles);
btn.setText("display");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
FillTransition ft = new FillTransition(Duration.millis(3000), rectangles, Color.RED, Color.BLUE);
ft.setCycleCount(4);
ft.setAutoReverse(true);
ft.play();
}
});
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
}

Categories

Resources