This is the next in a series of advanced topics in MATLAB. My definition of “Advanced” is somewhat nebulous and arbitrary, so even newer users can give this a watch.
Two and a half minute video shows how to use buttondownfcn to get the current point. This can be a fundamental skill that underlies many advanced GUIs.
I am having problem replicating the great example you have provided here. Specifically, I have create the following in an m file in an attempt to replicate what you have here…
I have done this kind of thing before, it does require some advanced maneuvers. Mostly it is about using ASSIGNIN to redefine a variable in the workspace as you modify it graphically. You need to place that function call in the end of the callback that modifies the data.
I try to use ButtonDownFcn to get information about image pixels in the current figure. The function works well before the image showed in axes, but after the image loading into the axes, the ButtonDownFcn does not work.
here is the code
%%%%%
function pushbutton_input_vimage_Callback(hObject, eventdata, handles)
%%%%
function Visible_iamge_ButtonDownFcn(hObject, eventdata, handles)
handles.mouse_pos_fig=get(handles.figure1,’currentpoint’);
axes_area=get(handles.Visible_iamge,’position’);
if (handles.mouse_pos_fig(1)(axes_area(1))) && …
(handles.mouse_pos_fig(2)(axes_area(2)))
handles.mouse_pos=get(hObject,’currentpoint’);
else
handles.mouse_pos=-1;
end
b=handles.mouse_pos(1,1:2);
if handles.mouse_track==2
set(handles.text_A,’string’,int2str(b));
elseif handles.mouse_track==3
set(handles.text_B,’string’,int2str(b));
elseif handles.mouse_track==4
set(handles.text_C,’string’,int2str(b));
end
Some of the graphics functions create a new axes and replace the old one. In doing this, the old callbacks, and certain other attributes are removed. You need to set them again.
As Doug said in comment 7, by default some of the high-level graphics functions (including image) clear many of the properties of the axes. You can restore those properties after calling IMAGE (as Doug suggested) or you can SET the axes NextPlot property to ‘replacechildren’ before calling IMAGE.
A similar type of problem, but with PLOT and the Tag property instead of IMAGE and the ButtonDownFcn property, is described in this document on our support website; the solution is the same.
I’d like to have a user select a rectangular area of an image using the rbbox method. I have a GUI (from GUIDE) with an axes control into which I can render the image (using imshow), and I register a buttondownfcn callback for the image object. When the user clicks, I start an rbbox in that callback. Unfortunately the values I get from rbbox are apparently scaled by some amount – when I then draw a rectangle using the return value from rbbox, it ends up somewhere else completely, and if I just use the width/height of the rbbox with the initial point, the size seems to be scaled by some constant factor.
I imagine this has to do with the rbbox call not using the right set of axes somehow, but I’m not sure how to fix that. Any suggestions?
I am not sure. Please send the simplest version of your code that exhibits this behavior with clear reproduction steps. I will see what I can do. It might make a good video!
Doug,
here’s what I’ve come up with to reproduce the problem:
1. Create a simple GUI consisting just of a figure with an axes object in it. I used GUIDE for this.
2. In the GUI’s OpeningFcn, I create an image object with the axes object as the parent, and set its buttondownfcn to a simple handler that draws a rectangle in what I would consider two reasonable ways. This looks as follows:
function imclk(hObject, eventdata)
ax = get(hObject, 'Parent');
pt = get(ax, 'CurrentPoint');
final_rect = rbbox();
otherbox = [ pt(1, 1:2) final_rect(3:4) ];
rectangle('Position', final_rect, 'Parent', ax);
rectangle('Position', otherbox, 'Parent', ax);
function imclktst_OpeningFcn(hObject, eventdata, handles, varargin)
% Choose default command line output for imclktst
handles.output = hObject;
im = imshow('test.jpg', 'Parent', handles.axes1) %this works best with a picture of Don Rickles.
set(im, 'ButtonDownFcn', @imclk );
% Update handles structure
guidata(hObject, handles);
Unfortunately, the rectangles don’t end up in the right spot, and I’m not quite sure how to proceed. I’d love suggestions!
Alright, I think I found a solution…
it turns out the values from the rbbox command need to be scaled according to the axes limits and the total size of the axes object:
So basically, ws and hs are scale factors that scale a given point in the figure (which is what rbbox returns, i guess) to a point in the image object. Hope this helps someone.
I have a question. I created a couple of figures in an interface I am testing out and I want it to close when I click on the x on the top rite hand of corner of the figure for any figure. How would i go about that doing that. I am very new to matlab. Thank you
Great video,
On my code, I have 2 different axes. Is it possible to modify the above to simply run an .m file rather than a function? I’d like to be able to run different scripts depending on which axes the user clicks on. Even using the functions I can’t seem to differentiate between the axes
I am attempting to do the something very similar:
I have files of .avi movies typically 40 seconds in length each. I want to make a simple line plot of the movie. The y axis would be empty and the x axis would be the length of the movie in seconds.
y = 0
plot([startOfMovie, endOfMovie], [y, y])
Each movie is a set of 10 repetition exercise. I want the user to be
able to use the plot interactively to identify the start and stop time
of each rep in a GUIDE gui.
by default the plot should have a line that represents the movie in seconds:
________________________
seconds
then after the user identifies start and stop times of each rep the
plot. For example, rep1 started at 5.5 secs and ended at 6.3 secs. So it will look like this:
_____-___-___-____-___-____
With each dash representing a starting and end time of a rep. (it doesn’t have to be a dash, it could just be a colored section or something else) Is
something like this possible in matlab? I also need to be able to
store each of those data points.
In general, it is better easier, and more in model to call functions, not scripts. It is easy to convert a script to a function. I recommend you go that route. It will help you here and in general. The function could just CALL your script.
hello,
Actually i am displaying a 300 by 300 point using a scatter subplot along a picture on the background on subplot. now what i need is when i click a point on scatter point i will display the actual point used to plot (not the axis). The code which i am using only display the axis of the plot not the actual value used to plot.
my code is
set(gca,'YDir','reverse')
subplot(1,2,1),
pomegranate = imread(train_data_desc(i).gbd_name);
imshow(pomegranate(:,:,1));
hold on
OrigImg = scatter(train_data_desc(i).gbd_pos(:,2),train_data_desc(i).gbd_pos(:,1),15,(cw{i}/15)');
set(OrigImg,'buttondownfcn',@call_OrigImg_fun);
title('Query Image')
for j=1:size(train_data_desc(i).gbd_pos,1)
text(train_data_desc(i).gbd_pos(j,2), train_data_desc(i).gbd_pos(j,1), num2str(j));
end
function call_OrigImg_fun(gcbo,eventdata,handles)
disp(get(gca,'Currentpoint'));
Leave a Reply
About
Brett & Jiro share their favorite user-contributed submissions from the File Exchange.
Thanks for another great tutorial.
If my plot were actually a 2-D figure, would it be possible to get the (x,y) and also the intensity at that spot (x/255)?
Thanks,
Daphne
I am having problem replicating the great example you have provided here. Specifically, I have create the following in an m file in an attempt to replicate what you have here…
function[] = graphinteract
figure(1)
plot(peaks)
set(gca,’buttondwnfcn’, @clicker)
function clicker(gcbo,eventdata,handles)
disp(get(gca,’Currentpoint’))
However, I always get the following error…
??? There is no ‘buttondwnfcn’ property in the ‘axes’ class.
Error in ==> graphinteract at 5
set(gca,’buttondwnfcn’, @clicker)
Any idea what I am doing wrong?
CW,
‘buttonDownFcn’ is spelled wrong in the above code.
Doug
How can this function/code be used to modify the data by dragging it?
If it cant be used to do this, then what function allows me to drag data points from a plot and modify the graph (and the new data) on the fly.
Thanks
Zach
Zach,
I have done this kind of thing before, it does require some advanced maneuvers. Mostly it is about using ASSIGNIN to redefine a variable in the workspace as you modify it graphically. You need to place that function call in the end of the callback that modifies the data.
-Doug
Doug,
I try to use ButtonDownFcn to get information about image pixels in the current figure. The function works well before the image showed in axes, but after the image loading into the axes, the ButtonDownFcn does not work.
here is the code
%%%%%
function pushbutton_input_vimage_Callback(hObject, eventdata, handles)
[vname,vpath]=uigetfile(‘*.bmp; *.img; *.jpg; *.tif’);
vimage=[vpath vname];
handles.user.plot_vimage=imread(vimage);
axes(handles.Visible_iamge)
image(handles.user.plot_vimage)
guidata(hObject, handles);
%%%%%
function popupmenu_vpoints_Callback(hObject, eventdata, handles)
if get(hObject, ‘value’)==2
handles.mouse_track=2;
elseif get(hObject, ‘value’)==3
handles.mouse_track=3;
elseif get(hObject, ‘value’)==4
handles.mouse_track=4;
end
guidata(hObject, handles);
%%%%
function Visible_iamge_ButtonDownFcn(hObject, eventdata, handles)
handles.mouse_pos_fig=get(handles.figure1,’currentpoint’);
axes_area=get(handles.Visible_iamge,’position’);
if (handles.mouse_pos_fig(1)(axes_area(1))) && …
(handles.mouse_pos_fig(2)(axes_area(2)))
handles.mouse_pos=get(hObject,’currentpoint’);
else
handles.mouse_pos=-1;
end
b=handles.mouse_pos(1,1:2);
if handles.mouse_track==2
set(handles.text_A,’string’,int2str(b));
elseif handles.mouse_track==3
set(handles.text_B,’string’,int2str(b));
elseif handles.mouse_track==4
set(handles.text_C,’string’,int2str(b));
end
guidata(hObject,handles);
Lorraine,
Some of the graphics functions create a new axes and replace the old one. In doing this, the old callbacks, and certain other attributes are removed. You need to set them again.
Doug
Doug,
Thanks, but how can i do that?
I tried to get the new axes by
h = findobj(handles.Visible_iamge,…),but it returned nothing
Lorraine
Lorraine,
When you call the image command, capture the handle to that, then get the parent.
Doug
Doug,
It works now, thanks : )!
Lorraine
Lorraine,
As Doug said in comment 7, by default some of the high-level graphics functions (including image) clear many of the properties of the axes. You can restore those properties after calling IMAGE (as Doug suggested) or you can SET the axes NextPlot property to ‘replacechildren’ before calling IMAGE.
http://www.mathworks.com/access/helpdesk/help/techdoc/ref/axes_props.html#NextPlot
A similar type of problem, but with PLOT and the Tag property instead of IMAGE and the ButtonDownFcn property, is described in this document on our support website; the solution is the same.
http://www.mathworks.com/support/solutions/data/1-168SX.html
I’d like to have a user select a rectangular area of an image using the rbbox method. I have a GUI (from GUIDE) with an axes control into which I can render the image (using imshow), and I register a buttondownfcn callback for the image object. When the user clicks, I start an rbbox in that callback. Unfortunately the values I get from rbbox are apparently scaled by some amount – when I then draw a rectangle using the return value from rbbox, it ends up somewhere else completely, and if I just use the width/height of the rbbox with the initial point, the size seems to be scaled by some constant factor.
I imagine this has to do with the rbbox call not using the right set of axes somehow, but I’m not sure how to fix that. Any suggestions?
@Marcel,
I am not sure. Please send the simplest version of your code that exhibits this behavior with clear reproduction steps. I will see what I can do. It might make a good video!
Doug
Doug,
here’s what I’ve come up with to reproduce the problem:
1. Create a simple GUI consisting just of a figure with an axes object in it. I used GUIDE for this.
2. In the GUI’s OpeningFcn, I create an image object with the axes object as the parent, and set its buttondownfcn to a simple handler that draws a rectangle in what I would consider two reasonable ways. This looks as follows:
function imclk(hObject, eventdata) ax = get(hObject, 'Parent'); pt = get(ax, 'CurrentPoint'); final_rect = rbbox(); otherbox = [ pt(1, 1:2) final_rect(3:4) ]; rectangle('Position', final_rect, 'Parent', ax); rectangle('Position', otherbox, 'Parent', ax); function imclktst_OpeningFcn(hObject, eventdata, handles, varargin) % Choose default command line output for imclktst handles.output = hObject; im = imshow('test.jpg', 'Parent', handles.axes1) %this works best with a picture of Don Rickles. set(im, 'ButtonDownFcn', @imclk ); % Update handles structure guidata(hObject, handles);Unfortunately, the rectangles don’t end up in the right spot, and I’m not quite sure how to proceed. I’d love suggestions!
Alright, I think I found a solution…
it turns out the values from the rbbox command need to be scaled according to the axes limits and the total size of the axes object:
So basically, ws and hs are scale factors that scale a given point in the figure (which is what rbbox returns, i guess) to a point in the image object. Hope this helps someone.
I have a question. I created a couple of figures in an interface I am testing out and I want it to close when I click on the x on the top rite hand of corner of the figure for any figure. How would i go about that doing that. I am very new to matlab. Thank you
@chin
There is a closerRequestfcn. Put close(gcf) in it.
Doug
Great video,
On my code, I have 2 different axes. Is it possible to modify the above to simply run an .m file rather than a function? I’d like to be able to run different scripts depending on which axes the user clicks on. Even using the functions I can’t seem to differentiate between the axes
I’ve tried the following:
axes(handles.axes3)
h(1)=imshow(image_1, [])
setappdata(gcf,’bdfcnhandle’,@load_stuff);
axes(handles.axes4)
h(2)=imshow(image_2, [])
setappdata(gcf,’bdfcnhandle’,@load_stuff2);
set(h,’buttondownfcn’,'feval(getappdata(gcf,”bdfcnhandle”));’);%
function load_stuff(hObject, eventdata, handles)
X=1
function load_stuff2(hObject, eventdata, handles)
X=2
I am attempting to do the something very similar:
I have files of .avi movies typically 40 seconds in length each. I want to make a simple line plot of the movie. The y axis would be empty and the x axis would be the length of the movie in seconds.
y = 0
plot([startOfMovie, endOfMovie], [y, y])
Each movie is a set of 10 repetition exercise. I want the user to be
able to use the plot interactively to identify the start and stop time
of each rep in a GUIDE gui.
by default the plot should have a line that represents the movie in seconds:
________________________
seconds
then after the user identifies start and stop times of each rep the
plot. For example, rep1 started at 5.5 secs and ended at 6.3 secs. So it will look like this:
_____-___-___-____-___-____
With each dash representing a starting and end time of a rep. (it doesn’t have to be a dash, it could just be a colored section or something else) Is
something like this possible in matlab? I also need to be able to
store each of those data points.
@Jim and griffin,
In general, it is better easier, and more in model to call functions, not scripts. It is easy to convert a script to a function. I recommend you go that route. It will help you here and in general. The function could just CALL your script.
Doug
hello,
Actually i am displaying a 300 by 300 point using a scatter subplot along a picture on the background on subplot. now what i need is when i click a point on scatter point i will display the actual point used to plot (not the axis). The code which i am using only display the axis of the plot not the actual value used to plot.
my code is
set(gca,'YDir','reverse') subplot(1,2,1), pomegranate = imread(train_data_desc(i).gbd_name); imshow(pomegranate(:,:,1)); hold on OrigImg = scatter(train_data_desc(i).gbd_pos(:,2),train_data_desc(i).gbd_pos(:,1),15,(cw{i}/15)'); set(OrigImg,'buttondownfcn',@call_OrigImg_fun); title('Query Image') for j=1:size(train_data_desc(i).gbd_pos,1) text(train_data_desc(i).gbd_pos(j,2), train_data_desc(i).gbd_pos(j,1), num2str(j)); end function call_OrigImg_fun(gcbo,eventdata,handles) disp(get(gca,'Currentpoint'));