matlabooplistenerpresetsetpropertyactionlistener

In Matlab, what is the correct syntax for passing new variables when using a PreSet listener


I'm writing a class definition that uses listeners to modify an object when certain properties are set. Like so:

classdef MyObject < handle
    properties (SetObservable)
        orientation = 'h'; % h = horizontal, v = vertical, o = other
        length
        width
    end
    methods
        % Constructor
        function mo = MyObject(o)
            mo.orientation = o;
            addlistener(mo, 'orientation', 'PreSet', @mo.changeOrientation);
        end

        % Change Orientation Listener
        function changeOrientation(mo, src, evnt)
            celldisp(src);
            celldisp(evnt);
            % I want a way to access newor here
            if mo.orientation == 'h' && newor == 'o'
                tempw = mo.width
                mo.width = mo.length
                mo.length = tempw;
            end
        end

        % Setter
        function set.orientation(mo, newor)
            mo.orientation = newor;
        end
    end
end

I want to be able to use the variable newor when I set the orientation. How do I pass the new orientation variable to the changeOrientation method?

I want to avoid moving the contents of changeOrientation into the set.orientation method because Matlab complains about properties (length and width) potentially not being initialized.

EDIT length is NOT dependent on orientation. I just need to swap length and width when the orientation changes. In other cases, the user should be able to set length or width to any positive value.


Solution

  • You cannot do this as the PreSet listener is just that, a listener. The data passed to the listener callback never makes it way back to the object and modifying its value within your listener has no influence.

    The purpose of using a PreSet listener is to get the value of the property before it is changed, but not to modify any values before they are actually assigned.

    In the code that you have posted, I would likely just do any validation/modification of orientations within the set.orientation method of your class.

    function updateLength(mo)
        % Change mo.length here and others based on mo.orientation
    end
    
    function set.orientation(mo, newor)
        % validate newor
        if dovalidation(newor)
            mo.orientation = newor;
        else
            error('invalid!');
        end
    
        % Now trigger a "callback"
        mo.updateDependentVariables()
    end
    

    EDIT

    Based on your comment, probably the better way to go about this is to make length have a shadow property length_ that holds the value the user assigns to length. When the user requests the value of length it can either return this stored value (if orientation == 'v') or the width (if orientation == 'h')

    classdef MyObject
        properties (Dependent)
            length   % Can be either `width_` or `length_`
            width    % Can be either `width_` or `length_`
        end
    
        properties
            orientation
        end
    
        properties (Access = 'protected')
            length_ = 12
            width_ = 10
        end
    
        methods
            function mo = MyObject(o)
                mo.orientation = o;
            end
    
            function set.orientation(mo, newor)
                % If the user supplies an "o" flip the orientation
                if strcmpi(newor, 'o')
                    if strcmpi(mo.orientation, 'v')
                        newor = 'h';
                    else
                        newor = 'v';
                    end
                end
    
                mo.orientation = newor;
            end    
    
            function set.length(mo, value)
                if strcmpi(mo.orientation, 'h')
                    self.length_ = value;
                else
                    self.width_ = value;
                end
            end
    
            function set.width(mo, value)
                if strcmpi(mo.orientation, 'h')
                    self.width_ = value;
                else
                    self.length_ = value;
                end
            end
    
            function res = get.width(mo)
                switch lower(mo.orientation)
                    case 'h'
                        res = mo.width_;
                    case 'v'
                        res = mo.length_;
                    otherwise
                        error('Invalid orientation');
                end
            end
    
            function res = get.length(mo)
                switch lower(mo.orientation)
                    case 'h'
                        res = mo.length_;
                    case 'v'
                        res = mo.width_;
                    otherwise
                        error('Invalid orientation');
                end
            end     
        end
    end
    

    This way you don't have to explicitly update length when you change the orientation.

    As a side note, I would not use length as a property as that gets a little confused with the built-in function length.