1

The code snippet below sets text in a JLabel, which is added to a JPanel, which is attached to a JFrame. No matter what I do though (such as repaint(), revalidate(), etc) I cannot get the UI to update the text until the Action Listener is done.

I have never had this problem before, possible because I have never had to have several things happen in a single firing of Action Listener. What am I missing?

TL;DR Why does the following not update the text on the screen until it has finished firing the Action Listener, even if I put in repaint() after each listPanel.add()?

final JFrame guiFrame = new JFrame();
final JPanel listPanel = new JPanel();
listPanel.setVisible(true);
final JLabel listLbl = new JLabel("Welcome");
listPanel.add(listLbl);

startStopButton.addActionListener(new ActionListener(){@Override public void         actionPerformed(ActionEvent event){
     if(startStopButton.getText()=="Start"){
                startStopButton.setVisible(false);
                listPanel.remove(0);

     JLabel listLbl2 = new JLabel("Could not contact”);
                listPanel.add(listLbl2);

     JLabel listLbl2 = new JLabel("Success”);
                listPanel.add(listLbl2);
     }
}
guiFrame.setResizable(false);
guiFrame.add(listPanel, BorderLayout.LINE_START);
guiFrame.add(startStopButton, BorderLayout.PAGE_END);

//make sure the JFrame is visible
guiFrame.setVisible(true);

EDIT: I attempted to implement SwingWorker, but still the interface is not updating until the action interface finishes firing. Here is my SwingWorker code:

@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();
    }
}

Basically, SwingWorker (as I implemented it) is not properly updating the JPanel and JFrame. If I try to do the repaint in the "done()", they are not updated either. What am I missing?

Additionally, as soon as the JOptionPane displays itself, no more panels can be added to my jframe. I am unsure what is causing that either.

austinthemassive
  • 5,907
  • 6
  • 21
  • 25
  • What text are you trying to update? The startStopButton or one of the two JLabels? –  Aug 30 '13 at 18:30
  • I want the Labels to be added graphically as soon as possible (I stripped out a bunch of processing that must take place before a label is created, and the user needs to know right now what is happening). – austinthemassive Aug 30 '13 at 18:33
  • Try leaving the remove() method out and setVisible() to check and see if the JLabels will appear although the button is still visible. Or you could try adding everything first with the two JLabels set as not visible then instead of adding them from the button set them to be visible and repaint(). –  Aug 30 '13 at 18:44
  • The button visibility changed nothing. Also, I cannot add everything invisibly since this is dynamic code (it has to process an initially unknown amount of variables and create an output from them). Thanks for the suggestions though. – austinthemassive Aug 30 '13 at 18:47

1 Answers1

3

The action listener is being executed on the Event Dispatch Thread. For tasks like that, consider using a SwingWorker.

This would allow you to process your logic without blocking the updates (and thus the repaints) of the JFrame.

At a high level, this is what I mean:

startStopButton.addActionListener(new ActionListener(){@Override public void         actionPerformed(ActionEvent event){
     if(startStopButton.getText()=="Start"){
          // Start SwingWorker to perform whatever is supposed to happen here.
     }

You can find some information on how to use SwingWorker here, should you need it.

Surveon
  • 729
  • 5
  • 12
  • hmmm. Looks promising. Basically I will call a SwingWorker method from my Action Listener, the SwingWorker will do all the processing, then return control to the Action Listener to finish firing? – austinthemassive Aug 30 '13 at 18:31
  • You don't necessarily need to return control. Since the swing worker will be running in a separate thread, all the listener has to do is start the process and let it flow. – Surveon Aug 30 '13 at 18:35
  • Will the listener thread wait while each worker thread is computing, or will it start all of the worker threads (without regard to one finishing first). I ask so I know how to design my worker – austinthemassive Aug 30 '13 at 18:37
  • 1
    The listener's activity is taking place on the EDT (event dispatch thread). The worker will take place in a separate thread, but has the ability to communicate with the EDT via a call back mechanism. This was explained nicely to me in a question I asked earlier - see if any of the information can help you [here](http://stackoverflow.com/questions/18535303/how-are-the-publish-and-process-methods-on-swingworker-properly-used). To clarify - the EDT will NOT wait on the worker thread. That's the point of using it. – Surveon Aug 30 '13 at 18:41
  • It looks like you might be able to simplify your issue a little bit by using a [JProgressBar](http://docs.oracle.com/javase/7/docs/api/javax/swing/JProgressBar.html) to represent the process that is occurring in the worker thread. – Surveon Sep 03 '13 at 13:33