0

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.

toshiomagic
  • 1,335
  • 1
  • 12
  • 39

2 Answers2

1

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.

Suever
  • 64,497
  • 14
  • 82
  • 101
  • This is what I would like to do. However, I have another property, length, that will change when the orientation changes. And when I try to set `mo.length` in the `set.orientation` method, matlab gets angry at me and tells me not to do that. – toshiomagic Mar 09 '16 at 20:55
  • @toshiomagic Yes MATLAB complains about that. It is because during object construction or loading from file, it's not guaranteed that both `length` and `orientation` are valid values. Have you considered making `length` a dynamic property? Can yo update your post to include information on this other property? – Suever Mar 09 '16 at 20:57
  • @toshiomagic You can also setup a "callback" functions of sorts like I have noted above which is similar to your previous setup just without the listener. – Suever Mar 09 '16 at 21:00
  • @toshiomagic updated with an example of using dependent properties for this. – Suever Mar 09 '16 at 21:05
  • length is not dependent. When someone gets mo.length they should be getting the value and not calculating it every time. I just need to swap length and width in certain cases. This is a much more complex object than I'm showing. – toshiomagic Mar 09 '16 at 21:06
  • @toshiomagic It now supports providing it with an orientation of 'o' which is translated to the opposite of the current orientation and stored. – Suever Mar 09 '16 at 21:14
  • length is not dependent – toshiomagic Mar 09 '16 at 21:16
  • @toshiomagic Please actually read my solution. I have used `height` to refer to one dimension and `width` as the other. As you said `length` can me **either** one so it *is* dependent. The `height` and `width` are the things that you would calculate and are not dependent. – Suever Mar 09 '16 at 21:17
  • no, it is not dependent. I cannot detail the entire application that I'm trying to create this object for. I cannot make length dependent on the orientation because the user can set the length and width however they want at any time. The terminology does not change like you are saying with height. There is no height. What you are calling 'height' is actually length. So when a user wants to set length, they will say mo.length = x. Regardless of the orientation. – toshiomagic Mar 09 '16 at 21:22
  • @toshiomagic Please checkout my new solution. It meets all the requirements that you have mentioned. – Suever Mar 09 '16 at 21:27
  • I appreciate the effort, but I cannot accept any "solution" in which length is dependent. I simply cannot do it. It would take a few pages to explain why this isn't possible. So I'll leave it at that. The user does not think of length in the same terms that you are thinking of it. – toshiomagic Mar 09 '16 at 21:32
  • @toshiomagic Well then it is simply too much to ask for this question. Your question was how you could work with your PreSet listener and I provided you two suitable alternatives. The first one will probably *even* work if you'd try it (hell, the second one probably would too). If you have more questions, create a new question but you got the answer to this question. – Suever Mar 09 '16 at 21:36
  • @toshiomagic Just curious. Did you even **_try_** my full second solution? I guarantee it will work for what you're talking about. And the first solution I provided doesn't have length as a dependent variable... – Suever Mar 09 '16 at 21:40
  • You are correct. Your method does work. The simple answer to the question title is that it is not possible. However, your solution, while syntactically correct, does not solve my specific problem. I'll mark the answer correct because it answers the question title. But I can't help if your feelings are hurt because I'm not going to use your solution. – toshiomagic Mar 11 '16 at 17:31
0

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
nirvana-msu
  • 3,877
  • 2
  • 19
  • 28
  • Can I set other variables inside of changeOrientation? See comment on other answer. – toshiomagic Mar 09 '16 at 20:57
  • Yes, you can - you need to make one of the properties [Dependent](http://uk.mathworks.com/help/matlab/matlab_oop/access-methods-for-dependent-properties.html) on the other. – nirvana-msu Mar 09 '16 at 21:00
  • The user can change the length at any time. It is not strictly dependent on the orientation. I just need to swap width and length when orientation is changed to 'o.' – toshiomagic Mar 09 '16 at 21:05