Drawing a Line in a Draw Widget Window
QUESTION: How do I draw a free-hand line in a draw widget window?
![]()
ANSWER: This question was asked recently on the IDL newsgroup. I modified some code that I wrote earlier to explain how to draw a rubberband box in a widget window. The example here can easily be extended to answer the question, "How to I create an irregular region of interest (ROI) in a widget program?"
The technique illustrated here uses the Device Copy method of erasing the display. For more information on this technique, see the rubberband box programming tip for regular IDL windows.
In the code below, use the LEFT mouse button to draw a free-hand line in the draw widget window. Use the RIGHT mouse button to draw a straight line in the draw widget window.
The essential algorithm is this. On a draw widget DOWN event I create a pixmap and copy the display information into it, set the starting coordinate of the line, and turn motion events on for the draw widget. On a draw widget MOTION event I erase the previous line (with a Device Copy), get the next line point, and draw another line in the display window. On a draw widget UP event I erase the last line drawn, turn motion events off for the draw widget, delete the pixmap, and do whatever it is I want to do with the line. (Here I simply leave it drawn on the image. You can erase it with the Erase Window button under the File menu item.)
The code for this widget program, named DrawLine, looks like this:
![]()
PRO Drawline_Button_Events, event
; All program button events handled here.
Widget_Control, event.top, Get_UValue=info, /No_Copy
; Which button caused this event?
Widget_Control, event.id, Get_Value=thisButtonValue
CASE thisButtonValue OF
'Quit': BEGIN
Widget_Control, event.top, /Destroy
RETURN
ENDCASE
'Erase Lines': BEGIN
WSet, info.wid
TV, BytScl(info.image, Top=info.drawColor-1)
ENDCASE
ENDCASE
Widget_Control, event.top, Set_UValue=info, /No_Copy
END
;------------------------------------------------------------------
PRO Drawline_Draw_Events, event
; All program draw widget events handled here.
; Deal only with DOWN, UP, and MOTION events.
IF event.type GT 2 THEN RETURN
; Get the info structure.
Widget_Control, event.top, Get_UValue=info, /No_Copy
; What kind of event is this?
eventTypes = ['DOWN', 'UP', 'MOTION']
thisEvent = eventTypes[event.type]
whichButton = ['NONE', 'LEFT', 'MIDDLE', 'NONE', 'RIGHT']
CASE thisEvent OF
'DOWN': BEGIN
; Which button was used? LEFT or RIGHT?
info.buttonUsed = whichButton(event.press)
; Turn motion events on for the draw widget.
Widget_Control, info.drawID, Draw_Motion_Events=1
; Create a pixmap. Store its ID. Copy window contents into it.
Window, /Free, /Pixmap, XSize=info.xsize, YSize=info.ysize
info.pixID = !D.Window
Device, Copy=[0, 0, info.xsize, info.ysize, 0, 0, info.wid]
; Initialize the starting coordinates of the line.
IF info.buttonUsed EQ 'RIGHT' THEN BEGIN
info.xstart = event.x
info.ystart = event.y
ENDIF ELSE BEGIN
info.xvalues = Ptr_New([event.x])
info.yvalues = Ptr_New([event.y])
ENDELSE
ENDCASE
'UP': BEGIN
; Erase the last line drawn. Destroy the pixmap.
WSet, info.wid
Device, Copy=[0, 0, info.xsize, info.ysize, 0, 0, info.pixID]
WDelete, info.pixID
; Turn draw motion events off. Clear events queued for widget.
Widget_Control, info.drawID, Draw_Motion_Events=0, Clear_Events=1
; Draw the final line.
IF info.buttonUsed EQ 'RIGHT' THEN BEGIN
PlotS, [info.xstart, event.x], [info.ystart, event.y], $
/Device, Color=info.drawColor
; Reinitialize the line starting coordinates.
info.xstart = -1
info.ystart = -1
ENDIF ELSE BEGIN
PlotS, *info.xvalues, *info.yvalues, /Device, $
Color=info.drawColor
; Reinitialize the pointers.
Ptr_Free, info.xvalues
Ptr_Free, info.yvalues
info.xvalues = Ptr_New()
info.yvalues = Ptr_New()
ENDELSE
ENDCASE
'MOTION': BEGIN
; Here is where the actual line is drawn and erased.
; First, erase the last line.
WSet, info.wid
Device, Copy=[0, 0, info.xsize, info.ysize, 0, 0, info.pixID]
IF info.buttonUsed EQ 'RIGHT' THEN BEGIN
; Draw a straight line.
PlotS, [info.xstart, event.x], [info.ystart, event.y], /Device, $
Color=info.drawColor
ENDIF ELSE BEGIN
; Get the points of the new free-hand line and draw it.
*info.xvalues = [*info.xvalues, event.x]
*info.yvalues = [*info.yvalues, event.y]
PlotS, *info.xvalues, *info.yvalues, /Device, Color=info.drawColor
ENDELSE
ENDCASE
ENDCASE
; Store the info structure.
Widget_Control, event.top, Set_UValue=info, /No_Copy
END
;------------------------------------------------------------------
PRO Drawline
; Open an image data set.
file = Filepath(SubDirectory=['examples','data'], 'ctscan.dat')
OpenR, lun, file, /Get_Lun
image = BytArr(256, 256)
ReadU, lun, image
Free_Lun, lun
xsize = (Size(image))[1]
ysize = (Size(image))[2]
; Create the TLB.
tlb = Widget_Base(Title='Free-Hand Lines in a Widget Program', $
MBar=menubase)
; Create some menu bar buttons.
fileID = Widget_Button(menubase, Value='File', $
Event_Pro='Drawline_Button_Events')
eraseID = Widget_Button(fileID, Value='Erase Lines')
quitID = Widget_Button(fileID, Value='Quit')
; Create the draw widget graphics window. Turn button events ON.
drawID = Widget_Draw(tlb, XSize=xsize, YSize=ysize, Button_Events=1, $
Event_Pro='Drawline_Draw_Events')
; Realize widgets and make draw widget the current window.
Widget_Control, tlb, /Realize
Widget_Control, drawID, Get_Value=wid
WSet, wid
; Load drawing color and display the image.
drawColor = !D.N_Colors-1
TVLCT, 255, 255, 0, drawColor
TV, BytScl(Image, Top=drawColor-1)
; Create an "info" structure with information to run the program.
info = { image:image, $ ; The image data.
wid:wid, $ ; The window index number.
drawID:drawID, $ ; The draw widget identifier.
pixID:-1, $ ; The pixmap identifier.
xsize:xsize, $ ; The X size of the graphics window.
ysize:ysize, $ ; The Y size of the graphics window.
xstart:-1, $ ; The starting X coordinate of the line.
ystart:-1, $ ; The starting Y coordinate of the line.
xvalues:Ptr_New(), $ ; The X coordinates of the free-hand line.
yvalues:Ptr_New(), $ ; The Y coordinates of the free-hand line.
buttonUsed:'NONE', $ ; A flag to indicate which button is used.
drawColor:drawColor } ; The rubberband box color.
; Store the info structure.
Widget_Control, tlb, Set_UValue=info, /No_Copy
; Start the program going.
XManager, 'drawline', tlb, /No_Block
END
![]()
To run this program, simple type this:
IDL> Drawline
Click and drag the LEFT button inside the graphics window to draw a freehand line. Click and drag the RIGHT button inside the graphics window to draw a straight line.
![]()
Copyright © 1997-1998 David W. Fanning
Last Updated 31 March 1998