Java Swingworker Thread to Update Main Gui

java swingworker thread to update main Gui

There is an excellent example from the JavaDocs

class PrimeNumbersTask extends
SwingWorker<List<Integer>, Integer> {

PrimeNumbersTask(JTextArea textArea, int numbersToFind) {
//initialize
}

@Override
public List<Integer> doInBackground() {
List<Integer> numbers = new ArrayList<Integer>(25);
while (!enough && !isCancelled()) {
number = nextPrimeNumber();
numbers.add(number);
publish(number);
setProgress(100 * numbers.size() / numbersToFind);
}

return numbers;
}

@Override
protected void process(List<Integer> chunks) {
for (int number : chunks) {
textArea.append(number + "\n");
}
}
}

JTextArea textArea = new JTextArea();
final JProgressBar progressBar = new JProgressBar(0, 100);
PrimeNumbersTask task = new PrimeNumbersTask(textArea, N);
task.addPropertyChangeListener(
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
progressBar.setValue((Integer)evt.getNewValue());
}
}
});

task.execute();
System.out.println(task.get()); //prints all prime numbers we have got

Take a look at publish and process

The underlying intention is that you need to update the UI from only within the Event Dispatching Thread, by passing the data you want to updated to the UI via the publish method, SwingWorker will call process for you within the context of the EDT

How to use SwingWorker to update the GUI in real-time?

your background threads should call method

String message="hi im processx";
SwingWorker.publish(message);

and you have to ovveride method

SwingWokrer.process().

this method is called on EDT when it is free with the message which was passed before to publish (might be one or more) in the argument.

You should display your progress there

Yo can read more about those methods in SwingWorker documentation

Swing Worker and GUI update

The call to execute() method for SwingWorker is the last instruction of WaitingFrame constructor: is there a better place for it?

There is nothing wrong with how you are calling it.

The dispose() method for WaitingFrame is called from SwingWorker's done() method, is it correct? If a count process is very fast, could the dispose method be called before the waiting frame is actually visible? As a result, i would have two open frames...

It is correct and the situation you describe cannot happen. SwingWorker.done() is called on EDT via a delayed SwingUtilities.invokeLater call and you already called JFrame.setVisible(true) before constructing the SwingWorker (on EDT).

Instead of multiple frames, consider using a dialog, perhaps a modal one if you are attempting to block user input (like here).

Is there a better method to interrupt the process and to manage the message dialogs shown to users? I used two boolean variables, valid and interrupted, to achieve my purpose...

There certainly is a better way of doing this, considering that your current code is dangerously close to not being thread safe. Consider using an AtomicBoolean instead of boolean if both EDT and swing worker need to access those two members.

You should also check the interrupted flag somewhere and exit your file listing loop upon it changing.

You can also wrap any swing code into a SwingUtilities.invokeLater call anywhere inside SwingWorker.doInBackground() for fine grained GUI updates. Doing this essentially has the same effect as if done() had been invoked but you control when and how many times it is called. Check here for a code example. Your own code does this to pass execution from the main thread to EDT inside your main() method (the reason for this code pattern is that all swing code must execute on EDT).

how to update GUI from swingworker which returns two different values

The second type parameter V in SwingWorker<T,V> is used for carrying out intermediate results by this SwingWorker's publish and process methods. This could be your custom class. Here is an example based on posted SSCCE (shortened for clarity):

class Progress {
private int task;
private int element;

public Progress(int task, int element) {
super();
this.task = task;
this.element = element;
}
...
}

public class Model extends SwingWorker<Integer, Progress> {
...
@Override
protected Integer doInBackground() throws Exception {
...
publish(new Progress(i, ii));
}
}

EDIT: example of process method implementation

@Override
protected void process(List<Progress> progressList) {
for (Progress p : progressList){
System.out.println(p.getTask() + " : " + p.getElement());
}
}

EDIT: example of UI update

Here is a slightly modified version of the worker implementation, similar to a sample demonstrated in SwingWorker manual. The only changes are introduction of textArea member and updated setProgress() call in doInBackground(). progress property is used to update the progress bar, process() is used to update text area.

public static class Model extends SwingWorker<Integer, Progress> {

private HashMap<String, Number> GUIparams;
private int session;
private int ticks;
private JTextArea textArea;

Model(HashMap<String, Number> KSMParams, JTextArea textArea) {
GUIparams = KSMParams;
session = (Integer)GUIparams.get("experimentsInSession");
ticks = (Integer)GUIparams.get("howManyTicks");

this.textArea = textArea;
}

@Override
protected void process(List<Progress> progressList) {
for (Progress p : progressList){
textArea.append(p.getTask() + " : " + p.getElement() + "\n");
System.out.println(p.getTask() + " : " + p.getElement());
}
}

/**
* Actual simulation
*/
@Override
protected Integer doInBackground() throws Exception {

int i=0;
while(!isCancelled() && i<session){
i++;
int ii=0;
while(!isCancelled() && ii<ticks){
// this is n, the task length and I'd like to update the GUI with this value
ii++;
}
//System.out.println(i);
// this is m, how many time the task has been repeated, and now it is updated in the GUI
publish(new Progress(i, ii));
//setProgress(i);
setProgress(100 * i / session);
Thread.sleep(1000);
}
return i;
}

/**
* Invoked when simulation exits
*/
@Override
protected void done() {
if (isCancelled()) {
Logger.getLogger(Model.class.getName()).log(Level.WARNING, "Experiment session cancelled by user. Closing Session...");
} else {
// do stuff

Logger.getLogger(Model.class.getName()).log(Level.WARNING, "Experiment session ended.");
}
}
}

Here is a demo initialization:

final JProgressBar progressBar = new JProgressBar(0, 100);
final JTextArea textArea = new JTextArea();
final JButton button = new JButton("Start");

button.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
HashMap<String, Number> map = Maps.newHashMap();
map.put("experimentsInSession", 10);
map.put("howManyTicks", 5);

Model task = new Model(map, textArea);
task.addPropertyChangeListener(
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
progressBar.setValue((Integer)evt.getNewValue());
}
}
});
task.execute();
}
});

Java Swingworker one after another and update GUI

1) How to run all the workers in a perfect sequencial order. i.e when worker1 is done then start worker 2, when worker2 is done then start worker 3 and so on.

A little trick to remember, you can put a SwingWorker into a ExecutorService

So, using something like...

public static class Worker extends SwingWorker {

private int sequence;

public Worker(int sequence) {
this.sequence = sequence;
}

@Override
protected Object doInBackground() throws Exception {
System.out.println(sequence + "...");
Thread.sleep(500);
return null;
}

}

As a test, you could use something like...

ExecutorService es = Executors.newFixedThreadPool(1);
for (int index = 0; index < 10; index++) {
es.submit(new Worker(index));
}
es.shutdown();

(You don't need to call shutdown, but otherwise my tested never allowed the JVM to terminate ;))

Which will run the workers in the order they are submitted.

Now, if you want to feed the values from one SwingWorker to another, you could do something like...

public abstract class ChainedWorker<T, V> extends SwingWorker<T, V> {

private ChainedWorker<T, ?> next;
private T previousValue;

public ChainedWorker(ChainedWorker<T, ?> next) {
this.next = next;
}

public void setPreviousValue(T previousValue) {
this.previousValue = previousValue;
}

@Override
protected void done() {
try {
T previous = get();
if (next != null) {
next.setPreviousValue(previous);
next.execute();
}
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
}

}

Which is simply a SwingWorker which allows you to provide a link in the chain (the next worker to call), which passes the value that this SwingWorker generated...or some such similar

Now, if you're really nuts and want to role your own, maybe something like...

public class ChainedWorkerBuilder {

private List<SwingWorker> workers;
private SwingWorker current;

public ChainedWorkerBuilder() {
workers = new ArrayList<>(25);
}

public ChainedWorkerBuilder add(SwingWorker worker) {
workers.add(worker);
return this;
}

public void execute() {
if (!workers.isEmpty()) {
SwingWorker worker = workers.remove(0);
worker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equals(evt.getPropertyName())) {
SwingWorker source = (SwingWorker)evt.getSource();
switch (source.getState()) {
case DONE:
source.removePropertyChangeListener(this);
execute();
break;
}
}
}
});
}
}

}

2) How to update a textfield in my Welcome class, with the values from the Samp class.

I don't think I'm following your code directly, but, if you did something like...

SwingWorker<Boolean, Void>worker =  new SwingWorker<Boolean, String>() {

protected Boolean doInBackground() throws Exception {
publish(fileName);
ZipUtility zu = new ZipUtility(fileName));
return zu.extract(fileName);
}

Then in the SwingWorkers process method you would be able to update the UI safely...

    @Override
protected void process(List<String> chunks) {
// Grab the last element...
textfield.setText(chunks.get(chunks.size() - 1));
}
}


Related Topics



Leave a reply



Submit