MATLAB Answers

Jason
0

Mouse click on Axes Problem

Asked by Jason
on 12 Jun 2018
Latest activity Commented on by Jan
on 22 Jan 2019
Accepted Answer by Jan
Hello. I am trying to use the mouse click on an axes to drive the position of an xystage. I have created my a grid with identifying text:
axes(handles.axes1); cla reset
hold on
ax = gca;
ax.XGrid = 'on';
ax.YGrid = 'on';
xticks(1:1:13)
yticks([1:1:9])
xlim([1 13]);
ylim([1 9]);
ax.GridColor = [0 .5 .5];
ax.GridLineStyle = '--';
ax.GridAlpha = 0.5;
ax.Layer = 'top';
hold on;
format bank
startingRows = linspace(1, 8, 8);
startingCols = linspace(1, 12, 12);
% % Now get the starting integer rows / cols.
startingRows = int32(startingRows(1:end))
startingCols = int32(startingCols(1:end))
ct=0;
for i=1:length(startingRows)
for j=1:length(startingCols)
ct=ct+1;
%Add region number
xr=double(startingCols(j))+0.3;
yr=double(startingRows(i))+0.5;
hold on
text(xr,yr,[num2str(i),',',num2str(j)],'FontSize', 8, 'Color', [1 0 1]);
end
end
set(handles.axes1,'Visible','on');
set(handles.axes1,'ButtonDownFcn',@ImageClickCallback); %An alternative to axes ButtonDownfunction
I then want to be able to click anywhere on the grid and return the number sin the grid. Its fine just to return the coordinates of the mouse. I have tried the axes1 ButtondownFunction via:
axes(handles.axes1)
cpt = get(gca,'CurrentPoint')
pt = cpt(1,1:2)
x=round(pt(1))
y=round(pt(2))
But it only works before the grid and numbers are drawn on. I have read this only works when actually clicking on the grid and not areas inside i.e. the boxes - which is what I want.

  0 Comments

Sign in to comment.

1 Answer

Jan
Answer by Jan
on 12 Jun 2018
Edited by Jan
on 12 Jun 2018
 Accepted Answer

It is prone to bugs to rely on the current axes object by cla and gca. You have the handle of the axes available, so use it. Replace:
axes(handles.axes1); cla reset
hold on
ax = gca;
by
ax = handles.axes1;
cla(handles.axes1, 'reset')
ax.NextPlot = 'add'; % EDITED, was 'on'
The format bank influences all following output to the command window. It is not useful here.
Replace
startingRows = linspace(1, 8, 8);
startingRows = int32(startingRows(1:end))
by the much simpler:
startingRows = 1:8;
(1:end) can be omitted at all, because it wastes time only.
I assume, the only problem with your code is, that the text objects shadow the axes, such that a click on them does not trigger the ButtonDownFcn of the axes in the background. You can let the text objects pass through the event by setting their 'HitTest' to 'off':
text(xr, yr, sprintf('%d,%d', i, j), 'FontSize', 8, 'Color', [1 0 1], ...
'HitTest', 'off');
Summary:
% ** UNTESTED CODE - WRITTEN IN THE FORUMS INTERFACE ONLY **
ax = handles.axes1;
cla(ax, 'reset')
ax.NextPlot = 'on'; % Equivalent to: hold on
ax.XGrid = 'on';
ax.YGrid = 'on';
ax.XTick = 1:13;
ax.YTick = 1:9;
ax.XLim = [1 13];
ax.YLim = [1 9];
ax.GridColor = [0 .5 .5];
ax.GridLineStyle = '--';
ax.GridAlpha = 0.5;
ax.Layer = 'top';
for i = 1:8
for j = 1:12
text(j + 0.3, i + 0.5, sprintf('%d,%d', i, j), ...
'FontSize', 8, 'Color', [1 0 1], ...
'HitTest', 'off');
end
end
ax.Visible = 'on';
ax.ButtonDownFcn = @ImageClickCallback;
And the callback:
function ImageClickCallback(AxesH, EventData)
cpt = get(AxesH, 'CurrentPoint');
pt = cpt(1, 1:2);
x = round(pt(1));
y = round(pt(2));
... what to do with x and y?
end
Using the dot notation "ax.Property = ..." only looks nicer, but is is no error to mix it with "set(ax, 'Property', ...)" and the more abstract functions like xticks and hold on.

  5 Comments

Jan
on 12 Jun 2018
@Jason: This was a typo. See EDITED: 'on' => 'add'
To get x and y in normalized coordinates:
xn = (x - 1) / 8
yn = (y - 1) / 8
To get the contents of the text objects:
TextH = gobjects(12, 8);
for i = 1:8
for j = 1:12
TextH(j, i) = text(j + 0.3, i + 0.5, sprintf('%d,%d', i, j), ...
'FontSize', 8, 'Color', [1 0 1], ...
'HitTest', 'off');
end
end
% Store the handles to the text objects in the UserData of the axes:
ax.UserData = TextH;
and in the callback:
function ImageClickCallback(AxesH, EventData)
cpt = get(AxesH, 'CurrentPoint');
pt = round(cpt(1, 1:2));
x = pt(1);
y = pt(2);
Str = AxesH.UserData(x, y).String
end
Sorry, I cannot test this currently. Maybe x and y have to be interchanged at some places.
Jason
on 12 Jun 2018
what does the following do:
ax.NextPlot = 'add';
Jan
on 22 Jan 2019
@Jason: The same as: hold on , but this uses the current axes. Look into the code of hold:
type hold
You can see, that this does some parsing and sets 'NextPlot' also. So I prefer the direct approach.

Sign in to comment.