I have a JavaFX program in which I am looking to assign an event handler for a button but I want the event to trigger an action in a parent unit.
I have a GridPane as my main pane.
When I start my program, I have another Group of shapes (Called a Block) embedded in this gridpane at a fixed location (0,0).
As part of my Block, I have a Triangle button called DownArrow. My Plan is that when this button is pressed, the Doc controller will place a new Block beneath the one where the triangle was pressed.
I am looking at using the DownArrow.setOnMouseClicked event handler and map this back to the routine in the Controller to Add a new Block... However, I need some sort of info callback to let the controller know what Block was pressed.
I'm Stumped.
I was looking at maybe creating a custom event handler that I can pass more parameters but it seems clumsy - is there something else I should be doing?
How to pass paremeter with an event in javafx?
See Code Below in full:
package editorscratchpad;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.scene.text.Font;
import javafx.stage.Stage;
/**
*
* #author a_curley
*/
public class EditorScratchPad extends Application {
HBox mainPanel;
VBox flowChart;
GridPane flowchartGrid;
VBox controlPanel;
Integer stepCount;
TextField descData;
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
descData = new TextField();
stepCount = 0;
btn.setText("Add Step");
btn.setOnAction((ActionEvent event) -> {
buttonClicked(event);
});
ScrollPane sp = new ScrollPane();
mainPanel = new HBox(20);
//mainPanel.setPrefWidth(400);
controlPanel = new VBox(5);
controlPanel.setAlignment(Pos.CENTER);
flowChart = new VBox();
flowChart.setPrefWidth(600);
flowChart.setAlignment(Pos.CENTER);
flowchartGrid = new GridPane();
flowchartGrid.setPrefWidth(600);
flowchartGrid.setHgap(10);
flowchartGrid.setVgap(10);
flowchartGrid.setPadding(new Insets(0, 10, 0, 10));
//sp.setContent(flowChart);
sp.setContent(flowchartGrid);
controlPanel.getChildren().add(btn);
controlPanel.getChildren().add(descData);
mainPanel.getChildren().add(sp);
mainPanel.getChildren().add(controlPanel);
Scene scene = new Scene(mainPanel, 800, 500);
primaryStage.sizeToScene();
primaryStage.setTitle("Flow chart");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public void buttonClicked(ActionEvent event) {
//Integer Step = stepCount*10;
//Integer Step = (flowChart.getChildren().size()+1)*10;
Integer Step = (flowchartGrid.getChildren().size() + 1);
String stateDesc = descData.getText();
if (stateDesc.length() < 3) {
stateDesc = "State " + Step.toString();
}
blockComponent newBlock = new blockComponent("S" + Integer.toString(Step * 10), stateDesc, Step); //<<<< Create the Block.
//flowChart.getChildren().add(newBlock.getComponent());
flowchartGrid.add(newBlock.getComponent(), 0, Step);
System.out.println("Added S" + Integer.toString(Step * 10));
stepCount++;
}
public void addNewBlock(ActionEvent event) {
Integer Step = (flowchartGrid.getChildren().size() + 1);
String stateDesc = "Step " + Integer.toString(Step * 10);
}
/**
* Subclass representing a Block Graphics only.
*/
public class blockComponent {
String Name;
String Desc;
Integer StateNo;
Integer XLoc;
Integer YLoc;
Integer blockHeight;
Integer blockWidth;
Integer stLabRad;
public blockComponent(String newName, String newDesc, Integer newSt) {
Name = newName;
Desc = newDesc;
StateNo = newSt;
XLoc = 0;
YLoc = 0;
blockHeight = 60;
blockWidth = 120;
stLabRad = 10;
}
public blockComponent(String newName, String newDesc, Integer newSt, Integer xCoOrd, Integer yCoOrd) {
this(newName, newDesc, newSt);
XLoc = xCoOrd;
YLoc = yCoOrd;
}
public Group getComponent() {
Group thisGroup = new Group();
// Define Rectangle
Rectangle Block = new Rectangle();
Block.setY(stLabRad);
Block.setX(stLabRad);
Block.setHeight(blockHeight);
Block.setWidth(blockWidth);
Block.setStroke(Color.BLACK);
Block.setStrokeWidth(2);
Block.setFill(Color.WHITESMOKE);
// Define state label
Circle stLab = new Circle();
stLab.setCenterX(stLabRad);
stLab.setCenterY(stLabRad);
stLab.setRadius(stLabRad);
stLab.setStroke(Color.PALEGREEN);
stLab.setFill(Color.PALEGREEN);
// Define State No.
Label stNo = new Label();
stNo.setFont(Font.font("Impact"));
stNo.setTextFill(Color.WHITE);
stNo.setLayoutX(0);
stNo.setLayoutY(0);
stNo.setText(StateNo.toString());
// Define the description
Label stD = new Label();
stD.setFont(Font.font("Impact"));
stD.setTextFill(Color.BLACK);
stD.setLayoutX(15);
stD.setLayoutY(15);
stD.setText(Desc.toString());
//---- Three Triangles for drawing. ----
// Down Arrow
Polygon downArrow = new Polygon();
downArrow.getPoints().addAll(new Double[]{
//X //Y
(blockWidth.doubleValue() / 2) + stLabRad, (blockHeight.doubleValue()),
((blockWidth.doubleValue() / 2) + 10) + stLabRad, (blockHeight.doubleValue() - 10),
((blockWidth.doubleValue() / 2) - 10) + stLabRad, (blockHeight.doubleValue() - 10)
// 70.0,60.0,
// 60.0,50.0,
// 80.0,50.0
});
downArrow.setStroke(Color.BLACK);
downArrow.setStrokeWidth(1);
downArrow.setFill(Color.LIGHTCYAN);
downArrow.setOnMouseEntered(new EventHandler<MouseEvent>() {
public void handle(MouseEvent me) {
downArrow.setStroke(Color.BLACK);
downArrow.setFill(Color.LIGHTCYAN);
}
});
downArrow.setOnMouseExited(new EventHandler<MouseEvent>() {
public void handle(MouseEvent me) {
downArrow.setStroke(Color.WHITESMOKE);
downArrow.setFill(Color.WHITESMOKE);
}
});
// try: https://stackoverflow.com/questions/35372236/how-to-pass-paremeter-with-an-event-in-javafx
//add all components to the group to display
thisGroup.getChildren().add(Block);
thisGroup.getChildren().add(stD);
thisGroup.getChildren().add(stLab);
thisGroup.getChildren().add(stNo);
thisGroup.getChildren().add(downArrow);
return thisGroup;
}
}
}
Not sure if this was the correct approach I was looking for but #0009laH response above for getSource() was the best that worked for me.
Related
In my current code every time I add something like Platform.runLater it will crash due to the fact that you cannot have a Thread.sleep in the main scene. This is the last instance of my working code, any help is appreciated.
import javafx.application.Platform;
import javafx.animation.KeyFrame;
import javafx.animation.PauseTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;
import javafx.scene.Group;
import javafx.scene.Scene;
import java.util.Random;
public class Testing2 extends Application {
private static final double MAX_X = 1000;
private static final double MAX_Y = 750;
private static String Converted = null;
private int MIN_X2 = 20;
private int MIN_Y2 = 20;
private int MAX_X2 = 950;
private int MAX_Y2 = 700;
private int clickCount = 0;
private Random random;
Random generator = new Random();
public int Seconds = 15;
Scene scene, scene2;
Stage window;
private final Text text2 = new Text("All Done!");
public void changeScene(Stage stage) {
StackPane root2 = new StackPane();
root2.getChildren().add(text2);
Scene scene2 = new Scene(root2, 200, 200);
stage.setScene(scene2);
}
public void init() {
random = new Random();
}
#Override
public void start(Stage primaryStage) throws Exception{
window = primaryStage;
Circle circle = new Circle(MAX_X / 2, MAX_Y / 2, 15);//(StartX, StartY, Size)
Text clickCountText = new Text("Clicks: " + clickCount);
Text Space = new Text(" ");
Text label = new Text("");
circle.setOnMouseClicked((event) -> {
circle.setCenterX(random.nextInt(MAX_X2 - MIN_X2) + MIN_X2);
circle.setCenterY(random.nextInt(MAX_Y2 - MIN_Y2) + MIN_Y2);
circle.getCenterX();
clickCount ++;
clickCountText.setText("Clicks: "+ clickCount);
});
Group layout = new Group(
circle,
new FlowPane(clickCountText, Space, label)
);
//create 2 strings, convert the number to a string, concat 3 strings and assign to variable, put variable in setText
PauseTransition pause = new PauseTransition(Duration.seconds(1));
pause.setOnFinished(event -> {
String String1 = "There are ";
String String2 = " Seconds Left";
for(int n=15; n>=0; n--){
Converted = Integer.toString(Seconds);
String String3 = String1 + Seconds + String2;
}}
);
final IntegerProperty i = new SimpleIntegerProperty(16);
Timeline timeline = new Timeline(
new KeyFrame(
Duration.seconds(1),
event -> {
i.set(i.get() - 1);
label.setText("You have: " + i.get() + " seconds remaining!");
}
)
);
timeline.setCycleCount(16);
timeline.play();
pause.play();
primaryStage.setScene(new Scene(layout, MAX_X, MAX_Y, Color.WHITE));
primaryStage.setResizable(false);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The program is supposed to generate a circle, and when it gets clicked it moves the circle to a random location on screen. Every time one is clicked it increases the click count by one. Once the timer hits 0 I want the scene to change to a screen that says the text, "You got __ clicks in 15 seconds! Good job!". I'm running this through eclipse so you can just paste it in and try it out.
An easy way to do it without threading is to create another timeline just the way you did the timeline, where you can setScene for your primaryStage, like that:
Timeline finishTimeline = new Timeline(
new KeyFrame(
Duration.millis(1),
event -> {
StackPane s = new StackPane();
s.getChildren().add(new Label("your label here"));
Scene scene = new Scene(s, 200, 200);
primaryStage.setScene(scene);
}
)
);
note that you should play this finishTimeline when you finish the first timeline, so you can use a SequentialTransition to play both in a sequential order:
SequentialTransition st = new SequentialTransition(timeline, finishTimeline);
st.play();
Now modify your code to be 100% precise.
I have two classes ViewTaskTest and ControllerTaskTest. The view has two buttons, one to create a random point and the other to start the controller. After the start, the controller will place 10 random points to the chart inside the view. But the points are visible only at the end. I want to see the points being placed one by one. I know, that I have to use somehow the Task-functions and I am struggling to understand this concept.
This is the ViewTaskTest-class:
package View;
import Controller.ControllerTaskTest;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.chart.Axis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.ScatterChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ViewTaskTest{
ScatterChart<Number, Number> scatterChart;
XYChart.Series<Number, Number> series = new XYChart.Series<Number, Number>();
Axis<Number> xAxis = new NumberAxis(0, 10, 2); ;
Axis<Number> yAxis = new NumberAxis(0, 10, 2); ;
Button buttonAddRandomPoint = new Button("Add random point");
Button buttonStartControllerTest = new Button("Start controller");
private final int MAIN_WINDOW_HEIGHT = 800;
private final int MAIN_WINDOW_WIDTH = 800;
ControllerTaskTest controller;
public ViewTaskTest() {
}
public void setController(ControllerTaskTest controller) {
this.controller = controller;
}
public void fillStage(Stage stage) {
stage.setTitle("QuickPID");
stage.setMinHeight(MAIN_WINDOW_HEIGHT);
stage.setMinWidth(MAIN_WINDOW_WIDTH);
stage.setMaxHeight(MAIN_WINDOW_HEIGHT);
stage.setMaxWidth(MAIN_WINDOW_WIDTH);
Scene sceneOne = new Scene(new Group());
scatterChart = new ScatterChart<Number, Number>(xAxis, yAxis);
scatterChart.getData().add(series);
// add a random point
buttonAddRandomPoint.setOnAction(e -> {
Double x = new Double(0.0);
Double y = new Double(0.0);
try{
x = Math.random() * 10;
y = Math.random() * 10;
series.getData().add(new XYChart.Data<Number, Number>(x, y));
} catch (Exception ex) {
System.out.println("Error");
}
});
buttonStartControllerTest.setOnAction(e -> {
controller.startAddingPoints();
});
VBox vboxButtons = new VBox(5);
HBox hboxMain = new HBox(5);
vboxButtons.getChildren().addAll(buttonAddRandomPoint, buttonStartControllerTest);
vboxButtons.setAlignment(Pos.BOTTOM_LEFT);
vboxButtons.setPadding(new Insets(10));
hboxMain.getChildren().addAll(scatterChart, vboxButtons);
sceneOne.setRoot(hboxMain);
stage.setScene(sceneOne);
}
public void addRandomPointFromController(Double x, Double y) {
System.out.println("starting view task");
Task<Integer> task = new Task<Integer>() {
#Override protected Integer call() throws Exception {
series.getData().add(new XYChart.Data<Number, Number>(x, y));
return 0;
}
};
task.run();
}
}
Here is the ControllerTestTask-Class:
package Controller;
import View.ViewTaskTest;
import javafx.concurrent.Task;
public class ControllerTaskTest {
ViewTaskTest view;
public ControllerTaskTest() {
}
public void setView(ViewTaskTest view) {
this.view = view;
}
public void startAddingPoints() {
System.out.println("starting controller task");
Task<Integer> task = new Task<Integer>() {
#Override protected Integer call() throws Exception {
for(int i = 0; i < 10; i++) {
Double x = Math.random() * 10;
Double y = Math.random() * 10;
view.addRandomPointFromController(x, y);
System.out.println("Adding point x = " + x + " and y = " + y);
Thread.sleep(100);
}
return 0;
}
};
task.run();
}
}
Here is the main-class:
package Test;
import Controller.ControllerTaskTest;
import View.ViewTaskTest;
import javafx.application.Application;
import javafx.stage.Stage;
public class Test extends Application{
private ViewTaskTest view= new ViewTaskTest();
private ControllerTaskTest controller = new ControllerTaskTest();
public static void main(String[] args) {
launch();
}
#Override
public void start(Stage stage) throws Exception {
view.setController(controller);
controller.setView(view);
stage.show();
view.fillStage(stage);
}
}
Some please explain me what I am doing wrong. I am experimenting with the Thread-funcitons but I cant solve my problem.
I'm doing my summer project, which is about a Reversi game by implementing the AI algorithms. I am so struggling with the GUI building section by using the JavaFX. I have set a mouse clicked event on an 8x8 board, when user click any block on the board, a chess piece will be put on the specified position. I am searching for a long time on net. But no use. Please help or try to give some ideas how to achieve this, even the main pane showed up, but the piece still cannot show up. Following are my code. the setpiece method is in the ReversiBoard class. Thanks.
the following is the main class
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.geometry.Rectangle2D;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.StrokeType;
import javafx.scene.text.Font;
/**
* #author Zibo Wang
* the main class is the main entrance of the GUI
*
*/
public class Main extends Application {
public static ReversiBoard[][] RevBoard = new ReversiBoard[8][8];
//public static ReversiAI robot = new ReversiAI();
public static boolean AIPlay = true;
public static boolean AIPiece = false;
public static char whoseTurn = ' ';
public static Circle[][] arrayCircle = new Circle[8][8];
public static int step = 0;
public int WIDTH = 65;
public static TextArea textArea = new TextArea();
public static Pane mainPane = new Pane();
#Override
public void start(Stage primaryStage) {
try {
GridPane grid = new GridPane();
// for (int i = 0; i < 8; i++) {
// for (int j = 0; j < 8; j++) {
// Rectangle rectangle1 = new Rectangle(WIDTH, WIDTH);
// rectangle1.setFill(Color.LIGHTGREEN);
// rectangle1.setStroke(Color.BLACK);
// Rectangle rectangle2 = new Rectangle(WIDTH, WIDTH);
// rectangle2.setFill(Color.DARKGREEN);
// rectangle2.setStroke(Color.BLACK);
// if ((i + j) % 2 == 0) {
// grid.add(rectangle1, i, j);
// } else {
// grid.add(rectangle2, i, j);
// }
// }
// }
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
grid.add(RevBoard[i][j] = new ReversiBoard(i, j), i, j);
grid.setLayoutX(65);
grid.setLayoutY(50);
/*
* inserting a background image of the GUI
*/
ImageView selectedImage = new ImageView();
Image image1 = new Image(test.class.getResourceAsStream("/pictures/Wood_3888x2592.jpg"));
selectedImage.setImage(image1);
selectedImage.setFitHeight(650);
selectedImage.setFitWidth(1060);
/*
* drawing a list of buttons, and add them into the VBox, which will be located
* at the right side of the main pane
*/
BoardController controller = new BoardController(null, null, null, null);
VBox vBox = new VBox(50);
vBox.setPadding(new Insets(20, 20, 625, 625));
vBox.setLayoutY(150);
vBox.getChildren().addAll(controller.getBtSet(), controller.getBtStart(), controller.getBtSurrender(),
controller.getBtExit());
/*
* Drawing a textArea, defining the font size as 16, the max width and the max
* height, and drop it at the right hand side of the main pane,
*/
textArea.setWrapText(true);
textArea.setFont(new Font(15));
textArea.setMaxWidth(250);
textArea.setMinHeight(500);
textArea.setLayoutX(740);
textArea.setLayoutY(75);
textArea.setEditable(false);
textArea.insertText(0, "Press the 'Start Game' button to strat the game!" + '\n' + '\n');
//recall the five function defined methods from the BoardController class
controller.settingButton(null);
controller.startButton(null);
controller.surrenderButton(null);
controller.exitButton(null);
controller.boardReset(null);
/*
* set a main pain which is used to contain the all GUI elements
*/
//Pane mainPane = new Pane();
//mainPane.getChildren().addAll(selectedImage, grid, vBox, textArea);
mainPane.getChildren().addAll(grid, vBox, textArea);
Scene scene = new Scene(mainPane, 1050, 650);
scene.getStylesheets().add(getClass().getResource("/application/application.css").toExternalForm());
primaryStage.setTitle("Welcome to AI Reversi");
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setResizable(false);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void gameStart() {
textArea.insertText(0, "Game Start!" + '\n' + '\n');
}
public static void whiteWin() {
textArea.insertText(0, "White piece wins!" + '\n' + '\n');
}
public static void blackWin() {
textArea.insertText(0, "Black piece wins!" + '\n' + '\n');
}
public static void main(String[] args) {
launch(args);
}
}
the following is the BoardController
package application;
import java.io.IOException;
import java.util.Optional;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
/**
* #author Zibo Wang this BoardController class mainly contains all implemented
* functions for the buttons
*/
public class BoardController {
private Button btSet;
private Button btStart;
private Button btSurrender;
private Button btExit;
private Alert startConfirmWindow = new Alert(AlertType.CONFIRMATION,
"The game has not ended yet, are you going to start a new game?");
private Alert setConfirmWindow = new Alert(AlertType.INFORMATION);
private Alert surrenderConfirmWindow = new Alert(AlertType.CONFIRMATION, "Are you going to surrender?");
private Alert exitConfirmWindow = new Alert(AlertType.CONFIRMATION, "Are you going to exit the game?");
private Alert winnerWindow = new Alert(AlertType.INFORMATION);
Main method = new Main();
public BoardController(Button btSet, Button btStart, Button btSurrender, Button btExit) {
this.btSet = new Button("Setting");
this.btStart = new Button("Start Game");
this.btSurrender = new Button("Surrender");
this.btExit = new Button("Finish Game");
}
public Button getBtSet() {
return btSet;
}
public void setBtSet(Button btSet) {
this.btSet = btSet;
}
public Button getBtStart() {
return btStart;
}
public void setBtStart(Button btStart) {
this.btStart = btStart;
}
public Button getBtSurrender() {
return btSurrender;
}
public void setBtSurrender(Button btSurrender) {
this.btSurrender = btSurrender;
}
public Button getBtExit() {
return btExit;
}
public void setBtExit(Button btExit) {
this.btExit = btExit;
}
/*
* this method defines the implemented function of the setting button
*/
public void settingButton(ActionEvent event) throws IOException {
// game setting button function
btSet.setOnAction(e -> {
if (Main.whoseTurn == ' ') {
Stage settingStage = new Stage();
Pane settingPane = new Pane();
// group1 is responsible to radio button 1 and 2
ToggleGroup group1 = new ToggleGroup();
// group2 is responsible to the option1 label
ToggleGroup group2 = new ToggleGroup();
ToggleGroup group3 = new ToggleGroup();
Label option1 = new Label("Game Battle Mode");
Label option2 = new Label("AI Battle Setting");
Label option3 = new Label("Game Difficulty");
option1.setStyle("-fx-font-size: 16px");
option1.setLayoutX(30);
option1.setLayoutY(15);
option2.setStyle("-fx-font-size: 16px");
option2.setLayoutX(30);
option2.setLayoutY(100);
option3.setStyle("-fx-font-size: 16px");
option3.setLayoutX(30);
option3.setLayoutY(190);
// adding the radio buttons on the new stage, the following part is for the
// "Game Battle Mode" setting
// default selection is set for the AI Battle
RadioButton radio1 = new RadioButton("AI Battle");
RadioButton radio2 = new RadioButton("Players Battle");
radio1.setLayoutX(50);
radio1.setLayoutY(50);
radio2.setLayoutX(200);
radio2.setLayoutY(50);
// adding the radio1 and radio2 buttons into the toggle group1
radio1.setToggleGroup(group1);
radio2.setToggleGroup(group1);
radio1.setSelected(true);
Line divLine1 = new Line(0, 80, 350, 80);
divLine1.setStrokeWidth(2f);
// adding the radio buttons on the new stage, the following part is for the "AI
// Battle Mode" setting
// default selection is set for the PC side takes the white chess piece
RadioButton radio3 = new RadioButton("PC - White piece");
RadioButton radio4 = new RadioButton("PC - Black piece");
radio3.setLayoutX(50);
radio3.setLayoutY(130);
radio4.setLayoutX(200);
radio4.setLayoutY(130);
radio3.setToggleGroup(group2);
radio4.setToggleGroup(group2);
radio3.setSelected(true);
Line divLine2 = new Line(0, 165, 350, 165);
divLine2.setStrokeWidth(2f);
// adding the radio buttons on the new stage, the following part is for the
// "Game Difficulty" setting
RadioButton radio5 = new RadioButton("Simple");
RadioButton radio6 = new RadioButton("Regular");
RadioButton radio7 = new RadioButton("Complex");
radio5.setLayoutX(50);
radio5.setLayoutY(220);
radio6.setLayoutX(200);
radio6.setLayoutY(220);
radio7.setLayoutX(50);
radio7.setLayoutY(250);
radio5.setToggleGroup(group3);
radio6.setSelected(true);
radio6.setToggleGroup(group3);
radio7.setToggleGroup(group3);
Line divLine3 = new Line(0, 280, 350, 280);
divLine3.setStrokeWidth(2f);
// inserting an background image of the selection window
ImageView bgImage = new ImageView();
Image image1 = new Image(test.class
.getResourceAsStream("/pictures/chessboard-perspective-background-vector_57911-3.jpg"));
bgImage.setImage(image1);
bgImage.setFitHeight(400);
bgImage.setFitWidth(350);
bgImage.setLayoutX(0);
bgImage.setLayoutY(60);
// adding two buttons at the bottom of the setting pane
// close button is used to close the window, confirm button is used to confirm
// all player's selection
Button cancelBt = new Button("Cancel");
Button confirmBt = new Button("Confirm");
cancelBt.setLayoutX(90);
cancelBt.setLayoutY(315);
confirmBt.setLayoutX(190);
confirmBt.setLayoutY(315);
// defining function of the cancel button, which enable user to close the
// setting board
cancelBt.setOnAction(event2 -> {
settingStage.close();
});
// defining function of the confirm button, clicking the button to change the
// game parameters
confirmBt.setOnAction(event3 -> {
if (radio1.isSelected() == true) {
Main.AIPlay = true;
radio1.setSelected(true);
} else if (radio2.isSelected() == true) {
Main.AIPlay = false;
radio2.setSelected(true);
}
if (radio3.isSelected() == true) {
Main.AIPiece = true;
radio3.setSelected(true);
} else if (radio4.isSelected() == true) {
Main.AIPiece = false;
radio4.setSelected(true);
}
settingStage.close();
});
settingPane.getChildren().addAll(bgImage, option1, radio1, radio2, divLine1, option2, radio3, radio4,
divLine2, option3, radio5, radio6, radio7, divLine3, cancelBt, confirmBt);
Scene scene = new Scene(settingPane, 350, 400);
settingStage.setScene(scene);
settingStage.setTitle("Game Setting");
settingStage.show();
settingStage.setResizable(false);
} else {
setConfirmWindow.setHeaderText(null);
setConfirmWindow.setContentText("Please finsih this game first, then doing the game setting change");
setConfirmWindow.showAndWait();
}
});
}
/*
* this method defines the implemented function of the start button
*/
public void startButton(ActionEvent event) {
btStart.setOnAction(e -> {
if (Main.whoseTurn == ' ') {
startConfirmWindow.setTitle("Game Start!");
startConfirmWindow.setHeaderText(null);
Optional<ButtonType> result = startConfirmWindow.showAndWait();
if (result.isPresent() && result.get() == ButtonType.OK)
boardReset(event);
} else {
boardReset(event);
}
});
}
/*
* this method defines the implemented function of the surrender button
*/
public void surrenderButton(ActionEvent event) {
btSurrender.setOnAction(e -> {
surrenderConfirmWindow.setTitle("Surrender");
surrenderConfirmWindow.setHeaderText(null);
// if the player takes the black piece, surrender window shows the white piece
// wins
if (Main.whoseTurn == 'B') {
Optional<ButtonType> computerWin = surrenderConfirmWindow.showAndWait();
if (computerWin.isPresent() && computerWin.get() == ButtonType.OK) {
Main.textArea.insertText(0, "White piece wins!" + '\n' + '\n');
Main.whiteWin();
winnerWindow.setContentText("White piece wins!!!");
winnerWindow.setHeaderText(null);
winnerWindow.showAndWait();
}
} else if (Main.whoseTurn == 'W') {
Optional<ButtonType> computerWin = surrenderConfirmWindow.showAndWait();
if (computerWin.isPresent() && computerWin.get() == ButtonType.OK) {
Main.textArea.insertText(0, "Black piece wins!" + '\n' + '\n');
Main.blackWin();
winnerWindow.setContentText("Black piece wins!!!");
winnerWindow.setHeaderText(null);
winnerWindow.showAndWait();
}
}
});
}
/*
* this method defines the implemented function of the exit button
*/
public void exitButton(ActionEvent event) {
btExit.setOnAction(e -> {
exitConfirmWindow.setTitle("Exit the Othello");
exitConfirmWindow.setHeaderText(null);
Optional<ButtonType> result = exitConfirmWindow.showAndWait();
// press the confirm button to exit the whole system
if (result.isPresent() && result.get() == ButtonType.OK) {
System.exit(1);
}
});
}
/*
* this method defines the function to reset the whole Reversi board
*/
public void boardReset(ActionEvent event) {
Main.gameStart();
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
Main.RevBoard[i][j].getChildren().remove(Main.arrayCircle[i][j]);
Main.RevBoard[i][j].piece = ' ';
}
Main.whoseTurn = ' ';
Main.step = 0;
//if the game is in AI battle mode, computer will pre-place four (2 for each colour)
//at the middle section of the Reversi board
if (Main.AIPiece == true) {
Main.RevBoard[3][3].setPiece(Main.whoseTurn, 3, 3, true);
Main.RevBoard[4][3].setPiece(Main.whoseTurn, 4, 3, true);
Main.RevBoard[3][4].setPiece(Main.whoseTurn, 3, 4, true);
Main.RevBoard[4][3].setPiece(Main.whoseTurn, 4, 3, true);
Main.whoseTurn = 'W';
}
}
}
the following is the MyPiece class
package application;
import javafx.application.Application;
import javafx.scene.effect.DropShadow;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
public class MyPiece extends Circle {
MyPiece(){
}
//setting the radius, colour and shadow effect for the piece
MyPiece(int radius, Color color){
super.setRadius(radius);
super.setStroke(color);
super.setFill(color);
DropShadow ds = new DropShadow();
ds.setOffsetX(3.0);
super.setEffect(ds);
}
}
package application;
import java.util.List;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
the following is the ReversiBoard class, mouse click event is defined at here
public class ReversiBoard extends Pane {
//B means black piece, W means white piece
public char piece = 'B';
public int playerStep;//used to count the piece steps
private Alert gameWinner = new Alert(AlertType.INFORMATION);
private MyPiece circle = null;
//private Media moveSong = new Media(getClass().getClassLoader().getResource("put.mp3").toString());
//private Media winSong = new Media(getClass().getClassLoader().getResource("win.wav").toString());
public ReversiBoard () {
}
public ReversiBoard(int x, int y) {
setStyle("-fx-border-color:black");
this.setPrefSize(65, 65);
this.setOnMouseClicked(e -> {
handleMouseClick(x, y);
});
}
public void handleMouseClick(int x, int y) {
// if (piece == ' ' && Main.whoseTurn != ' ' && Main.AIPlay == false) {
setPiece(Main.whoseTurn, x, y,true);
// //Main.textArea.insertText(0, "Evaluate:" + Main.robot.Evaluate(x, y, Main.whoseTurn) + " ");
//
// if (judge(Main.whoseTurn, x, y) == true)
// printWinner();
// else
// Main.whoseTurn = (Main.whoseTurn == 'B') ? 'W' : 'B';
//// MediaPlayer mediaPlayer = new MediaPlayer(moveSong);
//// mediaPlayer.setVolume(50);
//// mediaPlayer.play();
// }
//if AI is true, it is in the AI battle mode
// if (piece == ' ' && Main.whoseTurn != ' ' && Main.AIPlay == true) {
// if (Main.whoseTurn == ' ') {
// setPiece(Main.whoseTurn, x, y,true);
//Main.textArea.insertText(0, "Evaluate:" + Main.robot.Evaluate(x, y, Main.whoseTurn) + " ");
// if (judge(Main.whoseTurn, x, y) == true)
// printWinner();
// else
// Main.whoseTurn = (Main.whoseTurn == 'B') ? 'W' : 'B';
// MediaPlayer mediaPlayer = new MediaPlayer(moveSong);
// mediaPlayer.setVolume(50);
// mediaPlayer.play();
// }
// //若whoseTurn不为空 即未分出胜负,则执行搜索函数,根据人下的子,搜索AI的下一步落子
// if (Main.whoseTurn != ' ')
// Main.robot.search(x,y);
// }
}
public void setPiece(char c, int x, int y, boolean flag) {
piece = c;
MyPiece circle = null;
int row = x + 1;
int column = y + 1;
Main.step++;
Main.RevBoard[x][y].playerStep = Main.step;
if (piece == 'B') {
circle = new MyPiece(30, Color.BLACK);
//output the accurate piece positions in the text area
if(flag==true)
Main.textArea.insertText(0, "step:" + Main.step + " black piece:column" + column + ",row" + row + '\n' + '\n');
} else if (piece == 'W') {
circle = new MyPiece(30, Color.WHITE);
if(flag==true)
Main.textArea.insertText(0, "step:" + Main.step + " white piece:column" + column + ",row" + row + '\n' + '\n');
}
//bind the piece at the centre of each block
circle.centerXProperty().bind(Main.RevBoard[x][y].widthProperty().divide(2));
circle.centerYProperty().bind(Main.RevBoard[x][y].heightProperty().divide(2));
Main.RevBoard[x][y].getChildren().add(circle);
Main.RevBoard[x][y].piece = c;
Main.arrayCircle[x][y] = circle;
}
public boolean judge (char whoseTurn, int x, int y) {
return false;
}
public int checkCount(char whoseTurn, int x, int y, int xChange, int yChange) {
return yChange;
}
public void printWinner() {
}
}
I'm designing a stopwatch using JavaFX. The code runs well. Except for enormous cumulative memory leaks over time. The leak increases whenever I increase the Timeline's framerate. I'm currently on Ubuntu 16.04 with 4gigs of RAM, and the leak is happening at a speed of 300MB/min at 30fps. That's 5MBps. I can understand that this may happen due to the repetitive drawing over the Scene, but why would it be cumulative? Shouldn't the JVM take care of this?
Main.java :
package UI;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("StopWatch");
primaryStage.setScene(new Scene(getPane(), 400, 400));
primaryStage.show();
}
private BorderPane getPane(){
BorderPane pane = new BorderPane();
ClockUI clockUI = new ClockUI();
clockUI.setMinSize(200,200);
pane.setCenter(clockUI);
ButtonBar buttonBar = new ButtonBar();
Button startButton = new Button("Start");
startButton.setOnAction(e->clockUI.startClock());
Button pauseButton = new Button("Stop");
pauseButton.setOnAction(e->clockUI.stopClock());
Button resetButton = new Button("Reset");
resetButton.setOnAction(e->clockUI.resetClock());
buttonBar.getButtons().addAll(startButton, pauseButton, resetButton);
pane.setBottom(buttonBar);
return pane;
}
public static void main(String[] args) {
System.setProperty("prism.lcdtext","false");
launch(args);
}
}
ClockUI.java :
package UI;
import javafx.animation.*;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.transform.Rotate;
import javafx.util.Duration;
/**
* Created by subhranil on 23/6/17.
*/
public class ClockUI extends StackPane {
private final Rotate hourRotate;
private final Rotate minuteRotate;
private final Rotate secondRotate;
private final Timeline hourTimeline;
private final Timeline minuteTimeline;
private final Timeline secondTimeline;
private final ParallelTransition clockTransition;
public ClockUI() {
super();
Line hourHand = getHand(80, Color.WHITE);
hourRotate = getRotate(hourHand);
hourTimeline = createRotateTimeline(Duration.hours(12), hourRotate);
Line minuteHand = getHand(100, Color.WHITE);
minuteRotate = getRotate(minuteHand);
minuteTimeline = createRotateTimeline(Duration.minutes(60), minuteRotate);
Line secondHand = getHand(90, Color.WHITE);
secondRotate = getRotate(secondHand);
secondTimeline = createRotateTimeline(Duration.seconds(60), secondRotate);
clockTransition = new ParallelTransition(hourTimeline, minuteTimeline, secondTimeline);
Circle back = new Circle(120);
back.centerXProperty().bind(widthProperty().divide(2));
back.centerYProperty().bind(heightProperty().divide(2));
back.setStyle("-fx-fill: #555555");
setStyle("-fx-background-color: #333333;");
getChildren().addAll(back, hourHand, minuteHand, secondHand);
}
private Timeline createRotateTimeline(Duration duration, Rotate rotate) {
Timeline timeline = new Timeline(30);
timeline.getKeyFrames().add(new KeyFrame(duration, new KeyValue(rotate.angleProperty(), 360)));
timeline.setCycleCount(Animation.INDEFINITE);
return timeline;
}
public void startClock() {
if (clockTransition.getStatus() != Animation.Status.RUNNING) {
clockTransition.play();
}
}
public void stopClock() {
if (clockTransition.getStatus() == Animation.Status.RUNNING) {
clockTransition.pause();
}
}
public void resetClock() {
stopClock();
clockTransition.stop();
}
private Rotate getRotate(Line line){
Rotate r = new Rotate(0);
r.pivotXProperty().bind(line.startXProperty());
r.pivotYProperty().bind(line.startYProperty());
line.getTransforms().add(r);
return r;
}
private Line getHand(int size, Paint color) {
Line hand = new Line();
hand.startXProperty().bind(widthProperty().divide(2));
hand.startYProperty().bind(heightProperty().divide(2));
hand.endXProperty().bind(widthProperty().divide(2));
hand.endYProperty().bind(heightProperty().divide(2).subtract(size));
hand.setStroke(color);
hand.setStrokeWidth(3);
return hand;
}
}
INFO : I've tried various other methods, like running an ExecutorService, using Task and Thread, but all yield same results.
Try this and see if you are having the same problem.
ClockGUI
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.transform.*;
/**
*
* #author Sedrick
*/
public class ClockGUI {
Circle clockFace;
Line second;
Line minute;
Line hour;
Rotate secondRotation;
Rotate minuteRotation;
Rotate hourRotation;
AnchorPane currentClockFace;
public ClockGUI()
{
currentClockFace = new AnchorPane();
currentClockFace.setPrefSize(100, 100);
clockFace = new Circle(100 / 2, 100 / 2, 100 / 2);
clockFace.setStroke(Color.BLACK);
clockFace.setFill(Color.TRANSPARENT);
second = new Line(100 / 2, 100 / 2, 100 / 2, 100 / 2 - 40);
secondRotation = new Rotate();
secondRotation.pivotXProperty().bind(second.startXProperty());
secondRotation.pivotYProperty().bind(second.startYProperty());
second.getTransforms().add(secondRotation);
minute = new Line(100 / 2, 100 / 2, 100 / 2, 100 / 2 - 30);
minuteRotation = new Rotate();
minuteRotation.pivotXProperty().bind(minute.startXProperty());
minuteRotation.pivotYProperty().bind(minute.startYProperty());
minute.getTransforms().add(minuteRotation);
hour = new Line(100 / 2, 100 / 2, 100 / 2, 100 / 2 - 20);
hourRotation = new Rotate();
hourRotation.pivotXProperty().bind(hour.startXProperty());
hourRotation.pivotYProperty().bind(hour.startYProperty());
hour.getTransforms().add(hourRotation);
currentClockFace.getChildren().addAll(clockFace, second, minute, hour);
}
public AnchorPane getCurrentClock()
{
return currentClockFace;
}
public void rotateSecondLine()
{
secondRotation.setAngle(secondRotation.getAngle() + 6);
}
public double getRotateSecondLine()
{
return secondRotation.getAngle();
}
public void setRotateSecond(double degree)
{
secondRotation.setAngle(degree);
}
public void rotateMinuteLine()
{
minuteRotation.setAngle(minuteRotation.getAngle() + 6);
}
public double getRotateMinuteLine()
{
return minuteRotation.getAngle();
}
public void setRotateMinute(double degree)
{
minuteRotation.setAngle(degree);
}
public void rotateHourLine()
{
hourRotation.setAngle(hourRotation.getAngle() + 6);
}
public double getRotateHourLine()
{
return hourRotation.getAngle();
}
public void setRotateHour(double degree)
{
hourRotation.setAngle(degree);
}
}
Main
import javafx.animation.*;
import javafx.application.*;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;
import javafx.util.*;
/**
*
* #author Sedrick
*/
public class JavaFXApplication54 extends Application {
#Override
public void start(Stage primaryStage)
{
VBox root = new VBox();
ClockGUI cgui = new ClockGUI();
StackPane stackpane = new StackPane();
stackpane.getChildren().add(cgui.getCurrentClock());
root.getChildren().add(stackpane);
Button btn = new Button("Rotate seconds");
btn.setOnAction((event) -> {
cgui.rotateSecondLine();
});
Button btn2 = new Button("Rotate minutes");
btn2.setOnAction((event) -> {
cgui.rotateMinuteLine();
});
Button btn3 = new Button("Rotate hours");
btn3.setOnAction((event) -> {
cgui.rotateHourLine();
});
root.getChildren().addAll(btn, btn2, btn3);
Scene scene = new Scene(root, 300, 250);
Timeline timeline = new Timeline();
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.getKeyFrames().add(
new KeyFrame(Duration.seconds(1),
new EventHandler() {
// KeyFrame event handler
#Override
public void handle(Event event)
{
System.out.println(cgui.getRotateSecondLine());
cgui.rotateSecondLine();
if (cgui.getRotateSecondLine() >= 360) {
cgui.setRotateSecond(0);
cgui.rotateMinuteLine();
}
if (cgui.getRotateMinuteLine() >= 360) {
cgui.setRotateMinute(0);
cgui.rotateHourLine();
}
if (cgui.getRotateHourLine() >= 360) {
cgui.setRotateHour(0);
}
}
}
));
timeline.playFromStart();
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
This is a fan program where use a slider to increase and decrease the speed of the fan. I do not need the increase and decrease button, I only have them as a guide to help the slider figure out what happens when you scroll left or right. I will delete those later. My slider is not showing and I can't test it out. Where did I go wrong here?
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
import javafx.scene.control.Slider;
public class module2 extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
// Create fan pane
FanPane fanPane = new FanPane(100);
Slider mSlider = new Slider();
HBox scrollPane = new HBox(mSlider);
KeyFrame keyFrame = new KeyFrame(Duration.millis(10), e-> fanPane.spin());
Timeline fanTimeline = new Timeline(keyFrame);
fanTimeline.setCycleCount(Timeline.INDEFINITE);
// Buttons pause, resume, increase, decrease, reverse
Button pause = new Button("Pause");
pause.setOnAction(e-> fanTimeline.pause());
Button resume = new Button("Resume");
resume.setOnAction(e-> fanTimeline.play());
//fanPane.increase()
Button increase = new Button("Increase");
increase.setOnAction(e -> {
fanTimeline.setRate(fanTimeline.getCurrentRate() + 1);
mSlider.setValue(fanTimeline.getCurrentRate());
});
Button decrease = new Button("Decrease");
decrease.setOnAction(e -> {
fanTimeline.setRate(
(fanTimeline.getCurrentRate() - 1 < 0) ? 0 : fanTimeline.getCurrentRate() - 1);
mSlider.setValue(fanTimeline.getCurrentRate());
});
Button reverse = new Button("Reverse");
reverse.setOnAction(e-> fanPane.increment *= -1);
HBox hButtons = new HBox(pause,resume,reverse);
hButtons.setSpacing(10);
hButtons.setAlignment(Pos.CENTER);
hButtons.setPadding(new Insets(10, 10, 10, 10));
BorderPane borderPane = new BorderPane(fanPane, null, null, hButtons, null);
primaryStage.setScene(new Scene(borderPane));
primaryStage.setTitle("Spinning fan");
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
private class FanPane extends Pane {
private Circle c;
private Arc[] blades = new Arc[4];
private double increment = 1;
Slider mSlider = new Slider();
HBox scrollPane = new HBox(mSlider);
FanPane(double radius) {
setMinHeight(400);
setMinWidth(400);
c = new Circle(200,200,radius,Color.BLUE);
c.setStroke(Color.BLACK);
double bladeRadius = radius * 0.9;
for (int i = 0; i < blades.length; i++) {
blades[i] = new Arc(
c.getCenterX(), c.getCenterY(), // center point
bladeRadius, bladeRadius, // X and Y radius
(i * 90) + 30, 35); // start angle and length
blades[i].setFill(Color.YELLOW);
blades[i].setType(ArcType.ROUND);
}
getChildren().addAll(c);
getChildren().addAll(blades);
}
private void spin() {
for (Arc blade : blades) {
double prevStartAngle = blade.getStartAngle();
blade.setStartAngle(prevStartAngle + increment);
}
}
}
}