Continuously update a JLabel inside a loop from separate thread [closed]

匆匆过客 提交于 2021-02-04 21:37:40

问题


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.


回答1:


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());
    }
}


来源:https://stackoverflow.com/questions/41250944/continuously-update-a-jlabel-inside-a-loop-from-separate-thread

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!