0

I do not understand why this code does not work as expected. When the button is clicked, I expect it will set the JLabel to the text in the text field, wait 1 second, decrements the counter and then change the JLabel to that decremented value. When I run it and click the button it waits until the while loop condition is met, then just sets the JLabel to the final value.

I have tried to do it both in the action event explicitly and in the current form by calling the runTimer on click.

'''

package swinging;

import java.awt.FlowLayout;
import java.awt.Font;
import java.util.concurrent.TimeUnit;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;


public class PomGUI 
{
    public static void main(String[] args)
    {
        new pGUI();
    }
}


class pGUI extends JFrame
{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    JTextField tInput;
    JLabel lHeader, lClock;
    JButton bStart;
    JButton bStop;
    int remain;

    public pGUI()
    {

        lHeader = new JLabel("Personal Pomodoro Timer v5.0");
        tInput = new JTextField(5);
        lClock = new JLabel("0:00");
        bStart = new JButton("Start");
        bStop = new JButton("Stop");
        lHeader.setFont(new Font("Serif", Font.ITALIC, 24));

        add(lHeader);
        add(tInput);
        add(lClock);
        add(bStart);
        add(bStop);

        bStart.addActionListener(ae -> {
            try {
                runTimer();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        });

        setLayout(new FlowLayout());
        setVisible(true);
        setSize(400, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void runTimer() throws InterruptedException
    {
        remain = Integer.parseInt(tInput.getText());
        lClock.setText(Integer.toString(remain));
        while(remain > 0 )
        {
            TimeUnit.SECONDS.sleep(1);
            remain--;
            lClock.setText(Integer.toString(remain));
        }

    }

'''

nyct0phile
  • 11
  • 4
  • Your while loop is going to block the current thread. Which thread is that? The Swing event thread, or Event Dispatch Thread (EDT), the one that allows GUI interaction with the user and GUI painting to occur. So if you block it, the GUI freezes and is essentially dead in its tracks. The solution for this type of program is to use a [Swing Timer](https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html) in place of the while loop. Please read/study the tutorial as it will take a shift in thinking to learn how to use this properly, but once you get it figured out, your code *will* work. – Hovercraft Full Of Eels Apr 11 '20 at 21:56
  • Thank you. I resorted to the sleep mechanism because I could not get the Swing Timer to work, but I will just have to study it a bit harder. – nyct0phile Apr 11 '20 at 22:01
  • Yeah, sleeping the Swing event thread is *not* good, as you're finding out. Note that the Timer uses an ActionListener, and the listener's actionPerformed method is called repeatedly, until you tell the Timer to stop. This is all in place of the while loop, and no true loop is needed or used. – Hovercraft Full Of Eels Apr 11 '20 at 22:03

0 Answers0