Fanning Software Consulting

Returning LIST Widget Values

QUESTION: OK, I set the value of a list widget like this:

   listID = Widget_List(tlb, VALUE=['dog', 'toad', 'frog', 'coyote'], YSIZE=4)

But in the event handler, when I attempt to get the value of the list widget, so I can return it as the result of the function I get an error message that tells me this kind of widget doesn't have a value. It must have a value, I just set it! I notice the same thing happens with droplist widgets. Can you explain what is going on here?

ANSWER: No, I can't explain it. It's a mystery. Really.

But I can tell you that if you want the actual values of the list widget in an event handler, you are going to have to get them over there yourself. You can either store them in the state or info structure you use to pass information in widget programs, or--what is more frequently done--you can just store them in the user value of the list widget, so they are around when you need them.

If you do the latter, then getting the particular value of a user selection looks like this:

   Widget_Control, event.id, GET_UVALUE=theValues
   theValue = theValues[event.index]

Here is an example of a list widget used as a modal dialog widget. The program is named Choose_Item.

PRO Choose_Item_Event, event

   ; Get info structure pointer.
   Widget_Control, event.top, Get_UValue=ptr
   
   ; Get the item from the list widget and store it
   ; in the program pointer.
   Widget_Control, event.id, Get_UValue=listValues
   (*ptr).answer = listValues[event.index]
   (*ptr).cancel = 0
   
   ; Destroy the widget.
   Widget_Control, event.top, /Destroy
   
END ; ----------------------------------------------------


FUNCTION Choose_Item, items, Cancel=cancel, Group_Leader=group_leader

   IF N_Elements(items) EQ 0 THEN items = ['cow', 'dog', 'coyote', 'pig']
   
   ; Create the top-level base widget. Make it modal if you
   ; have a group_leader. Otherwise, will have to rely on this
   ; widget blocking. If so, DON'T call it from a widget program!
   IF N_Elements(group_leader) EQ 0 THEN BEGIN
      tlb = Widget_Base(Title='Animals...', Column=1)
   ENDIF ELSE BEGIN
      tlb = Widget_Base(Title='Animals...', Column=1, /Modal, Group_Leader=group_leader)
   ENDELSE
   
   ; Create list widget with choices. Store the choices in the UVALUE 
   ; of the list, so they are available in the event handler.
   listID = Widget_List(tlb, Value=items, UValue=items, YSize=N_Elements(items) < 20, XSize=25)
   
   ; Create a pointer for storing the user's selection. There is a cancel
   ; flag so I can tell if user killed the widget without making a selection.
   ptr = Ptr_New({answer:"", cancel:1})
   
   ; Store info pointer in UVALUE of TLB.
   Widget_Control, tlb, Set_UValue=ptr
   
   ; Realize the widgets.
   Widget_Control, tlb, /Realize
   
   ; Call XManager and BLOCK the command line. This will only work
   ; if this is the FIRST blocking program. Use GROUP_LEADER and MODAL
   ; keyword to be *sure* you block.
   XManager, 'choose_item', tlb
   
   ; Get the answer and cancel flag out of the program pointer and destroy the pointer.
   answer = (*ptr).answer
   cancel = (*ptr).cancel
   Ptr_Free, ptr
      
   ; Return the answer.
   RETURN, answer
   
END

The program is called like this:

   IDL> theItem = Choose_Item(['dog', 'toad', 'frog', 'coyote'], CANCEL=cancelled)

Notice the use of the CANCEL keyword. This can be used to distinuish from the user closing the dialog without making a selection (cancelled=1) or by making a selection (cancelled=0).

[Return to IDL Programming Tips]