How to get the right axes current point in a GUI after clicking on a pushbutton

6 views (last 30 days)
Here's the code for a small GUI that encapsulates my problem. After pushing the button, the user is expected to click somewhere on the image. If the point chosen lies inside the limits of the image, a point is plotted there. If it lies outside, nothing happens.
function testGUI
f = figure('Visible', 'off', 'Position', [360, 500, 450, 285]);
hbutton = uicontrol('Style', 'pushbutton', 'String', 'Add Point', 'Position', [315, 220, 90, 25]);
ha = axes('Units', 'pixels', 'Position', [50, 60, 200, 185]);
imagesc(ha, [1,2; 3,4])
colormap gray
f.Visible = 'on';
hbutton.Callback = @(h, e)buttonCallback(h, e, ha);
function buttonCallback(h, e, ha)
h.String = 'Click on image...';
waitforbuttonpress
pointerPosition = getPointerPosition;
if ~any(pointerPosition < 0.5) && ~any(pointerPosition > 2.5)
hold on
plot(ha, pointerPosition(1), pointerPosition(2), 'ro')
hold off
end
h.String = 'Add Point';
function pointerPosition = getPointerPosition
currentPoint = get(gca, 'CurrentPoint');
pointerPosition = currentPoint(1, 1:2);
To make my GUI more robust, I want to make sure that if the user accidentally clicks on any button while the code is stopped at waitforbuttonpress, the currently running instance of buttonCallback returns without doing anything. This would happen if the cursor position provided by the axes property CurrentPoint corresponded to the actual position of the pointer while clicking on the button, which lies outside of the axes.
Sadly, the property CurrentPoint of the axes only seems to correspond to the last point clicked inside the GUI but outside any button. That means that if, for instance, I click on some point inside the axes when the button isn't expecting a user input then I click two times on the button, a point will be added at the last point I clicked inside the axes before clicking the button, which is unacceptable.
Is there a way to get the axes property CurrentPoint to give the actual position of the pointer relative to the axes after I click on a button? If not, is there some other non-clunky way I can make buttonCallback know not to plot a point that wasn't provided by the user in the way intended, i.e. after clicking on the button? I know I could disable the button while it is expecting a user input, but the actual GUI I'm using is big and many things would have to be disabled. If there is a shorter way to achieve this, I'd rather know.
Thank you.
  2 Comments
Rik
Rik on 9 Jan 2019
Is there a reason why you can use the Calback property of the axis? Your button could set some switch that enable the code that plots a single point, which would then reset the switch to prevent another point while the button is not pressed again.
Sébastien Laberge
Sébastien Laberge on 9 Jan 2019
I'm using the Callback property of the button. The axes object doesn't have a Callback property.
I didn't include it here, but I already have a mechanism that makes every button on the GUI return without doing anything when the GUI is expecting the user to click on the axes. The problem I'm having is not about avoiding the execution of the button callback a second time in a row but making sure that when the execution of the first callback resumes and executes the code past waitforbuttonpress, the cursor position it uses is correct and not some leftover from before the button was clicked the first time.

Sign in to comment.

Accepted Answer

Rik
Rik on 9 Jan 2019
Since you don't already use the axes callback, I have modified your example so it will ignore all clicks outside the axis (and old clicks).
There are two things to be aware of. Firstly, this will not block execution of other (button) callbacks, and secondly, you will have to set the callback for any object you create inside the axis, as show in the creator function below.
Note that higher level functions like imshow will wipe a lot of callback properties.
function testGUI
f = figure('Visible', 'off', 'Position', [360, 500, 450, 285]);
hbutton = uicontrol('Style', 'pushbutton', 'String', 'Add Point', 'Position', [315, 220, 90, 25]);
ha = axes('Units', 'pixels', 'Position', [50, 60, 200, 185]);
imagesc(ha, [1,2; 3,4])
colormap gray
f.Visible = 'on';
hbutton.Callback = @buttonCallback;
ha.ButtonDownFcn=@AxesCallback;
children=ha.Children;
for n=1:numel(children)
children(n).ButtonDownFcn=@AxesCallback;
end
handles=struct;handles.f=f;handles.ha=ha;handles.hbutton=hbutton;
handles.waitforbuttonpress=false;%set default
guidata(f,handles)
end
function buttonCallback(h, e)
handles=guidata(h);
handles.hbutton.String = 'Click on image...';
handles.waitforbuttonpress=true;
guidata(handles.f,handles)
end
function pointerPosition = getPointerPosition
currentPoint = get(gca, 'CurrentPoint');
pointerPosition = currentPoint(1, 1:2);
end
function AxesCallback(h,e)
handles=guidata(h);
if ~handles.waitforbuttonpress
return
end
pointerPosition = getPointerPosition;
if ~any(pointerPosition < 0.5) && ~any(pointerPosition > 2.5)
hold on
plot(handles.ha, pointerPosition(1), pointerPosition(2), 'ro')
hold off
end
handles.hbutton.String = 'Add Point';
handles.waitforbuttonpress=false;
guidata(handles.f,handles)
end

More Answers (0)

Categories

Find more on Specifying Target for Graphics Output in Help Center and File Exchange

Products


Release

R2018a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!