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 |
---|---|
notify | send |
event.EventData | handle.EventData |
events block | schema.event |
event.listener | handle.listener |
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 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 type | event trigger |
---|---|
handle.ClassEventData | ClassInstanceCreated |
handle.EventData | ObjectBeingDestroyed |
handle.ChildEventData | ObjectChildAdded, ObjectChildRemoved |
handle.ParentEventData | ObjectParentChanged |
handle.PropertyEventData | PropertyPreGet, PropertyPostGet |
handle.PropertySetEventData | PropertyPreSet, 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); |
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.
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?
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:
So for example to obtain the handle for the current blockdiagram you could use:
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:
where gcb returns the currently selected block. Then
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:
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
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….
@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
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 …
@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:
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:
to identify the subsystems that it contains and add listeners for them.
Best Regards,
Donn
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.
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:
You will find in the list that Simulink windows are of type:
There are a small number of java methods for these objects and no events as far as I can tell.
Donn
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.
[…] During the series on UDD, we have mentioned the connection between UDD and Java. In UDD Events and Listeners we described how in Matlab, each Java object can have a UDD companion […]
Dear Yair,
is there a reason, why some properties can be listened to and some not?
eg:
is not working while:
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.
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 😉
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
[…] It doesn’t matter where or how exactly you store hListener, since you will not use it directly in your program. The important thing is just to store it *anywhere*, so that it remains in persistent (heap) memory. As long as the reference handle is “alive”, the listener will keep working. This is explained here. […]
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
@Rajeev – this is not the right forum for such questions. Try the Answers forum.
[…] Advanced users could use handle listeners to attached a callback such that when a plot element is deleted, so too are its associated context menus […]
[…] addlistener is basically equivalent to the following low-level UDD-based code snippet, as explained here […]
[…] in 0.29 seconds figure contains 1418 objects – drawn in 0.29 seconds …Advanced users could use handle listeners to attached a callback such that when a plot element is deleted, so too are its associated context […]
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
@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: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:In any case you can try any of the other axes events as described here: http://undocumentedmatlab.com/blog/undocumented-hg2-graphics-events
If you add the following commands to your list you will see that the last plot added will not trigger any event:
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
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
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:
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.
HOW CAN I MEET WITH THE MATRIX SOURCE
WHY IS YOUR INTERACTION OBSCURE
Ha ???
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
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!
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