sonicdeathmonkey
sonicdeathmonkey

Reputation: 23

Updating GUI from a runnable

I'm building a Swing application and one part of the functionality should be to process and output some text visually and audibly (using Mary TTS). I need some advice on the best way for the GUI and text processing classes to communicate.

The GUI class is a subclass of JPanel. Within that I have a class implementing Runnable, called LineProcesser, which prepares the text to be dispatched to an audio player. I'm using a thread executor to keep this off the EDT (that may not be the best way but it seems to achieve the result I'm after).

My intention is for LineProcessor to run through all the text and update a JTextArea at the end of each line. Additionally it will need to halt and wait for user input at certain points. After the user input has been completed the GUI class should tell it to resume processing.

The following code illustrates what I currently have:

public class MyPanel extends JPanel {
    ExecutorService lineExecutor = Executors.newSingleThreadExecutor();
    Runnable lineProcessor = new LineProcessor();

    public class LineProcessor implements Runnable {

        private int currentLineNo = 0;

            public LineProcessor() {
            //  ...
            }

            @Override
            public void run() {
                // call getText();  
                // call playAudio();
                currentLineNo++;
            }
        }
    }

    private JButton statusLbl = new JLabel();       
    private JButton mainControlBtn = new JButton();

    private void mainControlBtnActionPerformed(ActionEvent evt) {

        if (mainControlBtn.getText().equals("Start")) {
                          lineExecutor.submit(lineProcessor);
                          mainControlBtn.setText("Running");
        }
    }
}

How can LineProcessor notify GUI components that they need to change and how can it be paused and restarted from within the GUI? I'm confused as to whether I need a Swing Worker, property/event listeners or something else? The examples I've read sort of make sense but I can't see how I can apply them to the code I have here.

Upvotes: 2

Views: 5150

Answers (3)

Robin
Robin

Reputation: 36601

What you are looking for is a SwingWorker. This class allows to perform the work on a worker thread, having periodical updates on the EDT, and in the end update the EDT as well.

Several examples are available on SO and in the Swing tutorials. Just a few links

Reporting progress can be done with the publish method, these results will be passed to the process method in which you can update the UI. At the end, the done method is called allowing you to perform some final UI updates.

For the pause/restart functionality ... you can use an invokeAndWait in the doInBackground method with a blocking method call (for example showing a JOptionPane asking for user input). But if you start using invokeAndWait in the doInBackground it might be overkill to use the SwingWorker and you can simply opt for the approach @Hovercraft Full Of Eels suggested

Upvotes: 3

mprabhat
mprabhat

Reputation: 20323

You will have to use both SwingWorker and Event methodology.

  1. Place your long running code in Swing Worker.
  2. Create new property change Event, listener, manager
  3. In your SwingWorker, when the change event occurs, call PropertyChangeManager to notify all the liseners.
  4. All GUI components which wants to be notified with the event should register themselves with the PropertyChangeManager.
  5. Your PropertyChangeManager will call customProperyChange method of the PropertyChangeListener and will pass the properyChangeEvent

Upvotes: 4

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285403

All you need to do is wrap any Swing calls in a Runnable, and queue it on the EDT via SwingUtilities.invokeLater(myRunnable);. That's it. No need for a SwingWorker.

e.g.,

public class LineProcessor implements Runnable {
  private int currentLineNo = 0;
  Runnable LineProcessor = new LineProcessor();  // won't this cause infinite recursion?

  public LineProcessor() {
     // ...
  }

  @Override
  public void run() {
     // call getText();
     // call playAudio();
     currentLineNo++;

     SwingUtilities.invokeLater(new Runnable() {
        public void run() {
           // *** Swing code can go here ***
        }
     });
  }
}

Upvotes: 6

Related Questions