0

I'm working on a GUI for a program that is computationally intensive and takes some period of time to complete calculations. I want to display and update the processing time on the GUI, both for reference and as an indication to the user that the program is running. I've created a worker to deal with the processing time on a separate thread as follows:

public class Worker extends SwingWorker<String, String>{
    
    JLabel label;
    boolean run;
    
    public Worker(JLabel label)
    {
        this.label = label;
        this.run = true;
    }

    @Override
    protected String doInBackground() throws Exception {
        //This is what's called in the .execute method
        long startTime = System.nanoTime();
        while(run)
        {
            //This sends the results to the .process method
            publish(String.valueOf(System.nanoTime() - startTime));
            Thread.sleep(100);
        }
        return null;
    }
    
    public void stop()
    {
        run = false;
    }

    @Override
    protected void process(List<String> item) {
        double seconds = Long.parseLong(item.get(item.size()-1))/1000000000.0;
        String secs = String.format("%.2f", seconds);
        //This updates the UI
        label.setText("Processing Time: " + secs + " secs");
        label.repaint();
    }
}

I pass a JLabel to the Worker which it displays the processing time on. The following code creates the Worker and executes a runnable that carries out the main calculations.

Worker worker = new Worker(jLabelProcessTime);
worker.execute();
//Check for results truncation
boolean truncate = !jCheckBoxTruncate.isSelected();
long startTime = System.nanoTime();
String[] args = {fileName};
//run solution and draw graph
SpeciesSelection specSel = new SpeciesSelection(args, truncate);
Thread t = new Thread(specSel);
t.start();
t.join();
ArrayList<Double> result = specSel.getResult();
drawGraph(result);
worker.stop();

My problem is that the processing time does not update on the GUI until after the calculations have finished. I think I'm pretty close because without 't.join();' the timer updates fine, but the processing never completes. I'd really appreciate some help to figure out what's wrong.

3
  • 1
    A thumb-rule in Swing is to use SwingUtilities.invokeLater(Runnable) in order to add a GUI update task to Swing's queue, after you made your computation in a different thread than the GUI thread. Swing's engine will take care of thread-safely applying your update Commented Feb 12, 2018 at 9:39
  • 1
    Where are you doing t.join();? It looks like you're doing it on the UI thread which blocks it... Commented Feb 12, 2018 at 9:41
  • The lower part of the code is in an action performed event code block. Commented Feb 12, 2018 at 9:43

2 Answers 2

2

Your code is not working as you think it is...

I created MVCE for you...

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingWorker;

public class SwingWorkerTest extends JFrame {

    public SwingWorkerTest() {
        this.setLayout(new FlowLayout());
        JButton button = new JButton("run");
        JLabel label = new JLabel("time: -");
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                Worker worker = new Worker(label);
                worker.execute();
                //Check for results truncation
//              boolean truncate = !jCheckBoxTruncate.isSelected();
//              long startTime = System.nanoTime();
//              String[] args = {fileName};
                //run solution and draw graph
//              SpeciesSelection specSel = new SpeciesSelection(args, truncate);
//              Thread t = new Thread(specSel);
//              t.start();
//              t.join();
//              ArrayList<Double> result = specSel.getResult();
//              drawGraph(result);
                worker.stop();

                System.out.println("button's actionPerformed finished");
            }
        });

        this.getContentPane().add(button);
        this.getContentPane().add(label);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public static void main(String[] args) {
        new SwingWorkerTest();
    }
}

class Worker extends SwingWorker<String, String>{

    JLabel label;
    boolean run;

    public Worker(JLabel label)
    {
        this.label = label;
        this.run = true;
    }

    @Override
    protected String doInBackground() throws Exception {
        System.out.println("doInBackground..., run=" + run);
        //This is what's called in the .execute method
        long startTime = System.nanoTime();
//        while(run)
//        {
            System.out.println("running...");
            //This sends the results to the .process method
            publish(String.valueOf(System.nanoTime() - startTime));
            Thread.sleep(100);
//        }
        System.out.println("worker finished...");
        return null;
    }

    public void stop()
    {
//      System.out.println("stop");
//        run = false;
    }

    @Override
    protected void process(List<String> item) {
        System.out.println("processed");
        double seconds = Long.parseLong(item.get(item.size()-1))/1000000000.0;
        String secs = String.format("%.2f", seconds);
        //This updates the UI
        System.out.println("updating");
        label.setText("Processing Time: " + secs + " secs");
//        label.repaint();
    }
}

In short I found, that Worker.stop() is called before doInBackground as a result, your run is false and so publish is never called.

The "fixed" code above prints (after start I resized and I clicked on run button):

button's actionPerformed finished
doInBackground..., run=true
running...
processed
updating
worker finished...

and it shows:

enter image description here


new approach with a timer

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingWorker;
import javax.swing.SwingWorker.StateValue;
import javax.swing.Timer;

public class SwingWorkerTestNew extends JFrame {

    int progress = 0;

    public SwingWorkerTestNew() {
        GridLayout layout = new GridLayout(2, 1);
        JButton button = new JButton("run");
        JLabel label = new JLabel("progress: -");
        WorkerNew worker = new WorkerNew(label);
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                worker.execute();
                System.out.println("button's actionPerformed finished");
            }
        });

        this.getContentPane().setLayout(layout);
        this.getContentPane().add(button);
        this.getContentPane().add(label);

        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.pack();
        this.setVisible(true);

        Timer timer = new Timer(100, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (worker.getState() == StateValue.STARTED) {
                    ++progress;
                    label.setText(Integer.toString(progress));
                }
                if (worker.getState() == StateValue.DONE) {
                    label.setText("done");
                }
            }
        });
        timer.start();
    }

    public static void main(String[] args) {
        new SwingWorkerTestNew();
    }
}

class WorkerNew extends SwingWorker<String, String> {

    JLabel label;

    public WorkerNew(JLabel label) {
        this.label = label;
    }

    @Override
    protected String doInBackground() throws Exception {
        System.out.println("background");
        Thread.sleep(2000);
        System.out.println("done");
        return null;
    }

}
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for this. I still don't know how to resolve my problem though. Am I correct to say your intention was to show the problem rather than to offer a solution? Or am I missing something?
You stated your problem as "processing time does not update on the GUI". As you see "button's actionPerformed finished" is first message in console, so it is really running in a background. If you move your Thred.sleep(100) just before publish and it will show 0.10 secs. What is a problem you want to solve?
I would like the time displayed on the GUI to update every 100ms until the work on Thread t is complete.
In such case I'd use some daemon thread, that will be asking if your task is DONE, see getState
0

I was going about this in a far too complicated manner. No SwingWorker was required. I solved it as follows:

//Check for results truncation
boolean truncate = !jCheckBoxTruncate.isSelected();
String[] args = {fileName};
//run solution and draw graph
SpeciesSelection specSel = new SpeciesSelection(args, truncate);
Thread t = new Thread(specSel);
t.start();
long startTime = System.nanoTime();

new Thread()
{
   public void run() {
   while(!specSel.isFinished())
   {
    double seconds = (System.nanoTime() - startTime)/1000000000.0;
    String secs = String.format("%.2f", seconds);
    jLabelProcessTime.setText("Processing Time: " + secs + " secs");
    try {
        Thread.sleep(100);
      } catch (InterruptedException ex) {
        Logger.getLogger(SpecSelGUI.class.getName()).log(Level.SEVERE, null, ex);
      }
   }
ArrayList<Double> result = specSel.getResult();
    drawGraph(result);
   }
}.start();

1 Comment

It would be more correct to replace jLabelProcessTime.setText("Processing Time: " + secs + " secs"); with SwingUtilities.invokeLater(new Runnable { public void run() { jLabelProcessTime.setText("Processing Time: " + secs + " secs"); } } );

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.