JavaFX - Control and Concurrency - javafx

I have a sample Hello World JavaFx. I am using Eclipse and eFxclipse plugin. My Eclipse is kepler which is Eclipse 4.3.2 version and Java servion is Jdk1.7-045.
What I try to add is very little concurrency codes, I just want to update button text in the example. Could this backend task interact with upfront UI control, for example button, scene? If not, how could I make tailored backend task, then interact with UI control?
Thanks in advance
package com.juhani.fx.exer;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class HelloWorld extends Application{
private static final short COREPOOLSIZE=2;
private static final short MAXIMUMPOOLSIZE=2;
private static final int WORKQUEUECAPACITY=100;
private static Logger log = LogManager.getLogger(
HelloWorld.class.getName());
private ExecutorService executors = new ThreadPoolExecutor(COREPOOLSIZE,MAXIMUMPOOLSIZE,20,TimeUnit.MINUTES,new ArrayBlockingQueue<Runnable>(WORKQUEUECAPACITY));
public static void main(String[] args) {
LogMessage logMessage = new LogMessage("BEGIN",1.0,1,"HELLOWORLD");
log.trace(logMessage.toString());
launch(args);
}
#Override
public void start(final Stage primaryStage) throws InterruptedException {
primaryStage.setTitle("Hello World!");
final Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
final StackPane root = new StackPane();
root.getChildren().add(btn);
final Scene scene= new Scene(root,300,250);
primaryStage.setScene(scene);
primaryStage.show();
Task<Boolean> task = new Task<Boolean>() {
#Override
public Boolean call() {
for(int i=0;i<20;i++){
btn.setText("First row\nSecond row "+i);
primaryStage.show();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log.error(new LogMessage("entering interruption",1.0,2,"exception").toString());
}
}
return new Boolean(true);
}
};
executors.submit(task);
}
}

This answer specially talks about the use of Platform.runLater. If you are using Task, you are better off updating the UI using the method it provides as stated in
kleopatra's answer.
For updating the UI, you have to be on the Javafx thread.
Once you are on any other thread, use Platform.runLater() to update those data back to Javafx UI. A working example can be found below
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class HelloWorld extends Application {
private static final short COREPOOLSIZE = 2;
private static final short MAXIMUMPOOLSIZE = 2;
private static final int WORKQUEUECAPACITY = 100;
private ExecutorService executors = new
ThreadPoolExecutor(COREPOOLSIZE, MAXIMUMPOOLSIZE, 20, TimeUnit.MINUTES,
new ArrayBlockingQueue<Runnable>(WORKQUEUECAPACITY));
public static void main(String[] args) {
launch(args);
}
#Override
public void start(final Stage primaryStage) throws InterruptedException {
primaryStage.setTitle("Hello World!");
final Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
final StackPane root = new StackPane();
root.getChildren().add(btn);
final Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
Task<Boolean> task = new Task<Boolean>() {
#Override
public Boolean call() {
final AtomicInteger i = new AtomicInteger(0);
for( ; i.get() < 20; i.incrementAndGet()) {
Platform.runLater(new Runnable() {
#Override
public void run() {
btn.setText("First row\nSecond row " + i);
}
});
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {}
}
return Boolean.valueOf(true);
}
};
executors.submit(task);
}
}
For more information you can go through the links provided here

A Task is designed to interact with the ui on the fx-application thread, to take advantage of that support you should use it as designed :-)
As a general rule, you must not access ui in the call method [*] of the Task. Instead, update one of its properties (message, progress ...) and bind that property to your ui. Sample code:
Task<Boolean> taskWithBinding = new Task<Boolean>() {
#Override
public Boolean call() {
final AtomicInteger i = new AtomicInteger(0);
for( ; i.get() < 20; i.incrementAndGet()) {
updateMessage("First row\nSecond row " + i);
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
return Boolean.FALSE;
}
}
return Boolean.TRUE;
}
};
btn.textProperty().bind(taskWithBinding.messageProperty());
[*] The one exception is outlined (wrap the access into an runLater) in the other answer. Doing so is technically correct - but then you are by-passing a Task's abilities and could use an arbitrary Runnable ...

Related

javafx checkbox clicked while key pressed will not change state

I have a very simple problem, which I can't find in any other posts :
In javafx15 / java15.0.1, I am trying to click a Checkbox while pressing, for example, the CONTROL key... State is not changing.
I tried to catch the key (with a key event on the checkbox), and I do catch the control key pressed... But state of checkbox just does not change if a key is pressed simultaniously.
How to get this to just work in a transparent way ?
Here is a most basic simple code to illustate the problem :
package checkboxkeypressed;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.stage.Stage;
public class CheckboxWhileKeyPressedNotWorking extends Application {
private CheckBox checkbox = new CheckBox();
#Override
public void start(Stage primaryStage) {
checkbox.setText("Click me while pressing a key...");
Scene scene = new Scene(checkbox, 200, 50);
primaryStage.setTitle("Checkbox cannot be ticked while a key is pressed !!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Any help would be greatly appreciated.
Improving on the solution offered by etuygar:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.stage.Stage;
public class CheckboxWhileKeyPressedNotWorking extends Application {
private final CheckBox checkbox = new CheckBox();
private boolean isControlKeyDown = false;
#Override
public void start(Stage primaryStage) {
checkbox.setText("Click me while pressing <CNTRL> key");
checkbox.setOnMouseClicked(e->{
if(isControlKeyDown){
checkbox.fire(); //change check box state
}
});
Scene scene = new Scene(checkbox, 300, 50);
scene.setOnKeyPressed(keyEvent -> {
isControlKeyDown = keyEvent.isControlDown();
});
scene.setOnKeyReleased(keyEvent -> {
isControlKeyDown = keyEvent.isControlDown();
});
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
You can do something like this.
package checkboxkeypressed;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.input.KeyCode;
import javafx.stage.Stage;
public class CheckboxWhileKeyPressedNotWorking extends Application {
private CheckBox checkbox = new CheckBox();
private boolean ctrlOk, lastState;
#Override
public void start(Stage primaryStage) {
checkbox.setText("Click me while pressing a key...");
Scene scene = new Scene(checkbox, 200, 50);
scene.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.CONTROL) ctrlOk = true;
});
scene.setOnKeyReleased(event -> {
if (event.getCode() == KeyCode.CONTROL) ctrlOk = false;
});
checkbox.setOnMouseClicked(event -> {
if (!ctrlOk) {
checkbox.setSelected(lastState);
} else {
checkbox.setSelected(!checkbox.isSelected());
lastState = checkbox.isSelected();
}
});
primaryStage.setTitle("Checkbox cannot be ticked while a key is pressed !!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Thank you soooooo much #miss-chanandler-bong !
So would this answer be ok ? I'm forcing the checkbox to be armed, when armed is changing...
package checkboxkeypressed;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;
public class CheckboxWhileKeyPressed extends Application {
private CheckBox checkbox = new CheckBox();
#Override
public void start(Stage primaryStage) {
checkbox.setText("Click me while pressing a key...");
checkbox.armedProperty().addListener(changeListener -> checkbox.arm());
checkbox.arm();
checkbox.setOnKeyPressed(keyEvent -> processKeyPressed(keyEvent));
Scene scene = new Scene(checkbox, 200, 50);
primaryStage.setTitle("Checkbox CAN NOW be ticked while a key is pressed !!");
primaryStage.setScene(scene);
primaryStage.show();
}
private void processKeyPressed(KeyEvent keyEvent) {
if (keyEvent.getCode().equals(KeyCode.CONTROL)) {
System.out.println("Key IS control");
} else {
System.out.println("Key IS NOT control : '" + keyEvent.getCode().getName() + "'");
}
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX progress indicator stop spinning when heavy load runs

I am trying to implement busy indicator using ProgressIndicator. But when the heavy load starts the indicator freezes. A sample code is shown below.
import javafx.beans.value.ChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class BusyIcon {
private static Stage busyWindow;
public static void showBusyIcon(final Stage stage) {
busyWindow = new Stage(StageStyle.UNDECORATED);
//busyWindow.setOpacity(.3);
busyWindow.initOwner(stage);
busyWindow.initModality(Modality.WINDOW_MODAL);
StackPane stackPane = new StackPane();
final ProgressIndicator loadingIndicator = new ProgressIndicator();
loadingIndicator.setVisible(true);
stackPane.getChildren().add(loadingIndicator);
Scene scene = new Scene(stackPane, 100, 100);
scene.setFill(Color.TRANSPARENT);
busyWindow.setScene(scene);
ChangeListener<Number> widthListener = (observable, oldValue, newValue) -> {
double stageWidth = newValue.doubleValue();
busyWindow.setX(stage.getX() + stage.getWidth() / 2 - stageWidth / 2);
};
ChangeListener<Number> heightListener = (observable, oldValue, newValue) -> {
double stageHeight = newValue.doubleValue();
busyWindow.setY(stage.getY() + stage.getHeight() / 2 - stageHeight / 2);
};
busyWindow.widthProperty().addListener(widthListener);
busyWindow.heightProperty().addListener(heightListener);
busyWindow.setOnShown(e -> {
busyWindow.widthProperty().removeListener(widthListener);
busyWindow.heightProperty().removeListener(heightListener);
});
busyWindow.show();
}
public static void closeBusyIcon(final Stage stage) {
if (busyWindow != null) {
busyWindow.close();
busyWindow = null;
}
}
}
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import preloader.BusyIcon;
public class QuestionExample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Task Progress Tester");
StackPane testPane = new StackPane();
Button b = new Button("Load");
b.setOnAction((event) -> {
BusyIcon.showBusyIcon(primaryStage);
Task t = new Task() {
#Override
protected Object call() throws Exception {
try {
addNewComponent(testPane);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
};
t.setOnSucceeded((ev) -> {
BusyIcon.closeBusyIcon(primaryStage);
});
new Thread(t).start();
});
testPane.getChildren().add(b);
primaryStage.setScene(new Scene(testPane, 300, 250));
primaryStage.show();
}
private void addNewComponent(Pane testPane) {
try {
/**
* Some heavy load work will run here
*/
Thread.sleep(2000);
Platform.runLater(() -> {
try {
/**
* We need to change the fx controls here
*/
Button b1 = new Button("New Component");
testPane.getChildren().add(b1);
/**
* This may take some time
*/
Thread.sleep(2000);
} catch (Exception ex) {
ex.printStackTrace();
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
BusyIcon is used for showing progress indicator. If we are not using the Platform.runLater then it will throw 'Not in FX thread' exception will be thrown.
I suggest you try ControlsFX MaskerPane. The key is to set the MaskerPane visible and move it to the front of an AnchorPane before the task runs. When the task finishes, set it invisible and move it to the back of the AnchorPane.
DEMO:
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.controlsfx.control.MaskerPane;
/**
*
* #author blj0011
*/
public class MaskerPaneTest extends Application
{
#Override
public void start(Stage primaryStage)
{
MaskerPane mpDeterminate = new MaskerPane();
MaskerPane mpUndeterminate = new MaskerPane();
mpDeterminate.setVisible(false);
mpUndeterminate.setVisible(false);
Button btn = new Button();
btn.setText("Determinate");
btn.setOnAction((ActionEvent event) -> {
mpDeterminate.setVisible(true);
mpDeterminate.toFront();
Task<Void> task = new Task<Void>()
{
#Override
protected Void call() throws Exception
{
for (int i = 0; i < 40000000; i++) {
//Do something
updateProgress(i, 40000000);
}
return null;
}
};
mpDeterminate.progressProperty().bind(task.progressProperty());
task.setOnSucceeded((workerStateEvent) -> {
mpDeterminate.setVisible(false);
mpDeterminate.toBack();
});
new Thread(task).start();
});
Button btn2 = new Button();
btn2.setText("Undeterminate");
btn2.setOnAction((ActionEvent event) -> {
mpUndeterminate.setVisible(true);
mpUndeterminate.toFront();
Task<Void> task = new Task<Void>()
{
#Override
protected Void call() throws Exception
{
for (int i = 0; i < 100000; i++) {
//Do something
System.out.println("working!");
}
return null;
}
};
mpUndeterminate.progressProperty().bind(task.progressProperty());
task.setOnSucceeded((workerStateEvent) -> {
mpUndeterminate.setVisible(false);
mpUndeterminate.toBack();
});
new Thread(task).start();
});
StackPane root = new StackPane(mpDeterminate, mpUndeterminate, new VBox(btn, btn2));
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}

Regain javafx focus

I have an application which runs in background. With jkeymaster I have registered a global hotkey to be fired although my application is not the active one. This is working. I now want to switch my background app in foreground again. Tried with this code:
import com.tulskiy.keymaster.common.HotKey;
import com.tulskiy.keymaster.common.HotKeyListener;
import com.tulskiy.keymaster.common.Provider;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javax.swing.*;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
public class TestClientNotWorking extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws AWTException {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Btn said 'Hello World!'");
}
});
HBox root = new HBox(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
primaryStage.requestFocus();
Provider provider = Provider.getCurrentProvider(false);
provider.register(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
InputEvent.ALT_MASK | InputEvent.SHIFT_MASK),
new HotKeyListener() {
#Override
public void onHotKey(HotKey hotKey) {
Platform.runLater(new Runnable() {
#Override
public void run() {
System.out.println("Recievd");
primaryStage.toFront();
btn.requestFocus();
}
});
}
});
}
}
What happens:
My stage is brought back to foreground, but the focus still remains in the app, were it was before and is not transferred to my primaryStage. Can you please help my how I could gain this?
Thanks in advance
I compiled and run the code. As far as I understand your expectations are:
start application, register key hook
minimize jfx window
press key combination (alt shift space in your case)
result: jfx window is in foreground state, 'Hello World' button is focused
btn.requestFocus() works properly on my OS without any changes but my guess you need to try setIconified\setMaximized instead of primaryStage.toFront();
I changed a little bit your code pls try it and let us know if it works as you want.
public class TestClientNotWorking extends Application {
private final Provider provider = Provider.getCurrentProvider(false);
public static void main(final String[] args) {
launch(args);
}
#Override
public void start(final Stage primaryStage) throws AWTException {
final Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(final ActionEvent event) {
System.out.println("Btn said 'Hello World!'");
}
});
final HBox root = new HBox(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
root.requestFocus();// remove focus from btn
final KeyStroke stroke = KeyStroke.getKeyStroke("ctrl SPACE");// stroke = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.SHIFT_MASK | InputEvent.ALT_MASK);
provider.register(stroke, new HotKeyListener() {
#Override
public void onHotKey(final HotKey hotKey) {
Platform.runLater(new Runnable() {
#Override
public void run() {
System.out.println("Recievd");
primaryStage.setIconified(false); // primaryStage.toFront();
btn.requestFocus(); // set focus on btn
}
});
}
});
}
#Override
public void stop() throws Exception {
super.stop();
provider.stop();
}
}

Disable the Cancel Button in Progress Bar in JavaFx

I have made a progress bar in javafx. There is a cancel Button by default. I just want to disable this cancel button when my task got completed.
jobProgressView.setGraphicFactory(task -> {
return new Button("save");
});
Without more code, I'm only able to make a guess. Even your added code isn't enough to know all things from your implementation.
So this solution assumes, that you have a Task that is running and showing it's progress on a Progressbar. The Task here is wrapped in a service, which can be restarted (maybe you also need this?).
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.event.ActionEvent;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CancelButtonDemo extends Application {
Service<Integer> service = new MyService();
#Override
public void start(Stage primaryStage) {
Button start = new Button();
Button cancel = new Button();
ProgressBar progress = new ProgressBar(0);
start.setText("Run Task");
start.setOnAction((ActionEvent event) -> {
if (!(service.getState().equals(Worker.State.READY))) {
service.reset();
}
progress.progressProperty().bind(service.progressProperty());
service.start();
});
start.disableProperty().bind(service.runningProperty());
cancel.setText("Cancel Task");
cancel.setOnAction((ActionEvent event) -> {
service.cancel();
progress.progressProperty().unbind();
progress.setProgress(0);
});
cancel.disableProperty().bind(Bindings.not(service.runningProperty()));
VBox root = new VBox(20);
root.setAlignment(Pos.CENTER);
root.getChildren().addAll(start, progress, cancel);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Cancel Button Demo");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
class MyService extends Service<Integer> {
#Override
protected Task<Integer> createTask() {
return new Task<Integer>() {
#Override
protected Integer call() throws Exception {
int iterations;
for (iterations = 0; iterations < 10000000; iterations++) {
if (isCancelled()) {
updateMessage("Cancelled");
break;
}
updateMessage("Iteration " + iterations);
updateProgress(iterations, 10000000);
}
return iterations;
}
};
}
}
}
The above application looks like this:

JavaFX: Window closing event prevents button action

I did implement a popup window in a new stage and I'm now trying to close it, no matter where I click (excluding the popup itself). This works just fine. Although the popup windows disappears when I click on another element (e.g. a button) on the background, I'd still like to get the event for the button. Any suggestions on how to achieve this? I put together a short example of the situation.
package application;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 400, 400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
// the popup
Pane p = new Pane();
p.setPrefSize(100, 100);
p.setStyle("-fx-background-color: #660066");
final Stage popUp = new Stage();
Scene popUpScene = new Scene(p);
popUp.setScene(popUpScene);
Button btnShow = new Button("Show popUp");
root.setCenter(btnShow);
btnShow.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event event) {
ChangeListener stageFocusListener = new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> arg0, Boolean oldPropertyValue, Boolean newPropertyValue) {
if (!newPropertyValue) {
popUp.hide();
}
}
};
popUp.focusedProperty().addListener(stageFocusListener);
popUp.show();
}
});
Button btnTest = new Button("test");
root.setRight(btnTest);
btnTest.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event event) {
System.out.println("Button test clicked");
}
});
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
You can put the hiding event to the queue without disturbing the event handling procedure of the button
if (!newPropertyValue) {
Platform.runLater(new Runnable() {
#Override
public void run() {
popUp.hide();
}
});
}

Resources