Search code examples
javaswingjframeswingworker

Swing Worker not refreshing JFrame properly


I originally was attempting to update a JFrame and JPanel several times while in a Java Action Listener, but both would only update when the Action Listener completed all its tasks. Here is the link to my original question (Refreshing a JFrame while in an Action Listener).

I was told in the feedback to that question that Swing Worker should solve my problems. However, when I implemented Swing Worker (as seen below), nothing changed. The JFrame and JPanel still updated only when the Action Listener completed all tasks. My question is, am I missing something below? If not, how can I implement this in an Action Listener to properly update the Frame and Panel timely?

@Override
protected Integer doInBackground() throws Exception{
    //Downloads and unzips the first video.  
    if(cameraBoolean==true)
        panel.add(this.downloadRecording(camera, recording));
    else
        panel.add(new JLabel("Could not contact camera "+camera.getName()));

    panel.repaint();
    jframe.repaint();
    return 1;
}

private JLabel downloadRecording(Camera camera, Recording recording){
    //does a bunch of calculations and returns a jLabel, and works correctly
}

protected void done(){
    try{
        Date currentTime = new Timestamp(Calendar.getInstance().getTime().getTime());
        JOptionPane.showMessageDialog(jframe, "Camera "+camera.getName()+" finished downloading at "+currentTime.getTime());
    }catch (Exception e){
    e.printStackTrace();
    }
}

Solution

  • You have a misundertanding about how SwingWorker works. This class is intended to provide a way to update the GUI while heavy tasks are being performed. All of this is because Swing components updates take place in the Event Dispatch Thread (a.k.a. EDT) which is a particular thread.

    For instance, if you click a button and perform a time consuming task all in the EDT, then this thread will block untill this task finishes. Consequently, you'll see your GUI is frozen.

    Keeping this in mind, doInBackground() method runs in another different thread that's not the EDT which is ok. So don't call any Swing method in there:

    protected Integer doInBackground() throws Exception{
        //Downloads and unzips the first video.  
        if(cameraBoolean==true) // just use if(cameraBoolean), since this is a boolean
            panel.add(this.downloadRecording(camera, recording)); // NO!
        else
            panel.add(new JLabel("Could not contact camera "+camera.getName())); //NO!
    
        panel.repaint(); //NO, never!
        jframe.repaint();//NO, never!
        return 1;
    }
    

    Add a JLabel to this panel before executing your SwingWorker and update its text using publish() and process() methods instead:

    JPanel panel = new JPanel();
    final JLabel progressLabel = new JLabel("Some text before executing SwingWorker");
    panel.add(progressLabel);
    
    SwingWorker<Integer, String> worker = new SwingWorker<Integer, String>() {    
        @Override
        protected Integer doInBackground() throws Exception {
            if(cameraBoolean){
                pubish("Starting long process...");
                //Some processing here
                publish("Intermediate result to be published #1");
                //Some other processing stuff
                publish("Intermediate result to be published #2");
                //And so on...
                return 0;
            } else {
                publish("Could not contact camera "+camera.getName());
                return -1;
            }
        }
    
        @Override
        protected void process(List<String> chunks) {
            for(String string : chunks){
                progressLabel.setText(string);
            }
        }
    
        @Override
        protected void done() {
            progressLabel.setText("Finished!!!");
        }
    };
    
    worker.execute();
    

    Both process() and done() methods take place in the EDT so it's safe make GUI updates there. Take a look to this excelent example: Swing Worker Example for more details.