How to use javaFX Task in my case - Not duplicate - java

I know there are answers for my questions but I did not understand the problem in my code.
Why I get :
java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
I'm trying to add text from a Task to a Text flow using a method in the controller class, for some reason the program fails on .getChildren() method .
Call for Spliter in the controller class:
btnSplit.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event event) {
Thread split = new Thread(new Spliter(custToSplit, Controller.this));
split.setDaemon(true);
split.start();
}
});
class Spliter constractor :
public Spliter(File f, Controller c){
this.c = c;
this.cust = f;
}
c.updateHebFlow("Dir created: "+ newDir.getAbsolutePath() , INFO_TEXT);
Part of the Controller class :
#FXML
private TextFlow hebFlow;
#Override
public void initialize(URL location, ResourceBundle resources) {
assert hebFlow != null : "fx:id=\"hebFlow\" was not injected: check your FXML file 'MainXml.fxml'.";
public void updateHebFlow(String text,String type){
normalText = new Text();
errorText = new Text();
errorText.setFill(Color.RED);
infoText = new Text();
infoText.setFill(Color.BLUE);
switch(type){
case(ERROR_TEXT) :
errorText.setText(text);
hebFlow.getChildren().addAll(new Text("/n"), errorText);
break;
case(INFO_TEXT) :
infoText.setText(text);
hebFlow.getChildren().addAll(new Text("/n"), infoText);
break;
case(NORMAL_TEXT) :
normalText.setText(text);
hebFlow.getChildren().addAll(new Text("/n"), normalText);
break;
}
}
}
Call for updateHebFlow in Spliter class:
try{
c.updateHebFlow("Script by TulTul", INFO_TEXT);
}catch (Exception e){
e.printStackTrace();
}
From what I understand I cant change the UI from other class other then the controller so Ive made a method in the controller class that will make the changes and call it in the Task class, why I'm getting this exception?
If this is wrong what is the wright way ?

From what I understand I cant change the UI from other class other then the controller
Actually the correct statement is: "You cannot change the UI from any thread other than the JavaFX UI thread". So, the solution is to use Platform.runLater() from the Splitter as:
// Java 8
Platform.runLater(() -> {
c.updateHebFlow("Script by TulTul", INFO_TEXT);
});
// Java 7
Platform.runLater(new Runnable() {
public void run() {
c.updateHebFlow("Script by TulTul", INFO_TEXT);
}
});
Platform.runLater() is guaranteed to run the blocks in the JavaFX UI thread and in call order.

Related

Java - JProgressBar won't change value, and JLabel won't change text

I am writing a program in Java and I want to add a loading page where it displays a progress bar and a label saying what is loading and the bar showing how far it is to completing, I have everything set up so it should work but it doesn't, I do not know what is wrong (I am new to java so please have mercy)
I have tried having a boolean that is set to false by default and is set to true only after "after-all-set-code" has been executed (I am using netbeans to create the GUI) and when I call the function to update the text/progress bar, if the boolean is still set to false it will wait a second and retry till the "all-set-code" changes it to true, but that doesn't seem to work.
This is inside my main class
public class Xtra {
public static loadingPage LP = new loadingPage();
public static void main(String[] args) {
// Show loading page
LP.main(args);
// Set loading
LP.setLoading(0, "Fetching Config...");
// Get Config
// Durring the "getConfig" function it calls the function "LP.setLoading()" multiple times to update the loading bar & text
Xtra.getConfig();
// Set loading
LP.setLoading(0, "Loading user data...");
}
}
This is my setLoading() class inside of loadingPage:
public void setLoading(int per, String txt) {
if ("".equals(txt)) {
setLoadingValue(per);
} else {
setLoadingText(txt);
setLoadingValue(per);
}
}
This is my setLoadingValue() function:
public void setLoadingValue(int x) {
// This will turn true when the after-all-set code runs
while (!loadingBarLoaded) {
// Try to wait a second,
// Do this so it doesn't take up too much CPU
try {
// Wait a second
TimeUnit.SECONDS.sleep(1);
// Print to console
System.out.println("Loading value not loaded,\nWaiting another second");
// If there is an error waiting
} catch (InterruptedException ex) {
// Alert user
System.out.println("You cannot sleep now, monsters are nearby");
}
}
// After loaded boolean turns true
// Alert user of change
System.out.println("Setting loading value to " + x + "%");
// Change value
loadingBar.setValue(x);
}
This is my setLoadingText() function:
public void setLoadingText(String txt) {
// This will turn true when the after-all-set code runs
while (!loadingTextLoaded) {
// Try to wait a second,
// Do this so it doesn't take up too much CPU
try {
// Wait a second
TimeUnit.SECONDS.sleep(1);
// Print to console
System.out.println("Loading text not loaded,\nWaiting another second");
// If there is an error waiting
} catch (InterruptedException ex) {
// Alert user
System.out.println("You cannot sleep now, monsters are nearby");
}
}
// After loaded boolean turns true
// Alert user of change
System.out.println("Setting loading text to \"" + txt + "\"");
// Change value
loadingText.setText(txt);
}
and my after-all-set-code is loadingTextLoaded = true & loadingBarLoaded = true depening on what is finished
It's supposed to update the text and the value of the progress bar but it isn't, it is outputting the Settings loading text to "..." to the console & the Setting loading value to ...% to the console as well, but not changing the actual value or text of the components.
What am I doing wrong?
Minimalistic problem: (Main file)
// Inside main function
// Show loading page
LP.main(args);
LP.loadingBar.setValue(50);
LP.loadingText.setText("Hello");
// nothing changes. (Both are public so I can call them from different files
This is what the loadingBar & loadingText vars and declared like
public javax.swing.JProgressBar loadingBar;
public javax.swing.JLabel loadingText;
loadingBarLoaded & loadingTextLoaded is declared like this
public boolean loadingTextLoaded = false;
public boolean loadingBarLoaded = false;
loadingTextLoaded & loadingBarLoaded and changed to true after all the generated code puts them into place inside the window after the all-set-code is ran (I am using NetBeans GUI builder so the code is generated)
Inside the loadingPage.java this is how it is laid out.
The main window and there is a panel covering the entire main window and it has three things inside of it, a label at the top that sits in the middle, it has the application name "Xtra" and below it the loadingText subtitle that small and says what is currently loading, below that, a progress bar loadingBar.
Here is a screen shot of the layout
Sorry if I am not following the unwritten coding rules, I only started with java like a week ago.
Learn about threading in swing. You have to use a SwingWorker and do the loading in the doInBackground() method and return progress there.
import javax.swing.*;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
public class Main {
private JProgressBar prog;
public static void main(String[] args) throws InvocationTargetException, InterruptedException {
SwingUtilities.invokeAndWait(()-> new Main().initGUI());
}
private void initGUI(){
JDialog dialog = new JDialog();
dialog.setTitle("Progress");
prog = new JProgressBar();
prog.setMaximum(100);
dialog.add(prog);
dialog.pack();
dialog.setVisible(true);
BackgroundWorker bw =new BackgroundWorker();
bw.execute();
}
private class BackgroundWorker extends SwingWorker<String, Integer>{
#Override
protected String doInBackground() throws Exception {
try{Thread.sleep(1000);}catch(Exception e){}
publish(10);
try{Thread.sleep(1000);}catch(Exception e){}
publish(20);
try{Thread.sleep(1000);}catch(Exception e){}
publish(30);
try{Thread.sleep(1000);}catch(Exception e){}
publish(70);
try{Thread.sleep(1000);}catch(Exception e){}
publish(100);
return "finished";
}
#Override
protected void process(List<Integer> chunks) {
prog.setValue(chunks.get(chunks.size()-1));
}
}
}
Important is, that you don't access any Swing Component from the doInBackground() method, as this method is not called from the Swing Event Dispatch Thread.
I fixed it, when the GUI builder created the page it used this code
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new loadingPage().setVisible(true);
}
});
I changed it to:
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
LP.setVisible(true);
}
});
and at the begining of the file I put this:
public static loadingPage LP = new loadingPage();
and it seemed to work.

JProgressBar doesn't update in real time within a loop [duplicate]

This question already has answers here:
Can a progress bar be used in a class outside main?
(3 answers)
Closed 7 years ago.
It is the first time I have to work with a progress bar and I'm facing a problem, besides I try to call its setValue(x) from everywhere it keeps on 0% and goes straight to 100% after my method routine finishes.
I tried to make an inner class that extends Thread, then after I tried to start a new Thread within my "main" method, then for the last I tried to use the Observer. These ones seems to have worked according to this posts but unfortunately not to me
Update JProgressBar from new Thread
Problem making a JProgressBar update values in Loop (Threaded)
please, could someone help me???
public class MainClass {
private void checkFiles() {
Task task = new Task();
task.start();
//here I have some Files validation...I don't think it is important to solve the progressbar problem
//so it will be ommited
//in this point I tried to call update to test the observer solution I found in another post here
//task.update(null, null);
JOptionPane.showMessageDialog(this, "Done!");
//here the bar jumps from 0% to 100%
}
private class Task extends Thread implements Observer {
public Task() {
}
//Dont bother with the calculum as I haven't finished working on them....
//The relevant thing here is that it starts a new Thread and I can see the progress
//increasing on console using system.out but my progress bar still don't change from 0%.
public void run() {
int maxSize = 100;
final int partsSize = maxSize / listaArquivosSelecionados.size();
while (listFilesValidated.size() != listFilesToValidate.size()) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
int progress = listFilesValidated.size() * partsSize;
System.out.println("Progress" + progress);
progressBar.setValue(progress);
}
});
try {
Thread.sleep(100);
}
catch (InterruptedException e) {}
}
}
//Just tried to set any value to check if it would update before the files validation thread finishes its work.
#Override
public void update(Observable arg0, Object arg1) {
progressBar.setValue(66);
}
}
You can create another class of ProgressBar (see Oracle tutorial) and use this:
ProgressBar pbFrame = new ProgressBar();
pbFrame.setVisible(true);
Executors.newSingleThreadExecutor().execute(new Runnable() {
#Override
public void run() {
// run background process
}
});
Or you can use SwingWorker, for example:
SwingWorker worker = new SwingWorker<MyReturnType, Void>() {
#Override
public MyReturnType doInBackground() {
// do your calculation and return the result. Change MyReturnType to whatever you need
}
#Override
public void done() {
// do stuff you want to do after calculation is done
}
};
I had the same question some years ago.

Where to add events to nodes?

Can you tell me, where I should declare my event listeners for Nodes, which are added out side of my controller class?
The best way is to explain it with this example:
I have my controller:
public class FXMLDocumentController implements Initializable {
#FXML
private AnchorPane root;
#Override
public void initialize(URL url, ResourceBundle rb) {
TestTask test = new TestTask(root);
Thread th = new Thread(test);
th.start();
}
}
And then I have the Task, which is started in the initialize method:
public class TestTask extends Task<Void>{
private AnchorPane root;
public TestTask(AnchorPane root){
this.root = root;
}
#Override
protected Void call() throws Exception {
Button btn = new Button("TestButton");
Platform.runLater(() -> { root.getChildren().add(btn); });
return null;
}
}
What I'm doing here? I have a FXML with an AnchorPane as root element. It has the id root. So now I start the Task in which I add one Button to my root node. Now I want to register an action event to the button. My question is now, where can/should I register the listener. Normally I register them in the controller, but here I can't do this because the Button only exists in the Task class. I could register it in the Task class but I think that it not scales good with large applications. The other way would to return the node back, so that I can access it in the controller class, but here I have to check if it is already added (to do this I have to call task.get(), which stops my application. So now could you tell me: what is the best way to register the listener for the node?
Don't create the UI in the background thread. There is (at best) very rarely a need to do this. If you need to perform some long-running task that retrieves data you need in order to create your UI, return the data from the task, and create the UI in the task's onSucceeded handler:
public class SomeControllerClass {
#FXML
private AnchorPane root ;
public void initialize() {
Task<SomeDataType> task = new MyTask();
task.setOnSucceeded(e -> {
// this method executed on FX Application thread.
SomeDataType result = task.getValue();
// now create UI and update root, using the data retrieved
});
Thread thread = new Thread(task);
thread.start();
}
}
and
public class MyTask extends Task<SomeDataType> {
#Override
public SomeDataType call() {
SomeDataType result = longRunningProcess();
return result ;
}
}

Android How to invoke action to UI thread

Currently, I'm trying to download some contents from Web and would like to push it to ListView:
I created a Runnable to do the download content part:
Runnable postTest = new Runnable() {
#Override
public void run() {
try {
URL u = new URL("192.168.1.1/getList.php");
ContentValues c = new ContentValues();
c.put("token", networkCommunication.getToken());
String k = networkCommunication.downloadContent(u, c);
getParent().runOnUiThread(
new Runnable() {
public void run() {
Toast.makeText(Main.this, k, Toast.LENGTH_SHORT).show();
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
};
The code works fine until String k = networkCommunication.downloadContent(u, c);
But then, the "K" variable is not accessable in "Run" method, also, I tried changing the k variable to "Hello world" (i.e. print hello world when it finish downloading the content) but then another exception occurs:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.app.Activity.runOnUiThread(java.lang.Runnable)' on a null object reference
How to solve this problem?
Because getParent() is NULL. According to the documentation:
public final Activity getParent () Since: API Level 1
Return the parent activity if this view is an embedded child.
You can refer to this similar question : Android Activity.getParent() always returning null

JavaFX Multithreading

I am facing one problem in my JavaFx application.
Preface: I don't want to be specific to my application only but want to be generalized so that people like me will get an idea on similar situation.
Background: Implementing a Javafx application using fxml file and multi-threading concept.
Summary: I tried make an application which basically uses multi threading to do some task, and once multi- threading is completed, it will sequentially move to another task. While doing the multi-threading the Main GUI freezes.
What I did,
//This is Main class
Parent Root -> defined on top
public Parent createContent(){
try{
root = FXMLLoader.load(getClass().getResource("Layout.fxml"));
}catch { .....}
}
public void start(){
stage.setScene(new Scene(createContent()));
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
Thread.sleep(1000);
return null ;
}
};
task.setOnSucceeded(event->{
stage.show();
});
new Thread(task).run();
}
public static void main(String[] args) {
launch(args);
}
// This is inside the controller on button click event
#FXML
private void start(ActionEvent event) { <---- This one is button click event
Thread t1 = new Thread(new Mul());
t1.start();
Thread t2 = new Thread (new Mul());
t2.start();
}
// Finally
public class Mul implements Runnable {
public void type() {
for (int a = 0; a < 200000; a++) {
System.out.println( Thread.currentThread().getName()+ " says " + a);
}
}
#Override
public void run() {
type();
}
}
Now, here is the outcome.
If I just start the threads from the controller then My main application does not freezes while the thread are running in background. However, since the application runs in a sequence, i.e the next step only works when the threads complete their work
I can use t1.join() and t2.join() , but doing so will freezes my main application(Main application here is the main GUI) and I cannot proceed with it until the threads are completed.
What could be the optimal solution, so that I can use multi threading without blocking the main application or What am I doing wrong here? (info, I came to this solution by following up different suggestions from Stack overflow and google)
Why not do
Task<Void> task = new Task<Void>() {
#Override
public Void call() {
Mul m1 = new Mul();
m1.run();
Mul m2 = new Mul();
m2.run();
return null ;
}
};
new Thread(task).start();
If you really want to "chain" different tasks, call one from the other's onSucceeded handler:
Task<Void> task1 = new Task<Void>() {
#Override
public Void call() {
new Mul().run();
return null ;
}
};
Task <Void> task2 = new Task<Void>() {
#Override
public Void call() {
new Mul().run();
return null ;
}
};
task1.setOnSucceeded(e -> new Thread(task2).start());
new Thread(task1).start();
Obviously this is cleaner if you make Mul a Task subclass instead of a Runnable, and it's better to use an Executor with daemon threads, etc, but this should give you the idea.

Categories