Why does this work for Canvas but not for JPanel?

J

Jonck

Hi everybody,
I have a small Java app that allows you to move shapes around by clicking and dragging. The dragged shape has a dashed line to distinguish it from the original.

Now here is my question: when you run the code included at the bottom of this post it works just great. However, I would like to use this in a Swing app. So if I change the Mover class from Frame to JFrame and the MoverPanel from Canvas to JPanel I would expect things to work still (except on line 14 where you have to change add(mp) to getContentPane().add(mp)); after all, all method calls that are used here are valid for JPanel as well. But it doesn't work correctly, the dragged shape leaves behind a "trail", cluttering the JPanel very quickly.

Could anyone tell me how to translate this code to use Swing?

Thanks very much, Jonck

<code>

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
public class Mover extends Frame {
public static void main(String[] arg) {
new Mover();
}
Mover() {
super("Mover");
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e)
{ System.exit(0); } } );
MoverPanel mp = new MoverPanel();
add(mp);
pack();
show();
}
class MoverPanel extends Canvas
implements MouseListener, MouseMotionListener {
boolean moving = false;
int moveIndex;
int startx;
int starty;
int endx;
int endy;
Area[] area;
AffineTransform at;
BasicStroke dashes;
MoverPanel() {
addMouseListener(this);
addMouseMotionListener(this);
area = new Area[4];
area[0] = new Area(new Ellipse2D.Float(10,10,40,40));
area[1] = new Area(new Ellipse2D.Float(80,80,20,50));
area[2] = new Area(new Ellipse2D.Float(130,130,60,60));
area[3] = new Area(new Ellipse2D.Float(200,200,50,20));
setSize(300,250);
at = new AffineTransform();
float[] pattern = { 5f,5f };
dashes = new BasicStroke(1f,BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL,1f,pattern,0f);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
for(int i=0; i<area.length; i++)
g2.draw(area);
if(moving) {
g2.setTransform(at);
g2.setStroke(dashes);
g2.draw(area[moveIndex]);
}
}
public void mouseMoved(MouseEvent event) {
moving = false;
}
public void mouseDragged(MouseEvent event) {
if(!moving)
return;
endx = event.getX();
endy = event.getY();
at.setToTranslation(endx - startx,endy - starty);
repaint();
}
public void mousePressed(MouseEvent event) {
moving = false;
startx = event.getX();
starty = event.getY();
for(int i=0; i<area.length; i++) {
if(area.contains(startx,starty)) {
moving = true;
moveIndex = i;
at.setToTranslation(0,0);
break;
}
}
}
public void mouseReleased(MouseEvent event) {
if(moving) {
area[moveIndex].transform(at);
moving = false;
repaint();
}
}
public void mouseEntered(MouseEvent event) { }
public void mouseExited(MouseEvent event) { }
public void mouseClicked(MouseEvent event) { }
}
}

</code>
 
A

Andrew Thompson

I have a small Java app that allows you to move
shapes around by clicking and dragging. The dragged
shape has a dashed line to distinguish it from the original.

Please shorten the lines in your posts, news clients
generally wrap at 72 chars.
..I change the Mover class from Frame to JFrame and the
MoverPanel from Canvas to JPanel ..

Could anyone tell me how to translate this code to use Swing?

You forgot (at least) one thing..
<http://www.physci.org/guifaq.jsp#2.4>

....
public void paint(Graphics g) {

public void paintComponent(Graphics g) {
...

HTH

--
Andrew Thompson
http://www.PhySci.org/codes/ Web & IT Help
http://www.PhySci.org/ Open-source software suite
http://www.1point1C.org/ Science & Technology
http://www.lensescapes.com/ Images that escape the mundane
 
J

Jonck

Please shorten the lines in your posts, news clients
generally wrap at 72 chars.

Ok, sorry about that. I have changed the settings for my newsreader,
could you please confirm whether it's reading correctly for you this
time?
public void paintComponent(Graphics g) {

Thanks, you are of course right, I forgot to change paint to
paintComponent. However, when I do incorporate this change the behavior
is still incorrect, a dragged shape still leaves behind a "trail".

I'm sure I'm still doing something incorrect, as I've read many articles
on people using AffineTransform with Swing, but I can't seem to be able
to spot my mistake(s).

Thanks for your help, Jonck
 
T

Thomas G. Marshall

Jonck coughed up:
Ok, sorry about that. I have changed the settings for my newsreader,
could you please confirm whether it's reading correctly for you this
time?


Thanks, you are of course right, I forgot to change paint to
paintComponent. However, when I do incorporate this change the
behavior is still incorrect, a dragged shape still leaves behind a
"trail".

I'm sure I'm still doing something incorrect, as I've read many
articles on people using AffineTransform with Swing, but I can't seem
to be able to spot my mistake(s).

Thanks for your help, Jonck


Had you changed your code to this?

public void paintComponent(Graphics g)
{
super.paintComponent(g);
...
}
 
A

Andrew Thompson

Ok, sorry about that. I have changed the settings for my newsreader,
could you please confirm whether it's reading correctly for you this
time?

....errrm. Looks fine. But then my news client does not
even tell me how many chars I'm at, and I am not at all
sure of the exact number it should be ( I know it is somewhere
between 50 & 100 - shrugs ;-)
Thanks, you are of course right, I forgot to change paint to
paintComponent. However, when I do incorporate this change the behavior
is still incorrect, a dragged shape still leaves behind a "trail".

You are forgetting to paint the background, it has become
your repsonsibility since you overrode paintComponent()

You can either.

// call the parent's implementation before you paint
super.paintComponent( g );
...

...or

// paint the background yellow yourself..
Color tempColor = g.getColor();
g.setColor(Color.YELLOW);
g.fillRect(0,0,
this.getBounds().width,this.getBounds().height);
g.setColor(tempColor);
...

BTW. I went to compile and test your original example
and was surprised to find a complete AWT version.

Given your first post mentioned both a JFrame and a
JPanel, why did you post the AWT based code?

--
Andrew Thompson
http://www.PhySci.org/codes/ Web & IT Help
http://www.PhySci.org/ Open-source software suite
http://www.1point1C.org/ Science & Technology
http://www.lensescapes.com/ Images that escape the mundane
 
B

Babu Kalakrishnan

Jonck said:
Ok, sorry about that. I have changed the settings for my newsreader,
could you please confirm whether it's reading correctly for you this
time?




Thanks, you are of course right, I forgot to change paint to
paintComponent. However, when I do incorporate this change the behavior
is still incorrect, a dragged shape still leaves behind a "trail".

I'm sure I'm still doing something incorrect, as I've read many articles
on people using AffineTransform with Swing, but I can't seem to be able
to spot my mistake(s).

The update method of Canvas class clears the background and then calls
paint(). The JPanel class does it slightly differently, clearing of the
background is done in its paintComponent method. So as Thomas suggested,
adding a super.paintComponent() call at the start of the paintComponent
method should take care of that.

The setSize() call in the Panel constructor also needs to be changed.
You should use the setPreferredSize to set a preferred size.

BK
 
J

Jonck

You are forgetting to paint the background, it has become
your repsonsibility since you overrode paintComponent()

You can either.

// call the parent's implementation before you paint
super.paintComponent( g );
...

...or

// paint the background yellow yourself..
Color tempColor = g.getColor();
g.setColor(Color.YELLOW);
g.fillRect(0,0,
this.getBounds().width,this.getBounds().height);
g.setColor(tempColor);
...

Thanks very much! This works very well.
BTW. I went to compile and test your original example
and was surprised to find a complete AWT version.

Given your first post mentioned both a JFrame and a
JPanel, why did you post the AWT based code?

The AWT based code is what I had working, so I wanted to show this to
you guys so you would know what the desired behavior was for a Swing
version of the same thing.

Cheers, Jonck
 
J

Jonck

Had you changed your code to this?
public void paintComponent(Graphics g)
{
super.paintComponent(g);
...
} }

I hadn't, as you can probably tell I'm just getting started with the
Graphics side of Java, so I didn't know that the Canvas class took care
of clearing it's own background while JPanel did not.
But this works great, thanks for your help.

Cheers, Jonck
 
J

Jonck

The update method of Canvas class clears the background and then calls
paint(). The JPanel class does it slightly differently, clearing of
the background is done in its paintComponent method. So as Thomas
suggested, adding a super.paintComponent() call at the start of the
paintComponent method should take care of that.

Thanks for explaining the difference between Canvas and JPanel to me,
the super.paintComponent(Graphics) call did indeed take care of it.
The setSize() call in the Panel constructor also needs to be changed.
You should use the setPreferredSize to set a preferred size.

Yes, noticed that this is also a difference between Canvas and JPanel. I
also tried overriding getPreferredSize and this seems to work as well.
Could you tell me, are there situations when one would want to use
setPreferredSize and others when one would want to override
getPreferredSize or are they basically interchangeable?

Thanks, Jonck
 
B

Babu Kalakrishnan

Jonck said:
Yes, noticed that this is also a difference between Canvas and JPanel. I
also tried overriding getPreferredSize and this seems to work as well.
Could you tell me, are there situations when one would want to use
setPreferredSize and others when one would want to override
getPreferredSize or are they basically interchangeable?

The framework only calls getPreferredSize() on the component. So
overriding the getPreferredSize() and returning a specific number is the
one that is guaranteed to return your values. But as implemented,
calling setPreferredSize would achieve exactly the same result. If the
user has set a dimension through setPreferredSize(), a JPanel will
always return that value.

However, you must also remember that setting the preferredSize to
absolute numbers using either method is not a generally recommended
practice. (In fact the once such as yours where the JPanel is used
purely as a surface to paint on is probably the only instance where one
would recommend doing it). In the general case where a JPanel is used as
a container holding other components, you should always let the
framework determine the preferredSize automatically, since it then
allows the application to be portable across different windowing
systems, resolutions and user preference accessibility settings.

BK
 
T

Thomas G. Marshall

Andrew Thompson coughed up:
...errrm. Looks fine. But then my news client does not
even tell me how many chars I'm at, and I am not at all
sure of the exact number it should be ( I know it is somewhere
between 50 & 100 - shrugs ;-)


You are forgetting to paint the background, it has become
your repsonsibility since you overrode paintComponent()

You can either.

// call the parent's implementation before you paint
super.paintComponent( g );
..

..or

// paint the background yellow yourself..
Color tempColor = g.getColor();
g.setColor(Color.YELLOW);
g.fillRect(0,0,
this.getBounds().width,this.getBounds().height);
g.setColor(tempColor);

IMO, it is still better form to allow a super class the privilege of doing
what it thinks it ought to first.



....[rip]...
 
T

Thomas G. Marshall

Thomas G. Marshall coughed up:
Andrew Thompson coughed up:

IMO, it is still better form to allow a super class the privilege of
doing what it thinks it ought to first.

.....in the swing paradigm that is. Just IMO.

...[rip]...
 
T

Thomas G. Marshall

Babu Kalakrishnan coughed up:
The framework only calls getPreferredSize() on the component. So
overriding the getPreferredSize() and returning a specific number is
the one that is guaranteed to return your values. But as implemented,
calling setPreferredSize would achieve exactly the same result. If the
user has set a dimension through setPreferredSize(), a JPanel will
always return that value.

However, you must also remember that setting the preferredSize to
absolute numbers using either method is not a generally recommended
practice. (In fact the once such as yours where the JPanel is used
purely as a surface to paint on is probably the only instance where
one would recommend doing it). In the general case where a JPanel is
used as a container holding other components, you should always let
the framework determine the preferredSize automatically, since it then
allows the application to be portable across different windowing
systems, resolutions and user preference accessibility settings.

Yes.

And /if/ you should ever find yourself really needing to specific about
pixel dimensions, then PULEEZE check out the sizes of any fonts you are
using within it.

I can't tell you how many times I've seen (java and other) GUI apps
completely botch their appearance by drawing fonts that ended up too large
into too small an area. Think "what will happen if the user chose large
fonts" on a PC, for example.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top