How to set uicontextmenu for contourf (MATLAB2014b) - matlab

Originally I asked why pcolor and contourf don't work with this method, and I assumed they were symptoms of the same problem. This is not true, hence the new question.
Why does this not work with contourf? (and how do i get it to work?)
axes;
stuff = uicontextmenu('Parent',ancestor(axes,'figure'));
stuffm = uimenu('Parent',stuff,'Label','Change something');
x = randn(10);
h = contourf(x);
% pcolor works! contourf does not
%h = pcolor(x)
set(h,'uicontextmenu',stuff);

You're trying to assign the context menu to the wrong object.
The first output of contourf, as noted in the docs, is the "contour matrix", you want the handle to the object:
[M,c] = contourf(___) returns the contour matrix and the contour object c. Use c to set properties after displaying the contour plot.
So just change your code as follows:
[~,h] = contourf(x); % 2nd output is the object handle
set(h,'uicontextmenu',stuff);
Result is a working context menu:
Note you were also creating axes twice, I think the 2nd time is unintentional when creating the context menu, fix this like so:
ax = axes; % assign new axes to variable for later use
stuff = uicontextmenu('Parent',ancestor(ax,'figure')); % use ax, not new axes

Related

How to arrange multiple figures in an array of subplots while keeping all properties?

Imagine I have function myPlot which creates a simple plot and returns figure and axes handle:
function [ fig_handle, axes_handle ] = myPlot( myTitle )
x = linspace(-10,10,100);
y = x.^2;
fig_handle = figure;
plot(x,y,'Color','b')
ylim([0,42]); xlim([-42/10,42/10]);
ylabel('y'); xlabel('x');
title(myTitle);
grid on;
axes_handle = gca;
end
Now I want to call that function multiple times with different input parameters and concatenate them into an array of subplots. The solution I came up with is
[f1,a1] = myPlot('Plot #1');
[f2,a2] = myPlot('Plot #2');
figure(3)
s221 = subplot(211);
s222 = subplot(212);
copyobj(get(a1,'children'),s221)
copyobj(get(a2,'children'),s222)
it gives me
so the new 2-part plot does not keep any property of the two plots before. Of course I am aware that I can just do:
set(s221,'Ylabel',get(a1,'Ylabel'))
with all properties. But I try to avoid this. Is there something easier I am missing?
Another workaround is to copy the full axes object (so all the properties come along) into a new figure. The only property you have left to set yourself is the position according to the rules of subplot(xyz).
To start with your example :
[f1,a1] = myPlot('Plot #1');
[f2,a2] = myPlot('Plot #2');
Then copy the axis of each figure into the new figure
h4 = figure(4)
copyobj(get(f1,'children'),h4)
copyobj(get(f2,'children'),h4)
They are both here, but they are still superimposed. You just have to reposition them now.
For a quick workaround I use an intermediate figure to let subplot calculate the position for me but these positions (axes arrangement) could probably be calculated by yourself with a helper function (rip some code from subplot.m if you need).
%// Just to get some position calculated for me
figure(3)
s221 = subplot(211);
s222 = subplot(212);
Then I apply the positions to axes in the figure:
hl = flipud( get(h4,'Children') ) ;
set( hl(1),'Position', get(s221,'Position') )
set( hl(2),'Position', get(s222,'Position') )
Just be careful, the axes/children list is upside down (so the use of the flipud function), but if you put that in a loop, you could just run the loop backward.
Edit
If you are going to discard the original figure (f1 and f2), then you can also simply move the axes to the new figure (instead of copying them) by assigning the Parent property of the axes, then close the figure (to keep things tidy). Just use :
set(a1,'Parent',h4) ; close(f1)
set(a2,'Parent',h4) ; close(f2)
instead of the 2 lines with copyobj. The rest of the solution is identical. Not sure if there is a performance gain by moving an object instead of replicating it (Matlab may do a copy in the background anyway ... or not) but if it involve many figures with heavy data sets it may at least save some memory during the process.
One possible workaround could be the following, not really generic though. Would be interested in a solution solving the original problem.
If it is possible to modify the myPlot function one could pass the figure handle and subplot specifier and plot everything correctly from the beginning.
function [ fig_handle, axes_handle ] = myPlot(fig_handle, sub, myTitle )
figure(fig_handle); subplot(sub)
....
end
called by
f = figure(3);
[f,a1] = myPlot(f, 221,'Plot #1');
[f,a2] = myPlot(f, 222,'Plot #2');

Dynamic Legend (Updates in every recursion)

I got a for i=1:15. Inside I generate a variable d=1:0.01:10, which is the x'x axis and based on this, I create a continuous function F(d) which has 2 unique variables pitch and yaw. I then plot this using different colors in every recursion using cmap = hsv(15);. So then it is:
d=1:0.01:10;
cmap = hsv(15);
for i=1:15
pitch = unidrnd(10);
yaw = unidrnd(10);
for j=1:length(d)
F(j) = d(j)*3*pitch*yaw; %// some long calculation here
end
p1 = plot(d,F,'Linewidth', 1.0);
title ('blah blah')
set(p1, 'Color', cmap(i,:));
hold on;
legend (['pitch,yaw:', num2str(pitch) num2str(yaw)])
end
hold off;
This code updates the unique pitch, yaw values in every recursion (without space between them so it is kind irritating) but fails to:
Apply the proper color, visible in the figure.
Hold the color from the previous iteration and the values of pitch,yaw.
Semidocumented Solution
Adding lines to a legend in a loop can be accomplished with "dynamic legends", as described on undocumentedmatlab.com.
The idea is to replace the legend command with:
legend('-DynamicLegend');
Then update the plot command with a DisplayName parameter:
plot(d,F,'Linewidth',1.0,'DisplayName',sprintf('pitch,yaw: %d,%d',pitch,yaw));
Then plots that are added to the axes get added to the legend:
If semi-documented features are not your cup of tea, use the DisplayName trick and simply toggle the legend off/on. That is, instead of -DynamicLegend:
legend('off'); legend('show');
A different variation that does not use either DisplayName or -DynamicLegend is to delete and recreate the legend with an array of stored strings.
Official Solution
The official solution recommended by MathWorks it so grab the existing legends` line handles and manually update the legend with those handles. This is pretty painful by comparison to the dynamic legend solution above:
% Get object handles
[LEGH,OBJH,OUTH,OUTM] = legend;
% Add object with new handle and new legend string to legend
legend([OUTH;p1],OUTM{:},sprintf('pitch,yaw: %d,%d',pitch,yaw))
As an HG2 (default in R2014+) alternative to #chappjc's official MW solution, one can take advantage of legend being re-implemented as its own class rather than a kludge of other graphics objects. This has cleaned up things a bit so they are simpler to interact with.
Though these new legend objects do not have an exposed property linking legend items to plotted objects, they do have such a property, 'PlotChildren', which is an array of object handles.
For example:
x = 1:10;
y1 = x;
y2 = x + 1;
figure
plot(x, y1, 'ro', x, y2, 'bs');
lh = legend({'Circle', 'Square'}, 'Location', 'NorthWest');
pc = lh.PlotChildren
Returns:
pc =
2x1 Line array:
Line (Circle)
Line (Square)
To update our legend object without calling legend again, we can modify the 'PlotChildren' and 'String' properties of our existing legend object. As long as there is a 'String' entry for each object in 'PlotChildren', it will be rendered in the legend.
For example:
y3 = x + 2;
hold on
plot(x, y3, 'gp');
% To make sure we target the right axes, pull the legend's PlotChildren
% and get the parent axes object
parentaxes = lh.PlotChildren(1).Parent;
% Get new plot object handles from parent axes
newplothandles = flipud(parentaxes.Children); % Flip so order matches
% Generate new legend string
newlegendstr = [lh.String 'Pentagram'];
% Update legend
lh.PlotChildren = newplothandles;
lh.String = newlegendstr;
Which returns:
This functionality can be wrapped into a generic helper function to support appending one or more legend entries. We've done so with legtools on GitHub
As of MATLAB 2017a, legends update automatically when adding or removing graphics objects.
Thus, nothing specific needs to be done now. One creates the legend, then in a loop one can add lines to the axes and they'll automatically appear in the legend.

Trying to make MATLAB's figure stop 'blinking'

So I have a simple loop in MATLAB that does the following:
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
figure(1)
plot(randn(1,100));
figure(2);
plot(randn(1,100));
end
The x and y are made up, but that is the jist of it. Anyway, when I run this code, not surprisingly, MATLAB will make two figures and plot accordingly. The problem is, I get a sort of 'blinking' between figures when I do this, and it makes the quality of seeing x and y evolve over time poorer.
I discovered a way to make one of the plots smoother like this:
figure(1);
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
plot(randn(1,100));
drawnow
end
If I do this, then of course figure(1) will plot very smoothly showing x nicely, without figure(1) 'blinking' between plots, but now I cant show figure(2) or y!
How can I plot both those quantities on different figures (not subplots) smoothly without 'blinking'?
EDIT:
Thanks Geodesic for your answer, the solution works, however there is a subtlety that I did not think would be an issue, however it is.
1) I am unable to use 'imagesc' with this solution.
For example,
figure(1);
aone = axes;
figure(2);
atwo = axes;
for p = 1:100
x = 4.*randn(1,100);
y = 7.*rand(10,100);
plot(aone,x);
drawnow;
imagesc(atwo,y);
drawnow;
end
In this case the part with imagesc(atwo, y) crashes.
Your flicker is because you're generating each figure window again and again through the loop, which is forcing the window to come to the foreground each time. Generate the figures first, attach some axes to them, and plot your data to each axis like so:
figure(1);
aone = axes;
figure(2);
atwo = axes;
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
plot(aone,randn(1,100));
drawnow;
imagesc(y,'Parent',atwo);
drawnow;
end
Edit: functions like plot take an axis argument directly, but imagesc does not. In this particular case you'll need to send a Property Name/Value pair in as an argument. The 'Parent' of the image generated will be our axis atwo (see above).
For p = 1, create the plots you need, using the plot command or the imagesc command. Keep the handle of the resulting graphics object by getting an output argument: for example h = plot(.... or h = imagesc(..... This will be a Handle Graphics lineseries or image object, or something else, depending on the particular plot type you create.
For p = 2:100, don't use the plotting commands directly, but instead update the relevant Data properties of the original Handle Graphics object h. For example, for a lineseries object resulting from a plot command, set its XData and YData properties to the new data. For an image object resulting from an imagesc command, set its CData property to the new image.
If necessary, call drawnow after updating to force a flush of the graphics queue.

Drawing multiple plots to one figure frame and zooming with mouse click

I want to put multiple plots to one figure windows and when I click one of these it will be open on separate window. Is there any predefined function to do that or what is the trick that makes possible?
Yes you can to this, you need to define a callback-function that does what you want and then set the axes property ButtonDownFcn equal to this callback.
a(1) = subplot(311); // plot stuff
a(2) = subplot(312); // plot stuff
a(3) = subplot(313); // plot stuff
set(a,'ButtonDownFcn', #copyAxesToNewFigure);
With regards to creating a new Figure that contains a copy of the axes you clicked on, a function like this should work:
function copyAxesToNewFigure(hObject,eventdata)
childHandle = get(hObject, 'Children');
newFig = Figure;
newAx = Axes;
copyojb(childHandle, newAx);

Given a handle of a subplot, how to obtain all its associated colorbar handles?

Take the follow code for example:
Hsp=subplot(1,2,1);
image(rand(5,5));
Hc=colorbar;
subplot(1,2,2);
image(rand(5,6));
colorbar;
My question is how to obtain Hc, given only Hsp.
As is known, the type of a colorbar is axes. So I tried to search all the children of the subplot.
Hs=findall(Hsp,'type','axes');
But, there is no value in Hs which matches Hc.
Using the following script can find the handle of all colorbars which are children of an axes. Here Ha1 is the handle of the axes with image (e.g. a subplot), Hc1s are the handles of the peer colorbars of the axes.
function Hc1s = find_peer_colorbars_of_an_axes(Ha1)
Hf = get(Ha1,'parent');
Haxs = findobj(Hf,'type','axes');
IsC=false(1,length(Haxs));
Hc1s=[];
for i=1:length(Haxs)
if isa(handle(Haxs(i)),'scribe.colorbar');
H=handle(Haxs(i));
if isequal(double(H.axes),Ha1)
Hc1s=[Hc1s,Haxs(i)];
end
end
end
Your colorbars are children of the figure, not of your subplot axes (colorbars are themselves axes). Try
hc = get(hf, 'children')
to get a list of all children of the figure, where hf is the figure handle. I'm not sure how you would which element of hc is equal to your Hc, i.e. which is the first colorbar.
Edit:
If you need to use an object's handle later on, it is best to assign it to a variable when it is created and to use that variable throughout.
However, if you don't want to do this (although I strongly recommend that you do) I can think of two things you can do. They are not particularly elegant and are definitely more work that just assigning your object handle to a variable.
If you know the order in which the axes were created then you are in luck: in the list if children, the first child created is the last element in the list and the last child created is the first. For example,
hf = figure;
ha1 = subplot(1,2,1);
image(rand(5,5));
hc1 = colorbar;
ha2 = subplot(1,2,2);
image(rand(5,5));
hc2 = colorbar;
hcs = get(hf, 'children')
hcs =
206.0016
204.0011
176.0016
174.0011
[hc2, ha2, hc1, ha1]'
ans =
206.0016
204.0011
176.0016
174.0011
Since you want the first colorbar, which was the second child created, you can then use
hc(end-2)
Alternatively, when creating the colorbar which you want to refer to in the future, set it's tag property. In the above example, replace the line
hc1 = colorbar;
with
hc1 = colorbar('tag', 'myID');
You can then get the handle to this object later with
findobj(hf, 'type', 'axes', 'tag', 'myID')