banner



Matlab App Designer Axes Plot

Have you ever tried to customize the way in which tick labels appear in Matlab plot axes?

For example, setting the numerical precision of the labels, or adding some short descriptive text (for example, the units)? If you have, then I bet that you have encountered the following dilemma: Once we modify the tick labels (for discussion sake, let's assume the Y axis, so this is done by updating the YTickLabel property), then the corresponding YTickLabelMode property changes from 'auto' to 'manual' and loses its relationship to the tick values (YTick). So, if we now zoom or pan the plot, our new labels remain unchanged although the tick values have changed, causing much panic and frustration… If we also set the tick values manually, this solves that problem but leaves us with another: now, when we zoom or pan, we no longer see any ticks or tick labels at all!

Original plot Manual labels, auto ticks Manual labels, manual ticks

Original plot (left)
Manual labels, auto ticks (center)
Manual labels, manual ticks (right)

Of course, we can always trap the zoom and pan callback functions to update the tick labels dynamically while keeping the tick values automatically. This will work for these cases, but we need to do it separately for zoom and pan. Also, if we modify the axes limits explicitly (via the corresponding YLim property) or indirectly (by modifying the displayed plot data), then the callbacks are not called and the labels are not updated.

The solution – using a property change listener

A better way to solve this problem is to simply trap changes to the displayed tick values, and whenever these occur to call our dedicated function to update the labels according to the new tick values. This can be done by using UDD, or more precisely the ability to trap update events on any property (in our case, YTick). Such a mechanism was already demonstrated here in 2010, as one way to achieve continuous slider feedback. The idea is to use the built-in handle.listener function with the PropertyPostSet event, as follows:

hhAxes = handle(hAxes);            % hAxes is the Matlab handle of our axes            hProp =            findprop            (hhAxes,'YTick'            );            % a schema.prop object            hListener =            handle.listener                        (hhAxes, hProp,            'PropertyPostSet',            @myCallbackFunction);            setappdata            (hAxes,            'YTickListener', hListener);

Note that we have used setappdata to store the hListener handle in the axes. This ensures that the listener exists for exactly as long as the axes does. If we had not stored this listener handle somewhere, then Matlab would have immediately deleted the listener hook and our callback function would not have been called upon tick value updates. Forgetting to store listener handles is a common pitfall when using them. If you take a look at the addlistener function's code, you will see that it also uses setappdata after creating the listener, for exactly this reason. Unfortunately, addlistsner cannot always be used, and I keep forgetting under which circumstances, so I generally use handle.listener directly as above: It's simple enough to use that I find I never really need to use the simple addlistener wrapper, but you are welcome to try it yourself.

That's all there is to it: Whenever YTick changes its value(s), our callback function (myCallbackFunction) will automatically be called. It is quite simple to set up. While we cannot use TeX in tick labels yet (this will change in the upcoming HG2), using sprintf formatting does enable quite a bit of flexibility in formatting the labels. For example, let's say I want my tick labels to have the format '%.1fV' (i.e., always one decimal, plus the Volts units):

            function            myCallbackFunction(hProp,eventData)            %#ok - hProp is unused            hAxes = eventData.AffectedObject;    tickValues =            get            (hAxes,'YTick'            );    newLabels = arrayfun(            @            (value)            (            sprintf            (            '%.1fV',value)            ), tickValues,            'UniformOutput',false            );            set            (hAxes,            'YTickLabel', newLabels);            end            % myCallbackFunction          

Manual labels, automatically updated Manual labels, automatically updated

Manual labels, automatically updated

Handling duplicate tick labels

Of course, '%.1fV' may not be a good format when we zoom in to such a degree that the values differ by less than 0.1 – in this case all the labels will be the same. So let's modify our callback function to add extra decimals until the labels become distinct:

            function            myCallbackFunction(hProp,eventData)            %#ok - hProp is unused            hAxes = eventData.AffectedObject;    tickValues =            get            (hAxes,'YTick'            );            %newLabels = arrayfun(@(value)(sprintf('%.1fV',value)), tickValues, 'UniformOutput',false);            digits =            0;    labelsOverlap =            true;            while            labelsOverlap            % Add another decimal digit to the format until the labels become distinct            digits = digits +            1;            format            =            sprintf            (            '%%.%dfV',digits);       newLabels = arrayfun(            @            (value)            (            sprintf            (            format,value)            ), tickValues,            'UniformOutput',false            );       labelsOverlap =            (            length            (newLabels)            >            length            (            unique            (newLabels)            )            );            % prevent endless loop if the tick values themselves are non-unique            if            labelsOverlap            &&            max            (            diff            (tickValues)            )<            16*eps            break;            end            end            set            (hAxes,            'YTickLabel', newLabels);            end            % myCallbackFunction          

non-distinct labels distinct labels

Non-distinct labels                   distinct labels

ticklabelformat

Based on a file that I received from an anonymous reader a few years ago, I have prepared a utility called ticklabelformat that automates much of the set-up above. Feel free to download this utility and modify it for your needs – it's quite simple to read and follow. The usage syntax is as follows:

ticklabelformat(            gca,'y','%.6g V'            )            % sets y axis on current axes to display 6 significant digits            ticklabelformat(            gca,'xy','%.2f'            )            % sets x & y axes on current axes to display 2 decimal digits            ticklabelformat(            gca,'z',@myCbFcn)            % sets a function to update the Z tick labels on current axes            ticklabelformat(            gca,'z',{            @myCbFcn,extraData}            )            % sets an update function as above, with extra data          

Addendum for HG2 (R2014b or newer releases)

As I've indicated in my comment response below, in HG2 (R2014b or newer) we need to listen to the HG object's MarkedClean event, rather than to a property change event.

I've updated my ticklabelformat utility accordingly, so that it works with both HG1 (pre-R2014a) and HG2 (R2014b+). Use this utility as-is, and/or look within its source code for the implementation details.

Matlab App Designer Axes Plot

Source: https://undocumentedmatlab.com/blog_old/setting-axes-tick-labels-format

Posted by: buntingaceis1940.blogspot.com

0 Response to "Matlab App Designer Axes Plot"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel