3

I have a GUI which uses a JProgressBar. The GUI has JBUTTONs that invoke following code:

public void updateProgressBar(int x) {
    System.out.println(javax.swing.SwingUtilities.isEventDispatchThread());
    healthBar.setValue(x);
}

but I have also made a loop that calls the same method regularly.

public class Loop implements Runnable {
    public void run() {
        while (true) { Thread.sleep(2000); updateProgressBar(0); }
    }
}

Now, as I understand anything that changes my GUI needs to be executed from the EDT. JProgressBar.setValue(x) changes my GUI, and when it is called from the Loop class the isEventDispatchThread check fails, which is all good and understable. What I can't understand, however, is if that also means I should use the SwingUtilities.invokeLater() on setValue(). My concern is, since I don't know how setValue() actually works, that I use invokeLater() unnecessarily or even break something in using it.

I don't know how to ask my question any better than: If I know the method that changes my GUI isn't being called FROM the EDT, do I then also know I have to use invokeLater()?

user2651804
  • 1,464
  • 4
  • 22
  • 45
  • That `Loop` class isn't good, you're using both an infinite `while` as well as `Thread.sleep()` – BitNinja Sep 21 '14 at 00:20
  • Are you telling me it's bad because I use Thread.sleep() IN an infinite loop, or because I shouldn't use infinite loops and Thread.sleep() in any context? – user2651804 Sep 21 '14 at 00:24
  • Create a thread-safe TimerTask... – Mr. Polywhirl Sep 21 '14 at 00:24
  • @user2651804 Sorry, I should've elaborated. Either of those statements on their own (for the most part) is not considered good practice. Instead I recommend using a timer of some sort. Swing timers are automatically run on the EDT: http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html – BitNinja Sep 21 '14 at 00:29
  • 1
    The short answer to the question in your last sentence is yes. If you are doing anything that affects your Swing GUI, and it's not on the EDT already, you must put it on the EDT by using invokeLater (or invokeAndWait). – amalloy Sep 21 '14 at 00:30

1 Answers1

5

One possible solution is to create a method that checks if the thread is the EDT, and if so directly update the bar, otherwise queue it on the EDT:

public void updateProgressBar(int value) {
   progressBar.setValue(value);
}

public void safeUpdateProgressBar(final int value) {
   if (SwingUtilities.isEventDispatchThread()) {
      updateProgressBar(value);
   } else {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            updateProgressBar(value);
         }
      });
   }
}

But there are many ways to skin this cat. For example,

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.*;

public class ProgExample extends JPanel {
   private JProgressBar progressBar = new JProgressBar(0, 100);

   public ProgExample() {
      progressBar.setBorderPainted(true);
      progressBar.setStringPainted(true);
      add(progressBar);
      add(new JButton(new ProgressAction1("Action 1", KeyEvent.VK_1, this)));
      add(new JButton(new ProgressAction2("Action 2", KeyEvent.VK_2, this)));
      add(new JButton(new ProgressAction3("Action 3", KeyEvent.VK_3, this)));
   }

   public void updateProgressBar(int value) {
      progressBar.setValue(value);
   }

   public void safeUpdateProgressBar(final int value) {
      if (SwingUtilities.isEventDispatchThread()) {
         updateProgressBar(value);
      } else {
         SwingUtilities.invokeLater(new Runnable() {
            public void run() {
               updateProgressBar(value);
            }
         });
      }
   }

   private static void createAndShowGui() {
      ProgExample mainPanel = new ProgExample();

      JFrame frame = new JFrame("ProgExample");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class ProgressAction1 extends AbstractAction {
   private static final int MAX_VALUE = 100;
   protected static final long SLEEP_TIME = 100;
   protected static final int STEP = 2;
   private ProgExample gui;

   public ProgressAction1(String name, int mnemonic, ProgExample gui) {
      super(name);
      putValue(MNEMONIC_KEY, mnemonic);
      this.gui = gui;
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      new Thread(new Runnable() {
         private int value = 0;
         @Override
         public void run() {
            while (value <= MAX_VALUE) {
               gui.safeUpdateProgressBar(value);
               value += STEP;
               try {
                  Thread.sleep(SLEEP_TIME);
               } catch (InterruptedException e) {}
            }
            gui.safeUpdateProgressBar(MAX_VALUE);
         }
      }).start();
   }
}

class ProgressAction2 extends AbstractAction {
   private static final int MAX_VALUE = 100;
   protected static final long SLEEP_TIME = 100;
   protected static final int STEP = 2;
   private ProgExample gui;

   public ProgressAction2(String name, int mnemonic, ProgExample gui) {
      super(name);
      putValue(MNEMONIC_KEY, mnemonic);
      this.gui = gui;
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      final SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
         private int value = 0;

         @Override
         protected Void doInBackground() throws Exception {
            while (value <= MAX_VALUE) {
               setProgress(value);
               value += STEP;
               try {
                  Thread.sleep(SLEEP_TIME);
               } catch (InterruptedException e) {}
            }
            setProgress(MAX_VALUE);
            return null;
         }
      };
      worker.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent pcEvt) {
            if ("progress".equals(pcEvt.getPropertyName())) {
               gui.updateProgressBar(worker.getProgress());
            }
         }
      });
      worker.execute();
   }
}

class ProgressAction3 extends AbstractAction {
   private static final int MAX_VALUE = 100;
   protected static final int SLEEP_TIME = 100;
   protected static final int STEP = 2;
   private ProgExample gui;

   public ProgressAction3(String name, int mnemonic, ProgExample gui) {
      super(name);
      putValue(MNEMONIC_KEY, mnemonic);
      this.gui = gui;
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      new Timer(SLEEP_TIME, new ActionListener() {
         int value = 0;

         @Override
         public void actionPerformed(ActionEvent e) {
            if (value <= MAX_VALUE) {
               gui.updateProgressBar(value);
               value += STEP;
            } else {
               gui.updateProgressBar(MAX_VALUE);
               ((Timer) e.getSource()).stop();
            }

         }
      }).start();
   }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373