Not sure why this didn't work properly.

So I programmed out a clock for practice/educational value for myself, and I got it near the end and encountered a problem. My program has 2 sets of class fields and a few temporary ones. The first set of class fields are text fields (hours, mins, secs) and the second set are Integers (h, m, s) (not int's ... Integers). I have two methods (setText and setTime) that convert between these two sets. setText sets the text fields to whatever time is stored in the Integers, and setTime sets the Integer values to whatever is stored in the text fields (assuming they're valid ints, of course).

The code that was behaving strangely is shown below between the large comment lines. I needed some way to update the time, so I first tried changing the Integer values and then calling setText() ... but it didn't work. So I then tried setting the text fields and calling setTime() and that DID work. The end result of both should be the same, and yet it wasn't. I was wondering why not? Can anyone help/explain?

I figure it has something to do with Integers and mutability, but that doesn't seem likely since they're both declared at the class-level and not within a method. I did some debugging of it (added System.out.println messages) and found out that the Integer value was changing, but then it was being reset back to what it was initially. bleh - I think I'm doing a bad job of explaining it. Anyway - here's the entire code below. It works correctly currently. But I left the bit of code in that wasn't working properly - just commented out. If you uncomment those and comment the 4 lines above them, you'll hopefully see what I'm talking about.

import java.awt.BorderLayout;

import java.awt.Color;

import java.awt.Dimension;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.awt.geom.Ellipse2D;

import java.awt.geom.Line2D;

import java.util.Calendar;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JPanel;

import javax.swing.JTextField;

import javax.swing.Timer;

import javax.swing.event.DocumentEvent;

import javax.swing.event.DocumentListener;

// Math.sin, Math.cos, Math.tan use RADIANS .. not degrees.

// 0 rad/deg is at east and proceeds clockwise for increasing angle

publicclass myClockextends JFrameimplements ActionListener, DocumentListener

{

JTextField hours;

JTextField mins;

JTextField secs;

Calendar now;

ClockPane clock =new ClockPane();

JPanel textPane;

Integer m;double mrad;

Integer h;double hrad;

Integer s;double srad;

myClock()

{

setSize(300,360);

setResizable(false);

setTitle("Clock!");

// get starting time

now = Calendar.getInstance();

hours =new JTextField(String.valueOf(now.get(Calendar.HOUR)%12), 2);

mins =new JTextField(String.valueOf(now.get(Calendar.MINUTE)), 2);

secs =new JTextField(String.valueOf(now.get(Calendar.SECOND)), 2);

setTime();

// set the document listeners

hours.getDocument().addDocumentListener(this);

mins.getDocument().addDocumentListener(this);

secs.getDocument().addDocumentListener(this);

// create visual layout of frame

textPane = createSouthPane();

add(textPane, BorderLayout.SOUTH);

add(clock, BorderLayout.CENTER);

// start clock update timer - updates every second (1000 milliseconds)

new Timer(1000,this).start();

}

publicstaticvoid main(String[] args)

{

myClock app =new myClock();

app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

app.setVisible(true);

}

class ClockPaneextends JPanel

{

publicvoid paintComponent(Graphics g)

{

super.paintComponent(g);

Graphics2D g2 = (Graphics2D) g;

Dimension dim = getSize();

double midx = dim.width / 2;

double midy = dim.height / 2;

Ellipse2D e =new Ellipse2D.Double(midx - 140, midy - 140, 280, 280);

g2.draw(e);

srad = s.doubleValue() / 60 * 2 * Math.PI;

mrad = m.doubleValue() / 60 * 2 * Math.PI;

mrad = mrad + srad / 60;

hrad = h.doubleValue() / 12 * 2 * Math.PI;

hrad = hrad + mrad / 12 + srad / 720;

srad = srad - Math.PI / 2;

mrad = mrad - Math.PI / 2;

hrad = hrad - Math.PI / 2;

Line2D shand =new Line2D.Double(midx, midy, midx + (e.getWidth() / 2 - 10) * Math.cos(srad), midy + (e.getHeight() / 2 - 10) * Math.sin(srad));

Line2D mhand =new Line2D.Double(midx, midy, midx + (e.getWidth() / 2 - 10) * Math.cos(mrad), midy + (e.getHeight() / 2 - 10) * Math.sin(mrad));

Line2D hhand =new Line2D.Double(midx, midy, midx + (e.getWidth() / 2 - 40) * Math.cos(hrad), midy + (e.getHeight() / 2 - 40) * Math.sin(hrad));

g2.setPaint(Color.BLACK);

g2.draw(hhand);

g2.draw(mhand);

g2.setPaint(Color.RED);

g2.draw(shand);

}

}

private JPanel createSouthPane()

{

JPanel p =new JPanel();

p.add(new JLabel("Hours:"));

p.add(hours);

p.add(new JLabel("Mins:"));

p.add(mins);

p.add(new JLabel("Secs:"));

p.add(secs);

return p;

}

// sets the Integer values of h, m, s to what the text fields read

privatevoid setTime()

{

h =new Integer(hours.getText());

m =new Integer(mins.getText());

s =new Integer(secs.getText());

}

// sets the text fields hours, mins, secs to what the Integer values contain

privatevoid setText()

{

hours.setText(String.valueOf(h.intValue()));

mins.setText(String.valueOf(m.intValue()));

secs.setText(String.valueOf(s.intValue()));

}

// action listener for Timer

publicvoid actionPerformed(ActionEvent e)

{

int ss = s.intValue();

int mm = m.intValue();

int hh = h.intValue();

ss++;

mm = mm + ss / 60;

hh = hh + mm / 60;

ss = ss % 60;

mm = mm % 60;

hh = hh % 12;

/////////////////////////////////////////////////////////////////////////////////////////////////

hours.setText(String.valueOf(hh));

mins.setText(String.valueOf(mm));

secs.setText(String.valueOf(ss));

setTime();

//s = new Integer(ss);

//m = new Integer(mm);

//h = new Integer(hh);

//setText();

/////////////////////////////////////////////////////////////////////////////////////////////////

clock.repaint();

}

// document listener for text fields

publicvoid changedUpdate(DocumentEvent e)

{

if (mins.getText().equals("") || hours.getText().equals("") || secs.getText().equals("")) ;

else

{

setTime();

clock.repaint();

}

}

publicvoid removeUpdate(DocumentEvent e)

{

if (mins.getText().equals("") || hours.getText().equals("") || secs.getText().equals("")) ;

else

{

setTime();

clock.repaint();

}

}

publicvoid insertUpdate(DocumentEvent e)

{

if (mins.getText().equals("") || hours.getText().equals("") || secs.getText().equals("")) ;

else

{

setTime();

clock.repaint();

}

}

}

[11621 byte] By [JJCoolBa] at [2007-11-26 22:10:40]
# 1

Is there a particular reason you're keeping counters for the time instead of using a Calendar? To display the current time, create a new Calendar and get it's second, minute, and hour fields. When your ActionEvent fires, get the current time from Calendar, and update the fields. Don't ever read the fields that display the time, those are for display only. You won't need any of the variables to track the h, m, and s.

hunter9000a at 2007-7-10 10:58:23 > top of Java-index,Java Essentials,Java Programming...
# 2

> Is there a particular reason you're keeping counters

> for the time instead of using a Calendar? To display

> the current time, create a new Calendar and get it's

> second, minute, and hour fields. When your

> ActionEvent fires, get the current time from

> Calendar, and update the fields. Don't ever read the

> fields that display the time, those are for display

> only. You won't need any of the variables to track

> the h, m, and s.

I want to be able to read the text fields 'cause I wanted to be able to change the values shown in them and have the clock update. (That's what I started with... entering in an h, m, and s value and having the clock show the correct time) ... then I expanded the program so that it would initialize itself at the beginning to the current time on the computer. Then I enhanced it further that it would change it's values every second.

Anyway - doesn't creating a new Calender every second cause a lot of processing power? Or would it be less than the math operations and reassignment that I am doing here?

Never the less - that doesn't answer why the other implementation doesn't seem to work properly.

JJCoolBa at 2007-7-10 10:58:23 > top of Java-index,Java Essentials,Java Programming...
# 3

> I want to be able to read the text fields 'cause I

> wanted to be able to change the values shown in them

> and have the clock update.

What does reading the text fields have to do with setting the text fields? You can set their values to anything you want. Look up MVC, which stands for model-view-controller. The text fields should only be used to display information from the model, which is a Calendar. The controller is the timer which updates the view with data from the model every second.

> (That's what I started

> with... entering in an h, m, and s value and having

> the clock show the correct time) ... then I expanded

> the program so that it would initialize itself at the

> beginning to the current time on the computer. Then

> I enhanced it further that it would change it's

> values every second.

>

> Anyway - doesn't creating a new Calender every second

> cause a lot of processing power? Or would it be less

> than the math operations and reassignment that I am

> doing here?

Creating a new object once a second isn't a big deal. If you depend on the Timer frequency to keep time, it will eventually drift and be inaccurate. Getting the system time each update will prevent that. You're updating the view based on the model, then updating the model based on the view's values, then updating the model. It's a much cleaner design to separate those parts clearly. Since this is for your own education you ought to start using good design patterns.

> Never the less - that doesn't answer why the other

> implementation doesn't seem to work properly.

Can you be more desciptive than "behaving strangely"? What's happening?

hunter9000a at 2007-7-10 10:58:23 > top of Java-index,Java Essentials,Java Programming...
# 4

> What does reading the text fields have to do with

> setting the text fields? You can set their values to

> anything you want. Look up MVC, which stands for

> model-view-controller. The text fields should only be

> used to display information from the model, which is

> a Calendar. The controller is the timer which updates

> the view with data from the model every second.

I think you need to re-read everything that I've said up to now...

It started out the program WITHOUT a timer, where the user would type in some numbers in the text fields and the time the clock displayed would change to match what they typed in. I wanted to keep this behavior simply because I wanted to. I wasn't attempting to make an actual authentic clock. After I had the program working, then I wanted to enhance it so that it altered itself, as well as the user still being able to alter it. I suppose if I were going to program it again from scratch, I'd probably have the clock have some int's at the class level and use those to make the text fields and such. Anyway this program is not (and never has been) about keeping accurate time.

> Creating a new object once a second isn't a big deal.

> If you depend on the Timer frequency to keep time, it

> will eventually drift and be inaccurate. Getting the

> system time each update will prevent that. You're

> updating the view based on the model, then updating

> the model based on the view's values, then updating

> the model. It's a much cleaner design to separate

> those parts clearly. Since this is for your own

> education you ought to start using good design

> patterns.

I know they drift apart. That's not what I was interested in. And, afaik, "good design patterns" come with experience ... which is something that takes time to build and something that I am gaining. I'm not looking for a critique of my code here - I'm looking for a simple answer of why one approach worked properly and one didn't.

> Can you be more desciptive than "behaving strangely"?

> What's happening?

Did you read my original post?

One approach -> change the int's first, then update the fields.

another approach -> change the fields first, then update the int's.

One worked, one didn't.

JJCoolBa at 2007-7-10 10:58:23 > top of Java-index,Java Essentials,Java Programming...