Make Eventhandler act immediately? - java

private void run(ActionEvent event) throws InterruptedException {
button.setDisabled(true);
Thread.sleep(3000);
button.setDisabled(false);
}
This is my Code.
I want to do something at the place of Thread.sleep() while the Button is disabled.
But it does everything at once.
Can somebody help?

Here is an example that uses a Task on a different thread to execute the operation.
Button b = new Button("Button!");
b.setOnAction(e -> {
b.setDisable(true);
Task<Void> task = new Task<Void>(){
#Override
protected Void call() throws Exception {
Platform.runLater(() -> b.setText("Modified text")); // Use Platform.runLater here to modify the scene graph
Thread.sleep(3000);
return null;
}
};
task.setOnSucceeded(v -> {
System.out.println("Succeed");
b.setText("Button!");
b.setDisable(false);
});
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
});

Related

JavaFX throws exception when performing SQL query using Java.util.Timer [duplicate]

I have an application that has a TableView that has an attached listener so it refreshes as soon as it detects a change, but the thing is that I´m getting java.lang.IllegalStateException: Not on FX application thread; currentThread = Smack Listener Processor (0).
Here is my code:
/**
* This function resets the pagination pagecount
*/
public void resetPage() {
try {
System.out.println("RESET");
int tamRoster = this.loginManager.getRosterService().getRosterList().size();
paginationContactos.setPageCount((int)(Math.ceil(tamRoster*1.0/limit.get())));
int tamEnviados = this.loginManager.getRosterService().getEnviadasList().size();
paginationEnviadas.setPageCount((int)(Math.ceil(tamEnviados*1.0/limit.get())));
int tamRecibidas = this.loginManager.getRosterService().getRecibidasList().size();
paginationRecibidas.setPageCount((int)(Math.ceil(tamRecibidas*1.0/limit.get())));
} catch (Exception e) {
e.printStackTrace();
}
}
public void doSomething () {
this.loginManager.getRosterService().getRosterList().addListener(new ListChangeListener<RosterDTO>() {
#Override
public void onChanged(
javafx.collections.ListChangeListener.Change<? extends RosterDTO> c) {
// TODO Auto-generated method stub
resetPage();
while (c.next()) {
if (c.wasPermutated()) {
System.out.println("PERM");
} else if (c.wasUpdated()) {
System.out.println("UPD");
} else {
System.out.println("ELSE");
}
}
}
});
}
Altough it enters the resetPage method, I get that exception.
Why is this happening?
How can I fix it?
The user interface cannot be directly updated from a non-application thread. Instead, use Platform.runLater(), with the logic inside the Runnable object. For example:
Platform.runLater(new Runnable() {
#Override
public void run() {
// Update UI here.
}
});
As a lambda expression:
// Avoid throwing IllegalStateException by running from a non-JavaFX thread.
Platform.runLater(
() -> {
// Update UI here.
}
);
JavaFX code allows updating the UI from an JavaFX application thread. But from the above exception message it says that it is not using FX Application thread.
One way you can fix is to launch an FX Application thread from the resetPage method and do the modifications there.
I had a similar issue which I fixed without using the Platform.runLater():
"java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-6"
Button play = new Button("Play");
EventHandler<ActionEvent> playHandler = e1 -> {
Runnable runnable = () -> {
play.setText("Pause");
EventHandler<ActionEvent> timelineHandler e2 -> play();
timeline = new Timeline(new KeyFrame(Duration.millis(2000), timelineHandler));
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
};
Thread t = new Thread(runnable);
t.start();
};
play.setOnAction(playHandler);
SOLVED! Move everything unrelated outside of the runnable lambda:
Button play = new Button("Play");
EventHandler<ActionEvent> playHandler = e1 -> {
play.setText("Pause");
Runnable runnable = () -> {
EventHandler<ActionEvent> timelineHandler e2 -> play();
timeline = new Timeline(new KeyFrame(Duration.millis(2000), timelineHandler));
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
};
Thread t = new Thread(runnable);
t.start();
};
play.setOnAction(playHandler);

How to keep progress indicator running in javafx

I want a progress indicator to stop only after completely updating the ui. But it stops after executing last line of code in ui method. The indicator should work as:
1> Start the progress
2> Run the progress until all nodes are added to the scene
3> Stop after completion
The way i update is, i pass BorderPane to the thread and set its center a gridpane, which is the last line of code after which the indicator stops.
And the loginTask, if i start it inside Application Thread the indicator does not spin !
borderpane.setCenter(gpane);
UI method
{
Loading loadBar = new Loading(stage);
Task<Boolean> loginTask= checkCredTask();
loginTask.setOnSucceeded(value -> {
loadBar.hideProgress(); });
loadBar.startTask(loginTask);
(new Thread(loginTask)).start();
}
Progress Bar
public class Loading{
private static Stage stage;
private static ProgressIndicator p;
private static Alert alert;
public Loading(Stage s){
stage=s;
p=new ProgressIndicator();
alert = new Alert(AlertType.NONE);
}
public void startTask(Task<Boolean> cm){
if(p != null){
p.setProgress(-1);
p.progressProperty().unbind();
p.progressProperty().bind(cm.progressProperty());
alert.initOwner(stage);
alert.getDialogPane().setStyle("-fx-background-color:rgba(0,0,0,0);");
alert.getDialogPane().getScene().setFill(Color.TRANSPARENT);
alert.initStyle(StageStyle.TRANSPARENT);
alert.getDialogPane().setContent(p);
alert.show();
}
}
public void hideProgress(){
alert.setResult(ButtonType.CLOSE);
}
}
Task
private Task<Boolean> checkCredTask() {
Task<Boolean> loginTask = new Task<Boolean>() {
#Override
public Boolean call() {
Boolean result = false;
int flag = verifyCredential();
if (flag == 1) {
loadUI();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
result = true;
} else if (flag == 2) {
result = false;
}
return result;
}
};
return loginTask;
}
load UI method
ExecutorService execsrv = Executors.newSingleThreadExecutor();
execsrv.execute(new AdminUI(stage,pane,mb));
execsrv.shutdown();
Your code won't even compile. Task<Boolean> loginTask does not override abstract call() method. Even if you wrap
ExecutorService execsrv = Executors.newSingleThreadExecutor();
execsrv.execute(new AdminUI(stage,pane,mb));
execsrv.shutdown();
in call() method it makes no sense, because you are executing new task inside another task.
Assumig that new AdminUI(stage,pane,mb) is long running process and return boolean you should do it in this way:
Task<Boolean> loginTask = new Task<Boolean>() {
#Override
protected Boolean call() throws Exception {
// Start the UI
AdminUI ui = new AdminUI(stage, pane, mb);
return ui.getBooleanValue();
}
};
EDIT
As I said, you are executing AdminUI task inside loginTask what makes no sense. For now you are waiting only for login task but not for AdminUI task. Login task finishes faster than AdminUI that is why indicator stops to early. Extract AdminUI to another Task. This is pseudo code so you have to do it by your self.
Task<Boolean> loginTask= checkCredTask();
loginTask.setOnSucceeded(value -> {
//****** THIS IS PSEUDO CODE ***********
//start AdminUITask
//AdminUITask.setOnSucceeded(value -> {
//loadBar.hideProgress();
//});
});

I can't setText to label in javafx [duplicate]

I have an application that has a TableView that has an attached listener so it refreshes as soon as it detects a change, but the thing is that I´m getting java.lang.IllegalStateException: Not on FX application thread; currentThread = Smack Listener Processor (0).
Here is my code:
/**
* This function resets the pagination pagecount
*/
public void resetPage() {
try {
System.out.println("RESET");
int tamRoster = this.loginManager.getRosterService().getRosterList().size();
paginationContactos.setPageCount((int)(Math.ceil(tamRoster*1.0/limit.get())));
int tamEnviados = this.loginManager.getRosterService().getEnviadasList().size();
paginationEnviadas.setPageCount((int)(Math.ceil(tamEnviados*1.0/limit.get())));
int tamRecibidas = this.loginManager.getRosterService().getRecibidasList().size();
paginationRecibidas.setPageCount((int)(Math.ceil(tamRecibidas*1.0/limit.get())));
} catch (Exception e) {
e.printStackTrace();
}
}
public void doSomething () {
this.loginManager.getRosterService().getRosterList().addListener(new ListChangeListener<RosterDTO>() {
#Override
public void onChanged(
javafx.collections.ListChangeListener.Change<? extends RosterDTO> c) {
// TODO Auto-generated method stub
resetPage();
while (c.next()) {
if (c.wasPermutated()) {
System.out.println("PERM");
} else if (c.wasUpdated()) {
System.out.println("UPD");
} else {
System.out.println("ELSE");
}
}
}
});
}
Altough it enters the resetPage method, I get that exception.
Why is this happening?
How can I fix it?
The user interface cannot be directly updated from a non-application thread. Instead, use Platform.runLater(), with the logic inside the Runnable object. For example:
Platform.runLater(new Runnable() {
#Override
public void run() {
// Update UI here.
}
});
As a lambda expression:
// Avoid throwing IllegalStateException by running from a non-JavaFX thread.
Platform.runLater(
() -> {
// Update UI here.
}
);
JavaFX code allows updating the UI from an JavaFX application thread. But from the above exception message it says that it is not using FX Application thread.
One way you can fix is to launch an FX Application thread from the resetPage method and do the modifications there.
I had a similar issue which I fixed without using the Platform.runLater():
"java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-6"
Button play = new Button("Play");
EventHandler<ActionEvent> playHandler = e1 -> {
Runnable runnable = () -> {
play.setText("Pause");
EventHandler<ActionEvent> timelineHandler e2 -> play();
timeline = new Timeline(new KeyFrame(Duration.millis(2000), timelineHandler));
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
};
Thread t = new Thread(runnable);
t.start();
};
play.setOnAction(playHandler);
SOLVED! Move everything unrelated outside of the runnable lambda:
Button play = new Button("Play");
EventHandler<ActionEvent> playHandler = e1 -> {
play.setText("Pause");
Runnable runnable = () -> {
EventHandler<ActionEvent> timelineHandler e2 -> play();
timeline = new Timeline(new KeyFrame(Duration.millis(2000), timelineHandler));
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
};
Thread t = new Thread(runnable);
t.start();
};
play.setOnAction(playHandler);

JavaFX: Task will not update UI

I'm working on an Omaha online poker client written in javaFX+java.
I need to show an anchorPane containing 3 buttons after control.call() finishes executing. I know for a fact that control.call() finishes executing but for some reason task.setOnSucceeded(new EventHandler() {'s handle method does not update the User interface.
What am I doing wrong?
public void newRound() {
sog = StateOfGame.PREFLOP;
ControlGameOnNewThread control = new ControlGameOnNewThread();
Task task = new Task() {
#Override
protected Object call() throws Exception {
control.call();
return null;
}
};
task.setOnSucceeded(new EventHandler() {
#Override
public void handle(Event event) {
if (client.getAction() == FirstToAct.me) {
System.out.println("Task finsished");
showOptions(client.getToCall());
opponentBetField.setText(new Integer(opp.chipsInvested).toString());
myBetField.setText(new Integer(client.chipsInvested).toString());
System.out.println("Task finsished");
}
}
});
new Thread(task).start();
}
The problem is that you are updating the user interface in the other thread.. if you are in the other thread and you want to update the user interface
You need to call the
Platform.runLater(new Runnable() {
#Override public void run() {
//Update UI here
}
});
calling this will call the main thread and update all the necessary update to the user interface
EDIT
public void newRound() {
sog = StateOfGame.PREFLOP;
ControlGameOnNewThread control = new ControlGameOnNewThread();
Task task = new Task() {
#Override
protected Object call() throws Exception {
control.call();
return null;
}
};
task.setOnSucceeded(new EventHandler() {
#Override
public void handle(Event event) {
if (client.getAction() == FirstToAct.me) {
Platform.runLater(new Runnable() {
#Override public void run() {
System.out.println("Task finsished");
showOptions(client.getToCall());
opponentBetField.setText(new Integer(opp.chipsInvested).toString());
myBetField.setText(new Integer(client.chipsInvested).toString());
System.out.println("Task finsished");
}
});
}
}
});
new Thread(task).start();
}
As you override the call() method, you can override the succeeded() method: (See last example in the javadoc of Task.)
#Override protected void succeeded() {
super.succeeded();
updateMessage("Done!");
}
succeeded() is already called on the JavaFX Application Thread, so you do not need to do this for yourself.
Are you sure, that the code in your handle method isn't called? Maybe a NullPointerException is thrown, which you might not see? Is the comparison working as expected?
Try moving the code with if (client.getAction()... into the overridden succeeded() method and put a println before the if-statement in order to see whether it is called or not.
(EDIT: typos)
Your information has been helpful, but i think all methods suggested would have worked (including my initial one). The problem is that the task was never ended that's why the onTaskSucceded was never executed. In order to have the task exit after it has finished, i had to se the daemon to true:
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();

Display message during loading

I have a JavaFX with TabPane which holds Java Objects with data into different tabs. I found that when the content of the tab takes time to load because there are SQL queries for execution the application just hangs. Is there any way to display some "Loading" message during the content utilization? for example:
Tab.setContent(<some_heavy_Java_Object>);
Is there any workaround to solve this in JavaFX or Java?
P.S I tested this code sample but I get error when I try to run the code:
TabContentInfrastructure content;
class GetDailySalesService extends Service<ObservableList<Object>>
{
#Override
protected Task createTask()
{
return new GetDailySalesTask();
}
}
class GetDailySalesTask extends Task<ObservableList<Object>>
{
#Override
protected ObservableList<Object> call() throws Exception
{
content = new TabContentInfrastructure();
return (ObservableList<Object>) content.initTestTabContentData();
}
}
..........
VBox vbox = new VBox();
content = new TabContentInfrastructure();
vbox.getChildren().add(content.initTestTabContentData());
GetDailySalesService service = new GetDailySalesService();
Region veil = new Region();
veil.setStyle("-fx-background-color: rgba(0, 0, 0, 0.4)");
veil.setPrefSize(240, 160);
ProgressIndicator p = new ProgressIndicator();
p.setMaxSize(140, 140);
p.progressProperty().bind(service.progressProperty());
veil.visibleProperty().bind(service.runningProperty());
p.visibleProperty().bind(service.runningProperty());
//tableView.itemsProperty().bind(service.valueProperty());
StackPane stack = new StackPane();
stack.getChildren().addAll(vbox, veil, p);
service.start();
tabdata.setContent(stack);
Can you help me to solve this issue.
Another attempt to solve the issue:
Task<VBox> task = new Task<VBox>()
{
#Override
protected VBox call() throws Exception
{
TabContentInfrastructure content = new TabContentInfrastructure();
return content.initTestTabContentData();
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
Region veil = new Region();
veil.setStyle("-fx-background-color: rgba(0, 0, 0, 0.4)");
veil.setPrefSize(240, 160);
ProgressIndicator p = new ProgressIndicator();
p.setMaxSize(140, 140);
//p.progressProperty().bind(service.progressProperty());
veil.visibleProperty().bind(task.runningProperty());
p.visibleProperty().bind(task.runningProperty());
//vb.visibleProperty().bind(service.runningProperty().not());
//tableView.itemsProperty().bind(service.valueProperty());
StackPane stack = new StackPane();
task.setOnSucceeded(new EventHandler<WorkerStateEvent>()
{
#Override
public void handle(WorkerStateEvent t){
System.out.print("Entered setOnSucceeded**********" + t.getSource().getValue());
stack.getChildren().clear();
stack.getChildren().addAll(task.getValue());
}
});
stack.getChildren().addAll(veil, p);
tabdata.setContent(stack);
This time the result is null.
And another unsuccessful attempt.
StackPane stack = new StackPane();
Region veil = new Region();
ProgressIndicator p = new ProgressIndicator();
Task<VBox> task = new Task<VBox>()
{ // create new task
#Override
public VBox call() throws InterruptedException
{
Platform.runLater(new Runnable()
{ // USE THIS INSTEAD
#Override
public void run()
{
try
{
// ui updates here(inside application thread)
// this is needed if you want to update your ui
// you cannot update any ui from outside the application thread
TabContentInfrastructure content = new TabContentInfrastructure();
//stack.getChildren().clear();
stack.getChildren().addAll(content.initTestTabContentData());
}
catch (InterruptedException ex)
{
//Logger.getLogger(InfrastructureDataTabs.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
return null;
}
};
new Thread(task).start();
veil.setStyle("-fx-background-color: rgba(0, 0, 0, 0.4)");
veil.setPrefSize(240, 160);
p.setMaxSize(140, 140);
p.progressProperty().bind(task.progressProperty());
veil.visibleProperty().bind(task.runningProperty());
p.visibleProperty().bind(task.runningProperty());
//vb.visibleProperty().bind(service.runningProperty().not());
//tableView.itemsProperty().bind(service.valueProperty());
stack.getChildren().addAll(veil, p);
tabdata.setContent(stack);
you must load the data in a different Task Thread, I see that you are trying to do the same. The problem with your code is that you are not updating your progress bar. You must use updateProgress as shown here
http://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm#BABGJIDB
Here is a very nice example from Jewelsea where he has very nicely displayed the use of Task and how to use it to update the progress on the UI
Update progress bar and multiple labels from thread
Here you can find out how to use the Task as well as update the UI from the task
Some more Nice examples are
https://community.oracle.com/message/9927179#9927179
https://community.oracle.com/message/10631701#10631701
You should just execute the expensive computations in another thread and then update e.g. a progresss bar in the javafx application thread.
Also your application wont hang during the process anymore.
Like this:
Task task = new Task<Void>() { // create new task
#Override
public Void call() {
// do expensive computations here
Platform.runLater(new Runnable() { // return to application thread
#Override
public void run() {
// ui updates here(inside application thread)
}
});
return null;
}
};
new Thread(task).start(); // execute task in new thread
Hope it helps, Laurenz.
EDIT -------------
Task<Void> task = new Task<Void>() { // create new task
#Override
public Void call() {
try {
Thread.sleep(50); // this simulates expensive computations(in your case loading) - your app would hang for this duration
} catch (InterruptedException e) {
e.printStackTrace();
}
// REMOVE THE SLEEP AND PUT YOUR TASK HERE
// Main.this.root.setPrefHeight(50); // would NOT work(because outside application thread)
Platform.runLater(new Runnable() { // USE THIS INSTEAD
#Override
public void run() {
// ui updates here(inside application thread)
// this is needed if you want to update your ui
// you cannot update any ui from outside the application thread
}
});
return null;
}
};
new Thread(task).start(); // execute task in new thread

Categories

Resources