File Exchange

image thumbnail

Cursorbar

version 2.0.0.3 (41.9 KB) by

Cursorbar creates a cursor line attached to an axes or lines.

20 Downloads

Updated

View License

Editor's Note: This file was selected as MATLAB Central Pick of the Week

The Cursorbar can be dragged interactively across the axes. If attached to a plot, the cursor points are updated as well. The Cursorbar can be either horizontal or vertical.
Thanks to Yaroslav Don (http://www.mathworks.com/matlabcentral/profile/authors/3354683-yaroslav) for his assistance in updating cursorbar for MATLAB Graphics and for his contribution of new functionality.

Comments and Ratings (25)

I solved it by adding a listener to the UpdateCursorBar event as follows:

cblabel=text(cbar.TopHandle.XData,1.05*cbar.TopHandle.YData,'V1',...
'Color','r','FontWeight','bold','FontSize',10,'EdgeColor','r',...
'HorizontalAlignment','center');
addlistener(cbar,'UpdateCursorBar', @(~,~) set(cblabel,'Position',...
[cbar.TopHandle.XData,1.05*cbar2.TopHandle.YData,0]))

Thank you for this nice submission. I was wondering if it is possible to label the cursorbar, for example to have the text V1 above a vertical cursor, such that the user can have two cursorbars distinguishable by their labels. I understand I can change the markers at the top for example, but wanted to use a text label.

Yaroslav

@Marcel,
You can do it by listening to the `Location` property. See Example #7 (type `help cursorbar`) for more information. You can also listen to other properties, as specified in the example.

really nice. thanks
Is there any way to set a callback, in case of a cursor bar was moved? I would like to use to show in another figure data, which are related to the new x-position after a cursor move.

An excellent piece of code. However, I always get nervous when "undocumented Matlab" is invoked, because 'undocumented' means 'MathWorks is likely to remove or massively change in the next release.' If there's any way, in a future version, to avoid such calls, that would be great.

Yaroslav

Hi @Johannes,

To fix your issue, go to the file "Cursorbar\+graphics\@Cursorbar\updatePosition.m". Go to line #23 and fix it to

if pos(3) >= min([zlm Inf]) && pos(3) <= max([zlm -Inf])

Namely, change xlm->zlm. Everything should start working correctly.

Johannes

Dear Marjorie, first of all, I'd like to thank you for your great cursorbar tool. It is really useful and I use it quite a lot.
Just recently, I discovered an issue, which I'd like to share with you. I wanted to add cursorbars to a plot, where the x-values are negative. This works for a linear plot. However, it doesn't as soon as I change the x-axis to logarithmic scaling. I simply cannot move the cursorbars anymore. For positive values it works but not for the negative ones.
Do you have an idea how to fix that?
Thank you very much for your help!

Johannes

CU Chan

When the crossbar is used and linked to a plot, if the verticle cursorbar location is changed by "set(cb,'Location',1)" for instance, the horizontal cursorbar location doesn't update accordingly.

The root cause is that the listener is monitoring the Position property but not the Location. An easy fix would be to add a listener for the Location property to update the Position. This can't be done manually as some required methods are private.

Sebastian Hölz

Quick note: in 2016a and later "Example 2" (asign to axis) does not work, if you do not specify a position, e.g.:

cursorbar(gca,'CursorLineColor') % does not work

To fix this, explicitly specify the position:

cursorbar(gca,'CursorLineColor',[1 0 0], 'Position',[.5 0 0]) % OK

Meade

Meade (view profile)

@Yaroslav,

You got it! The use-case is a gui that has a 'print screen' button that gets rid of all the overlaid UI elements.

I actually stumbled upon your 'Tag' a short while after posting my last question. Now I know it was your intention!

Below is what I had been using. I'll swap out to your suggested syntax.

Thanks again for a great piece of matlab code.

<code>
htempFigAxsChildren = allchild(htempFigAxs);
hCursorbar_tempFig = findobj(htempFigAxsChildren,'Tag','CursorbarGroup');
delete(hCursorbar_tempFig);
</code>

Yaroslav

@Meade,

Finally I have understood your predicament: what you seek is to remove the Cursorbar, not to recreate it.

The solution is quite simple. It relies on some hidden features I added just in case:

<code>
%% Will work again
hGroup = findall(tempFig,'Tag','CursorbarGroup');
delete(hGroup);
</code>

The group mentioned here is the same |hggroup| you found with |findjobject|. Since it holds all the Cursorbar's Graphics objects, it's easy to delete it with all its children.

Meade

Meade (view profile)

@Yaroslav,

Thanks for the quick reply.
Hmmm, I'm not able to use your code snippet. I think my issue is that I can't use setappdata, guidata, or any passed arguments.

I was able to find a work around, but it's not very elegant. I'm curious if I'm just not using your solution correctly.

In short, I'm creating a copy of my GUI plot window into a new fig. I do NOT want the UI functionality of Cursorbar in this copied figure, but since its created using the |copyobj| cmd, all the creation/handle data is lost.

See below.

<code>
%% Dummy data & figure
f1 = figure();
hAx = axes('Parent',f1,...
'Position',[0 0 1 1]);

dummyData.X = [0:500];
dummyData.Y = rand(1,numel(dummyData.X));
hP = plot(hAx,...
dummyData.X,dummyData.Y,...
'Tag','hP');

hCursorbar = Cursorbar(hP);
drawnow

%% Make a new figure
tempFig = figure('visible','on',...
'MenuBar', 'none',...
'ToolBar', 'none',...
'NumberTitle', 'off');

% Find/copy the Axes & Legends from GUI axis window
newLeg = findobj(f1,'Tag','legend');
newAx = findobj(f1,'Type','axes');

% Copy everything to new figure;
copyobj([newAx],tempFig);
set(gca,'outerposition',[0 0 1 1]);
legend(gca,newLeg.String,'Interpreter','none');

% DOES NOT WORK
hContainer = getappdata(gcf,'GraphicsCursorbarContainer'); % extract the handle of the container
hCursorbar = hContainer.CursorbarHandle; % the handle of the Cursorbar
delete(hCursorbar);

% DOES WORK
% Delete 'Cursorbar' on the copied plots
htempFigAxs = findobj(tempFig,'type','axes');
htempFigAxsChildren = allchild(htempFigAxs(1));
hCursorbar_tempFig = htempFigAxsChildren(1);
delete(hCursorbar_tempFig);
</code>

Yaroslav

@Meade,

Thank you for your kind comment. It's always great to hear that it is useful.

Regarding your problem: the simplest way is, naturally, passing the object, or save it to some application data:

<code>
hCBar = cursorbar(gca); % Create a Cursorbar
doSomethingWith(hCBar); % Pass it to some function
setappdata(gcf,'MyCursorbar',hCBar); % save it to application data
</code>

Unfortunately, you cannot do any of those, since you lose the object during creation.

The reason you cannot find the handle, is because the Cursorbar _is not_ a Graphics object. Mathworks has prohibited inheritance from their Graphics objects (they all are Sealed), so I had to resort to some tricks. What you find with Yair Altman's |findjobj| is the |hggroup| that holds all the Graphics objects together; it is not the Cursorbar itself.

Don't worry – there is a way. When I added the saving/loading mechanism, I had to set a container that holds a reference to the Cursorbar object. It is there that you can find your desired handle. For instance, suppose you have created a Cursorbar as in Example #1 of the help:

<code>
%% Example 1: Simple Cursorbar
x = linspace(0,20,101);
y = sin(x);
%
h = plot(x,y);
cursorbar(h);
</code>

Now, all you need to do is to probe the container and extract the handle:

<code>
hContainer = getappdata(gcf,'GraphicsCursorbarContainer'); % extract the handle of the container
hCursorbar = hContainer.CursorbarHandle; % the handle of the Cursorbar
</code>

And, voilà!

Meade

Meade (view profile)

I've been using this toolbox for some months now and it's been terrific.

Now, I want to be able to find the handle to my cursorbar object, when I CAN'T PASS IT DURING CREATION.

I'm able to use Yair Altman's |findjobj| function to see the cursorbar, (listed as a child to the axis as "Group('Cursorbar')") but I'm unable to get a valid handle from this.

Any thoughts?

Meade

Meade (view profile)

@Yaroslav,

It's people like you that make the FEX great! That solves my issue. Thank you.

Yaroslav

@Meade,

After some analysis, I traced the buggy behaviour back to some internal implementations of the cursorbar. Apparently, having a panel as a parent makes it interpret the location incorrectly.

To solve it, go to the file "updateDataCursor" and change the line (#27)

<code>
hNewDataCursor.Position = [x y 0];
</code>

to

<code>
if strcmp(hAxes.Parent.Type,'figure'),
hNewDataCursor.Position = [x y 0];
else
axesPar = hAxes.Parent;
hAxes.Parent = ancestor(hAxes,'figure');
hNewDataCursor.Position = [x y 0];
hAxes.Parent = axesPar;
end

</code>

Meade

Meade (view profile)

@Yaroslav,

What I really want to do is use this terrific functionality within the excellent uisplitplane fcn from Yair Altman.

Here is his example snippet with the Cursorbar. You can see that the three different cursors behave differently, though the position arguments are the same:

<code>
figure();

[hDown,hUp,hDiv1] = uisplitpane(gcf,'Orientation','ver','dividercolor',[0,1,0]);
[hLeft,hRight,hDiv2] = uisplitpane(hDown,'dividercolor','r','dividerwidth',3);
t=0:.1:10;
hax1=axes('Parent',hUp); p1 = plot(t,sin(t));
hax2=axes('parent',hLeft); p2 = plot(t,cos(t));
hax3=axes('parent',hRight); p3 = plot(t,tan(t));

hCB1 = Cursorbar(p1);
hCB2 = Cursorbar(p2);
hCB3 = Cursorbar(p3);
</code>

Yaroslav

Hi,

Thank you for the great feedback!

It seems that the problem occurs because the axis is too tight in the panel. You can solve it by loosing a bit the position:

<code>

%%% CODE WORKS AGAIN
f3 = figure();
hPanel = uipanel(f3,'Units','norm',...
'Position',[0.025 0.025 0.95 0.95]);
hAx = axes('Parent',hPanel ,...
'Position',[0.06 0.06 0.92 0.92]);

dummyData.X = [0:500];
dummyData.Y = rand(1,numel(dummyData.X));
hP = plot(hAx,...
dummyData.X,dummyData.Y,...
'Tag','hP');

hCursorbar = Cursorbar(hP);
drawnow

</code>

Meade

Meade (view profile)

A fantastic bit of code.

I have one error in a gui I'm building: the Cursor object doesn't drag properly when the axes are not direct children of the figure. E.g., if the axes are in a uipanel, this breaks.

Any insight you can give would be most appreciated.

%%% CODE WORKS
f1 = figure();
hAx = axes('Parent',f1,...
'Position',[0 0 1 1]);

dummyData.X = [0:500];
dummyData.Y = rand(1,numel(dummyData.X));
hP = plot(hAx,...
dummyData.X,dummyData.Y,...
'Tag','hP');

hCursorbar = Cursorbar(hP);
drawnow

%%% CODE does NOT WORK
f2 = figure();
hPanel = uipanel(f2,'Units','norm',...
'Position',[0.25 0.25 0.5 0.5]);
hAx = axes('Parent',hPanel ,...
'Position',[0 0 1 1]);

dummyData.X = [0:500];
dummyData.Y = rand(1,numel(dummyData.X));
hP = plot(hAx,...
dummyData.X,dummyData.Y,...
'Tag','hP');

hCursorbar = Cursorbar(hP);
drawnow

%%% END CODE

Muhammad

Sebastian Hölz

Hi,
setting the "Location" property does not update the Cursorbar as indicated in the help. However this can be achieved by setting the "Position". I think there needs to be an explicit set.Location method.

Michelle Hirsch

Updates

2.0.0.3

Fixed a bug that prevented cursorbar motion when x-values are negative and the x-axis scaling is logarithmic.

2.0.0.2

Fixed inheritance issues of incorrect Command Window display.
Fixed clashing with axes zooming.
Changed DataCursorDummyTargetHandle.
Fixed target axes bug.
Added a documentation example for marker styles.

2.0.0.1

Updated license

2.0

Updates include a new interface, and save and load capability.

2.0

Updates include a new interface, and save and load capability.

1.2

Fixed set Location
Added logarithmic scale support
Added a custom display

1.1

Added a picture

MATLAB Release
MATLAB 8.4 (R2014b)

Download apps, toolboxes, and other File Exchange content using Add-On Explorer in MATLAB.

» Watch video

Win prizes and improve your MATLAB skills

Play today