Property value change listeners

For performance reasons, it is almost always better to respond to events (asynchronously), than to continuously check a property value (synchronous polling). Therefore, if we wish to do something when some property value is changed (e.g., log the event, shut down the system, liquidate the portfolio, call the police, …), then it is preferable to attach a property-change listener callback.

The standard (documented) way of attaching a value-change listener to Matlab class properties is via the addlistener function. This only works for handle (not value) classes, and only to those properties that have the SetObservable and/or GetObservable attribute turned on:

addlistener(hClassObject, propertyName, 'PostSet', @myCallbackFcn);

This is all nice and well for Matlab class properties, but what about HG (handle Graphics: plots & GUI) properties? Can we similarly listen to changes in (say) the axes limits? Until now this has been possible, but undocumented. For example, this will trigger myCallbackFcn(hAxes,eventData) whenever the axes limits change (due to zoom, pan, plotting etc.):

addlistener(gca, 'YLim', 'PostSet', @(hAxes,eventData) myCallbackFcn(hAxes,eventData));
 
% Or (shorter equivalent):
addlistener(gca, 'YLim', 'PostSet', @myCallbackFcn);

This could be very useful when such properties could be modified from numerous different locations. Rather than updating all these location to call the relevant callback function directly, we simply attach the callback to the property-change listener. It could also be useful in cases where for some reason we cannot modify the source of the update (e.g., third-party or legacy code).

In addition to PostSet, we could also set listeners for PreSet. Also, we could set listeners on PostGet and PreGet – this could be useful for calculating dynamic (dependent) property values.

Under the hood

The HG1 variant of addlistener is basically equivalent to the following low-level UDD-based code snippet, as explained here:

% Create the listener object
hAxes = handle(gca);  % a UDD axes class object
hProp = findprop(hAxes,'YLim');  % a schema.prop class object
hListener = handle.listener(hAxes, hProp, 'PropertyPostSet', @myCallbackFcn);
 
% persist the listener in memory for as long as the source object (hAxes) is alive
setappdata(hAxes, 'listener__', hListener);

(in old Matlab releases, addlistener was a regular m-function that placed the listeners in the source handle’s ApplicationData property, as the code above shows; newer releases reimplemented addlistener as an internal built-in function and the listener is now stored somewhere in the DLL’s inaccessible memory, rather than in the ApplicationData property)

In HG2, handle.listener no longer works. Fortunately, we don’t need it since we have addlistener that works the same way for both HG1 (UDD objects) and HG2 (MCOS objects). Kudos on the backward-compatibility aspect, MathWorks!

Categories: GUI, Handle graphics, Listeners, Low risk of breaking in future versions, Stock Matlab function, Undocumented feature

Tags: , , , ,

Bookmark and SharePrint Print

9 Responses to Property value change listeners

  1. David says:

    You can also call the event.proplistener constructor directly instead of using add listener which creates a listener not tied to the lifecycle of the object being listened to.

    http://www.mathworks.co.uk/help/matlab/ref/event.proplistener.html

    • @David – I think you have it backwards: event.proplistener creates a standalone listener that is not tied to the object’s lifecycle. This means that it works only as long as the listener variable is in memory (not cleared). In order to persist it to live as long as the object, the typical use would be to store this listener in the object’s UserData or ApplicationData properties.

      On the other hand, addlistener already does all this for us. It is therefore both easier and safer to use.

    • David says:

      Hi Yair,

      Maybe there should have been a comma or two in my original statement to avoid confusion as we are definitely both talking about the same thing. Typical usage (assuming we are all using OOP) would be to store the event.listener/event.proplistener as a property in the class where it was created. In my applications I almost always use event.listener and event.proplistener as advised by MathWorks.

      Revised:
      “You can also call the event.proplistener constructor directly, instead of using addlistener, which creates a listener not tied to the lifecycle of the object being listened to.”

    • David says:

      Or put another way…

      “Instead of using addlistener you can call the event.proplistener constructor directly which creates a listener not tied to the lifecycle of the object being listened to”.

  2. John says:

    Yair,

    Is it possible using this method or others to listen to properties that are not considered observable? My example is that I’d like to listen for, and respond to, changes to a figure’s ‘Position’ property. Ordinarily I trigger this by using ResizeFcn, but in this case I want the behavior to be triggered afterwards.

    I tried (from memory…)

    lh = addlistener(fignum, 'Position', 'PostSet', @mypostresizefcn);

    and MATLAB accepted this without error, but actually ignored it – evidently since Position is not observable. (MATLAB should at least give me an error to tell me that my listener is not acceptable.)

    Thanks,
    John

    • @John – I do not know of a way to listen to non-observable properties. Perhaps there is a way, but I don’t know it.

    • Erik says:

      @John – Not the most elegant solution – but you could try to listen for an Event that always executes when position is changed. Using Matlab 2015a; for example, to determine potential event names for an axes position change:

      hAxes = axes;
      md = ?matlab.graphics.axis.Axes;
      eventNames = {md.EventList.Name};
      for iEvent = 1:numel(eventNames)
          addlistener(hAxes, eventNames{iEvent}, @(~,~) disp(eventNames{iEvent}));
      end

      And then change your axes position in various manners to see what Events occur. My very-quick testing indicates Events {‘MarkedDirty’, ‘LocationChanged’, ‘SizeChanged’, ‘MarkedClean’} are called in that order.

      Keep in mind some Events {‘MarkedDirty’, ‘MarkedClean’} are called for all sorts of (non-position related) reasons.

    • Julian says:

      Hi John,

      you can listen to a ‘SizeChanged’ Property in HG2 handling

      h = addlistener(gcf,'SizeChanged',@func);
  3. Collin Pecora says:

    Is there a way to listen for property changes of ‘pure’ java objects created in matlab?

    Specificaly, I would like to listen to the value property of a jFormattedTextField as suggested by the javadocs

Leave a Reply


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