Donn Shull continues his exploration of the undocumented UDD mechanism, today discussing the important and extremely useful topic of UDD events
The UDD event model
The UDD event model is very similar to the MCOS event model. There is an excellent discussion of the MCOS event model in Matlab’s official documentation. Most of the MCOS information also applies to UDD if you make the following substitutions:
|MCOS Event Model||UDD Event Model|
|PreGet, PreSet||PropertyPreGet, PropertPreSet|
|PostGet, PostSet||PropertyPostGet, PropertyPostSet|
Event handler functions
To begin the UDD event model discussion we will start at the end, with the event handler. The event handler function requires at least two input arguments: the source object which triggered the event, and an object of type
handle.EventData or a subclass of
To demonstrate how this works, let’s write a simple event handler function. This event handler will display the class of the source event and the class of the event data:
function displayEventInfo(source, eventData) %DISPLAYEVENTINFO display the classes of source, data objects % % DISPLAYEVENTINFO(SOURCE, EVENTDATA) returns the classes % of the source object and the event data object % % INPUTS: % SOURCE : the event source % EVENTDATA : the event data if ~isempty(source) fprintf(1, 'The source object class is: %s',class(source)); end if ~isempty(eventData) fprintf(1, 'The event data class is: %s',class(eventData)); end end
Creating a listener
In the section on Creating a Simple UDD Class we used
schema.event in our
simple.object class definition file to create a
simpleEvent event. We now create an instance of
simple.object, then use handle.listener to wait (“listen”) for the
simpleEvent event to occur and call the displayEventInfo event handler function:
a = simple.object('a', 1); hListener = handle.listener(a,'simpleEvent',@displayEventInfo); setappdata(a, 'listeners', hListener);
hListener handle must remain stored somewhere in Matlab memory, or the listener will not be used. For this reason, it is good practice to attach the listener handle to the listened object, using the setappdata function, as was done above. The listener will then be alive for exactly as long as its target object is alive.
Creating an EventData object
Next, create the
handle.EventData object. The
handle.EventData object constructor requires two arguments: an instance of the events source object, and the name of the event:
evtData = handle.EventData(a, 'simpleEvent')
Generating an event
The last step is actually triggering an event. This is done by issuing the send command for the specified object, event name and event data:
>> a.send('simpleEvent', evtData) The source object class is: simple.object The event data class is: handle.EventData
If there is other information that you wish to pass to the callback function you can create a subclass of the
handle.EventData. Add properties to hold your additional information and use your subclass as the second argument of the send method.
Builtin UDD events
handle package has six event data classes which are subclasses of the base
handle.EventData class. Each of these classes is paired with specific UDD events that Matlab generates. Actions that trigger these events include creating/destroying an object, adding/removing objects from a hierarchy, and getting/setting property values. The following table lists the event names and
handle.*EventData data types returned for these events:
|event data type||event trigger|
As an example of some of these events let’s look at a question recently asked on the CSSM newsgroup. The basic idea is that we want to monitor an axis, automatically make any added lines to be green in color, and prevent patches from being added.
The solution is to monitor the
ObjectChildAdded event for an axis. We will write an event handler which checks the
handle.ChildEventData to see what type of child was added. In the case of lines we will set their color to green; patch objects will be deleted from the axis. Here is our event handler function:
function modifyAxesChildren(~, eventData) %MODIFYAXESCHILDREN monitor and axis and modify added children % % MODIFYAXESCHILDREN(SOURCE,EVENTDATA) is an event handler to % change newly-added lines to green and remove added patches % % INPUTS: % EVENTDATA : handle.ChildEventData object switch eventData.Child.classhandle.Name case 'line' eventData.Child.set('Color', 'green'); disp('Color changed to green.') case 'patch' eventData.Child.delete; disp('Patch removed.') end end
Next create an axis, and a listener which is triggered when children are added:
% create a new axes and get its handle a = hg.axes; % create the listener listen = handle.listener(a, 'ObjectChildAdded', @modifyAxesChildren); % add a line >> hg.line; Color changed to green. % try to add a patch >> hg.patch; Patch removed.
Removing a child with either the delete or the disconnect method generates an
ObjectChildRemoved event. The delete method also generates the
ObjectBeingDestroyed event. Changing a child’s parent with the up method generates an
Reading an object’s properties with either dot notation or with the get method generates
Changing the value of a property generates the
PropertyPostSet events. As we saw in the section on UDD properties, when the AbortSet access flag is ‘on’, property set events are only generated when a set operation actually changes the value of the property (as opposed to leaving it unchanged).
Note that the handle.listener syntax is slightly different for property events:
hProp = findprop(a, 'Value'); hListener = handle.listener(a,hProp,'PropertyPreGet',@displayEventInfo);
The final specialized event data object in the handle package is
handle.JavaEventData. In Matlab, Java classes are not UDD classes, but each Java instance can have a UDD peer. The peer is created using the handle function. The Java peers are created in either UDD’s
javahandle package or the
javahandle_withcallbacks package. As their names imply, the latter enables listening to Java-triggered events using a Matlab callback.
To illustrate how this works we will create a Java Swing
JFrame and listen for
% Create the Java Frame javaFrame = javax.swing.JFrame; javaFrame.setSize(200, 200); javaFrame.show; % Create a UDD peer for the new JFrame (two alternatives) javaFramePeer = javaFrame.handle('CallbackProperties'); % alternative #1 javaFramePeer = handle(javaFrame, 'CallbackProperties'); % alternative #2 % Create the a listener for the Java MouseClicked event listen = handle.listener(javaFramePeer, 'MouseClicked', @displayEventInfo);
When we click on the JFrame, our UDD peer triggers the callback:
The source object class is: javahandle_withcallbacks.javax.swing.JFrame The event data class is: handle.JavaEventData
Since we created our peer in the
javahandle_withcallbacks package, it is not necessary to create a listener using handle.listener. If we place our callback function handle in the MouseClickedCallback property it will be executed whenever the
MouseClicked event is triggered. Such *Callback properties are automatically generated by Matlab when it creates the UDD peer (details).
clear listen javaFramePeer.MouseClickedCallback = @displayEventInfo
This will work the same as before without the need to create and maintain a handle.listener object. If we had created our UDD peer in the
javahandle package rather than
javahandle_withcallbacks, we would not have the convenience of the MouseClickedCallback property, but we could still use the handle.listener mechanism to monitor events.
Creating callback properties for custom UDD classes
It is easy to add callback properties to user created UDD objects. The technique involves embedding a
handle.listener object in the UDD object. To illustrate this, we add a SimpleEventCallback property to our
simple.object, then use a SimpleEventListener property to hold our embedded handle.listener. Add the following to
simple.object‘s schema.m definition file:
% Property to hold our callback handle prop = schema.prop(simpleClass, 'SimpleEventCallback', 'MATLAB callback'); prop.setFunction = @setValue; % hidden property to hold the listener for our callback prop = schema.prop(simpleClass, 'SimpleEventListener', 'handle'); prop.Visible = 'off'; end function propVal = setValue(self, value) %SETVALUE function to transfer function handle from callback property to listener self.SimpleEventListener.Callback = value; propVal = value; end
Next we add the following to our simple.object constructor file:
% set the hidden listener property to a handle.listener simpleObject.SimpleEventListener = handle.listener(simpleObject, 'simpleEvent', );
Now if we set the SimpleObjectCallback property to a function handle, the handle is transferred to the embedded handle.listener Callback property. When a
simpleEvent event is generated, our
SimpleEventCallback function will be executed.
This series will conclude next week with a look at the special relationship between UDD and Java.