简体   繁体   中英

How to update JFrame Label within a Thread? - Java

I have tried a lot, but can't seem to get it to work.

I was told to use EDT with the following example.

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            //  Modify the GUI here
        }
});

I have read on this topic a lot and still don't understand. I get what a thread is, but the .invokeLater still makes no sense to me. Honestly if you can explain in detail this it would be a big help!

Goal of Program: To get the randomly generated key that is constantly created every second to update itself afterward in the GUI.

So there is an EDT (Event Dispatch Thread). All actions that appear on your screen are executed by the EDT. There is only one EDT per Swing application.

You are in some arbitrary thread and you want to update the GUI through that thread? Well like I said there is only one EDT for each swing application, so you have to tell that EDT to display the label (or whatever context you want).

The idea here, is you push this Runnable onto a queue that the EDT pulls from. Eventually, your runnable will be processed by the EDT when all other actions before it are completed.

I recommend you get the book Filthy Rich Clients . There's a chapter where they explain Swing's threading model to great detail.

Basically in Swing, any code that modifies the GUI should be executed in the Event Dispatcher Thread. The SwingUtilities class that you are using there provides you with an easy way to post events to the event queue that is then dispatched by the EDT. That's what the invokeLater method does, it takes a new Runnable() as argument which is ultimately executed on the EDT.

From the book:

The invokeLater() implementation takes care of creating and queuing a special event that contains the Runnable. This event is processed on the EDT in the order it was received, just like any other event. When its time comes, it is dispatched by running the Runnable's run() method.

If you're all looking to do is update the UI on a known schedule, try something like the following. This assumes that a JFrame is the component you wish to update every 1 second.

private static final int WAIT_LENGTH = 1000; // 1 second
private JFrame frame = new JFrame();

// Thread will update the UI (via updateUI() call) about every 1 second
class UIUpdater extends Thread {
  @Override
  void run() {
    while (true) {
      try {
        // Update variables here
      }
      catch (Exception e) {
        System.out.println("Error: " + e);
      }
      finally {
        frame.repaint();
        Thread.sleep(WAIT_LENGTH);
      }
    }
  }
}

To start this thread:

UIUpdater t = new UIUpdater();
t.start();

This is a pretty common element of all GUI programming. You have one thread that handles drawing the GUI, getting input, and running callbacks. If another thread tries to change the GUI related objects, it will conflict with the GUI thread. Say, for example, it was half way through drawing something and you change the color from a different thread.

All invokeLater does is queue up something for the GUI thread to run. By "later" it's really runs almost instantly but the current thread doesn't have to wait for it. The GUI thread may be doing a draw or waiting for a callback to return which would delay executing the code you gave it.

Needs to be a member so we can change it and still use it from an inner class

protected long secret=0;

... this needs to be in your code somewhere it'll get run...

JFrame f = new JFrame("foo");
new Thread(){
        public void run() {
            for(;;){
                try { 
                    sleep(1000);
                } catch Interrupted (Exception ix){ 
                    return;
                }
                // TODO update your secret key here
                // please don't use random()

                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        f.setTitle("secret "+x);
                    }
                });
            }
        }
   }).start();

....

Only ever update Swing from the EDT so that it paints properly.

When you are in the EDT ( running code in an event handler) you can call paintImmediately() if you really must.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM