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);
Related
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
I have written the following code to try and retrieve ONLY the axes and its plot from my MATLAB GUI.
F = getframe(gca);
figure();
image(F.cdata);
saveas(gcf,'PlotPic','png');
close(gcf);
I noticed, however, that this method does not include ANY of my axis labels or title. Is there any way which I can get the getframe function to include the axis labels and title?
I tried the following code but it did exactly the same
pl = plot(x,y);
xlabel('x')
ylabel('y')
ftmp = figure;
atmp = axes;
copyobj(pl,atmp);
saveas(ftmp,'PlotPic.png');
delete(ftmp);
I would do it using the rect option of the getframe function.
Basically you can provide a 2nd input argument to getframe, which then captures the content of the rectangle specified as argument. The nice thing is that you can use the handles to an axes, so it does not capture your whole GUI figure but rather a specific axes.
For example, using this line:
F = getframe(gca,RectanglePosition);
Concretely, you could set the coordinates of the rectangle such that they span both axis labels and the title as well. Here is a sample code. The pushbutton callback executes getframe and opens a new figure with the content of F.cdata:
function GUI_GetFrame
clc
clear
close all
%// Create GUI components
hFigure = figure('Position',[100 100 500 500],'Units','Pixels');
handles.axes1 = axes('Units','Pixels','Position',[60,90,400,300]);
handles.Button = uicontrol('Style','Push','Position',[200 470 60 20],'String','Get frame','Callback',#(s,e) GetFrameCallback);
%// Just create a dummy plot to illustrate
handles.Period = 2*pi;
handles.Frequency = 1/handles.Period;
handles.x = 0:pi/10:2*pi;
handles.y = rand(1)*sin(handles.Period.*handles.x);
plot(handles.x,handles.y,'Parent',handles.axes1)
title('This is a nice title','FontSize',18);
guidata(hFigure,handles); %// Save handles structure of GUI.
function GetFrameCallback(~,~)
handles = guidata(hFigure);
%// Get the position of the axes you are interested in. The 3rd and
%// 4th coordinates are useful (width and height).
AxesPos = get(handles.axes1,'Position');
%// Call getframe with a custom rectangle size.You might need to change this.
F = getframe(gca,[-30 -30 AxesPos(3)+50 AxesPos(4)+80]);
%// Just to display the result
figure()
imshow(F.cdata)
end
end
The GUI looks like this:
And once I press the pushbutton, this is the figure that pops up:
So the only trouble you have is to figure out the dimensions of the rectangle you need to select to capture the axis labels and the title.
Hope that solves your problem!
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');
In a for loop, I create a variable number of subplots that are displayed on a single figure. Can I also save each subplot as a separate, full size plot and image file (preferably JPG)?
Use copyobj to a new figure and then use saveas with the new figure handle:
Example code that YOU should have provided (see SSCCE):
figure
nLines = 2;
nColumns = 3;
handles = zeros(nLines,nColumns)
for line = 1:nLines
for column = 1:nColumns
handles(line,column)=subplot(nLines,nColumns,column+(line-1)*nColumns);
plot([line column]);
title(sprintf('Cool title (%d,%d)',line,column))
ylabel(sprintf('Ylabel yeah (%d,%d)',line,column))
xlabel(sprintf('Xlabel nah (%d,%d)',line,column))
end
end
Here I have the subplot handles saved, but supposing you don't have them saved:
axesH = findobj(gcf,'Type','axes','-not','Tag','legend'); % This will change the order from which the axes will be saved. If you need to save in the correct order, you will need access to the subplot handles
nAxes = numel(axesH)
newFig = figure;
for k=1:nAxes
newAxes=copyobj(axesH(k),newFig);
% Expand subplot to occupy the hole figure:
set(newAxes,'OuterPosition',[0 0 1 1]);
tightInset=get(newAxes,'TightInset');
set(newAxes,'Position',[tightInset(1:2) [1 1]-(tightInset(3:4)+tightInset(1:2))])
saveas(newFig,sprintf('axes_%d.jpg',k),'jpg');
delete(newAxes);
end
delete(newFig);
Example of one axes saved:
To remove the deadspace I used information available on this topic.
Say you have the handle to the subfigure's axis, ha, you can use getframe and frame2im as follows,
F = getframe(ha);
[im,map] = frame2im(F);
if isempty(map)
imwrite(im,'subframe.jpg');
else
imwrite(im,map,'subframe.jpg');
end
Note that this will save the axis exactly how it appears on your screen, so resize the figure to your liking before saving. To use as much figure real estate as possible, tryout the subfigure_tight function on MATLAB Central.
I am trying to plot both horizontal and vertical lines on a histogram that will be accurate to changing limits on both the x and y axes. I was using the line(X,Y) function, but cannot find a useful way to get the lines to be set depending on the parameters of the graph window.
I'm not entirely clear on what you want but here's the simplest answer to what I think you want:
Makes a sample histogram
y = randn(100,1);
hist(y,10)
Get the current limits of the x and y axes
xlimits = get(gca, 'XLim');
ylimits = get(gca, 'YLim');
Computes a single numeric value to plot a horizontal line.You'll want to replace this with your specific function of the axes limits
halfpt = ((ylimits(2)-ylimits(1))/2) + ylimits(1);
line(xlimits, [halfpt halfpt])
I'm not sure, but from your comment I'm suspecting that you aren't changing your axes programmatically, with say set(gca,'Xlim', [0 10]) but that you want to be able to drag the axes of your your figure with the mouse, say by using that hand/pointer button in the figure editor. In which case, one solution is to make your figure a GUI and write a callback function that handles line plotting that is a function of the xlim and ylim. Here's an example that always keeps the line in the middle of the axes regardless of how they are dragged:
function myGUI
figure('WindowButtonMotionFcn',#myCallback)
y = randn(100,1);
hist(y,10)
function myCallback(src,eventdata)
xlimits = get(gca, 'XLim');
ylimits = get(gca, 'YLim');
halfpt = ((ylimits(2)-ylimits(1))/2) + ylimits(1);
lh = findall(gcf,'Type','Line');
delete(lh);
myline = line(xlimits, [halfpt halfpt])
end
end