Using handles returned with findobj [duplicate] - matlab

I already have the functions required to drag and drop a single box in a figure in MATLAB. The code I wrote fills the figure with several boxes. With another loop I filled the figure with more boxes (which hold different information in string form).
These two sets of boxes are related by the numbers I placed in their UserData (corresponding numbers; for each box, there's another with the same UserData content). By finding boxes containing the same UserData (and thus relating them) I want to be able to relocate a member of the first set of boxes to the same position relative to the corresponding member of the second set of boxes, by means of right clicking on the box I just dragged (uicontextmenu).
function recallfcn(hObject,eventdata)
for ydx=1:2
diag_detail=get(gco,'UserData'); % This line should be in the drag fcn
diag_pos=get(gco,'Position'); % So should this one (for current objects)
xvar=diag_pos(1,1);
yvar=diag_pos(1,2);
detail=[diag_detail ydx];
set(findobj('UserData',detail),'Position',[xvar+(ydx-1.5) yvar+0.5 0.8 0.8]);
end
end
% ydx is only there to add another level of detail as I'm actually looking to move
% two boxes of the 'first kind', each of which have 2 numbers in user data, the first
% number being the same, and the second number distinguishing the first box from the
% second. The premise is the same.

I usually use findall instead of findobj, in case the handles of the objects are not visible from the outside. Other than that I don't see why your code wouldn't work.
Here's an example:
%# make a figure with two buttons, same userData
fh=figure,
uicontrol('userdata',[2 3],'parent',fh)
uicontrol('userData',[2 3],'units','normalized','position',[0.5 0.5,0.1 0.1],'parent',fh)
%# change color to red
set(findall(fh,'userData',[2 3]),'backgroundcolor','r')
%# move to the same position
set(findall(fh,'userData',[2 3]),'position',[0.3,0.3,0.1,0.1])

As Jonas alludes to, the 'HandleVisibility' property of an object will determine if the object shows up in its parent's list of children, and thus if it will be returned by functions like FINDOBJ. The standard fix is to use the function FINDALL instead.
However, the 'HandleVisibility' property also comes into play in determining whether or not an object can become the current object (i.e. returnable by the function GCO). If it is set to 'off', then that object can't become the current object. Additionally, if the 'HandleVisibility' property of the parent figure of an object is set to 'off' then none of its children (including said object) can become the current object.
If 'HandleVisibility' is set to 'on' or 'callback' for all your objects and figures, then I think everything should work fine.

you should inverse the ordre of x and y vector, and you can use just one loop, the changment in your code is :
x2=x(end:-1:1); % invers the ordre
y2=y(end:-1:1);
for i=1:length(x)
set(hLine,'xdata',x(i),'ydata',y(i)); % move the point using set
% to change the cooridinates.
set(hLine2,'xdata',x2(i),'ydata',y2(i));
M(i)=getframe(gcf);
end

Related

Attributing multiple Peer objects to a single item in legend

Simplified Problem
I'm plotting 10 items.
To generate my plot I am creating 10 independent Line objects in a for loop
x=1:10;y=1;10;names = num2str((1:10)');
for i = 1:10
my_plots(i) = plot(x(i),y(i),'.','Color',rgb(i,:),'MarkerSize',14);
end
legend(my_plots,names);
When I click on an item in the legend it toggles visibility of the corresponding line
(legHandle.ItemHitFcn = toggleLegendItem).
function toggleLegendItem(src,evnt)
if strcmp(evnt.Peer.Visible,'on')
evnt.Peer.Visible = 'off';
else
evnt.Peer.Visible= 'on';
end
end
This works fine
I run into issues when some of my plots are actually the same category.
In this simple example, I could combine lines 1:5 into one object in a number of different ways, and pass the combined object to the legend as a single item in my_plots.
Actual problem
All of my line objects are distributed between several independent axes/subplots. Each axis has to remain independent because I need to be able to freely rotate each subplot without disturbing the other plots.
Progress so far
I've been able to build a shared legend between axes (because same class items share the same color, I only need to link the first line for each class), but I cannot figure out how to link multiple line objects to a single legend item, so that I am able to properly all lines across axes on each legend item callback.
I have an array of lines.
{{Line Line Line}}
{{Line Line Line}}
{{Line Line Line}}
...
{{Line Line Line}}
Basically what I need to do is set multiple Peer objects to a single legend item, but I am not sure if that is possible.
If someone has another solution that allows for combing multiple line handles across axes that would be helpful too.
So looking at the legend() function, I found that it's not possible to instantiate a legend with multiple Peer objects per item. There may be a way to go back and add multiple references via some sort of "combined object" but I'm not sure if a "combined object" exists that can handle objects with different Parent handles.
A quick fix to my problem (which was attributing a callback to the same item in multiple axes) was to use the shared properties within each class and across axes to get the handles for all items within each class. In this case I already assigned color to be distinctive between categories but a more objective property like Tag could also be used.
function toggleLegendItem(src,evnt)
% Find all items in this category
obj = findobj(findall(gcf,'type','Scatter'),'CData',evnt.Peer.CData);
for oid = 1:length(obj)
if strcmp(obj(oid).Visible,'on')
obj(oid).Visible = 'off';
else
obj(oid).Visible= 'on';
end
end
end
The one nuisance that this solution leaves is that the legend is a child of only one axis. If each category is not present in all axes, then the color for those missing categories may not show up. Luckily when you toggle each item's visibility the color appears and functions as normal. I would guess some other errors like this could occur.

Communication between two or more GUIs

I've a GUI(i.e. lets call it 'First')through which i can choose to open other GUIs(let's call them 'Second' and 'Third').I want to put a 'pushbutton' on the 'First'GUI that allows me to manipulate the figure on the different axes of the 'Second' and 'Third' GUIs. So, i choose with the 'First'GUI if use either 'Second' or 'Third' GUI; once i've chosen that i start to work just with the GUI that i chose (so the Third one or Second ones). Now i want to have a pushbutton not on each GUIs (Second or Third) but only on the First one in order to manipulate the figure on the axes 1 of the Second or Third (depends on which one i've previously chosen). Furthermore this pushbutton that i want is optional and i need to refresh my axes after used that.
I've done my best to explain the situation,please if you know any solution help me out!!Thanks
Your question is similar to other examples, such as this.
To understand how to solve your problem, you need to remember that MATLAB "decides" which axes to update based on the axes handle provided by the user. If the user doesn't provide a handle, a default gca (the current axes) is used. The axes that gca points to, is the first child of type 'axes' of the figure, that is, the first entry in findobj(hFigure,'Type','axes'). You can read about setting the current axes here.
Having established that, the solution you are looking for would involve storing the axes handles somewhere, and retrieving the correct one when you are about to update a plot. A common place to store it is the "application-defined data" (appdata), accessible by setappdata and getappdata, as mentioned in the first link above and also here.
The procedure you should undergo is:
Upon creating a figure, store the axes handle in appdata by calling setappdata(0,name,val) (e.g. setappdata(0,'axTag1',handles.axTag1)) from your GUI initialization function. The value 0 for the 1st argument stores it in MATLAB's root object (you can think of it as the main MATLAB window), so that even if any of the figures is closed, the information is maintained as long as MATLAB is still open.
Whenever you want to modify an axes, just obtain the appropriate handle using value = getappdata(0,name) and use it to update the corresponding axes.

Moving multiple boxes in figure?

I already have the functions required to drag and drop a single box in a figure in MATLAB. The code I wrote fills the figure with several boxes. With another loop I filled the figure with more boxes (which hold different information in string form).
These two sets of boxes are related by the numbers I placed in their UserData (corresponding numbers; for each box, there's another with the same UserData content). By finding boxes containing the same UserData (and thus relating them) I want to be able to relocate a member of the first set of boxes to the same position relative to the corresponding member of the second set of boxes, by means of right clicking on the box I just dragged (uicontextmenu).
function recallfcn(hObject,eventdata)
for ydx=1:2
diag_detail=get(gco,'UserData'); % This line should be in the drag fcn
diag_pos=get(gco,'Position'); % So should this one (for current objects)
xvar=diag_pos(1,1);
yvar=diag_pos(1,2);
detail=[diag_detail ydx];
set(findobj('UserData',detail),'Position',[xvar+(ydx-1.5) yvar+0.5 0.8 0.8]);
end
end
% ydx is only there to add another level of detail as I'm actually looking to move
% two boxes of the 'first kind', each of which have 2 numbers in user data, the first
% number being the same, and the second number distinguishing the first box from the
% second. The premise is the same.
I usually use findall instead of findobj, in case the handles of the objects are not visible from the outside. Other than that I don't see why your code wouldn't work.
Here's an example:
%# make a figure with two buttons, same userData
fh=figure,
uicontrol('userdata',[2 3],'parent',fh)
uicontrol('userData',[2 3],'units','normalized','position',[0.5 0.5,0.1 0.1],'parent',fh)
%# change color to red
set(findall(fh,'userData',[2 3]),'backgroundcolor','r')
%# move to the same position
set(findall(fh,'userData',[2 3]),'position',[0.3,0.3,0.1,0.1])
As Jonas alludes to, the 'HandleVisibility' property of an object will determine if the object shows up in its parent's list of children, and thus if it will be returned by functions like FINDOBJ. The standard fix is to use the function FINDALL instead.
However, the 'HandleVisibility' property also comes into play in determining whether or not an object can become the current object (i.e. returnable by the function GCO). If it is set to 'off', then that object can't become the current object. Additionally, if the 'HandleVisibility' property of the parent figure of an object is set to 'off' then none of its children (including said object) can become the current object.
If 'HandleVisibility' is set to 'on' or 'callback' for all your objects and figures, then I think everything should work fine.
you should inverse the ordre of x and y vector, and you can use just one loop, the changment in your code is :
x2=x(end:-1:1); % invers the ordre
y2=y(end:-1:1);
for i=1:length(x)
set(hLine,'xdata',x(i),'ydata',y(i)); % move the point using set
% to change the cooridinates.
set(hLine2,'xdata',x2(i),'ydata',y2(i));
M(i)=getframe(gcf);
end

Does Matlab execute a callback when a plot is zoomed/resized/redrawn?

In Matlab, I would like to update the data plotted in a set of axes when the user zooms into the plot window. For example, suppose I want to plot a particular function that is defined analytically. I would like to update the plot window with additional data when the user zooms into the traces, so that they can examine the function with arbitrary resolution.
Does Matlab provide hooks to update the data when the view changes? (Or simply when it is redrawn?)
While I have yet to find one generic "redraw" callback to solve this question, I have managed to cobble together a group of four callbacks* that seem to achieve this goal in (almost?) all situations. For a given axes object ax = gca(),
1. Setup the zoom callback function as directed by #Jonas:
set(zoom(ax),'ActionPostCallback',#(x,y) myCallbackFcn(ax));
2. Setup a pan callback function:
set(pan(ax),'ActionPostCallback',#(x,y) myCallbackFcn(ax));
3. Setup a figure resize callback function:
set(getParentFigure(ax),'ResizeFcn',#(x,y) myCallbackFcn(ax));
4. Edit: this one no longer works in R2014b, but is only needed if you add, e.g., a colorbar to the figure (which changes the axis position without changing the figure size or axis zoom/pan). I've not looked for a replacement. Finally, setup an undocumented property listener for the axes position property itself. There is one important trick here: We must hold onto the handle to the handle.listener object as once it's deleted (or leaves scope), it removes the callback. The UserData property of the axes object itself is a nice place to stash it in many cases.
hax = handle(ax);
hprop = findprop(hax,'Position');
h = handle.listener(hax,hprop,'PropertyPostSet',#(x,y) myCallbackFcn(ax));
set(ax,'UserData',h);
In all these cases I've chosen to discard the default callback event arguments and instead capture the axis in question within an anonymous function. I've found this to be much more useful than trying to cope with all the different forms of arguments that propagate through these disparate callback scenarios.
*Also, with so many different callback sources flying around, I find it invaluable to have a recursion check at the beginning of myCallbackFcn to ensure that I don't end up in an infinite loop.
Yes, it does. The ZOOM mode object has the following callbacks:
ButtonDownFilter
ActionPreCallback
ActionPostCallback
The latter two are executed either just before or just after the zoom function. You could set your update function in ActionPostCallback, where you'd update the plot according to the new axes limits (the handle to the axes is passed as the second input argument to the callback).

How do I use TeX/LaTeX formatting for custom data tips in MATLAB?

I'm trying to annotate a polar plot with data tips labelled with 'R:...,Theta:...' where theta is actually the Greek symbol, rather than the word spelled out. I'm familiar with string formatting using '\theta' resulting in the symbol, but it doesn't work in this case. Is there a way to apply the LaTeX interpreter to data tips? Here's what I have so far:
f1=figure;
t=pi/4;
r=1;
polar(t,r,'.');
dcm_obj = datacursormode(f1);
set(dcm_obj,'UpdateFcn',#polarlabel)
info_struct = getCursorInfo(dcm_obj);
datacursormode on
where polarlabel is defined as follows:
function txt = polarlabel(empt,event_obj)
pos = get(event_obj,'Position');
x=pos(1);
y=pos(2);
[th,r]=cart2pol(x,y);
txt = {['R: ',num2str(r)],...
['\Theta: ',num2str(th*180/pi)]};
Update: This solution is primarily applicable to versions R2014a and older, since it appears to fail for newer versions, specifically R2014b and newer using the new handle graphics system. For newer versions using the new handle graphics system, a solution can be found here.
For some odd reason, the data cursor tool in MATLAB forcibly sets the data tip text to be displayed literally instead of with TeX/LaTeX interpreting (even if the default MATLAB settings say to do so). There also appears to be no way of directly setting text properties via the data cursor mode object properties.
However, I've figured out one workaround. If you add the following to the end of your polarlabel function, the text should display properly:
set(0,'ShowHiddenHandles','on'); % Show hidden handles
hText = findobj('Type','text','Tag','DataTipMarker'); % Find the data tip text
set(0,'ShowHiddenHandles','off'); % Hide handles again
set(hText,'Interpreter','tex'); % Change the interpreter
Explanation
Every graphics object created in the figure has to have a handle. Objects sometimes have their 'HandleVisibility' property set to 'off', so their handles won't show up in the list of child objects for their parent object, thus making them harder to find. One way around this is to set the 'ShowHiddenHandles' property of the root object to 'on'. This will then allow you to use findobj to find the handles of graphics objects with certain properties. (Note: You could also use findall and not worry about the 'ShowHiddenHandles' setting)
Turning on data cursor mode and clicking the plot creates an hggroup object, one child of which is the text object for the text that is displayed. The above code finds this text object and changes the 'Interpreter' property to 'tex' so that the theta symbol is correctly displayed.
Technically, the above code only has to be called once, not every time polarlabel is called. However, the text object doesn't exist until the first time you click on the plot to bring up the data tip (i.e. the first time polarlabel gets called), so the code has to go in the UpdateFcn for the data cursor mode object so that the first data tip displayed has the right text formatting.