Undocumented Matlab
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT

Setting axes tick labels format

April 18, 2012 18 Comments

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);

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

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

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

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.

Related posts:

  1. Customizing axes tick labels – Multiple customizations can be applied to tick labels. ...
  2. Customizing axes part 5 – origin crossover and labels – The axes rulers (axles) can be made to cross-over at any x,y location within the chart. ...
  3. Axes LooseInset property – Matlab plot axes have an undocumented LooseInset property that sets empty margins around the axes, and can be set to provide a tighter fit of the axes to their surroundings....
  4. Customizing axes rulers – HG2 axes can be customized in numerous useful ways. This article explains how to customize the rulers. ...
  5. Undocumented HG2 graphics events – Matlab's new HG2 graphics engine includes many new undocumented events that could be used in various ways. ...
  6. FIG files format – FIG files are actually MAT files in disguise. This article explains how this can be useful in Matlab applications....
Handle graphics Listener Pure Matlab schema.prop Undocumented function
Print Print
« Previous
Next »
18 Responses
  1. Ed April 25, 2012 at 06:56 Reply

    This is fantastic! I had long since given up hope of finding a solution to this problem. Thanks very much.

  2. Lubos Smolik January 9, 2013 at 02:40 Reply

    Brilliant!

    Very useful in 3D rotordynamics graphs when

    axis normal;

    axis normal;

    gives ugly results. Thank you very much 😉

  3. Yuri April 30, 2013 at 12:12 Reply

    Yair,
    Have you looked at how MATLAB works with scientific notation format of tick labels putting the exponent only into one place on the axes? The XTickLabels are only what one can see there without exponent. Where MATLAB hides the exponent? Is there a way to change it? to enforce this format? I know only way with manually changing the ticks and adding exponent as a text object. As in this question, for example:
    http://stackoverflow.com/questions/13326894/force-exponential-format-of-ticks-like-matlab-does-it-automatically
    Any undocumented staff?

    • Yair Altman April 30, 2013 at 12:52 Reply

      @Yuri – I do not know of a way to do this with the current HG system. HG axes have some related hidden properties (ExpFontAngle, ExpFontName, ExpFontSize, ExpFontStrikeThrough, ExpFontUnderline, ExpFontUnits, ExpFontWeight) but they don’t appear to have any effect as far as I can tell (maybe someone could find out).

      I believe that I saw some configurable exponent properties in the new HG2. I haven’t tested HG2 on the latest releases lately, so try it (using the -hgVersion2 startup switch as mentioned in the article) and let us know what you discover.

      p.s. – wow, has it been 2 full years already? – it’s about time HG2 got released…

      • Yair Altman May 28, 2013 at 16:55

        @Yuri – as followup, customizing the exponent is indeed possible in HG2, as I have recently shown.

  4. Nico May 13, 2013 at 05:09 Reply

    Just found this post yesterday and really liked it. Using this method, you can avoid the stupid identical labels that appear when you zoom in real close in a large plot. Also, the ticklabelformat function from matlabcentral can be modified quite easily to scale the tick values, which can be extremely helpful. Thanks, Yair.

  5. damayi December 2, 2014 at 23:38 Reply

    I always use this method for MATLAB 2013B, it works fine.
    However, it does not work for 2014b, the latest released version.

    • Yair Altman December 4, 2014 at 14:35 Reply

      @Damayi – This is a known issue that is due to the changes in property listeners that were done for the new HG2 graphics engine in R2014b.

      • damayi December 27, 2014 at 01:23

        Do you have a resolution for this problem?

    • Yair Altman January 5, 2015 at 07:28 Reply

      @Damayi – Yes, in HG2 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.

  6. Robert January 25, 2015 at 08:41 Reply

    Yair,

    thanks for providing this kind of input and the ticklabelformat. I’m also thankful for the comments as I had no idea why the callbacks for axis properties do not work anymore with HG2. Already in HG1 some things are confusing, since somehow the event argument is of different type, depending on…well I don’t really know. But in HG2 addlistener does somehow not work on those properties.

    Would it be possible to provide a fully working example callback function for HG2 (maybe in the docstring of the ticklabelformat function)?. The one given above apparently does not work (only tested with HG2). The second argument has only two properties: .Source and something else. I tried to access the ticks via – I think it was – .Source.Tick, but it did not work properly and even crashed MATLAB. All my old callbacks do not work anymore with the new HG2. Sad, that TWM does not supply easy instructions on updating customized tick labels as this is quite a substantial thing, when examining data using a plot and the pan/zoom functionality. Thanks.

  7. Sandor Toth August 3, 2015 at 07:01 Reply

    Dear Yair,

    do you know a way to change the XTickLabel vertical position, a.k.a its distance from the x-axis line? The generally suggested solution is to recreate the labels using the text() command, but there should be a simpler way to do it!
    Do you have any suggestion?

    Thanks,
    Sandor

  8. fulvio setti April 28, 2016 at 15:23 Reply

    YEEEESSSS !!! It’s many time that i searched this way to have MORE digit in the graphs !!!! THAAANK YOU !!!!

  9. Jim Hokanson November 29, 2017 at 21:18 Reply

    Yair,

    Would it be possible to add an “update” section by the blurb on the listener to describe the change with HG2? I’m specifically thinking something that references MarkedClean and YRuler. I’m fine but it might be nice for others …

    Thanks,
    Jim

    • Yair Altman December 6, 2017 at 16:29 Reply

      @Jim – done 🙂

  10. Ron Dekel June 20, 2018 at 15:43 Reply

    I wonder – is it possible to use images as axis tick labels?
    Ron

    • Yair Altman June 21, 2018 at 16:14 Reply

      @Ron – Tick labels are strings. If you want to use images, then set the ticklabels to empty (i.e., do not show any ticks) and programmatically add images at the relevant [negative] axes locations based on the computed tick positions. You will need to update the images and their location whenever the axes resizes or zooms or pans or changes its ticks in whichever way (details). To be honest, I’m not sure that it’s worth all this effort…

  11. Antonius Armanious August 7, 2019 at 12:51 Reply

    Do you know of a way to change the position of one of the tick labels so that it does not overlap with the tick labels from the neighbouring axes?
    Thanks

Leave a Reply
HTML tags such as <b> or <i> are accepted.
Wrap code fragments inside <pre lang="matlab"> tags, like this:
<pre lang="matlab">
a = magic(3);
disp(sum(a))
</pre>
I reserve the right to edit/delete comments (read the site policies).
Not all comments will be answered. You can always email me (altmany at gmail) for private consulting.

Click here to cancel reply.

Useful links
  •  Email Yair Altman
  •  Subscribe to new posts (feed)
  •  Subscribe to new posts (reader)
  •  Subscribe to comments (feed)
 
Accelerating MATLAB Performance book
Recent Posts

Speeding-up builtin Matlab functions – part 3

Improving graphics interactivity

Interesting Matlab puzzle – analysis

Interesting Matlab puzzle

Undocumented plot marker types

Matlab toolstrip – part 9 (popup figures)

Matlab toolstrip – part 8 (galleries)

Matlab toolstrip – part 7 (selection controls)

Matlab toolstrip – part 6 (complex controls)

Matlab toolstrip – part 5 (icons)

Matlab toolstrip – part 4 (control customization)

Reverting axes controls in figure toolbar

Matlab toolstrip – part 3 (basic customization)

Matlab toolstrip – part 2 (ToolGroup App)

Matlab toolstrip – part 1

Categories
  • Desktop (45)
  • Figure window (59)
  • Guest bloggers (65)
  • GUI (165)
  • Handle graphics (84)
  • Hidden property (42)
  • Icons (15)
  • Java (174)
  • Listeners (22)
  • Memory (16)
  • Mex (13)
  • Presumed future risk (394)
    • High risk of breaking in future versions (100)
    • Low risk of breaking in future versions (160)
    • Medium risk of breaking in future versions (136)
  • Public presentation (6)
  • Semi-documented feature (10)
  • Semi-documented function (35)
  • Stock Matlab function (140)
  • Toolbox (10)
  • UI controls (52)
  • Uncategorized (13)
  • Undocumented feature (217)
  • Undocumented function (37)
Tags
AppDesigner (9) Callbacks (31) Compiler (10) Desktop (38) Donn Shull (10) Editor (8) Figure (19) FindJObj (27) GUI (141) GUIDE (8) Handle graphics (78) HG2 (34) Hidden property (51) HTML (26) Icons (9) Internal component (39) Java (178) JavaFrame (20) JIDE (19) JMI (8) Listener (17) Malcolm Lidierth (8) MCOS (11) Memory (13) Menubar (9) Mex (14) Optical illusion (11) Performance (78) Profiler (9) Pure Matlab (187) schema (7) schema.class (8) schema.prop (18) Semi-documented feature (6) Semi-documented function (33) Toolbar (14) Toolstrip (13) uicontrol (37) uifigure (8) UIInspect (12) uitable (6) uitools (20) Undocumented feature (187) Undocumented function (37) Undocumented property (20)
Recent Comments
Contact us
Captcha image for Custom Contact Forms plugin. You must type the numbers shown in the image
Undocumented Matlab © 2009 - Yair Altman
This website and Octahedron Ltd. are not affiliated with The MathWorks Inc.; MATLAB® is a registered trademark of The MathWorks Inc.
Scroll to top