my dog learned polymorphism*
The moose likes Swing / AWT / SWT and the fly likes Repainting a JPanel after the componentResized event. Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of OCA/OCP Java SE 7 Programmer I & II Study Guide this week in the OCPJP forum!
JavaRanch » Java Forums » Java » Swing / AWT / SWT
Bookmark "Repainting a JPanel after the componentResized event." Watch "Repainting a JPanel after the componentResized event." New topic
Author

Repainting a JPanel after the componentResized event.

Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 547
    
    3

I swear I Googled this before posting here, but no joy.

My PaintPanel object extends JPanel. I would like it to paint its entire window with a BufferedImage that it holds a reference to, only recomputing the BufferedImage when the panel size chages. Here's my object code:


The problem I am having is that the componentResized method is called after the paint method. This means that, when the panel changes size, the paint method uses the old (that is, appropriate to the previous size) BufferedImage to paint itself. After that, the componentResized method creates a new BufferedImage with dimensions matching that of the resized panel. But, paint doesn't get called again until the next time something requires it; the mere fact of the panel being resized does not cause a call, after the call to the componentResized method, to the paint method.

I could call repaint inside the componentResized method, but that means the paint method would be called twice on every resize, once with the old BufferedImage and then again, as soon as the new image got rendered, with the new one. Not the worse inefficiency in the world, but precisely the kind of thing that always leaves me thinking I'm doing things wrong (where "wrong" means "without knowing there's a better way").

Can anyone help me alter the above, or suggest another approach, that would allow recomputation of the image everytime it is resized, with only one call to the paint method associated with that resizing, with the recomputation taking place before paint is called?

Thanks.
Tony Docherty
Bartender

Joined: Aug 07, 2007
Posts: 2339
    
  50
Not sure if this is the best way but you could override the panel's setBounds(int x, int y, int width, int height) method which is called whenever the panel size changes and in it set img to null (remember to call super.setBounds(..)). Then in the paint method if img is null recompute it.
Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 547
    
    3

Tony Docherty wrote:Not sure if this is the best way but you could override the panel's setBounds(int x, int y, int width, int height) method which is called whenever the panel size changes and in it set img to null (remember to call super.setBounds(..)). Then in the paint method if img is null recompute it.

A couple of quick tests match your suggestion: Component.setBounds does get called before JComponent.paint. If that's always the case when the JPanel changes size, then it looks like a promising approach.

Couple of questions: First, I gather that you recommend having paint test for img == null because you want to defer recomputation of the image to the next time paint is called. Is it ever the case that setBounds is called without a subsequent call to paint? Could that be when the containing JFrame is minimized, perhaps? Second, is there any advantage to having componentResized called after paint, that you can think of? (This is a typical Java/Swing programming experience for me. The methods all make sense to me individually, but the order in which Swing invokes them suggests I am badly misunderstanding what they are for. I just can't think of a reason why you wouldn't want compoenentResized called before the associated call to paint, but that's not how it works. Maybe Campbell Ritchie is right and I've just been programming C for too long... ).
Tony Docherty
Bartender

Joined: Aug 07, 2007
Posts: 2339
    
  50
Is it ever the case that setBounds is called without a subsequent call to paint?

Don't know but possibly. I was playing safe.

Second, is there any advantage to having componentResized called after paint

As this listener notification is performed on the same thread as the resize and repaint then if it was called after the resize and before the repaint it would leave the screen in a dirty state until the listener returned. This wouldn't be a problem as long as the listener completed quickly but Swing has no control over what you do in the listener. Aside from that the notification is the component has resized, I would expect that to mean the resize has been completed which would include any redraws etc.

Maybe Campbell Ritchie is right and I've just been programming C for too long

Don't worry one day you will wake up to a Eureka moment and it will make sense - well maybe not all of it but at least some of it
Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 547
    
    3

Tony Docherty wrote:
Is it ever the case that setBounds is called without a subsequent call to paint?

Don't know but possibly. I was playing safe.


Gotcha. I suppose that, to play it really, really safe, I could just check the dimensions in each call to paint and, if they have changed since last time, recompute the image then. I just hate doing anything on a repeated if-then basis in an event-driven system.


Second, is there any advantage to having componentResized called after paint

As this listener notification is performed on the same thread as the resize and repaint then if it was called after the resize and before the repaint it would leave the screen in a dirty state until the listener returned. This wouldn't be a problem as long as the listener completed quickly but Swing has no control over what you do in the listener. Aside from that the notification is the component has resized, I would expect that to mean the resize has been completed which would include any redraws etc.

Are you saying the listener does not run on the Event Dispatching Thread? How does one know that?

Maybe Campbell Ritchie is right and I've just been programming C for too long

Don't worry one day you will wake up to a Eureka moment and it will make sense - well maybe not all of it but at least some of it

Heh. At my age, "one day" had better come pretty soon.
Tony Docherty
Bartender

Joined: Aug 07, 2007
Posts: 2339
    
  50
Are you saying the listener does not run on the Event Dispatching Thread? How does one know that?

No I'm saying it does run on the EDT hence if it worked the way you hoped the redraw would stall waiting for the listener to finish.
Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 547
    
    3

Tony Docherty wrote:
Are you saying the listener does not run on the Event Dispatching Thread? How does one know that?

No I'm saying it does run on the EDT hence if it worked the way you hoped the redraw would stall waiting for the listener to finish.

Oh, I get you: the resize would actually make something change on the screen, but it wouldn't necessarily look very good until the subsequent paint method cleaned things up. If the listener took a long time to return, the changed screen would just sit there until it did.

Thanks!
Michael Dunn
Ranch Hand

Joined: Jun 09, 2003
Posts: 4632
probably reading this wrong, but perhaps using the drawImage() that takes width/height might be simpler.
(and using paintComponent(), not paint())
Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 547
    
    3

That would, I believe, scale the image. What I want to do is recompute it at the new resolution, whenever the panel's dimensions change, but to keep the computed image in a BufferedImage so I don't end up recomputing it just to repaint portions of the panel that are temporarily invisible due to overlapping windows, being moved off screen, or being minimized. I use the paint method instead of paintComponent because the image is all I want to show (there are no controls, child objects, or anything else in the JPanel, nor is any part of the JPanel left unpainted by the image). The overhead avoided by subclassing paint directly is minor, but some of the stuff I do has high frame-rates, so every little bit counts.
Darryl Burke
Bartender

Joined: May 03, 2008
Posts: 4642
    
    5

Stevens Miller wrote:I use the paint method instead of paintComponent because the image is all I want to show (there are no controls, child objects, or anything else in the JPanel, nor is any part of the JPanel left unpainted by the image).

In that case IMO you should be using an extended JComponent -- not JPanel.


luck, db
There are no new questions, but there may be new answers.
Paul Clapham
Bartender

Joined: Oct 14, 2005
Posts: 18714
    
    8

Pardon me if I missed something earlier in the thread, but couldn't you just, in your paint method, compare the component's current size to its size the last time that the paint method was called? And proceed accordingly? That would seem to me to be a bit simpler than fussing with the component-resized event.
Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 547
    
    3

Darryl Burke wrote:
Stevens Miller wrote:I use the paint method instead of paintComponent because the image is all I want to show...

In that case IMO you should be using an extended JComponent -- not JPanel.

I think you've got a good suggestion there. I use NetBeans. When I add a JPanel, its code-generator creates a variable for it in my JFrame:

This creates a type-clash if I customize the instantiation code to create a new object subclassed from JComponent (since that won't be a JPanel).

That's ultimately kind of frustrating because none of the other IDE-generated code makes use of any of the methods that are specific to a JPanel, but the deeper-down UI code may do so; I truly don't know. In any case, as is often the situation with IDEs, I am kind of stuck with a design decision made by someone above my pay-grade, as it were.
Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 547
    
    3

Paul Clapham wrote:Pardon me if I missed something earlier in the thread, but couldn't you just, in your paint method, compare the component's current size to its size the last time that the paint method was called? And proceed accordingly? That would seem to me to be a bit simpler than fussing with the component-resized event.

Yes, that works, though you have to keep a "memory" object of the prior dimensions (which you could initialize in your constructor). That's actually the approach I started with but, again, it requires that you "poll" that memory on every call to paint, which, in an event-driven system (one that actually does provide an event for when the object changes size) is rather anathema to my philosophies.

Based on Tom's observations, one could avoid all such polling (either of the size or Image object), by recomputing the entire image in setBounds, and just blithely repainting that image each time paint is called. Since the idea is to recompute only when necessary (because recomputation might take a long time), it would make sense, perhaps, to do the recomputation on its own thread so that, if a second setBounds call is made before the recomputation is complete, the recomputation could be interrupted and started anew. I may try that, as it would be a needlessly complex and difficult potentially elegant solution, and we all like those, now don't we?
Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 547
    
    3

Well, we were cooped up here during some snow and you can only play so much Uno, so I went ahead and tried the interruptible-thread approach. Seems to work rather handily.

I don't expect people to do my code-reviews for me, but here's the implementation if anyone else might find it useful (and, of course, if anyone sees problems or potential improvements, I'd be glad to know about those).

First, I created an interface that the code responsible for creating the image must implement. The second method can have an empty body, and everything will still work; you just don't get the benefit of being able to interrupt a lengthy redraw to start a new one in that case.



Here's the subclass of JPanel that detects when to create a new image, and manages repainting the latest image it has available when it needs to:



Here's the object that runs on its own thread to draw the image, notifying the PaintPanel object when a new image has been computed:



Finally, for testing, I created this object that draws a shaded sphere into whatever BufferedImage it is given. This is deliberately a slow enough process that it is possible to change the dimensions of the PaintPanel twice in quick enough succession that you can see the partially drawn image appear (when the second resize interrupts the rendering that was started by the first resize) then be overwritten later when the second image is fully rendered.



This has been a very useful exercise. Thanks to those who offered comments.
Tony Docherty
Bartender

Joined: Aug 07, 2007
Posts: 2339
    
  50
Thanks for posting your solution.

There are numerous ways of solving this sort of problem and different people haven't different preferred solutions so I'm not necessarily criticizing your approach but rather offering alternatives. If the number of times setBounds() is likely to be called is extremely low then creating and killing threads is an acceptable way of doing things (although I would implement Runnable rather than extending Thread). However, if there are likely to be many calls to setBounds() I would recommend you take a different approach such as having a background thread that waits on a blocking queue for render tasks to be added and then runs them. Currently executing tasks could be terminated early as per your current scenario but it means you don't need to kill the thread each time. Alternatively, look at the ExecutorService.

Having gone to the trouble of implementing background rendering, have you considered adding a feature to do paints every n milliseconds so the user sees the image build up rather than having to wait for the rendering to complete before seeing anything.
Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 547
    
    3

Tony Docherty wrote:Thanks for posting your solution.

There are numerous ways of solving this sort of problem and different people haven't different preferred solutions so I'm not necessarily criticizing your approach but rather offering alternatives.
And that is very much appreciated.

If the number of times setBounds() is likely to be called is extremely low then creating and killing threads is an acceptable way of doing things (although I would implement Runnable rather than extending Thread).
If I do that, where do I call Thread.start? (See line 21 of the RenderThread class.) I could create a Thread object where I currently create my RenderThread object (line 45 of PaintPanel), but I am guessing that means the PaintPanel object would have to call Thread.start itself (between lines 47/48). I felt myself drawn to the idea of of a subclass of Thread that "starts itself" sorta/kinda. Is "implements Runnable" generally preferred over "extends Thread"? I love this multi-thread stuff, but I admit I have a lot to learn about best practices.

However, if there are likely to be many calls to setBounds() I would recommend you take a different approach such as having a background thread that waits on a blocking queue for render tasks to be added and then runs them. Currently executing tasks could be terminated early as per your current scenario but it means you don't need to kill the thread each time.
That is a fine idea. I did some experiments recently with the LinkedTransferQueue class. Do you have something like that in mind with this approach? One aspect of Java that I often find intimidating is that there is always more than one way to do everything. Not that computer programming isn't that way all the time. It is, of course. But the Java libraries seem to have lots of classes that are similar, and I am frequently seized by insecurities over my choices. If I use a queued approach, how best do I choose the queue class to use?

Alternatively, look at the ExecutorService.
I just finished Bloch's "Effective Java," where he gives the same advice. (Pretty much mandates it, actually, calling for Thread objects to be maintained only in existing code, not in anything new.) I will have a look at those, but am finding Threads quite daunting enough at the moment. Definitely on the list, though.

Having gone to the trouble of implementing background rendering, have you considered adding a feature to do paints every n milliseconds so the user sees the image build up rather than having to wait for the rendering to complete before seeing anything.
Oh, that is a sweet idea! I'll do it. For those really intensive jobs (ray-tracing, maybe) it will remind me of my days at the NYIT Computer Graphics Lab. For historical reasons that are too painful to recount, we rendered everything to our oh-so-expensive frame buffers, which meant you could watch each and every pixel as it was colored in (instead of having one of our array of several VAX 11/780 computers render the image to memory, but, like I said, history rules...).

These are great suggestions, Tony. This is a very modest little project, but I expect to use it as a tool in other things, once it is fully tweaked. Do you think others would find it useful? Is there a repository on coderanch.com for such things?

Happy New Year!
Tony Docherty
Bartender

Joined: Aug 07, 2007
Posts: 2339
    
  50
Is "implements Runnable" generally preferred over "extends Thread"?

Yes. You extend a class when your class is-a-kind-of the class you are extending. Your RenderThread class is not a kind of Thread but rather it is code that is to be run by a thread in the background. Therefore, your class should implement Runnable and then you pass an instance of the class to a Thread to run. This can be via the queue approach I outlined, via an Executor type approach or by passing the Runnable instance to a Thread constructor (which would be equivalent to your current approach) etc.

That is a fine idea. I did some experiments recently with the LinkedTransferQueue class. Do you have something like that in mind with this approach? One aspect of Java that I often find intimidating is that there is always more than one way to do everything. Not that computer programming isn't that way all the time. It is, of course. But the Java libraries seem to have lots of classes that are similar, and I am frequently seized by insecurities over my choices. If I use a queued approach, how best do I choose the queue class to use?

I've not really thought about it in depth but my first thoughts are to use an ArrayBlockingQueue with a capacity of 1. The reason to limit the capacity to one is if there is already a queued render task waiting to be handled there is no point in adding another task (you may need to replace the existing task if you embed the panel size in it but you don't want to enqueue more than one task). The add task method could also be responsible for terminating any currently running task.
I would also write this as an inner class of the panel class rather than an external class.

I just finished Bloch's "Effective Java,"

Great book.

Do you think others would find it useful?

Very possibly. But if it is something people are crying out for someone will probably have already written it - not that that's a reason not to carry on experimenting yourself. Have you done a search to see if there is anything currently available?

Is there a repository on coderanch.com for such things?

Not that I'm aware of. But I guess you could post the finished code here and anyone searching for this type of behaviour should find this thread.
Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 547
    
    3

I took a whack at your suggestions for improvements, Tony.

To get rid of Thread objects, I used ExecutorService objects instead.

To see fixed-interval updates, I added another executor and a new "RefreshTask" that force a repaint periodically unless the background renderer is done.

I made the RenderTask and the RefreshTask classes inner classes of the PaintPanel class.


At lines 74 and 85, I do still use the static Thread.sleep method, for the in-progress repaints. I wanted to use the ScheduledExecutorService, but I couldn't sort out a way to stop or pause a scheduled task without shutting down the executor itself; that seemed anathema to the goal here, since I expect creating executors has at least as much overhead as creating Thread objects does.

I made a couple of changes to the interface the rendering method must support:


The "big one" is at line 41, where the rendering object must accept a Future object as a parameter to its interrupt method. The interrupt method still can have an empty body, but the Future object allows it to synchronize the interruption with setting and clearing the boolean flag that signals the rendering method that it is to interrupt itself. By waiting for the Future object to unblock, the interrupt method is sure to return only after the background renderer has exited (this prevents races that could make the interrupt flag unreliable).

Here's my renderer, with a 1ms Timer.sleep call in it to slow things down enough to test the every-100ms refresh I put in:



The last few lines (67-80) show how the interrupt method sets the interrupt flag, then waits for the background renderer to complete before clearing the interrupt flag and returning to the PaintPanel's setBounds method (where the next call to start the background renderer will follow immediately).

Going back to the PaintPanel class, at lines 30-37, I played a bit of a trick. In PaintPanel's constructor, I made as lightweight a call to ExecutorService's submit method as I could, with an anonymous Runnable inner class that has an empty run method. The idea was to have a valid reference to a Future object in futureRenderTask, one that was done already, so that on the first call to the renderer's interrupt method it would not be null (otherwise, either the caller or the interrupt method would have to check for null on every call, which I found kind of distasteful). I'd be curious to know what you think of such a practice.

Thanks again for the excellent guidance on this. Even if the above has bugs or other problems, I'm learning a lot here.

Cheers.
Tony Docherty
Bartender

Joined: Aug 07, 2007
Posts: 2339
    
  50
Going back to the PaintPanel class, at lines 30-37, I played a bit of a trick. In PaintPanel's constructor, I made as lightweight a call to ExecutorService's submit method as I could, with an anonymous Runnable inner class that has an empty run method. The idea was to have a valid reference to a Future object in futureRenderTask, one that was done already, so that on the first call to the renderer's interrupt method it would not be null (otherwise, either the caller or the interrupt method would have to check for null on every call, which I found kind of distasteful). I'd be curious to know what you think of such a practice.

I think using a dummy object is a reasonable approach especially as the Future object could be dereferenced in a number of places

BTW Rather than having an interrupt method which the renderer must implement why not use the Future object's cancel(true) method and have the renderer check the current thread's isInterrupted() method and quit if it returns true. That way you remove one method from the interface and simplify the whole process of interrupting the task. Note: If you implement this then depending what you do in the render method you may also need to catch and handle InterruptedEception.
Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 547
    
    3

Tony Docherty wrote:BTW Rather than having an interrupt method which the renderer must implement why not use the Future object's cancel(true) method and have the renderer check the current thread's isInterrupted() method and quit if it returns true. That way you remove one method from the interface and simplify the whole process of interrupting the task. Note: If you implement this then depending what you do in the render method you may also need to catch and handle InterruptedEception.

I kind of agonized over that for a few minutes. The doc on Future isn't explicit enough for me to have concluded that Future.cancel can be regarded by my renderer the same as a Thread.interrupt, though I would wager that it can. I also noticed what you have commented on as well: one must handle additional exceptions if one is blocked or in another condition that would generate exceptions upon thread-interruption. My thinking was that I ought to avoid writing the renderer in a way that reflected any "knowledge" on its part that it was being run in a worker thread. Not sure if that's a meaningful goal, but it put me off of handling interruption exceptions.

What I've done instead is borrow the model used by the ActionListener interface and the AbstractAction class: I created an AbstractRenderable class that provides an implementation of Renderable.interrupt, and also its own AbstractRenderable.isInterrupted method, leaving the user only responsible for implementing the Renderable.render method. If that never checks for interruption, it all still runs; you just don't get the benefit of being able to interrupt an incomplete rendering if a new one with new dimensions is needed sooner. But, if the programmer simply adds

at some appropriate point in their render routine, the whole thing works as you suggested. (The user must, of course, declare their renderer in a class that extends AbstractRenderable, which means it cannot be extended from some other class; one can still implement the entire Renderable interface in that case, by adding one's own interrupt method.)

I've fleshed out a proper set of javadoc for it all, and put it into a package with a legitimate name (com.dumblux.paintpanel). After more testing and maybe some tweaking, I'll put it up on my site. (Feel free to offer it via your site if you think it's useful; it might make a good companion to your ImagePanel.)
Tony Docherty
Bartender

Joined: Aug 07, 2007
Posts: 2339
    
  50
I've fleshed out a proper set of javadoc for it all, and put it into a package with a legitimate name (com.dumblux.paintpanel). After more testing and maybe some tweaking, I'll put it up on my site.

Please post the URL here so future viewers of this thread can access the code and also remember to PM me when you do.
Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 547
    
    3

Done! PaintPanel can be had as a downloadable zip, with jar file, source, javadoc, and a sample program.

Please have a look and let me know what you think. I've never "published" a Java library before.

Thanks for all your help!
 
 
subject: Repainting a JPanel after the componentResized event.