Quantcast
Channel: Hidden property – Undocumented Matlab
Viewing all articles
Browse latest Browse all 30

Controlling plot data-tips

$
0
0

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....
 

Viewing all articles
Browse latest Browse all 30

Trending Articles