Fanning Software Consulting

Resizing Draw Widget Windows

QUESTION: I've seen some of your programs and they almost all have resizeable graphics windows. How do you do that in IDL?

ANSWER: It is really quite simple to have resizeable graphics windows in IDL. The only trick, really, is to write your graphic display routines so that they go into windows in a size independent way. Many of the programs availabe here at Coyote's Guide to IDL Programming are designed to do exactly that. For example, TVImage is a program designed to make image display in resizeable graphics windows possible.

There are only four things that are essential to know to write resizeable graphics windows.

  1. How to turn resize events on for the top-level base widget.
  2. How to resize draw widgets.
  3. How to make your draw widget the current graphics window.
  4. How to re-display your graphics commands in the draw widget window.

Here is a short example program, named Draw_Resize, that can be used to demonstrate the four steps. As you read through the text below, examine the IDL program file for details. In addition to the Draw_Resize program, you will need two other programs from the Coyote's Guide to IDL Programming library to run the example program. These are TVImage and FSC_Colorbar. I loaded an image (with my LoadData program) and ran the program by typing these commands.

   image = LoadData(2)
   Resize_Draw, image

Here is an illustration of how the example program might appear when it first comes up on the display. (You can, of course, call the program with your own image data.)

A widget program before it is resized.

Step 1. Turn Resize Events on for the Top-Level Base

Turning resize events on for the top-level base is as easy as setting the TLB_Size_Events keyword in the Widget_Base command that creates the top-level base widget. Here is the particular line of code from the example program:

   tlb = Widget_Base(Column=1, Title='Draw Widget Resize Example', $
      TLB_Size_Events=1)

Note that the event handler for the top-level base is never assigned with the Event_Pro or Event_Func keywords, since it is improper to do so. Rather, the event handler for the top-level base is assigned with the Event_Handler keyword on the XManager command at the end of the widget definition module. Here is the particular command line in the example program:

   XManager, 'draw_resize', tlb, Event_Handler='Draw_Resize_TLB_Resize', $
      Cleanup='Draw_Resize_Cleanup', /No_Block

When a resize event occurs a WIDGET_BASE event structure will be sent to this event handler. The event structure will be defined like this:

   event = { WIDGET_BASE, $
             TOP: 0L, $
             ID: 0L, $
             HANDLER: 0L, $
             X: 0, $
             Y: 0 }

The TOP, ID, and HANDLER fields will have their normal meaning. The X and Y fields will be defined as the size of the top-level base in device coordinates after the widget has been resized. You will use the information in these two fields to size the draw widget appropriately.

Step 2. Resize the Draw Widget

All the action is now occuring in the event handler for this top-level base, which is named Draw_Resize_TLB_Resize in this example program. I'll show you the entire program code for this event handler here, since it is very short.

First, I get the info structure, which I use to store all the information my program needs to run. Other people store this information in common blocks (which I never use in widget programs) or in pointers. How you pass information along is up to you. I have placed color table vectors (so I can protect my program colors), the widget identifier of the draw widget (drawID), the window index number of the draw widget (wid), and the original (image) and scaled image (scaled) data in the info structure. The first two lines of code look like this.

   PRO Draw_Resize_TLB_Resize, event
   Widget_Control, event.top, Get_UValue=info, /No_Copy

Next, I resize the draw widget. Since in this program my draw widget essentially fills the top-level base (I'll show you what do to in a moment if this isn't true), I can use the new size of the top-level base--as reported in the event structure--to re-size the draw widget. The code looks like this:

   Widget_Control, info.drawID, Draw_XSize=event.x, Draw_YSize=event.y

Step 3. Make the Draw Widget the Current Graphics Window

The next step is to make the draw widget the current graphics window. Remember that in widget programs you must know where you are drawing graphics or you will be drawing into the wrong window. I also load the color table vectors at this time. This is a way for me to make sure that the graphics in this widget program are always being displayed with the proper colors. The code looks like this:

   WSet, info.wid
   TVLCT, info.r, info.g, info.b

Step 4. Redisplay the Graphics in the Window

The next step is to issue the IDL graphics commands that I originally used to display the graphics in the window. Sometimes this will be a graphics display routine I have written especially for this purpose. Sometimes there are so few commands, like in this example, that I can just type them over again. But the commands used in this example are these. Notice that I use an Erase command first, because like the TV command the TVImage command does not erase the display first.

   Erase
   TVImage, *info.scaled, Position=[0.1, 0.1, 0.9, 0.75]
   FSC_Colorbar, Range=[Min(*info.image), Max(*info.image)], Divisions=8, $
      Title='Elevation (m)', Font=0, Position=[0.1, 0.85, 0.9, 0.90]

Finally, I store the info structure and I am through.

   Widget_Control, event.top, Set_UValue=info, /No_Copy
   END ;----------------------------------------------------------------

Here is what the program looks like after it has been resized.

A widget program after it has been resized.

What About More Complex Widget Programs?

What happens if you have other widgets in the top-level base? Well, then you have to spend a bit of time calculating how big your draw widget should be with respect to the top-level base.

Here is a fairly simple example program, named Draw_Resize_Buttons, that illustrates the concept. The program has a row of buttons (which don't do anything) in the top-level base along with the draw widget. When the program first comes up, it looks like this:

A widget program with a row of buttons as well as a draw widget.

This program is almost identical to the program Resize_Draw discussed above, with just a couple of small changes. First of all, I want to know how much space in the top-level base is taken up by the row of buttons. The buttons are in a row base widget, so this question is really this: How big is the button base? If I know this, I can subtract this amount of space from the top-level base size when I resize the draw widget.

I find out the button base size by using the Widget_Info command with the Geometry keyword, like this. This code is executed just before the widget hierarchy is realized.

   buttonRowGeom = Widget_Info(buttonRowID, /Geometry)
   button_ysize = buttonRowGeom.ysize
   button_xsize = buttonRowGeom.xsize

I store these button sizes in the info structure, along with the other program information I need. Then I use the information in the event handler like this. Notice that I subtract the button base's Y size from the base widget's Y size. But I use the button base's X size to define a limit for how small you can make the draw widget. In this case you can't make a draw widget smaller than the size of the button base.

  Widget_Control, info.drawID, Draw_XSize=event.x > info.button_xsize, $
    Draw_YSize=event.y - info.button_ysize

Play with the program a bit to see how it works.

Does Your Window Flicker as it Resizes?

If you are running IDL on a Windows machine and your window flickers as you resize it, or it appears to re-draw itself over and over, then you probably have the "Show Window Contents While Dragging" option turned on in your operating system. You want to turn it off!

Right click on your desktop and choose Properties. You should be looking at the Display Properties dialog. Choose the Effects tab and de-select the "Show Window Contents While Dragging" option. (On some versions of Windows the Effects tab has been changed to a button inside the Appearance tab.) Apply the changes and try to resize your widget now. Isn't that better?

Turn off the

On Windows 7 computers you can right click on your Computer icon, and chose the Advanced System Settings selection to get the System Properties dialog. Click the Advanced tab, and choose the Performance->Visual Effects->Settings button. You will find the Show Window Contents While Dragging selection there.

Google
 
Web Coyote's Guide to IDL Programming