UDD Events and Listeners

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 ModelUDD Event Model
notifysend
event.EventDatahandle.EventData
events blockschema.event
event.listenerhandle.listener
PreGet, PreSetPropertyPreGet, PropertPreSet
PostGet, PostSetPropertyPostGet, 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 handle.EventData.

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);

Important: The 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

The builtin 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 typeevent trigger
handle.ClassEventDataClassInstanceCreated
handle.EventDataObjectBeingDestroyed
handle.ChildEventDataObjectChildAdded, ObjectChildRemoved
handle.ParentEventDataObjectParentChanged
handle.PropertyEventDataPropertyPreGet, PropertyPostGet
handle.PropertySetEventDataPropertyPreSet, PropertyPostSet

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 ObjectParentChanged event.

Reading an object’s properties with either dot notation or with the get method generates PropertyPreGet and PropertyPostGet events.

Changing the value of a property generates the PropertyPreSet and 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);

Java events

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 MouseClicked events:

% 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);

a simple Java Swing JFrame

a simple Java Swing JFrame

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.

Categories: Guest bloggers, Listeners, Medium risk of breaking in future versions, Stock Matlab function, Undocumented feature

Tags: , , , , , ,

Bookmark and SharePrint Print

32 Responses to UDD Events and Listeners

  1. Anubhav Jain says:

    I would like to know if it is possible to listen to events that happen when one drops a block or a subsystem from the Model library to the editor and when one changes the dialog parameters of a block. Furthermore, if such an event can be listened, what type of event would it be?

    • Donn Shull says:

      Anubhav – This is actually a very complex topic and an entire book could probably be written about it. When Simulink is installed two additional UDD packages are added to MATLAB. The first package is ‘DAStudio’ and as of R2011a it contains over 100 classes. The second package is Simulink and as of R2011a it contains over 250 classes. A Simulink block diagram is a UDD hierarchy similar to a handle graphics diagram. The top is Simulink.Root with Simulink.BlockDiagram objects as children of this root. You can obtain the UDD object for any Simulink handle with the command:

      uddObj = get_param(simHandle, 'UDDObject');

      So for example to obtain the handle for the current blockdiagram you could use:

      blkObj = get_param(bdroot, 'UDDObject');

      You can then use the the Child events described above to monitor for things added to the top level diagram. Subsystems would have to be handled seperately.

      Monitoring parameter changes is a complex matter for example with a gain block you can use:

      gainObj = get_param(gcb, 'UDDObject');

      where gcb returns the currently selected block. Then

      gainObj.Gain = 10;

      Will generate the Property events described above. Unfortunately changes to Gain made with either a set_param or through the Gain blocks dialog will not generate those events. One thing you can play around with if you have too much time on your hands is:

      toolRoot = DAStudio.ToolRoot;
      dialogs = toolRoot.getOpenDialogs;

      This will return a DAStudio.Dialog array of objects for any open Simulink Dialogs. With that and DAStudio.imDialog there are a number of ways to manipulate Simulink Dialogs. You would need to study the code in the simulink toolbox and the shared dastudio toolbox.

      Donn

  2. Anubhav Jain says:

    Hi Donn,
    I really appreciate your reply on this . I am very thankful to you.. I am getting the list of events by using get_param(gcb,’UDDObject’) command.I am unable to get the drop event i.e. the event which is generated upon dropping a block from simulink library to simulink editor window..

    Also I am able to listen to only namechangeevent .I am facing problems with connection event as the listeners are not getting triggered . Can you please elaborate upon the other events like Assertion , SelectionChangeEvent so that I can work upon them . Can I get the utility of all the events being listed by UDDObject command . I would be very greatful to you….

    • Donn Shull says:

      @Anubhav – Unfortunately I do not have a reference for the various events for each of the classes in the Simulink package. They are all “Undocumented” after all. The best way to learn about these events is a combination of guesswork, studying code supplied by The MathWorks, and experiment. As an example ‘SelectionChangedEvent’ has an obvious name and is used in about 4 MathWorks files. Experiment shows that it is triggered when a mouse clicks on a block that had not previously been selected in the block diagram the listener is attached to. Unfortunately expirement also shows that while adding a new block to the diagram changes the selected block that action does not generate a SelectionChangeEvent.

      I also have a guess as to why set_param and get_param do not generate events while direct manipulation of the Object parameters does. There is a Simulink Engine whichdoes the work of running a block diagram. Since you can use MATLAB and the set_param and get_param commands while a simulation is running it is reasonable to assume that the Simulink Engine runs in its own thread and it the engine may not generate or perhaps pass events back to the MATLAB thread. About a decade ago National Instruments filed a patent infringment lawsuit against The MathWorks regarding intelectual property relating to interacting with block diagrams. At the conclusion some functionality was removed from MATLAB / Simulink interaction. For example the Dials and Gauges blockset was discontinued and replaced with the Gauges blockset in order to comply with the court ruling.

      Good Luck with your work,

      Donn

  3. Anubhav Jain says:

    Hi Donn,
    Thanks a lot for your co-ordination… I am working upon all events by trial and error method.. I am able to register a few .. Just a last question ..

    Is there any possible way to register a drop event i.e. the event which is generated upon dropping a block from simulink library to simulink editor window.. If you can please help me out with this it would really solve my problem……….Just a last one …

    • Donn Shull says:

      @Anubhav – I do not know a way of directly working with the Simulonk editor in the way that you have asked. You can probably emulate the behaviour you are seeking in the following way. You will need persistent storage for an array of handle.listener objects. Your callback function will need access to this array. The first listener in your array wil be something like:

       hListener(1) = handle.listener(slroot, 'ObjectChildAdded', @myHandler);

      The myHandler function will need to add a listener to the array whenever a Simulink.BlockDiagram is added as a child to slroot (note slroot returns the Simulink.Root object which is a singleton.) The added listeners will need to monitor the Simulink.BlockDiagram objects in a similar way using the ‘ObjectChildAdded’ event. If the object being added to a block diagram is a Simulink.SubSystem then you will need to add annother listener to monitor the SubSystem for ‘ObjectChildAdded’ events. You will probably be able to do this all with one event handler which contains a switch statement to determine the action you wish to take depending on which child was added. Some final thoughts are that you may want to develop a system for keeping track of the listeners so you can remove unneeded ones when models are closed or subsystems are deleted from a model. Also when opening existing models you will need to build up a set of listeners for the subsystems that is contains. In that case you can use:

      find(get_param(addedModelName, 'UDDObject'), '-isa', 'Simulink.SubSystem');

      to identify the subsystems that it contains and add listeners for them.

      Best Regards,

      Donn

  4. Tarun Jhamnani says:

    Hi Donn,

    The article is really helpful.glad to finally get some insight into UDD classes.Its all new to me and sounds very interesting to play around. I have few doubts here,
    Is it possible to get the handle of Jframe simulink window and add new events for the same?and how to do that?
    If not ,atleast can we have handle of Jframe simulink window to modify the default window looks.

    • Donn Shull says:

      Tarun – I don’t think I will be able to help you with this. I believe that the Simulink editor is a “native” application rather than a java application. If you use the following command:

      windowList = com.mathworks.mwswing.desk.DTWindowRegistry.getInstance;

      You will find in the list that Simulink windows are of type:

      com.mathworks.mde.desk.MLNativeWindow

      There are a small number of java methods for these objects and no events as far as I can tell.

      Donn

    • Tarun Jhamnani says:

      Hi Donn,
      I was able to listen to the simulink events.When i used same for statechart i was not able to trace events such as object child added etc.Using the properties of chart block i was able to find callback for copy,move etc but not as an event.I wish to know is it possible to register listener foe statechart and its block.If yes, how?
      Thanks in advance.

  5. Pingback: Extending a Java class with UDD | Undocumented Matlab

  6. Bluesmaster says:

    Dear Yair,

    is there a reason, why some properties can be listened to and some not?
    eg:

     h = handle( gcf );
     l = handle.listener( h, findprop( h , 'CurrentObject') ,  'PropertyPostSet' , @(~,~) disp('changed'));

    is not working while:

     h = handle( gca );
     l = handle.listener( h, findprop( h , 'XLim') ,  'PropertyPostSet' , @(~,~) disp('changed'))

    will.

    CurrentPoint, CurrentObj are very important to me, but I do not want to use mouse-click-events. Because I am programming a module that should be independent. As always: Thanks a lot

    Bluesmaster

    • @Bluesmaster – I assume that the reason is that CurrentObject is a dependent rather than a regular property. As such it is computed on-the-fly whenever you retrieve it, and so there is no place where Matlab can place a listener and know that it has changed. In fact, it may change multiple times before you retrieve it again and Matlab won’t know this because nobody asked to retrieve the latest value.

    • Bluesmaster says:

      that sounds reasonably, the interessting question would be:
      what does it depend on? Or can it be listened somewhere else?

      But I guess i know the answer 😉

    • Bluesmaster says:

      just a small update: CurrentObject seems not to be dependent, as it can be set by the user.

      Anyway for anyone whos interessted, I suggest the following workaround in order to listen to the CurrentObject:

      – creating a listener for new figures ( l = handle.listener( 0 , ‘ObjectChildAdded’, @myHandler); )
      – adding a clicklistener to all new figures ( handle.listener( curFig ,’WindowButtonDownEvent’, @myClickhandler )
      – creating a timer with small start delay that calls the method that needs to know the currentObject
      – perform gco

      Timer is needed, because the clicklistener has higher priority than the update of the currentObject

      best regards

      Bluesmaster

  7. Pingback: Continuous slider callback | Undocumented Matlab

  8. Rajeev says:

    Here is what I am trying to achieve. I have NI DAQ that I use to acquire 2 channel data continuously (session based, running in background). I use the inbuilt listner to tap the data for plotting and writing to disk. One of the input is a square waveform. Using this waveform, I want to segment the second channel and store in a matrix and keep updating the matrix on each iteration. I tried a few options but couldn’t achieve. Any idea how should I proceed?

    Thanks
    RY

  9. Pingback: uicontextmenu performance | Undocumented Matlab

  10. Pingback: Property value change listeners | Undocumented Matlab

  11. Pingback: uicontextmenu performance | Undocumented Matlab

  12. Yury Brodskiy says:

    Dear Yair,

    Do you know any substitute for build-in UDD events in new HG2 graphics? I am particularity interested in the ‘ObjectChildAdded’ event for axes object.
    From introspection of matlab.graphics.axis.Axes, it is clear that:
    it is not possible to listen to “children” property;
    it is not possible to subclass Axes and use ‘PreUpdate’ or ‘PostUpdate’ events.

    Thank you,

    Best regards,
    Yury

    • Yair Altman says:

      @Yuri – I am not aware of a corresponding event, but you could trap the all-encompassing MarkedClean event and within the callback compare the current Children value with the pre-stored one (e.g., in the ApplicationData). For example, something like this:

      addlistener(gca, 'MarkedClean', @markedCleanCallback);
       
      function markedCleanCallback(hAxes,eventData)
         lastChildrenValue = getappdata(hAxes, 'Children');
         if ~isequal(hAxes.Children, lastChildrenValue)
            setappdata(hAxes, 'Children', hAxes.Children);
            newChildren = setdiff(hAxes.Children, lastChildrenValue);
            % now do something interesting with newChildren
         end
      end  % markedCleanCallback
    • Sebastian says:

      Hi,

      I have come across the same problem with Matlab 2015b and have tried Yair’s “markedCleanCallback” solution. Unluckily it does not work, since the MarkedClean event is only fired, if a new child to an axis changes the axis state. This means e.g. that if you plot a single point into the center of an existing axis the event will not be fired.

      Since the properties “Children” and the events “PostUpdate” are not observable and protected, respectively, I don’t see any way to monitor the state of axis children in HG2 at the moment.

      Curious to see if anyone proves me wrong …

      Cheers

      Sebastian

    • @Sebastian – I believe that you are mistaken: if you add a new point to an axes, even at the center, the markedClean event is indeed triggered. For example:

      >> f = figure;  % new figure
      >> a = axes;    % new axes
      >> addlistener(a, 'MarkedClean', @(h,e)disp(e));
      >> plot(.5,.5);  % new point at center of axes - triggers callback below:
       
        Update with properties:
       
             Source: [1x1 Axes]
          EventName: 'MarkedClean'
    • In any case you can try any of the other axes events as described here: http://undocumentedmatlab.com/blog/undocumented-hg2-graphics-events

    • Sebastian says:

      If you add the following commands to your list you will see that the last plot added will not trigger any event:

      plot(.5,.6,'.'); % axis limits change, MarkedClean-event is triggered
      hold on;
      plot(.5,.55,'.'); % axis limits do not change, MarkedClean-event is not triggered

      In essence the MarkedClean event is only triggered if the appearance of the axis somehow changes. Concerning the other events mentioned in your link (I also checked to see if there are any additional events in 2015b) I don’t think how they could be used to monitor the number of childs.

      Sebastian

    • Sebastian says:

      Good news,

      I just downloaded release 2016a and the following events are now available for axes:

      ‘ChildAdded’, ‘ChildRemoved’, ‘NodeChildAdded’, ‘NodeChildRemoved’

      They are all public, so this should be really useful.

      Sebastian

  13. Vishnu S Nair says:

    Hi,
    The discussion details helped me to solve my problem. But I have some doubts,
    If I change the name of state or transition, how can I listen the events. I tried this below code:

    HG = sfgco;
    hProp = findprop(HG,'Name');
    hListener = handle.listener(HG, hProp, 'PropertyPostSet', @lineCallback)

    It listen the event when I right click and change LabelString in properties of Stateflow object. How can I listen when changing the name of Stateflow object from GUI.
    Thank you in advance.

  14. John toscano says:

    HOW CAN I MEET WITH THE MATRIX SOURCE

    WHY IS YOUR INTERACTION OBSCURE

  15. Joan F. Alonso says:

    Dear Yair,
    After reading Yury’s post and your response, I am trying to do something similar to detect deleted children. But it turns out that I get a

     0x1 empty GraphicsPlaceholder array

    Is there any other way to detect if a user deleted a line or a patch?
    Thank you! And Happy New Year, by the way!

  16. Miguel Secundino says:

    This discussion is the only place that I found with some relation with my question:
    Could be possible minimize the Simulink editor window from Matlab?
    I have spended a lot of time looking for some answer.
    The reason is that I need open a Simulink block and after I need to hidde the editor window.
    Thanks in advance.
    M. Secundino

Leave a Reply


Your email address will not be published. Required fields are marked *