8

I have a message label and a submit button. The submit button will be pressed multiple times, and the action for the each press can take up to a minute.

When the button is pressed, I want to set the message to empty, and after the task is complete, I want to set the message to "Complete".

private void submitActionPerformed(java.awt.event.ActionEvent evt) {
   message = "";
   updateMessageLabel();

   doTheTask();

   /* this update is apply to the label after completion */
   message = "Complete";
}

Is it possible to update that message label before the submitActionPerformed() method is run (or in the method), but after the the button is clicked?

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Berek Bryan
  • 13,755
  • 10
  • 32
  • 43

2 Answers2

13

Although the Swing concurrency tutorial already contains some very good samples on how to deal with concurrency in Swing, find below an example which

  • contains a checkbox to prove the UI is still alive
  • has a progress bar, which gets updated from the SwingWorker
  • has a label, which gets updated once the SwingWorker is finished

    import javax.swing.JCheckBox;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JProgressBar;
    import javax.swing.SwingWorker;
    import java.awt.BorderLayout;
    import java.awt.EventQueue;
    import java.lang.reflect.InvocationTargetException;
    import java.util.List;
    import java.util.concurrent.ExecutionException;
    
    public class SwingWorkerExample {
      private static JProgressBar PROGRESS_BAR;
      private static JLabel OUTPUT_LABEL;
      private static JFrame createGUI(){
        JFrame testFrame = new JFrame( "TestFrame" );
    
        PROGRESS_BAR = new JProgressBar(  );
        PROGRESS_BAR.setMinimum( 0 );
        PROGRESS_BAR.setMaximum( 100 );
    
        OUTPUT_LABEL = new JLabel( "Processing" );
    
        testFrame.getContentPane().add( PROGRESS_BAR, BorderLayout.CENTER );
        testFrame.getContentPane().add( OUTPUT_LABEL, BorderLayout.SOUTH );
    
        //add a checkbox as well to proof the UI is still responsive
        testFrame.getContentPane().add( new JCheckBox( "Click me to proof UI is responsive" ), BorderLayout.NORTH );
    
    
    
        testFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        return testFrame;
      }
    
      public static void main( String[] args ) throws InvocationTargetException, InterruptedException {
        EventQueue.invokeAndWait( new Runnable() {
          @Override
          public void run() {
            JFrame frame = createGUI();
    
            frame.pack();
            frame.setVisible( true );
          }
        } );
        //start the SwingWorker outside the EDT
        MySwingWorker worker = new MySwingWorker( PROGRESS_BAR, OUTPUT_LABEL );
        worker.execute();
      }
      private static class MySwingWorker extends SwingWorker<String, Double>{
        private final JProgressBar fProgressBar;
        private final JLabel fLabel;
        private MySwingWorker( JProgressBar aProgressBar, JLabel aLabel ) {
          fProgressBar = aProgressBar;
          fLabel = aLabel;
        }
    
        @Override
        protected String doInBackground() throws Exception {
          int maxNumber = 10;
          for( int i = 0; i < maxNumber; i++ ){
            Thread.sleep( 2000 );//simulate long running process
            double factor = ((double)(i+1) / maxNumber);
            System.out.println("Intermediate results ready");
            publish( factor );//publish the progress
          }
          return "Finished";
        }
    
        @Override
        protected void process( List<Double> aDoubles ) {
          //update the percentage of the progress bar that is done
          int amount = fProgressBar.getMaximum() - fProgressBar.getMinimum();
          fProgressBar.setValue( ( int ) (fProgressBar.getMinimum() + ( amount * aDoubles.get( aDoubles.size() - 1 ))) );
        }
    
        @Override
        protected void done() {
          try {
            fLabel.setText( get() );
          } catch ( InterruptedException e ) {
            e.printStackTrace();
          } catch ( ExecutionException e ) {
            e.printStackTrace();
          }
        }
      }
    }
    
james.garriss
  • 12,959
  • 7
  • 83
  • 96
Robin
  • 36,233
  • 5
  • 47
  • 99
  • 3
    the process implementation isn't quite working as users might expect: their might be several (accumulated) values in the list, but it's setting the progressBar value to the first only. Instead, either loop through all or use the last only – kleopatra Jan 20 '12 at 12:12
  • just noticed another glitch (IMO): the method to call for starting a worker is `execute` (instead of run) – kleopatra Apr 01 '12 at 10:04
  • 1
    @kleopatra The class javadoc of the SwingWorker class agrees with you, so I will adjust the code. Good catch (again) – Robin Apr 01 '12 at 14:47
  • @Robin can you tell how can i use the loop you used to simulate long running task if my Long Running Task is DB select transaction. – Syed Muhammad Mubashir Nov 26 '12 at 03:23
  • @SyedMuhammadMubashir If you have a question, post one – Robin Nov 26 '12 at 07:06
10

Yes you can do this using SwingWorker thread, do all the pre submitActionPerformed() activities like updating the label, in the execute() method of the currentThread and call doTheTask() as a background job using worker Thread.

I suggest you to go through this documentation for reference about SwingWorker Thread

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Rajesh Pantula
  • 10,061
  • 9
  • 43
  • 52
  • 2
    It is best to link to the latest version of the JavaDocs. I have edited your answer to point to J2SE 7. For tips on getting a link to the latest docs, see [point 2 of advantages](http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7090875). – Andrew Thompson Jan 19 '12 at 03:20