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

81 views Asked by At

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.

2

There are 2 answers

14
Suever On BEST ANSWER

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.

3
nirvana-msu On

You do not need an extra listener for that. That's what custom setter / getter methods are for. There is no point having set.orientation method as in your example, it does nothing apart from assignment which would happen anyway. Instead, use this method to make the extra call to changeOrientation:

classdef MyObject < handle
    properties (SetObservable)
        orientation = 'h'; % h = horizontal, v = vertical
    end
    methods
        % Constructor
        function mo = MyObject(o)
            mo.orientation = o;
        end

        % Change Orientation Listener
        function changeOrientation(mo, newor)
            % Use newor here!
        end

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