wood burning stoves 2.0*
The moose likes Swing / AWT / SWT and the fly likes Rotating a JWindow with multiple JPanels Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Spring in Action this week in the Spring forum!
JavaRanch » Java Forums » Java » Swing / AWT / SWT
Bookmark "Rotating a JWindow with multiple JPanels" Watch "Rotating a JWindow with multiple JPanels" New topic
Author

Rotating a JWindow with multiple JPanels

Joe McCarthy
Greenhorn

Joined: Sep 28, 2005
Posts: 14
I have an application in which I want to display three different types of information in a window: some profile information about a person in one, a queue of thumbnail images in another, and some kind of banner image in a third. This information will be updated at separate intervals with separate methods, and so I thought it would be best to use a separate panel for each one.

The application will likely run in portrait-mode orientation, on a physical display that is tilted 90 (or 270) degrees, and so I have experimented with using the rotate() and translate() methods in Graphics2D. When I modified the SimpleGui2B code from Head First Java (the one that loaded the image of the cat), with a single JPanel on a single JFrame, these methods worked fine (rotating 0, 90 or 270 degrees). When I changed the JFrame to a JWindow, they still worked fine. When I added two more panels, though, with separate classes and paintComponent methods, they didn't work right anymore -- only the "top" panel was painted correctly (except when there was no rotation or translation).

I've found a few code examples for using AffineTransform, but all of them use a single image in a single container. I suspect I'm missing something rather basic about using AffineTransform ... or perhaps JPanels and JWindows. Any suggestions are welcome. Thanks!

[BTW, image files for the code below can be found at here.
I invoke the application with three parameters specifying the width, height and rotation of the display, e.g., "432 768 0", "432 768 90" or "432 768 270"]

H Lander
Greenhorn

Joined: Oct 23, 2005
Posts: 15
Here is something that gets you most of the way there. Basically, I've replaced the window's content pane with one that does the necessary rotation. This means that the individual panels do not need to be rotated.

The one remaining issue I noticed is that when part of the window is obscured, and then reshown it does not paint right. I suspect that some other method rather than the content pane paint() method I wrote is being called to repaint only portions of the window. When the entire window is repainted it works fine.

Good Luck,
Henry

Joe McCarthy
Greenhorn

Joined: Sep 28, 2005
Posts: 14
Hi Henry,

Thanks for the help!

I like the idea of modifying the container rather than modifying the individual panels. I'd earlier been trying to use rotate and translate on the graphics for the frame (window) itself, but it hadn't occurred to me to use the ContentPane of the window (doh!).

I see what you mean about the repainting issue. When I run the application with a 90 or 270 degree rotation, and move my DOS command prompt window across the application window in a westerly direction, it "uncovers" the profileCanvas painted in an untranslated/unrotated position, centered in the X-axis (although, oddly, if I move the DOS window across the very top few pixels of the application window, it does not have this effect). Moving in southerly direction appears to "restore" the rotated version. I re-inserted my System.out.println() calls, and the "uncovering" problem appears to occur whenever paintComponent() of any of three subpanels is called without a corresponding (and immediately preceding) call to the paintComponent of RotatedCanvas (which is where fillRect() is called, along with rotate() and translate()). I tried declaring theCP at the top level, and invoking theCP.paintComponent() from each of the other methods, but this resulted in even odder behavior. I don't know if this description helps you (or anyone else) with any further diagnosis, but additional help is most welcome.

BTW, I noticed the additional "translate" call you inserted, for modifying the X coordinates ; testing the application with different window sizes showed that the correct translation is dependent upon the width and height of the window, so I declared a new variable to represent this:



Thanks again for your help!
H Lander
Greenhorn

Joined: Oct 23, 2005
Posts: 15
Joe:

Reading your post made me realize that I did someting I had not inteded. I had intended to overload the paint() method not the paintComponent() method in the RotatedContentPane. (Careless cut and paste on my part.)

When I made the change (see below) it fixed the repaint problem. (Make sure to call super.paint(g) from this method not super.paintComponent(g))

Regards,
Henry


[ October 24, 2005: Message edited by: H Lander ]
Joe McCarthy
Greenhorn

Joined: Sep 28, 2005
Posts: 14
Henry: thanks for persevering in helping me out! Using paint() vs. paintComponent() solves the problem ... though I am now curious about what the difference is between these methods (I'll browse around for some info).

One last thing I notice is that using the new contextPane seems to have shifted everything down a few pixels from my version (which added panels directly to the JWindow). I welcome any input from you, or anyone else, who can help me understand why this is happening, and/or how I can eliminate these extra pixels. Thanks!
H Lander
Greenhorn

Joined: Oct 23, 2005
Posts: 15
Joe:

To eliminate the extra pixels make the change below to the RotatedContentPane constructor. By default, a JPanel uses a FlowLayout which has a 5 pixel inset on all four sides. The change specifies a Flow Layout without any inset.

To understand the difference between paint() and paintComponent() read the Javadoc for the paint() method in the javax.swing.JComponent class. Basically, the paint() method does everything needed to paint the component and all of its contents. One of the things it does is to call the paintComponent() method.

Regards,
Henry


Ken Blair
Ranch Hand

Joined: Jul 15, 2003
Posts: 1078
Originally posted by Joe McCarthy:
Henry: thanks for persevering in helping me out! Using paint() vs. paintComponent() solves the problem ... though I am now curious about what the difference is between these methods (I'll browse around for some info).

One last thing I notice is that using the new contextPane seems to have shifted everything down a few pixels from my version (which added panels directly to the JWindow). I welcome any input from you, or anyone else, who can help me understand why this is happening, and/or how I can eliminate these extra pixels. Thanks!


paintComponent() only paints the area that needs to be painted, as specified by the clipping. Without looking at your code I'd guess it wasn't getting called in some instances because the component it was on didn't have any visible area to paint.
Joe McCarthy
Greenhorn

Joined: Sep 28, 2005
Posts: 14
Henry: thanks for the fix and explanation!
Ken: thanks for the additional clarification!
Craig Wood
Ranch Hand

Joined: Jan 14, 2004
Posts: 1535
Joe McCarthy
Greenhorn

Joined: Sep 28, 2005
Posts: 14
Craig: thanks for the additional solution!

I like the way that you have simplified the rotation & translation into a single call to rotate() with the x and y offset parameters. I also like the use of different colors, to help me see where the panel boundaries are. I will go back and plug this into the earlier SimpleGui2B code and play around with the three parameter version of rotate() to better understand how this works.

On the other hand, I like the way that the code modification that Henry provided earlier handles all the manipulation in the enclosing contentPane, so that the individual panel classes need not take notice of whether any rotation is necessary. Perhaps I can combine the best of both ... and then combine the "final" version with the image scaling code you helped me with earlier.

Thanks again!
Joe McCarthy
Greenhorn

Joined: Sep 28, 2005
Posts: 14
I've spent most of today trying to better understand the two solutions provided by Henry and Craig. I tried a few different ways to combine them, but all failed. So, I decided to move on to the next phase of development -- enabling a variable number of thumbnail images to be shown in the QueuePanel. To do this, I decided to use a GridLayout and add a number of ImagePanels (one per grid element), and then use drawImage -- if there is an image for that panel -- or fillRect -- if there is no image for that panel. If there are fewer images than panels, the blank rectangles should go on the right portion of the screen.

In the version Henry provided, my modifications work fine when the JWindow is upright (0 degree rotation), but I couldn't make this work when the window is rotated 90 or 270 degrees. In the version that Craig provided, I could get it to work in all three rotation angles, but when it is rotated 270 degrees, the images are updated right to left (well, top to bottom). I tried using

but this didn't have any effect.

I'm posting the full version of the code below. Craig & Henry: if you're still paying attention to this thread, the differences are
  • using a Thread in go()
  • a new Runnable class, QueueMonitor
  • revisions to the QueuePanel class (GridLayout of ImagePanels)
  • a new ImagePanel class


  • Before posting the full code below, I'll note that the only differences between the modifications I made in Craig's version and those I made in Henry's version was the paintComponent() method in QueuePanel, which is not defined in Craig's version, and the paintComponent() in ImagePanel. In Henry's version, I use the following in QueuePanel
    and the following in ImagePanel

    Without further ado, the latest version is posted below. If anyone can help me figure out how to get the images in the QueuePanel to display left-to-right when the JWindow is rotated 270 degrees, I'd welcome any input. Other suggestions on how to better implement this are welcome, too. Thanks!



    [Update: I had posted a subsequent message, to share the code I used to graphically explore the Graphics2D rotate() method; I deleted it for fear that this message might not get seen (in such a long thread), and will repost that message later. I also inserted the modified paintComponent() method for ImagePanel from the version of Henry's code I modified.]

    [Update 2: For now, I added a kludge so that the ImagePanels are added in reverse order to the QueuePanel when the rotation angle is 270 ... I updated the code above (with "kludge warning" comment), but would still welcome a more elegant solution and/or explanation as to why this would happen. Thanks!]
    [ October 26, 2005: Message edited by: Joe McCarthy ]
    Craig Wood
    Ranch Hand

    Joined: Jan 14, 2004
    Posts: 1535
    added a kludge so that the ImagePanels are added in reverse order to the QueuePanel
    when the rotation angle is 270 ... explanation as to why this would happen

    In the QueuePanel constructor you set the layout to GridLayout: 1 row for rotationRadians
    = 0 and numImages rows when rotationRadians = 90/270. GridLayout will add the ImagePanels
    from top to bottom in the vertical (grid) layouts. At rotationRadians = 90 this is what
    you want. At rotatinRadians = 270 the first ImagePanel is added at the top of the gui, not
    what you want. To see this try

    So you did the right thing in reversing the indices in the layout process.

    Other suggestions:
    1 — Random.nextInt may give a better performance for long–term use
    2 — from 'paintComponent' in ImagePanel, to center the image in the component
    Joe McCarthy
    Greenhorn

    Joined: Sep 28, 2005
    Posts: 14
    I'm glad to learn that my "kludge" isn't such a bad solution, after all. I've incorporated the Random.nextInt call and the centering code you provided. Now I'm going back to my image scaling code (that you helped me with earlier) to start sewing this together.

    I'm going post the code I wrote to experiment with the rotate( theta, offset, offset ) method you had earlier provided ... in case it's of use to anyone else. Not terribly elegant (particularly the enormously tall JSpinner components), but it was helpful to me in visualizing the effects of different parameters ... and, er, in learning more about a few other Swing components.

    Thanks, once again, for helping me out!

     
    With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
     
    subject: Rotating a JWindow with multiple JPanels