Controlling plot data-tips

Plot data tips are a great visualization aid for Matlab plots. They enable users to interactively click on a plot location and see a tool-tip that contains the clicked location’s coordinates. The displayed tooltip text is even customizable using documented properties of the datacursormode object.

plot data tips

plot data tips

A client has recently asked me to automatically display an attached data-tip to the last data point of a plotted time series of values. The idea was to immediately see what the latest value of the data series is.

Unfortunately, the official documentation clearly says that:

You place data tips only by clicking data objects on graphs. You cannot place them programmatically (by executing code to position a data cursor).

Well, this has never stopped us before, has it?

Creating new data tips

Under the hood, data tips use a data-cursor mode, which shares many similarities in behavior and programming code with the other plot modes (zoom, pan, data-brushing, etc.). At any one time, only a single such mode can be active in any figure window (this is a known limitation of the design). The code itself it actually quite complex and handles numerous edge-cases. Understanding it by simply reading the code (under %matlabroot%\toolbox\matlab\graphics\) is actually pretty difficult. A much easier way to understand the programming flow is to liberally distribute breakpoints (start in datacursormode.m) and interactively activate the functionality, then debug the code step-by-step.

Luckily, it turns out that the code to create a new data-tip is actually quite simple: first get the data-cursor mode object, then create a new data tip using the mode’s createDatatip() method, update some data-tip properties and finally update the data-tip’s position:

% First plot the data
hLine = plot(xdata, ydata);
 
% First get the figure's data-cursor mode, activate it, and set some of its properties
cursorMode = datacursormode(gcf);
set(cursorMode, 'enable','on', 'UpdateFcn',@setDataTipTxt, 'NewDataCursorOnClick',false);
% Note: the optional @setDataTipTxt is used to customize the data-tip's appearance
 
% Note: the following code was adapted from %matlabroot%\toolbox\matlab\graphics\datacursormode.m
% Create a new data tip
hTarget = handle(hLine);
hDatatip = cursorMode.createDatatip(hTarget);
 
% Create a copy of the context menu for the datatip:
set(hDatatip,'UIContextMenu',get(cursorMode,'UIContextMenu'));
set(hDatatip,'HandleVisibility','off');
set(hDatatip,'Host',hTarget);
set(hDatatip,'ViewStyle','datatip');
 
% Set the data-tip orientation to top-right rather than auto
set(hDatatip,'OrientationMode','manual');
set(hDatatip,'Orientation','top-right');
 
% Update the datatip marker appearance
set(hDatatip, 'MarkerSize',5, 'MarkerFaceColor','none', ...
              'MarkerEdgeColor','k', 'Marker','o', 'HitTest','off');
 
% Move the datatip to the right-most data vertex point
position = [xdata(end),ydata(end),1; xdata(end),ydata(end),-1];
update(hDatatip, position);

Note: If you don’t like messing with the code, consider using Tim Farajian’s MakeDataTip utility, which basically does all this behind the scenes. It is much easier to use as a stand-alone utility, although it does not give you the flexiblility with all the data-tip properties as in the code above.

Updating an existing data tip

To modify the appearance of a data-tip, we first need to get access to the hDatatip object that we created earlier, either programmatically, or interactively (or both). Since we can access pre-stored handles only of programmatically-created (not interactively-created) data-tips, we need to use a different method. There are actually two ways to do this:

The basic way is to search the relevant axes for objects that have Tag=’DataTipMarker’. For each data-tip, we will get two such handles: one for the marker (Type=’line’) and the other for the text box tooltip (Type=’text’). We can use these to update (for example) the marker size, color and style; and the text’s font, border and colors.

A better way is to access the graphics.datatip object itself. This can be done using two hidden properties of the datacursormode object:

% Get the list of all data-tips in the current figure
>> cursorMode = datacursormode(gcf)
cursorMode =
	graphics.datacursormanager
 
>> cursorMode.DataCursors
ans =
	graphics.datatip: 2-by-1
 
>> cursorMode.CurrentDataCursor
ans =
	graphics.datatip
 
>> cursorMode.CurrentDataCursor.get
            Annotation: [1x1 hg.Annotation]
           DisplayName: ''
           HitTestArea: 'off'
          BeingDeleted: 'off'
         ButtonDownFcn: []
              Children: [2x1 double]
              Clipping: 'on'
             CreateFcn: []
             DeleteFcn: []
            BusyAction: 'queue'
      HandleVisibility: 'off'
               HitTest: 'off'
         Interruptible: 'on'
                Parent: 492.005493164063
    SelectionHighlight: 'on'
                   Tag: ''
                  Type: 'hggroup'
              UserData: []
              Selected: 'off'
             FontAngle: 'normal'
              FontName: 'Helvetica'
              FontSize: 8
             FontUnits: 'points'
            FontWeight: 'normal'
             EdgeColor: [0.8 0.8 0.8]
       BackgroundColor: [1 1 0.933333333333333]
             TextColor: [0 0 0]
                Marker: 'o'
            MarkerSize: 5
       MarkerEdgeColor: 'k'
       MarkerFaceColor: 'none'
       MarkerEraseMode: 'normal'
             Draggable: 'on'
                String: {'Date: 01/09/11'  'Value: 573.24'}
               Visible: 'on'
             StringFcn: []
             UpdateFcn: []
         UIContextMenu: [1x1 uicontextmenu]
                  Host: [1x1 graph2d.lineseries]
           Interpolate: 'off'

We can see that the returned graphics.datatip object includes properties of both the text-box and the marker, making it easy to modify. Moreover, we can use its aforementioned update method to move the datatip to a different plot position (see example in the code above). In addition, we can also use the self-explanatory getCursorInfo(), getaxes(), makeCurrent(), movetofront() methods, and a few others.

Cursor mode and data-tip properties

The graphics.datacursormanager and the graphics.datatip objects have several public properties that we can use:

>> cursorMode.get
              Enable: 'off'
    SnapToDataVertex: 'on'
        DisplayStyle: 'datatip'
           UpdateFcn: @setDataTipTxt
              Figure: [1x1 figure]
 
>> cursorMode.CurrentDataCursor.get
            Annotation: [1x1 hg.Annotation]
           DisplayName: ''
           HitTestArea: 'off'
      ... % See the list above

Both these objects have plenty of additional hidden properties. You can inspect them using my uiinspect utility. Here is a brief list for reference (R2011b):

graphics.datacursormanager:

  • CurrentDataCursor
  • DataCursors
  • Debug
  • DefaultExportVarName
  • DefaultPanelPosition
  • EnableAxesStacking
  • EnableZStacking
  • ExternalListeners
  • HiddenUpdateFcn
  • NewDataCursorOnClick
  • OriginalRenderer
  • OriginalRendererMode
  • PanelDatatipHandle
  • PanelHandle
  • PanelTextHandle
  • UIContextMenu
  • UIState
  • ZStackMinimum

graphics.datatip:

  • ALimInclude
  • ApplicationData
  • Behavior
  • CLimInclude
  • DataCursorHandle
  • DataManagerHandle
  • Debug
  • DoThrowStartDragEvent
  • EmptyArgUpdateFcn
  • EnableAxesStacking
  • EnableZStacking
  • EraseMode
  • EventObject
  • ExternalListenerHandles
  • HelpTopicKey
  • HostAxes
  • HostListenerHandles
  • IncludeRenderer
  • Invalid
  • IsDeserializing
  • MarkerHandle
  • MarkerHandleButtonDownFcn
  • Orientation
  • OrientationMode
  • OrientationPropertyListener
  • OriginalDoubleBufferState
  • PixelBounds
  • PointsOffset
  • Position
  • SelfListenerHandles
  • Serializable
  • TextBoxHandle
  • TextBoxHandleButtonDownFcn
  • Version
  • ViewStyle
  • XLimInclude
  • YLimInclude
  • ZLimInclude
  • ZStackMinimum
  • uistate

As can be seen, if we really want, we can always use the MarkerHandle or TextBoxHandle directly.

Deleting data tips

To delete a specific data-tip, simply call the cursor mode’s removeDataCursor() method; to delete all data-tips, call its removeAllDataCursors() method:

% Delete the current data-tip
cursorMode.removeDataCursor(cursorMode.CurrentDataCursor)
 
% Delete all data-tips
cursorMode.removeAllDataCursors()

Have you used plot data-tips in some nifty way? If so, please share your experience in a comment below.

p.s. – did you notice that Java was not mentioned anywhere above? Mode managers use pure-Matlab functionality.

Related posts:

  1. Draggable plot data-tips Matlab's standard plot data-tips can be customized to enable dragging, without being limitted to be adjacent to their data-point. ...
  2. Accessing plot brushed data Plot data brushing can be accessed programmatically using very simple pure-Matlab code...
  3. Plot LineSmoothing property LineSmoothing is a hidden and undocumented plot line property that creates anti-aliased (smooth unpixelized) lines in Matlab plots...
  4. Plot LimInclude properties The plot objects' XLimInclude, YLimInclude, ZLimInclude, ALimInclude and CLimInclude properties are an important feature, that has both functional and performance implications....
  5. Controlling callback re-entrancy Callback reentrancy is a major problem for frequently-fired events. Luckily, it can easily be solved....
  6. Undocumented scatter plot jitter Matlab's scatter plot can automatically jitter data to enable better visualization of distribution density. ...

Categories: Handle graphics, Hidden property, Medium risk of breaking in future versions, Stock Matlab function, Undocumented feature, Undocumented function

Tags: , , , , ,

Bookmark and SharePrint Print

40 Responses to Controlling plot data-tips

  1. Isaac says:

    I have used programmable datatips to pull various extrema and their time hacks off a telemetry stream during V&V of flight software. It is VERY useful. I did not know about the other properties you’ve shown above — very cool. Thanks!

    • Dani says:

      Hi Isaac! I just arrived to this post ,and what a surprise, because I use all this tips for flight test software aswell (data processing of flight parameters)!! He he he we all arrive to the same places….True Story XD

      Amazing work Mr. Altman by the way!!

  2. K says:

    Hi,

    Thanks very much for this useful article!

    I have a small problem I was hoping you can help me resolve. When I use your code above, the datatip is displayed in the right location on the curve, but instead of having the X and Y values, it contains a message that says “Error in datacursor datatip string function”. I tried updating the “String” field of hDatatip, but that doesn’t seem to help.

    I would really appreciate any suggestions!

    Thank you,
    K

  3. Dènis Riedijk says:

    I have used this as a custom tooltip for a patch object. (Depending on position above the patch, a different tooltip needs to be displayed)
    Just initialise the hDataTip hidden and without a cursor and set a timer for making it visible (and reset the timer in a windowbuttonmotionfcn so it displays after your mouse hovers for a certain amount of seconds)

    cursorMode = datacursormode(figureHandle);
    set(cursorMode, 'UpdateFcn',@setDataTipTxt, 'SnapToDataVertex', 'off', 'NewDataCursorOnClick',false);
    hDatatip = cursorMode.createDatatip(handle(patchHandle));
    set(hDatatip,'Host',handle(patchHandle), 'ViewStyle','datatip','OrientationMode','manual','Orientation','top-right');
    set(hDatatip, 'MarkerFaceColor','none', 'MarkerEdgeColor','none', 'HitTest','off', 'Visible', 'off');

    To use this as a tooltip, you might need to additionally do the following to make sure your clicks arrive at the right object and not at the datacursor or textbox:

    set(0,'ShowHiddenHandles', 'on');
    set(get(hDatatip,'Children'), 'HitTest','off'); % Otherwise our clicks end up in the default callbacks of data-tips.
    set(0,'ShowHiddenHandles', 'off');
  4. John says:

    Great tip, thanks a lot! The following line doesn’t seem to work in Matlab r2011b, but removing it works just fine:

    set(cursorMode, 'enable','on', 'UpdateFcn',@setDataTipTxt, 'NewDataCursorOnClick',false);

    I used it to find and mark phase and gain margin on a Bode plot.

  5. Felipe says:

    Hello,
    How can I pass a third argument to @setDataTipTxt?

    I have a graph with several plots, each of them comes from a different source file. I want the datatip to tell me (X,Y) plus the name of the source file. For that reason, I was willing to pass a third argument with the name of the source file. Am I on the right way?

    great blog.
    thank you.

    • @Felipe – you can do it like this:

      set(cursorMode, 'UpdateFcn',{@setDataTipTxt,sourceFilename});
    • Felipe says:

      @Yair, thanks for your answer.

      I’d tried what you told me, but it seems there is a problem when appending sourceFilename to the output cell array of @setDataTipTxt. I’ve tried several ways but always get the same message: “Error in custom datatip string function”.

      Do you have any idea why this is happening?

      Thanks again.

    • It means that you have an error in your setDataTipTxt function. Put a breakpoint there (or a try-catch block) to see exactly where and why.

    • Felipe says:

      Hi Jair,

      I believe my setDataTipTxt function is OK, I made it in the same way as it is explained in this blog:

      http://blogs.mathworks.com/videos/2011/10/19/tutorial-how-to-make-a-custom-data-tip-in-matlab/

      I even made the same experiment (changing ‘Y’ for ‘$’) and everything seems fine. The problem begins when I try to append sourceFilename to the cell array output of setDataTipTxt

      Do you have any idea why this may be happening? do you know any other turnaround?

      Thanks a lot for your help.

    • @Felipe – you probably forgot to declare the extra parameter(s) properly. See here.

  6. Apn says:

    Hello Yair,

    I’m new to matlab so this is likely to be a very basic question but I haven’t been able to find an answer so far. Maybe you can help.

    The question is: How can I make a datatip display full integer value?

    My X values are in the range 0 to 1500000
    When I make a datatip it displays:
    X: 3.669e+05
    Y: 17

    I would like it to be:
    X: 366915
    Y: 17

    Is that possible?

    Any help will be appreciated.

    Thanks

  7. Pingback: Handle Graphics Behavior | Undocumented Matlab

  8. Dani says:

    Hi Yair,
    First off, I’m fairly new to Matlab but I love your blog – its super informative and I’m looking forward to getting more practice so I can appreciate it more!
    I was hoping you could help me with a minor predicament. I’m using the data cursor for its point selection property but would rather the textbox not be displayed at all. I’ve tried almost everything, including suppressing it in the updateFcn callback using:

    function output_txt = getRidofTextBox(obj,event_obj, datapointhandle)
    h = get(datapointhandle, 'DataCursors');
    j = get(h, 'TextBoxHandle');
    set(j, 'Visible', 'off');

    but this doesn’t work, since it would appear the property just gets reset in a later callback. I’m running out of ideas and was hoping you would be able to point me in the right direction.
    Thanks!

    • Yair Altman says:

      @Dani – try setting the output_txt to ” (empty), and datapointhandle.CurrentDataCursor‘s BackgroundColor and EdgeColor properties to ‘w’ (or rather, to your axes’ BackgroundColor)

    • Dani says:

      @Yair
      Thanks for looking into it! Unfortunately I’ve tried the empty string and I either get the warning ‘Error in custom datatip string function’, or it defaults back to a previous allowable value. Switching the box to white also doesn’t solve the problem since its still displayed on top of the plot – unless I misunderstood your advice. Thanks anyways!

    • @Dani – an error means that you have some Matlab error in your callback function, and then Matlab automatically defaults back to the default. Anyway, if an empty string is not allowed, you can always try a sting containing a single space character. The data-tip box will then be so small that the obstruction of the plot data will probably be unnoticed.

  9. Ben says:

    What is the proper way to create a datatip in window (instead of datatip) mode programmatically?

    I’ve tried this function based on your code and it works except I get tooltips instead of data in the window:

    function hDatatip = CustomDataTip(hObj, index, callbackhand)
        cursorMode = datacursormode(ancestor(hObj, 'figure'));
     
        % enable datatips
        datacursormode on;
        set(cursorMode, 'updatefcn', callbackhand);
        set(cursorMode, 'NewDataCursorOnClick', false);
        set(cursorMode, 'DisplayStyle', 'window');
     
        % Delete all data-tips
        cursorMode.removeAllDataCursors();
     
        hDatatip = createDatatip(cursorMode, hObj);
     
        % adjust properties
        set(hDatatip, 'UIContextMenu', get(cursorMode, 'UIContextMenu'));
        set(hDatatip, 'HandleVisibility', 'off');
        set(hDatatip, 'Host', hObj);
        set(cursorMode, 'DisplayStyle', 'window');
        %set(get(hDatatip,'TextBoxHandle'), 'Visible', 'off');
     
        % create datatip
        xData = get(hObj, 'XData');
        yData = get(hObj, 'YData');
        pos = [xData(index) yData(index)];
        set(get(hDatatip, 'DataCursor'), 'DataIndex', index, 'TargetPoint', pos);
        set(hDatatip, 'Position', pos);
        updateDataCursors(cursorMode);
    end
    • Ben says:

      Hi, I just figured I’d report back, I think I figured it out (please excuse the ugly code, I’m sure this can be made much cleaner, but it seems to work). All off the functions called I simply stole verbatim from datacursormode.m and pasted into the same file as this function.

      function hDatatip = CustomDataTip(hObj, index, callbackhand) 
          cursorMode = datacursormode(ancestor(hObj, 'figure'));
          hMode = getuimode(ancestor(hObj, 'figure'), 'Exploration.Datacursor');
          hTool = localGetObj(hMode);
       
          % enable datatips
          set(cursorMode, 'enable','on', 'UpdateFcn',callbackhand, 'NewDataCursorOnClick',false);
       
          % Delete all data-tips
          cursorMode.removeAllDataCursors();
       
          hDatatip = createDatatip(cursorMode, hObj);
       
          % adjust properties
          set(hDatatip, 'UIContextMenu', get(cursorMode, 'UIContextMenu'));
          set(hDatatip, 'HandleVisibility', 'off');
          set(hDatatip, 'Host', hObj);
          set(cursorMode, 'DisplayStyle', 'window');
       
          % set up listener to datatip
          h = handle.listener(hDatatip, 'UpdateCursor', {@localUpdatePanel,hTool});
          addlistener(hDatatip,h);
       
          % Listen to datatip existence and feed to panel
          h = handle.listener(hDatatip, 'ObjectBeingDestroyed', {@localUpdatePanel,hTool});
          addlistener(hDatatip,h);
       
          localPanelUIOn(hMode,hTool);
          set(get(hDatatip,'TextBoxHandle'), 'Visible', 'off');
       
          % Update string in panel
          localUpdatePanel([],[],hTool);
       
          % create datatip
          xData = get(hObj, 'XData');
          yData = get(hObj, 'YData');
          pos = [xData(index) yData(index)];
          set(get(hDatatip, 'DataCursor'), 'DataIndex', index, 'TargetPoint', pos);
          set(hDatatip, 'Position', pos);
          updateDataCursors(cursorMode);
      end
  10. Werner says:

    I’ve used your datatip “undocumented documentation” and it helped me a lot with enhancing the information extraction from graphics. However, the datatips have an unexpected behavior on mac users, which makes it bottom of the ylabel text, complicating their visualization. I’ve managed to solve this issue explicitly overriding the position of the ylabel to a z value lower than the datatip one, but now my ylabel will move around when I zoom or pan my axis. I over-read the ActionPostCallback function from the zoom and pan handles, to replace the position after there where a change on the axis, but this creates an effect like the one you explained here:

    http://undocumentedmatlab.com/blog/setting-axes-tick-labels-format/

    By chance, do you know any event listener that is linked to the axis label, or to movement on the axis, so that I can solve this issue without seeing my axis label flying around my figure until mouse is released? More details available here:

    http://stackoverflow.com/questions/17584094/put-datatip-stack-on-top-of-axis-label-and-update-axes-label-after-a-change-was

    Thank you very much, and congratulations for this site, it is very useful!

  11. Julien says:

    Yair,
    I massively abuse of the handle.listener syntax to listen to property changes of UDD objects. Could you give an example of what the equivalent syntax looks like in HG2 ?
    Moreover, do you know if the same handle.listener syntax still works in HG2 to listen to java events ?
    Thanks !

  12. Pingback: Draggable plot data-tips | Undocumented Matlab

  13. Franko says:

    The graphics.datacursormode object has an UpdateFcn handle. Opening graphics.datatip.updatestring.m reveals that the graphics.datatip object itself has an UpdateFcn. The latter is more useful because the cursorMode UpdateFcn is called as

    myUpdateFcn([], hDatatipEvent);

    whereas the datatip object’s UpdateFcn is called as

    myUpdateFcn(hDatatip, hDatatipEvent);

    So, potentially you can have different datatips with different UpdateFcn in the same figure. This is useful for advanced visualization involving multiple datatips in the same figure.

  14. Graham says:

    Great article Yair, a nice insight into the workings of data tips. Is there any way you know in which HTML can be included in data tips?
    I’m running R2011b & interested in being able to provide links with matlab: href to trigger some activity.

    I’ve tried the simple way of just outputting HTML from the UpdateFcn (as below) but without any success

    txt = sprintf('<a href="why" rel="nofollow">Link to data</a>');
  15. Jon says:

    Hi Yair,

    I used this excellent writeup to create custom tooltips for a bunch of figures. Everything mostly works, but the above method fails in one case, and I have no idea why.

    Briefly: I create a single empty figure and set its WindowButtonDownFcn property to @myMagnifyFcn. I then plot nine subplots (arranged as 3×3) into that figure. The helper script myMagnifyFcn determines which of the subplots receives a mouse click, then creates a new figure and copies just that axes into it (using findobj, etc.). This much works just fine.

    I then attempt to create the “custom tooltip” functionality for the latter (one-axes) figure as per your example, namely:

    cursormodeMagnifiedFig = datacursormode(gcf);
    set (cursormodeMagnifiedFig, 'NewDataCursorOnClick', true, 'SnapToDataVertex', on, 'UpdateFcn' {@myToolTipFcn, arg1, arg2, arg3});

    This is exactly the same code that I have working for other figures. But in this case, even though I can toggle the data cursor button in the latter figure just fine, @myToolTipFcn never seems to get called — I put a debug print statement at the top and it never executes.

    I’m wondering if there is something scope-related that causes the above to fail. As I said, the datacursormode stuff works just fine for all the other figures I create in my parent script. Furthermore, I examined the contents of cursormodeMagnifiedFig in the debugger — it turns up as a graphics.datacursormanager object, just like the corresponding cursormodeFig does with all my “regular” figures, and the contents are identical in all cases — so I’m confident that the arguments to UpdateFcn (= myToolTipFcn) are getting passed correctly.

    I’m stuck because I can’t figure out (no pun intended) how to debug what Matlab is doing — it simply doesn’t fire the helper function and fails silently. Can you suggest any good way to debug the datacursormode process?

    Thanks very much in advance (for this post specifically and for the blog in general),

    Jon

    • @Jon – your gcf probably points to another figure handle. Try using the actual figure handle rather than gcf – if it’s a hidden handle then gcf will not return it.

    • Jon says:

      Hi Yair,

      Thanks for the quick response. I had a hunch that might be an issue, but wrote “gcf” in my code snippet above for brevity. Since that apparently _does_ make a difference, I should clarify that the actual setup in myMagnifyFcn is

      h_magnifiedFig = figure('tag', 'magnifiedAxes', 'keyPressFcn', get(hobj, 'keyPressFcn'), 'Name', 'Magnified Axes');
      cursormodeMagnifiedFig = datacursormode(h_magnifiedFig);
      set (cursormodeMagnifiedFig, 'NewDataCursorOnClick', ...);

      … and it is this setup that doesn’t work. I’m really baffled as to how else to debug this.

      FWIW, I’m stuck using a R2006b on this project. Seems much more likely that it’s my fault and not Matlab’s, but any chance this could be an HG-related bug?

      Jon

      p.s. Sorry about the double post earlier, and thanks for cleaning it up.

    • Try adding a drawnow; pause(0.1); after the figure creation and before you retrieve datacursormode, maybe it will help. I no longer have access to R2006b (too old) so I can’t test. Good luck!

    • Jon says:

      Tried that but no luck. Argh.

      FWIW, checked that gcf, h_magnifiedFig, and get(gca,’Parent’) [where gca is the axes copied into the magnified figure] are all equal to one another — so the issue isn’t with operating on the wrong (or hidden) figure handle.

      Anyway, I will try digging a bit further with the debugger and maybe ask over at the newsgroup to see if anyone else has an idea. In any case, I will report back if I ever get to the bottom of this.

      Thanks again,

      -Jon

  16. Jon says:

    I think I’ve found a solution. Following up here as promised, in the unlikely event someone else runs into this (obscure?) issue.

    In my @myMagnifyFcn callback, the relevant part of the code to copy the clicked-upon axes and its contents to the new empty figure window is as follows:

    % Find all axes that are just axes (as opposed to legends, colorbars, etc.)
    figAxes = findobj(hObject, 'type', 'axes', '-not', 'tag', 'legend', '-not', 'tag', 'Colorbar');
    indAxes = find(figAxes == gco);
     
    % Assuming the click uniquely selects a particular axes, copy it to a new figure along with its contents
    if length(indAxes) == 1,
        h_magnifiedFig = figure('tag', 'magnifiedAxes', 'keyPressFcn', get(hobj, 'keyPressFcn'), 'Name', 'Magnified Axes');
        zoomedAxes = copyobj(figAxes(indAxes), h_magnifiedFig);
    end;

    (Note that legends and colorbars are handled separately later in the function, since I’ve found that COPYOBJ does not seem to handle them correctly, at least on R2006b.)

    The problem is that COPYOBJ either also mis-copies the ‘hittest’ property of some (all?) objects, or else they are not inherited automatically from the parent figure. This causes subsequent mouse clicks into the new (“magnified”) figure to be ignored, so they fail to fire the callback.

    So, after all that, the solution to this problem seems to be a one-liner(!) following the COPYOBJ statement:

    set(findobj(gcf,'type','patch'),'hittest','on')

    and some small tweaks to the code that assembles the tooltip text (which are specific to my setup, so I’ll skip them here).

    Hope this helps someone in the future. Thanks again, Yair, for doing all the heavy lifting.

    -Jon

  17. Jon says:

    One more…

    I have a callback function outputTxt = myCallback(obj, eventObj) to create customized datatips as described here, but I need to limit that behavior to only work for certain objects (specifically, plotted data points only, and not other lines showing mean, standard deviation, etc.).

    I’m able to check which object is clicked selected with the data cursor by looking at the properties of clickedObj = get(eventObj, ‘target’). But even if I set outputTxt = {”}, I still get a (default) data tip. I would like to cleanly delete this.

    The “Deleting data tips” section above shows how to do this from the parent script, but I’m wondering if it is possible to do this _from within the callback_. The variable obj is empty, while eventObj (whose class is graphics.datatipevent) has two properties, Target (a handle) and Position (a 2×1 matrix) — neither of which points to the datacursormode object used in the example. As a last resort, I tried executing this inside the callback:

    dummy = datacursormode(gcf);
    dummy.removeDataCursor(dummy.CurrentDataCursor);

    … and that sort of works, except that sometimes it deletes not only the desired (errant) data tip but also other (valid) ones already created in the same figure, which should all be left alone.

    Any suggestions would be most welcome.

    Thanks,

    -Jon

  18. Mahmoud says:

    Thanks Dr.Yair Altman for your really nice tutorial.
    I am writing a program with 4-context menus; one of them is related to datacursor mode, I can switch between any of them except datacursormode one, I CANNOT Switch it off, So pleae how to do it in some example demo?
    Thanks in advance

    • @Mahmoud – I am not sure I understand your problem exactly. If you’d like me to look at your code, please contact me via email (the link is at the top left of every page here) for a short consultancy.

    • Mahmoud says:

      function CursorMode_Off_test
      close all, clear all, clc;
      f=figure;
      xdata=-10*pi:pi/10:10*pi;ydata=sin(xdata);
      hLine = plot(xdata, ydata);
      %%================================================================================
      hax=gca;
      hcmenu = uicontextmenu;
      h1=uimenu(hcmenu,’label’,'Line Style’);
      h2=uimenu(hcmenu,’label’,'Color’);
      %
      hcb1 = ['set(gco,''LineStyle'',''--'')'];
      hcb2 = ['set(gco,''LineStyle'','':'')'];
      hcb3 = ['set(gco,''LineStyle'',''-'')'];
      hcb_r = ['set(gco,''color'',''r'')'];
      hcb_g = ['set(gco,''color'',''g'')'];
      hcb_b = ['set(gco,''color'',''b'')'];
      hcb_B = ['set(gco,''color'',''k'')'];
      %
      item1 = uimenu(h1,’Label’,'dashed’,'Callback’,hcb1);
      item2 = uimenu(h1,’Label’,'dotted’,'Callback’,hcb2);
      item3 = uimenu(h1,’Label’,'solid’,'Callback’,hcb3);
      item4 = uimenu(h2,’Label’,'red’,'Callback’,hcb_r);
      item5 = uimenu(h2,’Label’,'green’,'Callback’,hcb_g);
      item6 = uimenu(h2,’Label’,'blue’,'Callback’,hcb_b);
      item7 = uimenu(h2,’Label’,'black’,'Callback’,hcb_B);
      %How to turn on/off this menu and keep other menus work
      item8 = uimenu(hcmenu,’Label’,'Cursor Mode’,'Callback’,@update_FCN_call);
      %
      hlines = findall(hax,’Type’,'line’);

      for line = 1:length(hlines)
      set(hlines(line),’uicontextmenu’,hcmenu)
      end

      end

      function update_FCN_call(~,~)
      cursormode = datacursormode(gcf);
      set(cursormode,’enable’,'on’,'UpdateFcn’, @myupdatefcn);
      end

      function cp = myupdatefcn(obj,event_obj)

      pos = get(event_obj,’Position’);

      cp ={['X: ',num2str(pos(1))], …
      ['Y: ',num2str(pos(2))]};
      end

Leave a Reply

Your email address will not be published. Required fields are marked *

*

<pre lang="matlab">
a = magic(3);
sum(a)
</pre>