14 Replies - 5081 Views - Last Post: 29 December 2011 - 01:01 AM Rate Topic: -----

#1 x68zeppelin80x  Icon User is offline

  • D.I.C Addict

Reputation: 130
  • View blog
  • Posts: 576
  • Joined: 07-March 09

Swing Timer Resume Issue

Posted 27 December 2011 - 11:09 PM

Ok, I have a Stopwatch program that has start, stop, restart, and lap buttons. All the buttons work fine, except the start button when the mode is set to STOPPED. This "resume" check sets the prev to be the last recorder time, and when start is called, the timer should set the now to be the difference of the current system time and the prev time, but the time changes drastically. Any suggestions? Is there anything I should do to improve this program?'

import java.awt.*;
import java.awt.event.*;
import java.text.DecimalFormat;
import javax.swing.*;

@SuppressWarnings("serial")
public class Stopwatch extends JFrame implements ActionListener {

	private JLabel display, lbl_lap;
	private JPanel pnl_button, pnl_lap;
	private JTextArea ara_lap;
	private JButton btn_start, btn_stop, btn_reset, btn_lap;

	private DecimalFormat df = new DecimalFormat("000.00");
	private Timer timer = new Timer(10, this);
	private long now = System.currentTimeMillis();
	private long prev;
	private int lap;

	private enum Mode {
		RUNNING, STOPPED, RESET
	}

	private Mode mode;

	public Stopwatch() {
		init();
		this.setSize(300, 200);
		this.setTitle("Stopwatch");
		this.setLocationRelativeTo(null);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setResizable(true);
		this.setVisible(true);
	}

	private void init() {
		display = new JLabel();
		display.setHorizontalAlignment(JLabel.CENTER);
		display.setText(getTime());
		display.setFont(new Font("Dialog", Font.BOLD, 48));

		pnl_button = new JPanel(new GridLayout(1, 4));
		btn_start = new JButton("Start");
		btn_stop = new JButton("Stop");
		btn_reset = new JButton("Reset");
		btn_lap = new JButton("Lap");
		btn_start.addActionListener(this);
		btn_stop.addActionListener(this);
		btn_reset.addActionListener(this);
		btn_lap.addActionListener(this);

		pnl_button.add(btn_start);
		pnl_button.add(btn_stop);
		pnl_button.add(btn_reset);
		pnl_button.add(btn_lap);

		pnl_lap = new JPanel(new BorderLayout());
		lbl_lap = new JLabel("Laps");
		lbl_lap.setHorizontalAlignment(JLabel.CENTER);
		ara_lap = new JTextArea("", 14, 8);
		pnl_lap.add(lbl_lap, BorderLayout.NORTH);
		pnl_lap.add(ara_lap, BorderLayout.CENTER);
		lap = 0;

		this.add(display, BorderLayout.CENTER);
		this.add(pnl_button, BorderLayout.SOUTH);
		this.add(pnl_lap, BorderLayout.EAST);
	}

	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == btn_start) {
			if (mode == Mode.STOPPED) {
				now = System.currentTimeMillis() - prev;
			}
			timer.start();
			mode = Mode.RUNNING;
		}

		if (e.getSource() == btn_stop) {
			prev = System.currentTimeMillis();
			timer.stop();
			mode = Mode.STOPPED;
		}

		if (e.getSource() == btn_reset) {
			timer.restart();
			lap = 0;
			ara_lap.setText("");
			now = System.currentTimeMillis();
			display.setText(getTime());
			mode = Mode.RESET;
		}

		if (e.getSource() == btn_lap) {
			ara_lap.append(String.format("%3s. %s\n", Integer.toString(++lap),
					display.getText()));
		}

		if (mode == Mode.RUNNING) {
			display.setText(getTime());
		}
	}

	private String getTime() {		
		return df.format((System.currentTimeMillis() - now) / 1000d);
	}

	public static void main(String[] args) {
		new Stopwatch();
	}
}


This post has been edited by x68zeppelin80x: 27 December 2011 - 11:17 PM


Is This A Good Question/Topic? 0
  • +

Replies To: Swing Timer Resume Issue

#2 GregBrannon  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2205
  • View blog
  • Posts: 5,239
  • Joined: 10-September 10

Re: Swing Timer Resume Issue

Posted 28 December 2011 - 05:47 AM

Your use of System.currentTimeMillis() as a reference for 'now' and during a stop/pause needs to be rethought.

For example, if you start your program and just let it sit unstarted at 0.00 for a while, when you do press 'start', the counter will jump to the time elapsed since the program started (or was last reset) and begins counting from there.

I'll play with it a bit to see if I can generalize a solution.
Was This Post Helpful? 0
  • +
  • -

#3 CasiOo  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1414
  • View blog
  • Posts: 3,136
  • Joined: 05-April 11

Re: Swing Timer Resume Issue

Posted 28 December 2011 - 06:31 AM

I wouldn't be using system.currentMillies at all.

You have your timer which ticks at a given delay. Your screen repaints after this delay, so how about you use this delay with your elapsed time :) Simply add the delay to your elapsed time for every timer tick.

import java.awt.*;
import java.awt.event.*;
import java.text.DecimalFormat;
import javax.swing.*;

@SuppressWarnings("serial")
public class Stopwatch extends JFrame implements ActionListener {

	private JLabel display, lbl_lap;
	private JPanel pnl_button, pnl_lap;
	private JTextArea ara_lap;
	private JButton btn_start, btn_stop, btn_reset, btn_lap;

	private final int delay = 16;
	
	private DecimalFormat df = new DecimalFormat("000.00");
	private Timer timer = new Timer(delay, this);
	private long elapsedTime;
	private int lap;

	private enum Mode {
		RUNNING, STOPPED, RESET
	}

	private Mode mode = Mode.STOPPED;

	public Stopwatch() {
		init();
		this.setSize(300, 200);
		this.setTitle("Stopwatch");
		this.setLocationRelativeTo(null);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setResizable(true);
		this.setVisible(true);
	}

	private void init() {
		display = new JLabel();
		display.setHorizontalAlignment(JLabel.CENTER);
		display.setText(getTime());
		display.setFont(new Font("Dialog", Font.BOLD, 48));

		pnl_button = new JPanel(new GridLayout(1, 4));
		btn_start = new JButton("Start");
		btn_stop = new JButton("Stop");
		btn_reset = new JButton("Reset");
		btn_lap = new JButton("Lap");
		btn_start.addActionListener(this);
		btn_stop.addActionListener(this);
		btn_reset.addActionListener(this);
		btn_lap.addActionListener(this);

		pnl_button.add(btn_start);
		pnl_button.add(btn_stop);
		pnl_button.add(btn_reset);
		pnl_button.add(btn_lap);

		pnl_lap = new JPanel(new BorderLayout());
		lbl_lap = new JLabel("Laps");
		lbl_lap.setHorizontalAlignment(JLabel.CENTER);
		ara_lap = new JTextArea("", 14, 8);
		pnl_lap.add(lbl_lap, BorderLayout.NORTH);
		pnl_lap.add(ara_lap, BorderLayout.CENTER);
		lap = 0;

		this.add(display, BorderLayout.CENTER);
		this.add(pnl_button, BorderLayout.SOUTH);
		this.add(pnl_lap, BorderLayout.EAST);
	}

	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == btn_start) {
			if (mode == Mode.STOPPED) {
				timer.start();
				mode = Mode.RUNNING;
			}
		}

		if (e.getSource() == btn_stop) {
			timer.stop();
			mode = Mode.STOPPED;
		}

		if (e.getSource() == btn_reset) {
			elapsedTime = 0;
			lap = 0;
			ara_lap.setText("");
			display.setText(getTime());
			timer.stop();
		}

		if (e.getSource() == btn_lap) {
			ara_lap.append(String.format("%3s. %s\n", Integer.toString(++lap),
					display.getText()));
		}

		if (mode == Mode.RUNNING) {
			elapsedTime += delay;
			display.setText(getTime());
		}
	}

	private String getTime() {	
		long totalSeconds = elapsedTime / 1000;
		long hours = totalSeconds / 3600;
		long minutes = (totalSeconds % 3600) / 60;
		long seconds = totalSeconds % 60;
		return hours + ":" + minutes + ":" + seconds;
	}

	public static void main(String[] args) {
		new Stopwatch();
	}
}


This post has been edited by CasiOo: 28 December 2011 - 06:32 AM

Was This Post Helpful? 1
  • +
  • -

#4 GregBrannon  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2205
  • View blog
  • Posts: 5,239
  • Joined: 10-September 10

Re: Swing Timer Resume Issue

Posted 28 December 2011 - 07:14 AM

I thought your approach was interesting, viable, and the changes needed to make it work were minimal. I would have preferred to provide a few hints on how to correct, but I'm not that clever. Summarizing, I added a third timekeeping variable, elapsedTime, that is recorded when the stopwatch is paused and then used as the starting point when the stopwatch resumes. The major changes are in actionPerformed() and getTime(), but they are minimal. Let me know if you have any questions.

import java.awt.*;
import java.awt.event.*;
import java.text.DecimalFormat;
import javax.swing.*;

@SuppressWarnings("serial")
public class StopWatch extends JFrame implements ActionListener {

    private JLabel display, lbl_lap;
    private JPanel pnl_button, pnl_lap;
    private JTextArea ara_lap;
    private JButton btn_start, btn_stop, btn_reset, btn_lap;

    private DecimalFormat df = new DecimalFormat("000.00");
    private Timer timer = new Timer(10, this);

    // modified now to startTime and added elapsedTime
    private long startTime;
    private float elapsedTime;
    private int lap;

    private enum Mode {
        RUNNING, STOPPED, RESET
    }

    private Mode mode;

    public StopWatch() {
        init();
        this.setSize(300, 200);
        this.setTitle("Stopwatch");
        this.setLocationRelativeTo(null);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setResizable(true);
        this.setVisible(true);
    }

    private void init() {
        display = new JLabel();
        display.setHorizontalAlignment(JLabel.CENTER);
        
        // changed initialization of display to "000.00"
        display.setText( "000.00" );
        display.setFont(new Font("Dialog", Font.BOLD, 48));

        pnl_button = new JPanel(new GridLayout(1, 4));
        btn_start = new JButton("Start");
        btn_stop = new JButton("Stop");
        btn_reset = new JButton("Reset");
        btn_lap = new JButton("Lap");
        btn_start.addActionListener(this);
        btn_stop.addActionListener(this);
        btn_reset.addActionListener(this);
        btn_lap.addActionListener(this);

        pnl_button.add(btn_start);
        pnl_button.add(btn_stop);
        pnl_button.add(btn_reset);
        pnl_button.add(btn_lap);

        pnl_lap = new JPanel(new BorderLayout());
        lbl_lap = new JLabel("Laps");
        lbl_lap.setHorizontalAlignment(JLabel.CENTER);
        ara_lap = new JTextArea("", 14, 8);
        pnl_lap.add(lbl_lap, BorderLayout.NORTH);
        pnl_lap.add(ara_lap, BorderLayout.CENTER);
        
        // these initializations are unnecessary but left them for clarity
        lap = 0;
        startTime = 0;

        this.add(display, BorderLayout.CENTER);
        this.add(pnl_button, BorderLayout.SOUTH);
        this.add(pnl_lap, BorderLayout.EAST);
    }

    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == btn_start) {
            startTime = System.currentTimeMillis();
            timer.start();
            mode = Mode.RUNNING;
        }

        if (e.getSource() == btn_stop) {
            timer.stop();
            elapsedTime = Float.parseFloat( display.getText() );
            mode = Mode.STOPPED;
        }

        if (e.getSource() == btn_reset) {
            timer.restart();
            lap = 0;
            ara_lap.setText("");
            // reset the time keeping variables
            startTime = 0;
            elapsedTime = 0;
            // reset the display
            display.setText( "000.00" );
            mode = Mode.RESET;
        }

        if (e.getSource() == btn_lap) {
            ara_lap.append(String.format("%3s. %s\n", Integer.toString(++lap),
                    display.getText()));
        }

        if (mode == Mode.RUNNING) {
            display.setText(getTime());
        }
    }

    private String getTime() {
        // reworked the calculation of displayed time
        return df.format( elapsedTime + 
                ( System.currentTimeMillis() - startTime ) / 1000d );
    }

    public static void main(String[] args) {
        new StopWatch();
    }
}

Was This Post Helpful? 1
  • +
  • -

#5 x68zeppelin80x  Icon User is offline

  • D.I.C Addict

Reputation: 130
  • View blog
  • Posts: 576
  • Joined: 07-March 09

Re: Swing Timer Resume Issue

Posted 28 December 2011 - 10:13 AM

View PostGregBrannon, on 28 December 2011 - 02:14 PM, said:

I thought your approach was interesting, viable, and the changes needed to make it work were minimal. I would have preferred to provide a few hints on how to correct, but I'm not that clever. Summarizing, I added a third timekeeping variable, elapsedTime, that is recorded when the stopwatch is paused and then used as the starting point when the stopwatch resumes. The major changes are in actionPerformed() and getTime(), but they are minimal. Let me know if you have any questions.

elapsedTime = Float.parseFloat( display.getText() );


I was going to go this route, thanks.
Was This Post Helpful? 0
  • +
  • -

#6 x68zeppelin80x  Icon User is offline

  • D.I.C Addict

Reputation: 130
  • View blog
  • Posts: 576
  • Joined: 07-March 09

Re: Swing Timer Resume Issue

Posted 28 December 2011 - 08:19 PM

OK, I have made some significant changes. Instead of using java.lang.System.currentTimeMillis() I am using java.util.Calendar.getInstance().getTimeInMillis(). This way I can use a java.text.SimpleDateFormat("kk:mm:ss.SS") for my display. The only problems that I am having are the hours is being displayed as 19... and the milliseconds keep jumping around from a length of 2 to a length of 3. I just don't know what is going on?

import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.util.Calendar;
import javax.swing.*;

@SuppressWarnings("serial")
public class StopWatch2 extends JFrame implements ActionListener {

	private JLabel lbl_display, lbl_lap;
	private JPanel pnl_display, pnl_button, pnl_lap;
	private JTextArea ara_lap;
	private JButton btn_start, btn_stop, btn_reset, btn_lap;

	private Format timeFormat = new SimpleDateFormat("kk:mm:ss.SS");
	private Timer timer = new Timer(1, this);

	private long startTime, delay;
	private int lap;

	private enum Mode {
		RUNNING, STOPPED, RESET
	}

	private Mode mode;

	public StopWatch2() {
		init();
		reset();
		this.setSize(600, 200);
		this.setTitle("Stopwatch");
		this.setLocationRelativeTo(null);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setResizable(true);
		this.setVisible(true);
	}

	private void init() {
		// Set up the display
		pnl_display = new JPanel();
		pnl_display.setBackground(Color.BLACK);
		lbl_display = new JLabel();
		lbl_display.setHorizontalAlignment(JLabel.CENTER);
		lbl_display.setVerticalAlignment(JLabel.CENTER);
		lbl_display.setFont(new Font("Dialog", Font.BOLD, 72));
		lbl_display.setForeground(Color.GREEN);
		// Add the display label to the panel
		pnl_display.add(lbl_display, BorderLayout.NORTH);

		// Set up the button panel
		pnl_button = new JPanel(new GridBagLayout());
		btn_start = new JButton("Start");
		btn_stop = new JButton("Stop");
		btn_reset = new JButton("Reset");
		btn_lap = new JButton("Lap");
		// Add ActionListeners to the buttons
		btn_start.addActionListener(this);
		btn_stop.addActionListener(this);
		btn_reset.addActionListener(this);
		btn_lap.addActionListener(this);
		// Add the buttons to the button panel
		int CENTER = GridBagConstraints.CENTER;
		addComponent(pnl_button, btn_start, 0, 0, 1, 1, CENTER);
		addComponent(pnl_button, btn_stop, 1, 0, 1, 1, CENTER);
		addComponent(pnl_button, btn_reset, 2, 0, 1, 1, CENTER);
		addComponent(pnl_button, btn_lap, 3, 0, 1, 1, CENTER);

		// Set up the laps panel
		pnl_lap = new JPanel(new BorderLayout());
		pnl_lap.setBackground(Color.WHITE);
		lbl_lap = new JLabel("Laps");
		lbl_lap.setHorizontalAlignment(JLabel.CENTER);
		lbl_lap.setForeground(Color.BLUE);
		ara_lap = new JTextArea("");
		ara_lap.setPreferredSize(new Dimension(100, 0));
		// Add the label and text area to the lap panel
		pnl_lap.add(lbl_lap, BorderLayout.NORTH);
		pnl_lap.add(ara_lap, BorderLayout.CENTER);

		// Add the panels to the frame
		this.add(pnl_display, BorderLayout.CENTER);
		pnl_display.add(pnl_button, BorderLayout.SOUTH);
		this.add(pnl_lap, BorderLayout.EAST);
	}

	private void reset() {
		lbl_display.setText("00:00:00.00");
		ara_lap.setText("");
		startTime = 0;
		delay = 0;
		lap = 0;
	}

	private void addComponent(JPanel panel, JComponent component, int xPos,
			int yPos, int width, int height, int align) {
		GridBagConstraints grid = new GridBagConstraints();
		grid.gridx = xPos;
		grid.gridy = yPos;
		grid.gridwidth = width;
		grid.gridheight = height;
		grid.anchor = align;
		grid.insets = new Insets(3, 6, 3, 6);
		grid.fill = GridBagConstraints.NONE;
		panel.add(component, grid);
	}

	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == btn_start) {
			startTime = Calendar.getInstance().getTimeInMillis();
			;
			timer.start();
			mode = Mode.RUNNING;
		}

		if (e.getSource() == btn_stop) {
			timer.stop();
			mode = Mode.STOPPED;
		}

		if (e.getSource() == btn_reset) {
			timer.restart();
			reset();
			mode = Mode.RESET;
		}

		if (e.getSource() == btn_lap) {
			ara_lap.append(String.format("%3s.) %s\n", Integer.toString(++lap),
					lbl_display.getText()));
		}

		if (mode == Mode.RUNNING) {
			lbl_display.setText(getTime());
		}

		else if (mode == Mode.STOPPED) {
			delay += Calendar.getInstance().getTimeInMillis() - startTime;
		}
	}

	private String getTime() {
		// reworked the calculation of displayed time
		long time = delay + Calendar.getInstance().getTimeInMillis()
				- startTime;
		return timeFormat.format(time);
	}

	public static void main(String[] args) {
		new StopWatch2();
	}
}


Was This Post Helpful? 0
  • +
  • -

#7 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8343
  • View blog
  • Posts: 31,890
  • Joined: 06-March 08

Re: Swing Timer Resume Issue

Posted 28 December 2011 - 08:29 PM

java.text.SimpleDateFormat("kk:mm:ss.SS")

S represents milliseconds, they range from 0 to 999 may be you want
java.text.SimpleDateFormat("kk:mm:ss.SSS") if you always want 3 decimals
Was This Post Helpful? 0
  • +
  • -

#8 x68zeppelin80x  Icon User is offline

  • D.I.C Addict

Reputation: 130
  • View blog
  • Posts: 576
  • Joined: 07-March 09

Re: Swing Timer Resume Issue

Posted 28 December 2011 - 08:32 PM

View Postpbl, on 29 December 2011 - 03:29 AM, said:

java.text.SimpleDateFormat("kk:mm:ss.SS")

S represents milliseconds, they range from 0 to 999 may be you want
java.text.SimpleDateFormat("kk:mm:ss.SSS") if you always want 3 decimals


I was thinking of doing that, I guess that will do. Now as for the 19 as the hour... I tried HH, hh, and kk and none of them are 00. I thought subtracting 2 dates that were a couple seconds, minutes... apart would set the hours to zero. Am I missing something here?
Was This Post Helpful? 0
  • +
  • -

#9 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8343
  • View blog
  • Posts: 31,890
  • Joined: 06-March 08

Re: Swing Timer Resume Issue

Posted 28 December 2011 - 08:41 PM

kk is for hours in 1-24 format
HH is for hours in 0-23 format
KK is for hours in 0-11 format (AM/PM)
hh is for hours in 1-12 format (AM/PM)
Was This Post Helpful? 0
  • +
  • -

#10 x68zeppelin80x  Icon User is offline

  • D.I.C Addict

Reputation: 130
  • View blog
  • Posts: 576
  • Joined: 07-March 09

Re: Swing Timer Resume Issue

Posted 28 December 2011 - 08:56 PM

I guess I was not clear enough with you.

I don't know why the clock says i.e. 19:00:04.342 when 19 hours have not passed by..?
Was This Post Helpful? 0
  • +
  • -

#11 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8343
  • View blog
  • Posts: 31,890
  • Joined: 06-March 08

Re: Swing Timer Resume Issue

Posted 28 December 2011 - 09:24 PM

What is your time zone ? Do you use saving daylight time ?
Was This Post Helpful? 0
  • +
  • -

#12 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8343
  • View blog
  • Posts: 31,890
  • Joined: 06-March 08

Re: Swing Timer Resume Issue

Posted 28 December 2011 - 09:45 PM

If you open a DOS windows and type
> time
what do you have ?
Was This Post Helpful? 0
  • +
  • -

#13 x68zeppelin80x  Icon User is offline

  • D.I.C Addict

Reputation: 130
  • View blog
  • Posts: 576
  • Joined: 07-March 09

Re: Swing Timer Resume Issue

Posted 28 December 2011 - 10:28 PM

View Postpbl, on 29 December 2011 - 04:45 AM, said:

If you open a DOS windows and type
> time
what do you have ?


Microsoft Windows [Version 6.1.7601]
Copyright © 2009 Microsoft Corporation. All rights reserved.

C:\>time
The current time is: 0:26:09.05
C:\>

Eastern Daylight Time (EDT) : UTC−5
Was This Post Helpful? 0
  • +
  • -

#14 x68zeppelin80x  Icon User is offline

  • D.I.C Addict

Reputation: 130
  • View blog
  • Posts: 576
  • Joined: 07-March 09

Re: Swing Timer Resume Issue

Posted 29 December 2011 - 12:20 AM

I made a quick-fix and the program seems to be working fine now, but I don't think that it was what I should be doing...
private String getTime() {
  long time = delay + Calendar.getInstance().getTimeInMillis() - startTime - 68400000;
  // Note:
  // 1000ms * 60sec * 60mins * 19hrs = 68400000 = 19 hrs in ms
  return timeFormat.format(time);
}


Was This Post Helpful? 0
  • +
  • -

#15 GregBrannon  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2205
  • View blog
  • Posts: 5,239
  • Joined: 10-September 10

Re: Swing Timer Resume Issue

Posted 29 December 2011 - 01:01 AM

With your latest approach, I have a vision of the official timekeeper at a track meet standing at the finish line with a paper calendar. Right tool for the job?

Okay, Java's Calendar class is much more than a paper calendar, but even so, it may not be the right tool for the job. Comparing this approach to your previous, there is significantly more to do to calculate and display elapsed time in hundreds of seconds, and that "more to do" results in overhead that robs accuracy from the result.

As I think you've found, it was an interesting exercise and a learning experience, and that's what really matters.
Was This Post Helpful? 1
  • +
  • -

Page 1 of 1