While-loop causes window to crash in JavaFX - java

I am trying to create a very simple, easy-to-use program that actively reads and displays the position of the mouse. I have seen many tutorials that create programs that read the position of the mouse only when it is inside the window of the GUI application, or after hitting a button, but I want one that displays the position of the mouse in all areas of the screen. This is what I have:
import java.awt.MouseInfo;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class MouseCoordinates extends Application{
public static void main(String[] args) {
launch();
}
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Mouse Reader");
Label x = new Label();
Label y = new Label();
StackPane layout = new StackPane();
layout.getChildren().addAll(x, y);
Scene scene = new Scene(layout, 600, 500);
primaryStage.setScene(scene);
primaryStage.show ();
double mouseX = 1.0;
double mouseY = 1.0;
while(true){
mouseX = MouseInfo.getPointerInfo().getLocation().getX();
mouseY = MouseInfo.getPointerInfo().getLocation().getY();
x.setText("" + mouseX);
y.setText("" + mouseY);
}
}
}
I understand that this while-loop is the cause of the window crashing, but I can not figure out a way around it. Can anyone explain why I can not use a while-loop for JavaFX, as well as a way to solve this?

Your start() method don't have any change to exit the loop and therefore to return as you defined an infinite loop : while(true){...} without return statement.
Why not use a Timeline ?
Timeline timeLine = new Timeline(new KeyFrame(Duration.seconds(1), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
mouseX = MouseInfo.getPointerInfo().getLocation().getX();
mouseY = MouseInfo.getPointerInfo().getLocation().getY();
x.setText("" + mouseX);
y.setText("" + mouseY);
}
}));
timeLine.setCycleCount(Timeline.INDEFINITE);
timeLine.play();
or with a lambda :
Timeline timeLine = new Timeline(new KeyFrame(Duration.seconds(1),
e -> {
mouseX = MouseInfo.getPointerInfo().getLocation().getX();
mouseY = MouseInfo.getPointerInfo().getLocation().getY();
x.setText("" + mouseX);
y.setText("" + mouseY);
}
));
timeLine.setCycleCount(Timeline.INDEFINITE);
timeLine.play();
Another way to address your requirement could be using addEventFilter( MouseEvent.MOUSE_MOVED) on the StackPane object :
layout.addEventFilter(MouseEvent.MOUSE_MOVED, e -> {
x.setText("" + e.getScreenX());
y.setText("" + e.getScreenY());
});
The MouseEvent class provides both X and Y absolute position on the device and
on the source component :
getScreenX()
Returns absolute horizontal position of the event.
getScreenY()
Returns the absolute vertical y position of the event
getX();
Horizontal position of the event relative to the origin of the
MouseEvent's source.
getY();
Vertical position of the event relative to the origin of the
MouseEvent's source.

Related

ImageView onMouseClicked does not work with transparent images [duplicate]

JavaFx ImageView doesn't trigger Mouse Events such as press or drag if you click or drag on a transparent pixel, is there anyway to work around this and detect mouse events from transparent areas ?
I have this image
that i added into this very simple JavaFX scene
using an ImageView named view and i want to move it with Mouse Drag events
so i wrote this code
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application{
double initMx, initMy,initX, initY;
#Override
public void start(Stage ps) throws Exception {
StackPane pane = new StackPane();
Image im = new Image("0.png");
ImageView view = new ImageView(im);
double fact = im.getWidth() / im.getHeight();
view.setFitHeight(300);
view.setFitWidth(300 * fact);
view.setOnMousePressed(e->{
initX = view.getTranslateX();
initY = view.getTranslateY();
initMx = e.getSceneX();
initMy = e.getSceneY();
});
view.setOnMouseDragged(e->{
double dx = initMx - e.getSceneX();
double dy = initMy - e.getSceneY();
double nx = initX - dx;
double ny = initY - dy;
view.setTranslateX(nx);
view.setTranslateY(ny);
});
pane.getChildren().add(view);
Scene scene = new Scene(pane, 500, 500);
ps.setScene(scene);
ps.show();
}
public static void main(String[] args) {
launch(args);
}
}
this code works fine so far,
but if you press or drag somewhere like under his ears ( or anywhere transparent ) nothing will happen ! how to fix this !
The more natural and easiest solution would have been to just set pick on bounds to true.
view.setPickOnBounds(true);
You can do so by setting this image as a graphic in a Button like so
button.setGraphics(new ImageView(im));
Note: You will need to remove style from the button after adding the ImageView by setting the button background with a transparent background color
Try this, if you didn't yet:
view.setOnMouseDragged(e->{
double dx = initMx - e.getX();
double dy = initMy - e.getY();

Create an Ellipse in javavfx where the scaling is from the top left (like a Rectangle) rather than center

I have a javafx program that generates a canvas where a user can draw an Ellipse. I am using the press down, drag and release technique for the mouse.
However I want to be able to scale the shape in size from the top left corner of the shape (much like a Rectangle is scaled) rather than its center. Is there any suggestions to how I could achieve this?
#Override
public void start(Stage ellipseStage) {
//Create ellipse
ellipsePane = new Pane();
ellipsePane.setMinSize(600,600);
ellipsePane.setOnMousePressed(e -> {
if (e.getButton() == MouseButton.PRIMARY) {
ellipse = new Ellipse();
ellipse.setCenterX(e.getX());
ellipse.setCenterY(e.getY());
ellipse.setStroke(Color.ORANGE);
ellipse.setFill(Color.BLACK);
ellipsePane.getChildren().add(ellipse);
}
//if we double click
if (e.getClickCount() == 2) {
ellipse = null;
}
});
//When the mouse is dragged the ellipse expands
ellipsePane.setOnMouseDragged(e -> {
if (ellipse != null) {
ellipse.setRadiusX(e.getX() - ellipse.getCenterX());
ellipse.setRadiusY(e.getY() - ellipse.getCenterY());
}
});
Scene scene = new Scene(ellipsePane);
ellipseStage.setScene(scene);
ellipseStage.show();
}
public static void main(String[] args) {
launch(args);
}}
One option is to keep track of the location of the original mouse press, then set the center X/Y properties of the Ellipse to always be the midpoint between the origin and where the mouse is currently (i.e. where it's dragged). The radii would be half the distance between the origin and the current location as well. Here's an example:
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Ellipse;
import javafx.stage.Stage;
public class Main extends Application {
private Pane pane;
private Point2D origin;
private Ellipse ellipse;
#Override
public void start(Stage primaryStage) {
pane = new Pane();
pane.setOnMousePressed(this::handleMousePressed);
pane.setOnMouseDragged(this::handleMouseDragged);
pane.setOnMouseReleased(this::handleMouseReleased);
primaryStage.setScene(new Scene(pane, 600.0, 400.0));
primaryStage.show();
}
private void handleMousePressed(MouseEvent event) {
event.consume();
origin = new Point2D(event.getX(), event.getY());
ellipse = new Ellipse(event.getX(), event.getY(), 0.0, 0.0);
pane.getChildren().add(ellipse);
}
private void handleMouseDragged(MouseEvent event) {
event.consume();
ellipse.setCenterX((origin.getX() + event.getX()) / 2.0);
ellipse.setCenterY((origin.getY() + event.getY()) / 2.0);
ellipse.setRadiusX(Math.abs(event.getX() - origin.getX()) / 2.0);
ellipse.setRadiusY(Math.abs(event.getY() - origin.getY()) / 2.0);
}
private void handleMouseReleased(MouseEvent event) {
event.consume();
ellipse = null;
origin = null;
}
}
So I presume what you mean is that when you drag the ellipse, the edge position changes but you want the edge position to stay the same and the center to move.
ellipsePane.setOnMouseDragged(e -> {
if (ellipse != null) {
double x0 = ellipse.getCenterX() - ellipse.getRadiusX();
double y0 = ellipse.getCenterY() - ellipse.getRadiusY();
//the new radii
double rxp = e.getX() - x0;
double ryp = e.getY() - y0;
//the new center positions. to keep the origin the same.
double cx = x0 + rxp;
double cy = y0 + ryp;
ellipse.setRadiusX(rxp);
ellipse.setRadiusY(ryp);
ellipse.setCenterX(cx);
ellipse.setCenterY(cy);
}
});

Add constraints to lines in javaFX

How would I go about adding a constraint to how much a line can be dragged? I have a stick man and you can drag all his arms and legs, head and back about but I want them to stay the same length as they started off, so you can't stretch them longer or shorter than they should be, just move them up and down, side to side, in a circle etc. I guess i have to do something with the start/end x and y but im not sure how to set a set constraint to it and also still have it be draggable and stay the same length
private Line connectLines(Line line, Circle startNode, Circle endNode) {
line.startXProperty().bind(startNode.centerXProperty().add(startNode.translateXProperty()));
line.startYProperty().bind(startNode.centerYProperty().add(startNode.translateYProperty()));
line.endXProperty().bind(endNode.centerXProperty().add(endNode.translateXProperty()));
line.endYProperty().bind(endNode.centerYProperty().add(endNode.translateYProperty()));
return line;
}
//mouse pressed event
EventHandler<MouseEvent> mousePressed = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
System.out.println("pressed");
sceneX = e.getSceneX();
sceneY = e.getSceneY();
translateCircleX = ((Circle)(e.getSource())).getTranslateX();
translateCircleY = ((Circle)(e.getSource())).getTranslateY();
}
};
//mouse dragged event
EventHandler<MouseEvent> mouseDragged = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
System.out.println("dragged");
double offsetX = e.getSceneX() - sceneX;
double offsetY = e.getSceneY() - sceneY;
double newTranslateCircleX = translateCircleX + offsetX;
double newTranslateCircleY = translateCircleY + offsetY;
((Circle)(e.getSource())).setTranslateX(newTranslateCircleX);
((Circle)(e.getSource())).setTranslateY(newTranslateCircleY);
}
};
Here is an example. This example does not use Circle.setTranslate#. It uses Circle.setCenter#. It also uses Math.hypot to keep track of the Line length. If the line length becomes greater than or equal to 100, the change in the shape movements is subtracted.
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class TableViewDemo2 extends Application
{
double sceneX, sceneY;
Circle circle = new Circle(15, Color.RED);
Circle circle2 = new Circle(15, Color.BLUE);
Line line = new Line();
private Line connectLines(Line line, Circle startNode, Circle endNode)
{
line.startXProperty().bind(startNode.centerXProperty());
line.startYProperty().bind(startNode.centerYProperty());
line.endXProperty().bind(endNode.centerXProperty());
line.endYProperty().bind(endNode.centerYProperty());
return line;
}
//mouse pressed event
EventHandler<MouseEvent> mousePressed = new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent e)
{
System.out.println("pressed");
sceneX = e.getSceneX();
sceneY = e.getSceneY();
Circle tempCircle = ((Circle) e.getSource());
tempCircle.toFront();
}
};
//mouse dragged event
EventHandler<MouseEvent> mouseDragged = new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent e)
{
System.out.println(Math.hypot(line.getBoundsInLocal().getWidth(), line.getBoundsInLocal().getHeight()));
System.out.println("dragged");
double offSetX = e.getSceneX() - sceneX;
double offSetY = e.getSceneY() - sceneY;
Circle tempCircle = ((Circle) (e.getSource()));
tempCircle.setCenterX(tempCircle.getCenterX() + offSetX);
tempCircle.setCenterY(tempCircle.getCenterY() + offSetY);
if (Math.hypot(line.getBoundsInLocal().getWidth(), line.getBoundsInLocal().getHeight()) >= 100) {
tempCircle.setCenterX(tempCircle.getCenterX() - offSetX);
tempCircle.setCenterY(tempCircle.getCenterY() - offSetY);
}
sceneX = e.getSceneX();
sceneY = e.getSceneY();
}
};
#Override
public void start(Stage stage)
{
circle.setOnMouseDragged(mouseDragged);
circle2.setOnMouseDragged(mouseDragged);
Line returnLine = connectLines(line, circle, circle2);
StackPane root = new StackPane(new Pane(circle, circle2, returnLine));
stage.setTitle("TableView (o7planning.org)");
Scene scene = new Scene(root, 450, 300);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
launch(args);
}
}

How to make 3D shapes appear correctly with a camera?

I'm trying to get a few shapes to "play nicely" with each other. I have 2 boxes next to each other and a cylinder "on top" of one of the boxes. I put them in a SubScene with a camera that can zoom in and out with a scrolling operation and pan by dragging. Here is my MCVE:
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Cylinder;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
public class MyApp extends Application {
DoubleProperty transX = new SimpleDoubleProperty();
DoubleProperty transY = new SimpleDoubleProperty();
DoubleProperty transZ = new SimpleDoubleProperty();
double startX, startY, curX, curY;
#Override
public void start(Stage stage) throws Exception {
Box box1 = new Box(30, 30, 5);
box1.setMaterial(new PhongMaterial(Color.RED));
Box box2 = new Box(30, 30, 5);
box2.setMaterial(new PhongMaterial(Color.GREEN));
box2.setTranslateX(32);
Cylinder cyn = new Cylinder(5, 15);
Group root = new Group(box1, box2, cyn);
root.getTransforms().addAll(new Translate(0, 0, 200),
new Rotate(-60, Rotate.X_AXIS),
new Rotate(-45, Rotate.Z_AXIS));
SubScene subs = new SubScene(root, 0, 0, true, SceneAntialiasing.BALANCED);
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setFarClip(1000);
camera.setNearClip(0);
camera.translateZProperty().bind(transZ);
camera.translateXProperty().bind(transX);
camera.translateYProperty().bind(transY);
subs.setCamera(camera);
Pane pane = new Pane(subs);
pane.setStyle("-fx-border-color: red;" +
"-fx-border-width: 3;");
subs.widthProperty().bind(pane.widthProperty());
subs.heightProperty().bind(pane.heightProperty());
pane.setOnScroll(e -> transZ.set(transZ.get() + e.getDeltaY() * 0.2));
pane.setOnMousePressed(e -> {
startX = curX = e.getX();
startY = curY = e.getY();
});
pane.setOnMouseDragged(e -> {
startX = curX;
startY = curY;
curX = e.getX();
curY = e.getY();
double deltaX = curX - startX;
double deltaY = curY - startY;
double deltaZ = deltaY * Math.sqrt(3);
transX.set(transX.get() - deltaX);
transY.set(transY.get() - deltaY);
transZ.set(transZ.get() + deltaZ);
});
Scene scene = new Scene(pane, 500, 500);
stage.setScene(scene);
stage.sizeToScene();
stage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
}
I have 2 problems here:
the Z order is specified by the order in which I put them in the group. I want the Z order to "Take care of itself". If I have shapes in space with specific coordinates and sizes and a camera at a specific position and angle I should be seeing a unique view without needing to specify the order. Here is a picture:
it looks like the green box is closer to the camera than the red box but my code doesn't imply this.
the cylinder moves relative to the box when the camera pans. In the image I mark in blue lines the location of the cylinder on the red box:
and now i pan the camera and get
I want the relative location of the shapes to not change as I pan the camera.
Edit: i removed this from the question and ask it in another one:
the shapes are shown on top of the border:
i thought that because the shapes are in the pane then pane's border will be higher in the Z order. I want the border to appear on top.
I guess it is actually a bug in JavaFX but at least it is easy to fix. Just don't set the near clipping pane of the camera to 0. Changing this line of your code to
camera.setNearClip(10);
will solve your first two problems.
Try to enable the Group Depth test with the following line:
setDepthTest(DepthTest.ENABLE);

java fx creating balls when create button is clicked

I am trying to create balls whenever create button is clicked. I am able to create a single ball but for some reason not able to create multiple balls when the 'Create' button is clicked repeatedly.
Any help is appreciated.
package week3;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Slider;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.TilePane;
import javafx.geometry.Orientation;
import javafx.geometry.Insets;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.util.Duration;
public class BounceBallControl1 extends Application {
public final double radius = 10;
private double x = radius, y = radius;
private double dx = 1, dy = 1;
private Circle circle = new Circle(x, y, radius);
private Timeline animation;
#SuppressWarnings("restriction")
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
//BallPane ballPane = new BallPane();
Button btnCreate = new Button("Create");
Button btnDelete = new Button("Delete");
btnCreate.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
btnDelete.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
TilePane tileButtons = new TilePane(Orientation.HORIZONTAL);
tileButtons.setPadding(new Insets(5, 5, 5, 70));
tileButtons.setHgap(20.0);
tileButtons.getChildren().addAll(btnCreate, btnDelete);
Slider slSpeed = new Slider();
slSpeed.setMax(20);
//rateProperty().bind(slSpeed.valueProperty());
BorderPane pane = new BorderPane();
//pane.setCenter(ballPane);
pane.setTop(slSpeed);
pane.setBottom(tileButtons);
btnCreate.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
for(int i=0;i<=100;i++)
circle.setFill(Color.TURQUOISE); // Set ball color
pane.getChildren().add(circle); // Place a ball into this pane
// Create an animation for moving the ball
animation = new Timeline(
new KeyFrame(Duration.millis(50), (e -> {
// Check boundaries
if (x < radius || x > pane.getWidth() - radius) {
dx *= -1; // Change ball move direction
}
if (y < radius || y > pane.getHeight() - radius) {
dy *= -1; // Change ball move direction
}
// Adjust ball position
x += dx;
y += dy;
circle.setCenterX(x);
circle.setCenterY(y);
})));
animation.setCycleCount(Timeline.INDEFINITE);
animation.play(); // Start animation
}
});
// Create a scene and place it in the stage
Scene scene = new Scene(pane, 250, 250);
primaryStage.setTitle("BounceBallSlider"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
}
/*public DoubleProperty rateProperty() {
return animation.rateProperty();
}*/
}
You use the same instance of circle each time you click add button. So every time the same circle is added to the scene. Try by creating new circle each time, it will be ok.

Categories

Resources