This week's giveaway is in the EJB and other Java EE Technologies forum.
We're giving away four copies of EJB 3 in Action and have Debu Panda, Reza Rahman, Ryan Cuprak, and Michael Remijan on-line!
See this thread for details.
The moose likes Swing / AWT / SWT and the fly likes Only getting a PropertyChangeEvent at the end of SwingWorker Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of EJB 3 in Action this week in the EJB and other Java EE Technologies forum!
JavaRanch » Java Forums » Java » Swing / AWT / SWT
Bookmark "Only getting a PropertyChangeEvent at the end of SwingWorker" Watch "Only getting a PropertyChangeEvent at the end of SwingWorker" New topic
Author

Only getting a PropertyChangeEvent at the end of SwingWorker

Bd Howard
Greenhorn
Ranch Hand

Joined: Mar 30, 2012
Posts: 80
Hello,

I am revisiting an assignment from my first Java class, where we had to do a simple GUI for painting the Mandelbrot Set. I'd like to add a JProgressBar where the value is determined by the loop in doInBackground(). My BufferedImage is 500 pixels wide, so I use publish(x/5) and setProgress(x/5) to set the value in the JProgressBar.

The problem is I cannot get the bar to update during the execution of the loop. Neither the PropertyChangeListener in Paint() in MB.java, nor the process() override in BuildImage.java seems to have an effect anywhere except at the end of the loop, at which point the bar shows 99%.

I've included below the two relevant classes. The class ComplexNumber is not included since it has nothing to do with the GUI.

I hope I am just overlooking something, but if I need to do a structural overhaul, I'll just have to suck it up and make the changes. :-)

Cheers
BD


MB.java






BuildImage.java


I've got just enough Java knowledge to royally screw everything up. :-)
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19541
    
  16

1) You should not mix AWT and Swing. Stop using Canvas, and use JPanel instead.

2) When you want to do custom painting with JPanel, you should override paintComponent(Graphics) instead. Make sure the first statement is super.paintComponent(g);.

3) You are now creating a new dialog with a new progress bar every time the canvas is repainted. That could be several times a second. Don't do that.

You should redesign your application a bit. The SwingWorker should be called either when the application starts, or upon a button press or something similar. It should most definitely not be called from any painting method.
To get the Image from the SwingWorker to the MB instance, class MB should get an Image field. The paintComponent method will then simply paint this image unless it's null (not set yet). The SwingWorker will set this Image field when it's done().


SCJP 1.4 - SCJP 6 - SCWCD 5 - OCEEJBD 6
How To Ask Questions How To Answer Questions
Bd Howard
Greenhorn
Ranch Hand

Joined: Mar 30, 2012
Posts: 80
Thank you for your time, Rob. I do appreciate the number of tips you gave. When I asked my Professor for a critique (he does not grade them, his grad students do) he gave a few general tips, but nothing like yours. I hesitated posting that much code thinking people would just skip over this thread shaking their head. :-)

I never thought about the fact that I was mixing AWT and Swing. I'll stop doing that.

As far as the new dialog with every repaint, I noticed that and wondered myself. I found that example on the Net, but I guess I picked the wrong one to follow. :-(

When you say my MB instance should have an Image field, do you mean a BufferedImage since that is what I make, or do I convert the BufferedImage to an Image? Can I just do a cast here? I chose a BufferedImage because I did not like how the image was drawn, line by line. I prefer to wait with a black screen and have it appear when finished. I guess with SwingWorker setting the Image inside done(), I don't have a need to use a BufferedImage. Isn't a BufferedImage more efficient though? Or am I mixing apples and oranges here?

I may be misunderstanding something, but what is the difference between publish() and setProgress()? Can I pick one of those to use, or do I need to use both? If I use publish(), is my code for the JProgressBar/Dialog inside the process() override? Or does that lead to another multiple creation of dialog problem? The more I read in the JavaDoc, the more I think I need only setProgress() for this particular program since I am using the loop counter as my "percentage complete".

Again, thanks for the help. I've got two weeks before the Summer semester starts, so I plan to do a lot of Java in between the other projects I need to complete. I'll be posting more noob questions soon. :-)

BD
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19541
    
  16

Bd Howard wrote:When you say my MB instance should have an Image field, do you mean a BufferedImage since that is what I make, or do I convert the BufferedImage to an Image? Can I just do a cast here? I chose a BufferedImage because I did not like how the image was drawn, line by line.

BufferedImage extends Image

I guess with SwingWorker setting the Image inside done(), I don't have a need to use a BufferedImage.

You can still use BufferedImage for creating the image. Afterwards you can set just that created BufferedImage as the Image of the MB instance.

Isn't a BufferedImage more efficient though? Or am I mixing apples and oranges here?

I wouldn't bother worrying about performance until you get performance problems. If BufferedImage works then just use that.

I may be misunderstanding something, but what is the difference between publish() and setProgress()? Can I pick one of those to use, or do I need to use both? If I use publish(), is my code for the JProgressBar/Dialog inside the process() override? Or does that lead to another multiple creation of dialog problem? The more I read in the JavaDoc, the more I think I need only setProgress() for this particular program since I am using the loop counter as my "percentage complete".

With publish / process you can work with any type of data. With setProgress you're limited to the progress. In your case that's just what you need.
Bd Howard
Greenhorn
Ranch Hand

Joined: Mar 30, 2012
Posts: 80
BufferedImage extends Image


Who says I can't type faster than I can think? Either I am one hell of a typist, or my thought process is kinda slow.

Thanks. I'm making the changes now.

BD
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19541
    
  16

You're welcome
Bd Howard
Greenhorn
Ranch Hand

Joined: Mar 30, 2012
Posts: 80
Back again,

The paintComponent(Graphics g) override needs that Graphics g. When I construct my BufferedImage in another class, do I also need the Graphics object I used to draw that BufferedImage to be sent back to the paintComponent()? Or will the Graphics g inside the paintComponent work with that static BufferedImage that is set from the done() in my SwingWorker thread of that other class? I do the cast to Graphics2D.

I am reusing code I know worked to draw my BufferedImage via a SwingWorker. When my paintComponent() in the GUI class draws the BufferedImage, all I get is a black background. Therefore, I am wondering if I am passing all that I need to pass, or if I am losing something in the translation. I can draw test shapes fine from within paintComponent(), but the static image does not paint aside from the background.

So in a nutshell, what I am doing is drawing an initial image with the default values in my class that extends SwingWorker. The done() sets the static bufferedImage inside my GUI class to the result of the Worker. The GUI build finishes and displays all of the components, except I am just getting a black box for my static image.

My question is how would you pass that BufferedImage around? Do I need to pass more than just the image? Is the Graphics instance that does the drawing, needed elsewhere, or is it not needed anymore once it completes the draw? Can another instance of Graphics work with that BufferedImage?

I want to put your answers to these conceptual questions to work before I give up and paste code.
Darryl Burke
Bartender

Joined: May 03, 2008
Posts: 4523
    
    5

After the BufferedImage has had its content drawn, call repaint()


luck, db
There are no new questions, but there may be new answers.
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19541
    
  16

Bd Howard wrote:The paintComponent(Graphics g) override needs that Graphics g. When I construct my BufferedImage in another class, do I also need the Graphics object I used to draw that BufferedImage to be sent back to the paintComponent()? Or will the Graphics g inside the paintComponent work with that static BufferedImage that is set from the done() in my SwingWorker thread of that other class? I do the cast to Graphics2D.

There should be two Graphics objects - one for creating the BufferedImage content, and one for drawing the BufferedImage on screen.
The first you can get by calling getGraphics() or preferably createGraphics() on the BufferedImage. If you use createGraphics() you don't need to do any casting to Graphics2D. You should call dispose() on the Graphics object when you're done.
The second one is the one passed to paintComponent.
Bd Howard
Greenhorn
Ranch Hand

Joined: Mar 30, 2012
Posts: 80
When I post code, should it be the whole class (even if it is long) or just the relevant methods?

The code I posted above is mostly stuff that does not have bearing on my original questions.

BD
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19541
    
  16

It should be an SSCCE (<= link).
Bd Howard
Greenhorn
Ranch Hand

Joined: Mar 30, 2012
Posts: 80
Hi,

I believe what I have here is an SSCCE.

As pointed out on the SSCCE page, the mere act of me simplifying my code down to the bare minimum has led me much closer to the resolution. I can now get my image to paint, but I have questions. I know, I know, there is always a catch.

Why does the print statement inside paintComponent() print out as if it is in an infinite loop? Shouldn't paintComponent() only be called when a change is detected?

Why is repaint() needed in paintComponent()? If I remove that, no image is displayed. Come to think of it, that repaint() explains the looping of my print statement. So that means the repaint() should not be there. If that is the case, how can I get my image to print without the reprint()?

There are three classes below. ComplexNumber has nothing to do with this problem, but it is required to generate the image. The code will paint the image, but with the infinite loop in full swing.

Thank you for your time.

BD

MB.java




BuildImage.java




ComplexNumber.java
Bd Howard
Greenhorn
Ranch Hand

Joined: Mar 30, 2012
Posts: 80
Here is something curious:

If I add this into my paintComponent(), the image will display if repaint() is called somewhere between 650 and 750 times or greater. The cut-off value changes with each run of the program. If I reduce "count" below that, I get no image.




Anyone know why this is the case?

BD
Bd Howard
Greenhorn
Ranch Hand

Joined: Mar 30, 2012
Posts: 80
Think I have it figured out. :-)
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19541
    
  16

When you call repaint(), this will lead to a new call to paintComponent. This will again call repaint(), and your program is busy painting itself forever.
Bd Howard
Greenhorn
Ranch Hand

Joined: Mar 30, 2012
Posts: 80
I gave up on my want to have a whole separate class for the SwingWorker. I just set up an inner class that extends SW. Ended up being much simpler. I tend to make things too complicated in my quest to organize.

Everything paints correctly, AND with no need for a repaint() inside paintComponent().

Thanks for your help

BD
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Only getting a PropertyChangeEvent at the end of SwingWorker
 
Similar Threads
It prints me pointer repeatedly
Not getting the variable values.
adding graphic objects to JPanel
street view & satilite view is not working
Drawing text inside a Graphics2D shape