I am using MATLAB for my data analysis. In my scripts I create figures with fit results so that I can quickly play with fit parameters and see how they change my end results.
My question is whether there is a simple way to be able to just refresh my figures along with subplots and annotations without losing the positions and sizes of the subplots and annotations.
I.e.: I would like to be able manually position my figures on my workspace (I use Linux), manually adjust figure size/position, subplot sizes/positions and annotation sizes/positions and then have their content update, when I rerun the script that does my fitting.
I do realize that the command figure(...) does this nicely and it works, but I am having the problem, that when I resize/move subplots and move annotations, that they're sizes/positions get lost, when I rerun the script.
I am aware that I probably need to use the subplot/annotation handles for this but the question is, what the most elegant and simple way is to do this? Since I need the code to also work when run the first time (i.e. no figures/subplot/annoations yet existent), will I need a lot of if-clause, to check if the handles already exist?
I've been using MATLAB for quite some time and for almost equally long it's been bothering me that I don't know an elegant way to do this!
I had two ideas:
use the "File > Generate Code..." functionality. MATLAB will create
a function that recreates the figure with any modification you made
interactively.
manually retrieve the properties of interest for the objects manipulated
and apply them again when you rerun your scripts. You could either
maintain a list of handles for those graphics objects, or even use the
'Tag' in combination with the FINDOBJ function to locate such objects.
I will illustrate the latter idea with an example:
When the script is run for the first time, we give the user the chance to make changes to the figures interactively. Once done, we retrieve the 'Position' property of figures and all children components contained inside them. These values are then saved to a MAT-file.
Now the user adjusts some parameters and reruns the script. We check for the presence of the MAT-file. If it exists, we load the saved position values and apply them to the figures and their descendant objects, thus restoring the components to their last saved state.
This solution is rather simplistic, thus if changes are made to the script breaking the hierarchy of the graphics handles, you will have to delete the MAT-file, and run the script again.
%# close all figures
close all
%# your script which creates figures
figure, plot(rand(100,1))
figure
subplot(121), plot( cos(linspace(0,6*pi,100)) )
subplot(122), image, axis image, axis ij
%# check for MAT-file
if exist('script_prefs.mat','file')
%# load saved values
load script_prefs.mat
%# get opened figures, and find objects with a position property
fig = get(0, 'Children'); %# allchild(0)
obj = findobj(fig, '-property','position');
try
%# apply values to position property
set(fig, {'Position'},figPos);
set(obj, {'Position'},objPos);
catch
%# delete MAT-file
delete script_prefs.mat
end
else
%# get opened figures, and find objects with a position property
fig = get(0, 'Children');
obj = findobj(fig, '-property','position');
%# wait for the user to finish customizing
waitFig = figure('Menubar','none', 'Position',[200 200 200 40]);
h = uicontrol('Units','normalized', 'Position',[0 0 1 1], ...
'String','Done?', 'Callback','uiresume(gcbf)');
uiwait(waitFig);
close(waitFig);
%# get position property of figures and tagged objects
figPos = get(fig, 'Position');
objPos = get(obj, 'Position');
%# save values to file
save script_prefs.mat figPos objPos
end
I take it you mean you want to refresh the plots themselves, but not anything else.
When you perform a plot(), specify an output argument to retrieve a line handle. Then, when you want to plot different, data, manually adjust that line handles' XData and YData:
lh = plot(xdata,ydata);
%# do some calculations here
...
%# calculated new values: newX and newY
set(lh, 'XData', newx, 'YData', newy);
This is likewise for anything else you want to refresh but not recreate - get the handle corresponding to the graphics object and manually update its properties at a low level.
Related
How can I have diferent markers or line styles when I am plotting same variable from 8 diferent data files in one figure?
I have got my code that reads multiple excel files and it is perfectly fine with plotting the variable I call for from all 8 diferent excel files.
Although I already have the plot with different colors that I have defined. However, somehow, I cannot define any marker or diferent Line Styles. I have attached my plotting codes below; please also see the figure
ax = gca;
grid on;
hold on
plot(Pin_dBm,Pout_Meas_dbm,"LineWidth",2)
%ax.LineStyleOrder={'-o','-+','-*','-x','-s','-d','-v','->'};
ax.ColorOrder=[1 0 0; 1 0 1; 0 1 0; 0.4660 0.6740 0.1880; 0 0 1; 0.3010 0.7450 0.9330; 0.8500 0.3250 0.0980; 0.9290 0.6940 0.1250]
ylabel('Output Power [dBm]','Color','K')
xlabel('Input Power [dBm]')
title('Output Power comparison - ON mode (0V)', 'Color', 'k')
legend('3F50Sa1','3F100Sa1', '5F50Sa1','5F100Sa1','7F50Sa1','7F100Sa1','9F50Sa1','9F100Sa1','location', 'bestoutside')
To change trace (or marker) properties you have to use the plot handle, not the figure handle, to change line properties like LineWidth and Color.
To grab the plot handle you have to plot like this
hf1=figure(1)
ax = gca;
grid on;
hp1=plot(Pin_dBm,Pout_Meas_dbm)
hp1.LineWidth=2
hp1.Color='r' % red
..
hf1 is the figure handle, not the trace or curve or graph handle.
The figure is the frame containing traces.
You don't have to, this is ok
hf1=figure
but it's good practice to number figures figure(n) as soon as generated, it helps reading code if later on someone has to findout what figure handle belongs to what figure.
now you tell the graph container, the figure with handle hf1 not to remove the 1st trace regardless of what you plot next:
hold(ax1,'on')
and now, with a for loop or one by one, or in any other way you choose, you can add more traces onto same figures changing the properties you choose using a different plot handle
hp2=plot(Pin_dBm2,Pout_Meas_dbm2)
hp2.LineWidth=1.5
hp1.Color='b' % blue
..
and as you mention in your question you can send the same variable updated with different input data or file
update_Pin_dBm;
update_Pout_Meas_dbm;
If you do not update Pin_dBm and-or Pout_Meas_dbm the next plot with different properties may be identical or similar and overlap the previous trace.
hp3=plot(Pin_dBm,Pout_Meas_dbm)
hp3.LineWidth=1
hp1.Color=[0 1 1] % cyan
The figure handle hf1 could still be used to change trace properties, because as the container to the plot, has as children property the handle to the plot. But no one uses what is otherwise a rather long Java-like expression
Note: The term marker is generally used, not for the graph line, the plot, but for the pointers on the graph line that scopes and also MATLAB allows to put, to read a particular (x,y).
If you find this answer useful, would you please be so kind to accept it?
Thanks for reading my answer.
I want to make 4 separate sub-figures each in a different position. my goal was to setup the figures first and following add objects to each figure with the goal to animate the objects later. Everything seems to work fine until I try to parent the objects to the sub-figure. the error doesn't occur when I add the objects right after building the sub-figure using the following code.
H1 = subplot('Position',[0.2,0.2,0.2,0.2]);
rectangle('Parent',H1,'Position',[10,20,20,20])
The error seems to rise when I try to callback to a figure built afterwards as seen in the code I am currently working on below.
screencolor = [0,0,0];
StimWindow = figure('MenuBar','none', ... % Build Window for stimulus
'Color',screencolor);
figuresize = get(0,'ScreenSize');
set(StimWindow,'Position',figuresize);
set(0,'defaultaxesposition',[0 0 1 1])
Stimsubfigures{1} = subplot('Position',[0,0,1,1]); % First subplot figure which spans the entire screen
set(Stimsubfigures{1},'xLim',[0,100])
set(Stimsubfigures{1},'YLim',[0,100])
set(Stimsubfigures{1},'Visible','off')
% create subplots for stim system 3 plate setup
for aa = 2:4
Stimsubfigures{aa} = subplot(...
'Position',[.01+aa*.21,.2,.2,.2], ...
'color','none');
set(Stimsubfigures{aa},'xLim',[0,100])
set(Stimsubfigures{aa},'YLim',[0,100])
set(Stimsubfigures{aa},'Visible','off')
end
OrtDish = rectangle(...
'Parent',Stimsubfigures{1},...
'Position', [0,0,100,100],...
'facecolor',screencolor,...
'edgecolor',[.5,0,0],...
'curvature',[1,1],...
'LineWidth',3);
The error displayed states an object cannot be attached to a deleted handle. I will provide the exact error when I reach a computer.
This is weird as the handle wasn't deleted, it was stored in a cell matrix.
The problem comes from the overlapping subplot: you draw a full-scale subplot and then on top of it some new ones.
As the Matlab doc says,
If a SUBPLOT specification causes a new axes to overlap an existing axes, the existing axes is deleted - unless the position of the new and existing axes are identical.
If you replace subplot by axes, you are good to go.
I'm working on a custom progress monitor with some graphs. I've noticed that Matlab's waitbar creates a figure with some special properties so that if you do
plot(rand(100,1));
wb = waitbar(0);
plot(rand(100,1));
the second plot ends up replacing the first plot and not in wb. Is there a property I can set so that when I create my progress monitor and then plot something afterwards, the graph doesn't end up in my figure?
To be clear, I'm trying to have
plot(rand(100,1));
temp = MyProgressBar();
plot(rand(100,1));
create a figure for the first plot, create a different figure in the second line, then plot a new graph in the third line.
To protect your progress bar figure against subsequent plotting operations, I would set the 'HandleVisibility' property of its axes to 'off'. That should prevent it ever becoming the current axes, thus keeping subsequent plotting commands from modifying or adding to it. It's a good practice for stand-alone figures/GUIs in general that you turn off the handle visibility of all objects (figure, uicontrols, etc.) in this way to insulate them against being modified by outside code. This is almost certainly what is done in the code for waitbar.
As an additional aside, it's good practice to target your plots to a given axes by passing the axes handle as the first argument. You also have to make sure that, if you want new plots to be added to existing plots, you use things like the hold command first. Here's how I'd rework your example, assuming you want the two plots to appear on the same axes:
plot(rand(100,1)); % Creates new figure and axes
hAxes = gca; % Get the axes handle
hold on; % Allow subsequent plots to be added
temp = MyProgressBar();
plot(hAxes, rand(100,1)); % Will be added to the first plot axes
I have figures (in Matlab's fig file format) each of which contains a line plot with two lines (representing EEG curves), axes, bunch of labels etc.
I want to:
change the color of the lines
remove some of the labels
I would loop over the fig files and do the same thing for each of them.
Is there a list of all the objects within the figure that I could index and edit? How can I get to those objects using commands (i.e. without the gui)?
Line, lable, etc. are children of axis, which is itself a child of a figure. What you need to do is acquire handles to the objects you want to change through this hierarchy.
% Get a handle to the figure
hfig = openfig('testfig');
% Get all children of the CurrentAxes. Most of what you want is here.
axes_obj = allchild(hfig.CurrentAxes);
% Edit Axes object according to its type
For ii = 1:length(axes_obj)
switch axes_obj(ii).Type
case 'Text'
% Do something, for example:
axes_obj(ii).String = 'changed';
case 'Line'
% Do something, for example:
axes_obj(ii).MarkerEdgeColor = 'b';
end
end
% Save figure
savefig(hfig, 'testfig')
You can see all the properties of the object you wish to edit by simply typing axes_obj(ii) in the command window.
This should be a problem with a trivial solution, but still I wasn't able to find one.
Say that I have 2 matlab figures fig1.fig, fig2.fig which I want to load and show in the same plotting window.
What should I do?
I mean, I am pretty sure that I can accomplish the task using some low(er) level graphic command which extracts contents from one image and put them in the second one, nonetheless I cannot believe that there is not any high level function (load fig2 on top of fig1) that does this...Comparing 2 plots (unfortunately already saved) is a very common task, I'd say.
Its not clear if you want to extract data from the figures and compare the data, or if you want to combine the plots from two figures into a single figure.
Here is how you combine two figures into one (if thats what you want to do)..
First load the figures:
fig1 = open('FigureFile1.fig');
fig2 = open('FigureFile2.fig');
Get the axes objects from the figures
ax1 = get(fig1, 'Children');
ax2 = get(fig2, 'Children');
Now copy the hangle graphics objects from ax2 to ax1. The loop isn't neccesary if your figures only have a single axes
for i = 1 : numel(ax2)
ax2Children = get(ax2(i),'Children');
copyobj(ax2Children, ax1(i));
end
Note This example assumes that your figures have the same nubmer of axes and that you want to copy objects from the first axes in the second figure to the first axes on the first figure. Its up to you to figure out the proper indexing if the axes indices aren't lined up.
The answer slayton gave is good. Here's another tip: If you have two plots opened in two separate Matlab figure windows, don't forget you can point-and-click copy the proper plots. Do this by clicking the arrow pointer in the Matlab figure window, and then clicking on the plotted line. Copy the (plotted line, textbox, etc...) object. Then, similarly select the axis in the other Matlab figure window and paste it.
I give this 'silly' solution because it has proven to be useful in in collaboration meetings. Point-and-click copying in front of someone (like your adviser) communicates exactly what curves are being compared, and it prevents you from having to fire up code in front of others.
You can also go to File in the menu, Generate Code, for each plots.
Then copy and paste both in the same mfile, with a "hold on" in between and changing details related to the appearance.
Then run the new m-file.