my goal is to see the colorchange of the rectangle after every second ,after the Stage + Scene appears.
I researched and tried several things:
code under primaryStage.show() [look at my examplecode]
primaryStage.setOnShown() or primaryStage.setOnShowing()
EventHandler from Stage
EventHandler from Scene
Button with eventhandler
All in vain.
In most situation the stage comes, then the program performs the colorchange in the background (without visualization) and at last the scene appears with the endresult. Or version 2: I see nothing, the code goes through and in the end comes immediately the final result.
Here is my code:
package sample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
GridPane gridPane = new GridPane();
Rectangle[] recs = new Rectangle[10];
for (int i = 0; i < recs.length; i++) {
recs[i] = new Rectangle(30, 30, Color.GREEN);
recs[i].setStroke(Color.BLACK);
gridPane.add(recs[i], i, 0);
}
primaryStage.setTitle("Code after primaryStage.show()");
primaryStage.setScene(new Scene(gridPane, 400, 300));
primaryStage.show();
for (Rectangle rec : recs) {
Thread.sleep(1000);
rec.setFill(Color.ORANGE);
}
}
public static void main(String[] args) {
launch(args);
}
}
The issue here is that your loop is running on the main application thread, so it locks any GUI updates until it's completed.
Perform the loop on its own thread instead and use Platform.runLater() to update each rectangle:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
GridPane gridPane = new GridPane();
Rectangle[] recs = new Rectangle[10];
for (int i = 0; i < recs.length; i++) {
recs[i] = new Rectangle(30, 30, Color.GREEN);
recs[i].setStroke(Color.BLACK);
gridPane.add(recs[i], i, 0);
}
primaryStage.setTitle("Code after primaryStage.show()");
primaryStage.setScene(new Scene(gridPane, 400, 300));
primaryStage.show();
new Thread(() -> {
for (Rectangle rec :
recs) {
try {
Thread.sleep(1000);
Platform.runLater(() -> rec.setFill(Color.ORANGE));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
public static void main(String[] args) {
launch(args);
}
}
So What's Happening?
new Thread(() -> {
Opens up a new thread in the background of your application so that the UI remains responsive.
We can then start your loop within a try/catch block.
Platform.runLater(() -> rec.setFill(Color.ORANGE));
When working with a background thread, it's important to know that you cannot make changes to the UI directly. This line tells JavaFX to execute the rec.setFill() statement on the JavaFX Application thread.
.start();
You've already created the new Thread, this just starts it.
Here is another approach using TimeLine. Code from here.
import java.util.concurrent.atomic.AtomicInteger;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
GridPane gridPane = new GridPane();
Rectangle[] recs = new Rectangle[10];
for (int i = 0; i < recs.length; i++) {
recs[i] = new Rectangle(30, 30, Color.GREEN);
recs[i].setStroke(Color.BLACK);
gridPane.add(recs[i], i, 0);
}
primaryStage.setTitle("Code after primaryStage.show()");
primaryStage.setScene(new Scene(gridPane, 400, 300));
primaryStage.show();
AtomicInteger counter = new AtomicInteger();
Timeline oneSecondsWonder = new Timeline(new KeyFrame(Duration.seconds(1), (ActionEvent event) -> {
System.out.println("this is called every 1 second on UI thread");
recs[counter.getAndIncrement()].setFill(Color.ORANGE);
}));
oneSecondsWonder.setCycleCount(recs.length);
oneSecondsWonder.play();
}
public static void main(String[] args)
{
launch(args);
}
}
Related
I have created a simple app to simulate dynamic node creation with JAVAFX.
This app has the ability to create a new window whenever user want it by clicking the "New" button.
User can add a new node which is TitledPane to the window by clicking "Add Task" button and then clicking "Add" button on the dialog window.
There are an unexpected behavior which I want to fix. This app is only add new node (TitledPane in this case) to the last created window.
And the all of the nodes on the previous window will vanish.
You can see the following video to better understand what I mean.
VIDEO
https://youtu.be/eaWmu3zuuhE
NETBEANS PROJECT
Just in case you want to play with it.
https://drive.google.com/file/d/0B4Sbb8Ym-lcZLUIyWHV5ZXRSZE0/view?usp=sharing
CODES:
TasksList.java
package taskslist;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.stage.Stage;
public class TasksList extends Application {
DisplayWhich display = new DisplayWhich();
Stage primaryStage;
Parent startWindow;
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
initStart();
}
private void initStart(){
display.showDialogWindow();
}
public static void main(String[] args) {
launch(args);
}
}
TheList.java
package taskslist.view;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import taskslist.DisplayWhich;
public class TheList extends BorderPane {
public static VBox listWrapper;
public static ScrollPane listScroller;
public ObservableList<TitledPane> tasks;
private List<String> titles = new ArrayList<>();
public TheList(){
tasks = FXCollections.observableArrayList();
listWrapper = new VBox(5);
listScroller = new ScrollPane(listWrapper);
}
public void setTitles(String... title){
titles = Arrays.asList(title);
}
public List<String> getTitles(){
return titles;
}
public void loadSavedList(){
for(int i=0; i<getTitles().size();i++){
String ttlString = getTitles().get(i);
this.createTask(ttlString);
}
// Display Tasks
listWrapper.getChildren().addAll(this.tasks);
}
// Dialong for adding a new task and also editing a task
private void addTaskDialog(){
GridPane container = new GridPane();
Scene scene = new Scene(container, 150, 50);
Stage addNewTask = new Stage();
addNewTask.initModality(Modality.APPLICATION_MODAL);
addNewTask.setTitle("Add Task");
TextField title = new TextField();
Button confirm = new Button("Add");
// Create Task
confirm.setOnAction((ev) -> {
String ttlString = title.getText();
this.createTask(ttlString);
listWrapper.getChildren().clear();
listWrapper.getChildren().addAll(this.tasks);
addNewTask.close();
});
container.add(title, 0, 1);
container.add(confirm, 0, 5);
addNewTask.setScene(scene);
addNewTask.showAndWait();
}
// Assemble all this.tasks list components
public void render(){
setCenter(listScroller);
loadSavedList();
Button newProject = new Button("New");
Button addTask = new Button("Add Task");
BorderPane listBottom = new BorderPane();
HBox bottomLeft = new HBox();
bottomLeft.getChildren().add(newProject);
listBottom.setLeft(bottomLeft);
HBox bottomRight = new HBox();
bottomRight.getChildren().add(addTask);
listBottom.setRight(bottomRight);
newProject.setOnAction((evt) -> {
DisplayWhich display = new DisplayWhich();
display.showDialogWindow();
});
addTask.setOnAction((e) -> {
addTaskDialog();
});
setBottom(listBottom);
}
// Cteate task from strings
private void createTask(String... strings){
String taskTitle = strings.length > 0 ? strings[0] : "";
TitledPane task = new TitledPane();
task.setPrefWidth(647);
task.setExpanded(false);
task.setText(taskTitle);
this.tasks.add(task);
}
}
NewDialog.java
package taskslist.view;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import taskslist.DisplayWhich;
public class NewDialog {
DisplayWhich display = new DisplayWhich();
Stage stage = new Stage();
Parent startWindow = new AnchorPane();
#FXML
private Button cancelNew;
#FXML
private Button confirmCreation;
/**
* Initializes the controller class.
*/
#FXML
private void initialize() {
}
#FXML
private void cancelNewCreation(ActionEvent event) {
((Stage)cancelNew.getScene().getWindow()).close();
}
#FXML
private void confirmCreateNew(ActionEvent event) {
((Stage)confirmCreation.getScene().getWindow()).close();
TheList wrap = new TheList();
TheWindow window = new TheWindow();
window.makeWindow(wrap);
wrap.setTitles("one", "two", "three", "four");
wrap.render();
}
}
DisplayWhich.java
package taskslist;
import java.io.IOException;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import taskslist.view.TheList;
public class DisplayWhich {
Stage stage = new Stage();
Parent startWindow = new AnchorPane();
public DisplayWhich(){}
public Stage showDialogWindow(){
try {
stage.initModality(Modality.APPLICATION_MODAL);
stage.setTitle("Create New Project");
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/taskslist/view/newDialog.fxml"));
startWindow = loader.load();
Scene scene = new Scene(startWindow);
stage.setScene(scene);
stage.setOnCloseRequest((event) -> {
System.out.println("test");
});
stage.showAndWait();
} catch (IOException ex) {
ex.printStackTrace();
}
return stage;
}
}
TheWindow.java
package taskslist.view;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TheWindow {
public TheWindow(){}
public void makeWindow(BorderPane group) {
Stage mainWindow = new Stage();
Scene scene = new Scene(group, 650, 550);
mainWindow.setScene(scene);
mainWindow.setTitle("Task List");
mainWindow.centerOnScreen();
mainWindow.show();
}
}
Why that weird behavior happening and how to fix it so it only adds new node to the same window where the clicked "Add Task" button is located?
These fields should not be static:
public static VBox listWrapper;
public static ScrollPane listScroller;
I recently started coding in FXML/JavaFX using Eclipse and one of the projects I'm working on requires me to make a drop down menu with combobox, checkboxes etc.. So my question is would it be possible to make the MenuButton display a VBox/HBox when clicked with those inside?
Here is a one of the simplest example of menu:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.stage.Stage;
public class MenuFX extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Group group = new Group();
Scene scene = new Scene(group, 800, 600);
MenuBar menuBar = new MenuBar();
Menu someValues = new Menu("Values");
for (int i = 0; i < 60; i++) {
MenuItem item = new MenuItem("Value " + i);
someValues.getItems().add(item);
}
menuBar.getMenus().add(someValues);
group.getChildren().addAll(menuBar);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
More expanding example here.
When I change scenes on my stage with the following code, my stage changes size. However the button in the top right to maximize/minimize the windows says that the stage is still maximized even though it is clearly not.
How am I able to keep the stage maximized when a scene change happens?
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class Program2 extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
try {
StackPane p = new StackPane();
primaryStage.setTitle("Chart Application");
Label loader = new Label("Loading...");
loader.setGraphic(new ImageView(new Image("https://media.giphy.com/media/FmcNeI0PnsAKs/giphy.gif")));
loader.setFont(new Font(35));
p.setStyle("-fx-background: #FFFFFF;");
p.getChildren().add(loader);
StackPane.setAlignment(loader, Pos.CENTER);
primaryStage.setScene(new Scene(p));
primaryStage.setMaximized(true);
Task<VBox> task = new Task<VBox>() {
#Override
public VBox call() {
VBox result = new VBox();
for(int i = 0; i < 50000; i++) { //Here simply for small delay
result.getChildren().add(new Label(Integer.toString(i)));
}
return result ;
}
};
task.setOnSucceeded(e -> {
VBox result = task.getValue();
ScrollPane scrollPane = new ScrollPane();
scrollPane.setContent(result);
scrollPane.setStyle("-fx-background: #FFFFFF;");
primaryStage.setScene(new Scene(scrollPane));
});
new Thread(task).start();
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
}
As this is operating system specific I image I am using Windows 10 with JDK 8 u112 and JavaFX 8 with the e(fx)clipse plugin for eclipse
Instead of replacing the scene, use the same scene and replace its root:
primaryStage.getScene().setRoot(scrollPane);
I'm new to javaFX and I wanted to make a simple code that counted how many times a person pressed a button and displayed the count on the application itself. Currently I have my code printing the counter in my IDE and would just like to some how attach it to the scene(eg I click run and every time I click the button it prints how many times I've clicked it in my workbench). I looked around stack overflow and youtube but the closest I got to what I was looking for was printing it in my IDE. Thanks for any help.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class JavaFXTest extends Application {
private int counter = 0;
public static void main (String [] args){
Application.launch();
}
#Override
public void start(Stage primaryStage) throws Exception {
Stage stage = new Stage();
stage = primaryStage;
Pane pane = new Pane();
pane.setPrefSize(400,400);
Button button = new Button("Smash it!");
HBox root = new HBox(5, pane);
button.setOnAction(e -> {
counter();
});
root.getChildren().add(button);
Scene scene1 = new Scene(root,1000, 800, Color.AQUA);
stage.setScene(scene1);
stage.setTitle("ButtonSmash!");
stage.show();
}
public void counter(){
counter++;
System.out.println(counter);
}
}
Here is the full code:
package StackOverFlow;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class JavaFXTest extends Application {
private int counter = 0;
private Label label = new Label("Count: ");
public static void main (String [] args){
Application.launch();
}
#Override
public void start(Stage primaryStage) throws Exception {
Stage stage = new Stage();
stage = primaryStage;
Pane pane = new Pane();
pane.setPrefSize(400,400);
Button button = new Button("Smash it!");
HBox root = new HBox(5, pane);
button.setOnAction(e -> {
label.setText("Count: "+Integer.toString(counter));
counter();
});
root.getChildren().add(button);
label.relocate(0, 0); // You can put this label, wherever you want!
root.getChildren().add(label);
Scene scene1 = new Scene(root,1000, 800, Color.AQUA);
stage.setScene(scene1);
stage.setTitle("ButtonSmash!");
stage.show();
}
public void counter(){
counter++;
//System.out.println(counter);
}
}
You had to make one label and to add it to your pane.getChildren();
And whenever you press the button you need to change text from that label.
I am adding some drag and drop behavior to my application where the user can DnD stuff onto a canvas with custom logic and rendering code. I need to get the mouse's position relative to the canvas (i.e. 0,0 being top-left) but the DragEvent does not let me do this, and mouse events do not get delivered to the canvas during the DnD.
Is it possible to allow the mouse events to be delivered while a drag-and-drop is in progress?
You can do this in a dragOver handler, which is invoked any time the mouse moves on the node on which the handler is registered during the drag.
Simple example:
import java.util.stream.Stream;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class DragToCanvas extends Application {
private Color draggingColor ;
#Override
public void start(Stage primaryStage) {
Pane dragFromPane = new Pane();
Canvas canvas = new Canvas(600, 600);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.ANTIQUEWHITE);
gc.fillRect(0, 0, 600, 600);
Label coordinateLabel = new Label("[ ]");
coordinateLabel.setStyle("-fx-font-size: 24;");
Color[] colors = {Color.RED, Color.GREEN, Color.BLUE, Color.BLACK} ;
for (int i = 0; i < colors.length; i++) {
drawCircle(colors[i], dragFromPane, i);
}
canvas.setOnDragOver(e -> {
if (draggingColor != null
&& e.getDragboard().hasString()
&& e.getDragboard().getString().equals("circle")) {
e.acceptTransferModes(TransferMode.COPY);
}
coordinateLabel.setText(String.format("[%.1f, %.1f]", e.getX(), e.getY()));
});
canvas.setOnDragDropped(e -> {
if (e.getDragboard().hasString()
&& e.getDragboard().getString().equals("circle")) {
gc.setFill(draggingColor);
gc.fillOval(e.getX()-25, e.getY()-25, 50, 50);
draggingColor = null ;
e.setDropCompleted(true);
}
});
BorderPane root = new BorderPane();
root.setCenter(canvas);
root.setLeft(dragFromPane);
root.setBottom(coordinateLabel);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private void drawCircle(Color c, Pane dragFromPane, int index) {
Circle circle = new Circle(60, 60*(index+1), 25);
circle.setFill(c);
dragFromPane.getChildren().add(circle);
circle.setOnDragDetected(e -> {
Dragboard db = circle.startDragAndDrop(TransferMode.COPY);
db.setDragView(circle.snapshot(null, null));
ClipboardContent cc = new ClipboardContent();
cc.putString("circle");
db.setContent(cc);
draggingColor = c ;
});
}
public static void main(String[] args) {
launch(args);
}
}