0

Is there a way to continuously update a JLabel inside a loop from a separate thread. Right now the Jlabel gets only updated once the loop is over.

Please do correct me if am wrong, I think this is because the Event Dispatcher Thread is on halt till the other thread finishes executing.

Is there any way that I can run both threads at the same time?

I tried giving a Thread.sleep(300) inside the loop but didn't work.

Janak
  • 460
  • 5
  • 15
  • 2
    Of course there is. But without code it's hard to tell where the problem arises. –  Dec 20 '16 at 20:46
  • 1
    Please try to come up with [mcve]. But probably you just need to look into http://stackoverflow.com/questions/6567870/what-does-swingutilities-invokelater-do ... you want to call SwingUtilties.invokeLater() in order to update that label from the other thread. And for sure, you dont want to sleep ON the EDT. – GhostCat Dec 20 '16 at 20:46
  • If you wanna work with swing and avoid having nightmares regarding UI updates, try using "SwingWorker". Those threads are augmented with capabilities that allows you to perform underground task with the ability to update the UI wihtout having to sneak to the bottom of the Event dispatch thread (of the Swing Framework) affairs. – Victor Dec 20 '16 at 21:04
  • I would use a Swing Timer for this – Hovercraft Full Of Eels Dec 20 '16 at 21:06
  • As shown in update to answer. -- Hey Janak -- where's your code?? – Hovercraft Full Of Eels Dec 20 '16 at 21:43

1 Answers1

4

Please do correct me if am wrong, I think this is because the Event Dispatcher Thread is on halt till the other thread finishes executing.

This is wrong. If the other thread is truly running off of the Swing event thread, both should run simultaneously. You've got a bug where either:

  1. You think that you're running off of the Swing event thread, but you're not or
  2. You think that you're updating the GUI on the Swing event thread, but you're not.

Bottom line is that you've got a bug in code not shown, and for better answers, show pertinent code.

e.g.,

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.util.concurrent.TimeUnit;

import javax.swing.*;

@SuppressWarnings("serial")
public class UpdateLabel extends JPanel {
    // label to update
    private JLabel statusLabel = new JLabel("");

    public UpdateLabel() {
        JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 3, 3));
        topPanel.add(new JLabel("Counter:"));
        topPanel.add(statusLabel);

        JPanel bottomPanel = new JPanel();
        bottomPanel.add(new JButton(new StartThreadAction("Start Thread")));

        setLayout(new BorderLayout());
        add(topPanel, BorderLayout.PAGE_START);
        add(bottomPanel, BorderLayout.PAGE_END);
    }

    // fail safe method that is guaranteed to add text to the label
    // **on the Swing event thread**
    public void setLabelText(final String text) {
        if (SwingUtilities.isEventDispatchThread()) {
            statusLabel.setText(text);
        } else {
            SwingUtilities.invokeLater(() -> {
                statusLabel.setText(text);
            });
        }
    }

    // Abstract Action for our JButton
    private class StartThreadAction extends AbstractAction {
        protected static final int MAX_COUNT = 10;
        protected static final long SLEEP_TIME = 1;

        public StartThreadAction(String name) {
            super(name);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            // start a new thread
            new Thread(new Runnable() {
                private int counter = 0;

                @Override
                public void run() {
                    // within the background thread update a counter and sleep
                    while (counter < MAX_COUNT) {
                        String text = "" + counter;
                        setLabelText(text); // call our fail safe method
                        counter++;
                        try {
                            // sleep for 1 second
                            TimeUnit.SECONDS.sleep(SLEEP_TIME);
                        } catch (InterruptedException e) {
                            // rare time it's OK to leave this blank
                        }
                    }
                }
            }).start();
        }
    }

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

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

    public static void main(String[] args) {
        // start GUI on the Swing event thread
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

Having said all this, for something like a simple count with delay, I'd use a Swing Timer because there's no worries about calling code on or off the EDT, since the Timer guarantees that all calls will be on this thread.

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;   
import javax.swing.*;

@SuppressWarnings("serial")
public class UpdateLabel2 extends JPanel {
    // label to update
    private JLabel statusLabel = new JLabel("");
    private Timer timer;

    public UpdateLabel2() {
        JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 3, 3));
        topPanel.add(new JLabel("Counter:"));
        topPanel.add(statusLabel);

        JPanel bottomPanel = new JPanel();
        bottomPanel.add(new JButton(new StartTimerAction("Start Timer")));

        setLayout(new BorderLayout());
        add(topPanel, BorderLayout.PAGE_START);
        add(bottomPanel, BorderLayout.PAGE_END);
    }

    // Abstract Action for our JButton
    private class StartTimerAction extends AbstractAction {
        protected static final int MAX_COUNT = 10;
        private static final int TIMER_DELAY = 1000; // 1 second
        private int counter = 0;

        public StartTimerAction(String name) {
            super(name);
            putValue(MNEMONIC_KEY, KeyEvent.VK_S);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (timer != null && timer.isRunning()) {
                return; // wait until timer finishes
            }
            timer = new Timer(TIMER_DELAY, new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e2) {
                    if (counter >= MAX_COUNT) {
                        timer.stop();
                        counter = 0;
                    } else {
                        counter++;
                        statusLabel.setText("" + counter);
                    }
                }
            });
            timer.start();
        }
    }

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

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

    public static void main(String[] args) {
        // start GUI on the Swing event thread
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373