Updating JavaFX ProgressIndicator multiple times from a Thread - java

I am working on the design of a multi-threading app in Javafx and would like to have a TableView with columns for Name and Progress of each Thread. After doing much research I found a similar example of what I am trying to accomplish here:
JavaFX Update progressbar in tableview from Task
(Which points to this: 'https://community.oracle.com/message/10999916')
The problem I am running into, however, is illustrated well in this example; how can you call a 'Task' object multiple times to update a ProgressIndicator?
My understanding from Oracle's documentation is that a Task object "is a one-shot class and cannot be reused". It would seem then that one can only invoke the call() method of a Task object once. I need to update the Task multiple times as it progresses through a Thread class, not call it once and arbitrarily increment through a For loop.
I have read about binding to Listeners and creating Service classes, but I am unsure if those are actual resolutions to this problem. I would therefore like to ask if this is even possible in Javafx, or if perhaps I am overlooking something. In the event someone has accomplished this in the past, it would be tremendously helpful if you might be able to illustrate how through the example provided previously.
Any direction on this would be appreciated, thank you.
-Drew
EDIT 1: I edited my wording as it was inaccurate.
EDIT 2: Here is an example with some pseudo code. Say I had a class with the following code:
public static class TaskEx extends Task<Void>{
#Override
protected Void call(){
updateProgress(.5, 1);
return null
}
public static void callThread() {
TableView<TaskEx> table = new TableView<TaskEx>();
//Some code for data in table.
TableColumn progressColumn = new TableColumn ("Progress");
progressColumn.setCellValueFactory(new PropertyValueFactor("progress");
table.setItems(<data>);
table.getColumns();addAll(progressColumn);
ExecutorService executor = Executors.newFixedThreadPool(<SomeNumber>);
for(TaskEx task : table.getItems(){
Threading.ThreadClass newThread = new Threading.ThreadClass(task);
executor.submit(newThread, <uniqueID>);
}
}
Then say I had a second class for Threading with this logic:
static class ThreadClass extends Thread{
Task progressTask;
public ThreadClass(Task task, Integer id){
progressTask = task;
}
public void run(){
ExecutorService executor = Executors.newFixedThreadPool(<someNumber>);
//This invokes the Task call for the correct progressIndicator in the Tableview.
//It will correctly set the progressIndicator to 50% done.
executor.submit(progressTask);
/* Main logic of the Threading class that involves the 'id' passed in. */
//This will do nothing because you cannot invoke the Task call more than once.
executor.submit(progressTask);
}
}
That is the sort of workflow I need, but I'm unsure how to accomplish this.

It seems like you don't get what we were talking about. You are trying to do your logic in the Thread.run(), and then each thread is creating a Task just to do the update of progress.
What you need is really to shift your logic from Thread.run() to Task.call(). Your thread is really just a thread, and all it does is to run a Runnable object (which is the Task).
public class TaskEx extends Task<Void> {
#Override
protected Void call() {
// Do whatever you need this thread to do
updateProgress(0.5, 1);
// Do the rest
updateProgress(1, 1);
}
}
public static void callThread() {
TableView<TaskEx> table = new TableView<TaskEx>();
ObservableList<TaskEx> data = FXCollections.observableArrayList<>();
data.add(new TaskEx()); // Add the data you need
TableColumn progressColumn = new TableColumn("Progress");
progressColumn.setCellValueFactory(new PropertyValueFactory("progress"));
progressColumn.setCellFactory(column -> {
return new TableCell<TaskEx, Double> {
private final ProgressBar bp = new ProgressBar();
#Override
public void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
}
else {
bp.setProgress(item.doubleValue());
setGraphic(bp);
}
}
}
});
table.setItems(data);
table.getColumns().add(progressColumn);
ExecutorService executor = Executors.newFixedThreadPool(data.size());
for (TaskEx task : table.getItems()) {
executor.submit(task);
}
}
This implement removes ThreadClass because there should not be any logic that must be done at a thread sub-class. If you really need to access the thread object as part of your logic, call Thread.getCurrentThread() from your TaskEx.call().
This implement also opens multiple threads doing exactly the same thing (which is quite meaningless). If you need to do a set of different logics, you can either make a set of different Task subclasses, or add a constructor taking in Runnable objects in TaskEx.
E.g.
public class TaskEx extends Task<Void> {
private final Runnable[] logics;
public TaskEx(Runnable[] logics) {
this.logics = logics;
}
#Override
protected Void call() {
for (int i = 0; i < logics.length; i++) {
logics[i].run();
updateProgress(i, logics.length);
}
}
}

Related

can't get sequential behavior Java Swing [duplicate]

basically, I have this code which was initially working with console i/o now I have to connect it to UI. It may be completely wrong, I've tried multiple things although it still ends up with freezing the GUI.
I've tried to redirect console I/O to GUI scrollpane, but the GUI freezes anyway. Probably it has to do something with threads, but I have limited knowledge on it so I need the deeper explanation how to implement it in this current situation.
This is the button on GUI class containing the method that needs to change this GUI.
public class GUI {
...
btnNext.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
controller.startTest(index, idUser);
}
});
}
This is the method startTest from another class which contains instance of Question class.
public int startTest() {
for (int i = 0; i < this.numberofQuestions; i++) {
Question qt = this.q[i];
qt.askQuestion(); <--- This needs to change Label in GUI
if(!qt.userAnswer()) <--- This needs to get string from TextField
decreaseScore(1);
}
return actScore();
}
askQuestion method:
public void askQuestion() {
System.out.println(getQuestion());
/* I've tried to change staticaly declared frame in GUI from there */
}
userAnswer method:
public boolean userAnswer() {
#SuppressWarnings("resource")
Scanner scanner = new Scanner(System.in);
if( Objects.equals(getAnswer(),userInput) ) {
System.out.println("Correct");
return true;
}
System.out.println("False");
return false;
}
Thanks for help.
You're correct in thinking that it related to threads.
When you try executing code that will take a long time to process (eg. downloading a large file) in the swing thread, the swing thread will pause to complete execution and cause the GUI to freeze. This is solved by executing the long running code in a separate thread.
As Sergiy Medvynskyy pointed out in his comment, you need to implement the long running code in the SwingWorker class.
A good way to implement it would be this:
public class TestWorker extends SwingWorker<Integer, String> {
#Override
protected Integer doInBackground() throws Exception {
//This is where you execute the long running
//code
controller.startTest(index, idUser);
publish("Finish");
}
#Override
protected void process(List<String> chunks) {
//Called when the task has finished executing.
//This is where you can update your GUI when
//the task is complete or when you want to
//notify the user of a change.
}
}
Use TestWorker.execute() to start the worker.
This website provides a good example on how to use
the SwingWorker class.
As other answers pointed out, doing heavy work on the GUI thread will freeze the GUI. You can use a SwingWorker for that, but in many cases a simple Thread does the job:
Thread t = new Thread(){
#Override
public void run(){
// do stuff
}
};
t.start();
Or if you use Java 8+:
Thread t = new Thread(() -> {
// do stuff
});
t.start();

How to make this search in a new thread

I have search as you type functionality although it searches very fast and I can't notice it even with no multithreading I still want to know how would I use multithreading on this
search.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observableValue, String s, String s2) {
manager.searchString(s2);
listView.getItems().setAll(manager.getList());
}
});
So basically there is a TextField that when its text is changed I go and call a search method in object manager which puts its search result in an array when it finishes.
Then the ListView should update its data to this new array when it finishes.
How can I make the search on one thread and when it finishes it update the list data?!
I believe I can't just call the list function from other thread because GUI stuff should be called from only one thread.
To do this efficiently in a different thread is not as simple as it sounds.
You don't want to create and execute a new thread every time a key is pressed because:
There is system overhead for thread creation which would make that an extremely intensive process
There's no guarantee that the threads will execute and complete in the order they are created, so you may get an earlier thread finishing after a subsequent one and consequentially updating the list with invalid entries.
You could use a single-thread executor service (which keeps one thread alive and uses it to execute Runnables passed into it in order), which would be more efficient, but you'd need to remember to shut it down when your text field is destroyed (if you do ever destroy your text field). Something along these lines:
// first of all, at the class level (assuming listView and manager are both class-level variables, preferably final ones, too):
// An INNER class implementing Runnable which will carry out the searching
private class Searcher implements Runnable {
private volatile boolean cancelled = false;
private final String searchTerm;
Searcher(String searchTerm) {
this.searchTerm = searchTerm;
}
public void cancel() {
cancelled = true;
}
#Override
public void run() {
// remember that there's no guarantee that this will execute before the NEXT keypress, so we add a check to ensure that we still want to perform the search when it gets executed:
if (!cancelled) {
manager.searchString(searchTerm);
Platform.runLater(listViewUpdater); // listViewUpdater is defined below
}
}
}
// a Runnable to actually update the GUI after a seach has been performed
private Runnable listViewUpdater = new Runnable(){
#Override
public void run() {
listView.getItems().setAll(manager.getList());
}
}
private ExecutorService executor = Executors.newSingleThreadExecutor();
private Searcher lastSearcher = null;
// ... then, in the method which sets up the GUI
search.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observableValue, String s, String s2) {
if (lastSearcher != null) {
lastSearcher.cancel(); // prevents lastSearcher from running if it hasn't done so already
}
lastSearcher = new Searcher(s2);
executor.submit(lastSearcher);
}
});
The downside is you are creating a new object every time the value changes, but that isn't nearly as bad as creating a new Thread every time.

how to suspend execution in java using thread

i have created a wizard programatically. it contain 3 panels. the second one is devicePane and third one is detailsPane. the third panel consist of a progress bar. i want my program to start a function process() after displaying the third panel? is it possible using thread?
else if(ParserMainDlg.this.POSITION==1){
if(sqlConnectionPane.executeProcess()==true){
devicePane.setDeviceList();
ParserMainDlg.this.POSITION++;
fireStateChanged(oldValue);
}
}
else if(ParserMainDlg.this.POSITION==2){
System.out.println("position:"+ParserMainDlg.this.POSITION);
if(devicePane.executeProcess()==true){
ParserMainDlg.this.POSITION++;
fireStateChanged(oldValue);
}
I want sqlConnectionPane.executeProcess() to call a function which starts executing after displaying the devicePane Panel?
You can definitly use a thread to execute your task, this is the preferred way of handling a long running task.
You have multiple options here. All options include making a callback to your wizard, to update the progressbar.
You can make your own task class wich does exactly this, or you can use the existing SwingWorker. "SwingWorker itself is an abstract class; you must define a subclass in order to create a SwingWorker object; anonymous inner classes are often useful for creating very simple SwingWorker objects."
Using the swing worker we just learned about you can use something like this:
SwingWorker<Integer, Integer> backgroundWork = new SwingWorker<Integer, Integer>() {
#Override
protected final Integer doInBackground() throws Exception {
for (int i = 0; i < 61; i++) {
Thread.sleep(1000);
this.publish(i);
}
return 60;
}
#Override
protected final void process(final List<Integer> chunks) {
progressBar.setValue(chunks.get(0));
}
};
backgroundWork.execute();
Note that you will have to break your task down into smaller parts to actually be able to display progress.

properly writing a model with a property intended for view binding

I have a class MyModel with a property datalogEnabled (and other ones, but let's start there) that I would like to be able to implement properly for use with UI/view binding.
public class MyModel {
static final String KEY_DATALOG_ENABLED = "datalogEnabled";
final private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
final private Object syncLock = new Object();
final private Datalogger datalogger = new Datalogger();
public void addPropertyChangeListener(PropertyChangeListener pcl)
{
this.pcs.addPropertyChangeListener(pcl);
}
public void removePropertyChangeListener(PropertyChangeListener pcl)
{
this.pcs.removePropertyChangeListener(pcl);
}
public boolean isDatalogEnabled()
{
synchronized (this.syncLock)
{
return this.datalogEnabled;
}
}
public void setDatalogEnabled(final boolean enable) {
boolean old;
synchronized (this.syncLock) {
old = this.datalogEnabled;
this.datalogEnabled=enable;
}
/* begin snippet X: this is wrong for threading reasons */
this.pcs.firePropertyChange(KEY_DATALOG_ENABLED, old, enable);
setDatalogEnabledNow(enable);
/* end snippet X */
}
setDatalogEnabledNow(boolean b)
{
this.datalogger.setEnable(b);
}
/* other methods */
}
public class Datalogger() {
public void setEnable(boolean enable) { ... }
}
Except for snippet X, this seems right, but I'm not sure. What's getting me is that the various ways of accessing/setting/listening to the property may happen on different threads, and what I need to do is to act upon the datalogEnabled property somewhere (do some file I/O) within my Datalogger class, on another thread besides the Swing UI thread, because I don't want the UI thread to be unresponsive.
How can I properly rewrite snippet X?
In my overall program, I have an instance of ExecutorService. I could add an Executor (superclass of ExecutorService) as a constructor parameter in the MyModel class, and do this for snippet X:
this.pcs.firePropertyChange(KEY_DATALOG_ENABLED, old, enable);
this.executor.execute(new Runnable() {
#Override public void run() { setDatalogEnabledNow(enable); }
});
Should I put the firePropertyChange call into the deferred Runnable task as well? (is firePropertyChange supposed to be called immediately or after a property change really takes effect)
Or should the Datalogger class have an Executor as well so it can coordinate various tasks?
I'm confused on this one....
The listeners of a model can be uis components, or domains components. If a listener is a ui component, it must run on the edt, and not if the listener is domain.
The event come from a ui component, or from a domain component. If the event come from a ui component, the fire method is on the edt, and it is not if the event come from a domain.
So it's a little complicated... My opinion is than each listener should work for itself : a ui listener goes on the edt if it is not, and a domain goes not if it is. So the fire method stays on it's original thread.
2 cts.

Does SwingWorker has to be a nested class?

I'm wondering if SwingWorker has to be a nested class within my main GUI. I'd rather make it an external class to keep the GUI clear from any of my programs logic.
I tried to make the SwingWorker class external, which works fine for the process, unfortunately I can't access any of my GUI fields from the SwingWorker class.
Whenever I try to access an attribute, such like a label or whatever from within SwingWorker's done() method I get a nullPointer exception.
Any advice would be much appreciated!
First of all thank you very much Jeff! Works fine so far, even though I could not follow you on the second option you presented.
One of my background tasks calculates a certain size (long value), so it would be nice to get that value from my GUI.
You suggested to work with getters and setters but unfortunately I've got no idea on how to implement them in the SwingWorker class.
I tried it like this:
public void setSize(long totalSize) {
this.totalSize = totalSize;
}
public long getTotalSize() {
return totalSize;
}
The setter is invoked at the end of the doInBackground() method. Unfortunately I can't use the get() method from my GUI.
final MySwingWorker w = new MySwingWorker();
Runnable r = new Runnable() {
public void run() {
// do something with w.get()
}
};
w.setRunnable(r);
w.execute();
The object creation of "w" does not work in my case as the constructor requires an object of Runnable.
Am I missing something?
Please go easy on me, it's the first time I work with SwingWorker. :)
Again, thank you very much for your help!
You can make the SwingWorker an external class. However, just like any other class, if it can't see the variables (e.g. the label you want to set), of course it won't be able to set it. One thing you could do is pass the worker a Runnable that it executes when it is complete.
public class MySwingWorker extends SwingWorker {
private final Runnable r;
public MySwingWorker(Runnable r) {
this.r = r;
}
public void doInBackground() {...}
public void done() { r.run(); }
}
Now from the GUI, you might do something like
Runnable updateLabel = new Runnable() {
public void run() {
label.setText("myValue");
}
};
SwingWorker w = new MySwingWorker(updateLabel);
w.execute();
This gets a bit trickier if you want to use the result of the SwingWorker, though it is possible. Rather than passing the Runnable to the swing worker's constructor, you would have a setter method and then it would be something like:
final MySwingWorker w = new MySwingWorker();
Runnable r = new Runnable() {
public void run() {
// do something with w.get()
}
};
w.setRunnable(r);
w.execute();
In either case, the Runnable is functioning similarly to a closure that is executed when the worker is finished.

Categories