Creating a Graphics Event Handler Class
You can create a custom event handler to handle events in a graphics WINDOW or a WIDGET_WINDOW. The event handler class must be a subclass of GraphicsEventAdapter. Your subclass can then override any of the following methods: ::MouseDown, ::MouseUp, ::MouseMotion, ::MouseWheel, ::KeyHandler, and ::SelectChange.
Each function method must return a value of 0 to turn off default event handling or 1 to perform default event handling.
See the WINDOW example and WIDGET_WINDOW example, below.
Syntax
The syntax of the various event handler methods are similar, as the following list shows.
Result = obj.MouseDown(Window, X, Y, Button, KeyMods, Clicks)
Result = obj.MouseMotion(Window, X, Y, KeyMods)
Result = obj.MouseUp(Window, X, Y, Button)
Result = obj.MouseWheel(Window, X, Y, Delta, KeyMods)
Result = obj.KeyHandler(Window, IsASCII, Character, KeyValue, X, Y, Press, Release, KeyMods)
Result = obj.SelectChange(Window, Graphic, Mode, WasSelected)
Arguments
Window
The object reference of the window in which the event occurred.
X
The x-coordinate location of the mouse cursor (in device coordinates) at the time of the event.
Y
The y-coordinate of the mouse cursor (in device coordinates) at the time of the event.
Button
The value of the clicked button. Possible values are:
Value |
Mouse Button |
---|---|
1 |
Left |
2 |
Middle |
4 |
Right |
Clicks
The value indicating how many button clicks occurred. The value is 1 for a single click and 2 for a double click.
Delta
The value indicating the direction and number of movements of the mouse wheel.
Pushing the wheel generates positive values, pulling the wheel generates negative values. The magnitude of the value depends on the device setting for the individual mouse, but is usually limited to small integer values such as +1, -1, +2, -2, etc.
KeyMods
The value containing a bitwise mask indicating which modifier keys are active at the time the mouse event happens. Possible values are:
Value |
Modifier Key |
1 |
Shift |
2 |
Control |
4 |
Caps lock |
8 |
Alt |
IsASCII
A scalar byte value that indicates whether the keyboard event corresponds to an ASCII character. If IsASCII is non-zero, the Character argument will be set to a byte value corresponding to the character of the pressed key. If IsASCII is zero, the KeySymbol argument will be set to a numeric value indicating the key that was pressed.
Character
If IsASCII is non-zero, this argument is set to a byte value corresponding to the ASCII character of the key that was pressed. Otherwise, this argument is set to zero.
KeyValue
If IsASCII is zero, this argument is set to a value that indicates the key that was pressed. Otherwise, this argument is set to zero. Possible values are:
Value |
Key |
---|---|
1 |
Shift |
2 |
Control |
3 |
Caps lock |
4 |
Alt |
5 |
Left |
6 |
Right |
7 |
Up |
8 |
Down |
9 |
Page up |
10 |
Page down |
11 |
Home |
12 |
End |
Note: On East Asian (Chinese, Japanese, Korean) localized Windows operating systems with an Asian language pack installed, characters entered in the Windows Input Method Manager (IMM)composition window are returned in the the KeyValue argument as unsigned integers representing a Wide character (Unicode value). The I18N_WIDECHARTOMULTIBYTE routine can convert these characters to multibyte strings. For more information, see Internationalizing Code.
Press
The value indicating that this event represents a key press. This argument is non-zero if the event is the result of pressing the key.
Release
The value indicating that this event represents a key release. This argument is non-zero if the event is the result of releasing the key.
Graphic
The graphic to be selected or unselected.
Mode
A value representing the mode that was used for the current selection. Possible values are:
Value |
Selection Type |
0 |
Unselect |
1 |
Select |
2 |
Toggled selection |
3 |
Additive |
WasSelected
A value indicating if the graphic item was already selected prior to this event. A value of 1 indicates an item was previously selected; 0 if not selected.
GraphicsEventAdapter Class
The GraphicsEventAdapter class provides a default implementation for all of the event handler methods. The default implementation returns a value of 1 for each of the methods. For convenience, you should create your event handler as a subclass of GraphicsEventAdapter and only override the methods that you need.
The GraphicsEventAdapter class is written in the IDL language. Its source code can be found in the file graphicseventadapter__define.pro
in the lib/graphics
subdirectory of the IDL distribution.
Example Using WINDOW
In the following example code, the RBBox class create a "rubber-band" box. The RBBox::MouseDown
method creates a polygon and caches the start point of a drag operation. In the RBBox::MouseMotion
method the polygon is updated with the current location of the cursor. In the RBBox::MouseUp
method, the polygon is again updated and the color of the polygon is modified. Copy this code into a new IDL file and run it. To test the code, drag and drop the mouse to draw a box in the graphic window.
FUNCTION RBBox::MouseDown, oWin, $
x, y, iButton, KeyMods, nClicks
if (~ISA(self.poly)) then begin
; Add a hidden polygon for the rubber-band box.
self.poly = POLYGON([0,0,0],[0,0,0], /DEVICE, $
LINESTYLE='--', /HIDE, $
FILL_TRANSPARENCY=90, FILL_BACKGROUND = 1, FILL_COLOR='red')
endif
self.x0 = x
self.y0 = y
self.buttonDown = 1
self.poly.HIDE = 0
self.poly.SetData, [0,0,0], [0,0,0]
self.poly.LINESTYLE='--'
RETURN, 0 ; Skip default event handling
END
FUNCTION RBBox::MouseMotion, oWin, x, y, KeyMods
IF self.buttonDown then begin
x0 = self.x0
y0 = self.y0
xVector=[x0,x0,x,x,x0]
yVector=[y0,y,y,y0,y0]
xy = self.poly.ConvertCoord(xVector, yVector, /DEVICE, /TO_NORMAL)
self.poly.SetData, REFORM(xy[0,*]), REFORM(xy[1,*])
ENDIF
RETURN, 0 ; Skip default event handling
END
FUNCTION RBBox::MouseUp, oWin, x, y, iButton
IF (~self.buttonDown) THEN RETURN, 0
x0 = self.x0
y0 = self.y0
xVector=[x0,x0,x,x,x0]
yVector=[y0,y,y,y0,y0]
xy = self.poly.ConvertCoord(xVector, yVector, /DEVICE, /TO_NORMAL)
self.poly.SetData, REFORM(xy[0,*]), REFORM(xy[1,*])
self.poly.LINESTYLE='-'
self.buttonDown=0
; Clear the current selections
oSelect = oWin.GetSelect()
FOREACH oVis, oSelect do oVis.Select, /UNSELECT
; Do a hit test and select new items.
oVisList = oWin.HitTest(x0+(x-x0)/2, y0+(y-y0)/2, $
DIMENSIONS=ABS([x-x0, y-y0]) > 10)
FOREACH vis, oVisList do begin
if vis ne self.poly then vis.Select, /ADD
ENDFOREACH
RETURN, 0 ; Skip default event handling
END
PRO RBBox__define
; We must subclass from GraphicsEventAdapter.
void = {RBBox, inherits GraphicsEventAdapter, $
X0: 0, Y0:0, BUTTONDOWN:0, POLY:OBJ_NEW()}
END
PRO RBBoxEventsTest
x = Findgen(200)
y = Sin(x*2*!PI/25.0)*Exp(-0.01*x)
p = PLOT(x, y, TITLE='Click, hold mouse down and drag, release to draw box')
p.window.EVENT_HANDLER=Obj_New('RBBox')
END
Example Using WIDGET_WINDOW
In this example, only the MouseMotion event method is used. The event coordinates (in device coordinates) are converted to data coordinates and displayed in a label widget below the draw widget. A crosshair is also drawn at the nearest plot data point. Save all of the code in a file, exwidgetwindowevents.pro
, and then run the ExWidgetWindowEvents
procedure.
FUNCTION ExWidWin::Init, plot, label, crosshair
self.plot = plot
self.label = label
self.crosshair = crosshair
return, 1
END
FUNCTION ExWidWin::MouseMotion, oWin, x, y, KeyMods
; Convert from screen coordinates to data coordinates.
xy = self.plot.ConvertCoord(x, y, /DEVICE, /TO_DATA)
; "Snap" the location to the nearest plot point.
xy = self.plot.GetValueAtLocation(xy[0])
; Update the crosshair location and the label
self.crosshair.LOCATION = xy
probe = STRING(xy[0:1], FORMAT='(2F9.2)')
WIDGET_CONTROL, self.label, SET_VALUE=probe
return, 1 ; Perform default event handling
END
PRO ExWidWin__define
void = {ExWidWin, inherits GraphicsEventAdapter, $
plot: OBJ_NEW(), label: 0L, crosshair: OBJ_NEW()}
END
PRO ExWidgetWindowEvents_event, event
; Be sure to process the internal window event first.
; This handles selection, translation, rotation, etc.
w = WIDGET_EVENT(/NOWAIT)
CASE TAG_NAMES(event, /STRUCTURE_NAME) of
'WIDGET_BASE': BEGIN
; Handle base resize events. Retrieve our cached padding,
; and our new size.
WIDGET_CONTROL, event.id, GET_UVALUE=pad, TLB_GET_SIZE=newSize
wDraw = WIDGET_INFO(event.top, FIND_BY_UNAME='ex_widwin_window')
; Change the draw widget to match the new size, minus padding.
xy = newSize - pad
WIDGET_CONTROL, wDraw, $
DRAW_XSIZE=xy[0], DRAW_YSIZE=xy[1], $
SCR_XSIZE=xy[0], SCR_YSIZE=xy[1]
END
ELSE: ; do nothing
ENDCASE
END
PRO ExWidgetWindowEvents
; Create the widgets (start unmapped)
wBase = WIDGET_BASE(/COLUMN, /TLB_SIZE_EVENTS, MAP=0)
wDraw = WIDGET_WINDOW(wBase, $
UNAME='ex_widwin_window', $
X_SCROLL_SIZE=640, Y_SCROLL_SIZE=512)
w1 = WIDGET_LABEL(wBase, /ALIGN_LEFT, /DYNAMIC)
WIDGET_CONTROL, wBase, /REALIZE
; Cache the padding between the base and the draw
WIDGET_CONTROL, wBase, TLB_GET_SIZE=basesize
padding = basesize - [640, 512]
WIDGET_CONTROL, wBase, SET_UVALUE=padding
; Retrieve the newly-created Window object.
WIDGET_CONTROL, wDraw, GET_VALUE=win
; Make sure this is the current window
win.Select
p = PLOT(/TEST, /CURRENT)
; Change crosshair to "manual" mode and set some properties.
c = p.CROSSHAIR
c.STYLE = 'Manual'
c.COLOR = 'Red'
c.LINESTYLE = '-'
; Cache the graphics references for the event handlers
handler = OBJ_NEW('ExWidWin', p, w1, c)
win.EVENT_HANDLER = handler
; Draw the widgets and start the event processing
WIDGET_CONTROL, wBase, /MAP
XMANAGER, 'ExWidgetWindowEvents', wBase, /NO_BLOCK
END