I'm writing a script that will create 2 suplots, and have either a single slider that scrolls the x axis of both plots, or 2 sliders that individually control each subplots x axis.
I have been using an adapted version of Steven Lords FileExchange scrolling plot demo for my slider.
Right now it will only update the most recent plot (as it is currently using gca in its callback function). I've tried just replacing gca with the axes I want (the variables first_plot or second_plot) but this doesn't seem to work.
My question is then, how should I adapt this function to either control both plots or each plot individually? Here is an example of the script I am writing:
x=0:1e-2:2*pi;
y=sin(x);
dx=2;
first_plot = subplot(2,1,1);
plot(x, y);
scrollplot(dx, x)
%Plot the respiration and probe data with scrolling bar
second_plot = subplot(2,1,2);
plot(x, y);
scrollplot(dx,x)
% dx is the width of the axis 'window'
function scrollplot(dx, x)
a=gca;
% Set appropriate axis limits and settings
set(gcf,'doublebuffer','on');
set(a,'xlim',[0 dx]);
% Generate constants for use in uicontrol initialization
pos=get(a,'position');
Newpos=[pos(1) pos(2)-0.1 pos(3) 0.05];
% This will create a slider which is just underneath the axis
% but still leaves room for the axis labels above the slider
xmax=max(x);
%S= set(gca,'xlim',(get(gcbo,'value')+[0 dx]));
S=['set(gca,''xlim'',get(gcbo,''value'')+[0 ' num2str(dx) '])'];
% Setting up callback string to modify XLim of axis (gca)
% based on the position of the slider (gcbo)
% Creating Uicontrol
h=uicontrol('style','slider',...
'units','normalized','position',Newpos,...
'callback',S,'min',0,'max',xmax-dx);
end
Thanks!
You're almost there, but there's some structural issues from when you modified the code from only one set of axes.
The key thing to do is change the callback function from a string to an actual local function. This makes handling the callback much simpler!
I've adapted your code to work with two (or more) axes. Note we only need to set up the scroll bar once! You were setting it up for each axes (the scroll bars were stacked on top of each other) and both scrollers only operated on gca. Just naming the axes isn't enough to change gca, you have to use those variables! I've assigned the axes to an array for easy manipulation.
Please see the comments for details:
x=0:1e-2:2*pi;
y=sin(x);
% dx is the width of the axis 'window'
dx=2;
% Initialise the figure once, and we only need to set the properties once
fig = figure(1); clf;
set( fig, 'doublebuffer', 'on');
% Create a placeholder for axes objects
ax = gobjects( 2, 1 );
% Create plots, storing them in the axes object
ax(1) = subplot(2,1,1);
plot(x, y);
ax(2) = subplot(2,1,2);
plot(x, y);
% Set up the scroller for the array of axes objects in 'ax'
scrollplot( dx, x, ax)
function scrollplot( dx, x, ax )
% Set appropriate axis limits
for ii = 1:numel(ax)
set( ax(ii), 'xlim', [0 dx] );
end
% Create Uicontrol slider
% The callback is another local function, this gives us more
% flexibility than a character array.
uicontrol('style','slider',...
'units', 'normalized', 'position', [0.1 0.01 0.8 0.05],...
'callback', #(slider, ~) scrollcallback( ax, dx, slider ), ...
'min', 0, 'max', max(x)-dx );
end
function scrollcallback( ax, dx, slider, varargin )
% Scroller callback loops through the axes objects and updates the xlim
val = slider.Value;
for ii = 1:numel(ax)
set( ax(ii), 'xlim', val + [0, dx] );
end
end
Related
I am trying to plot a single data set in terms of dates (x axis), I would like to add on the second (top) x axis with interval of time in days. Any suggestion?
One way is to create another axes on top of the first, and put the xaxis location at the 'top' for that. Here is a small example.
% Some example data
d = linspace(now,now-7,7);
y = randn(size(d));
% Create first axes
ax1 = axes;
plot(d,y);
datetick(ax1, 'x', 'yy-mm-dd')
% Create second axes
ax2 = axes;
plot(d,y,'Visible', 'off'); % No need to show doubles
set(ax2, 'Position', ax1.Position, 'XAxisLocation', 'top', ...
'Color', 'none' ,'YTick', []);
datetick(ax2, 'x', 'ddd')
I have two axes: one for viewing images and the other for plotting graphs. I get this error when I try to specify which axes I want to plot the data on: Error using plot. A numeric or double convertible argument is expected when trying plot(handles.axis,curve,x,y).
figure
handles.axis = gca;
x = 1:10;
y = 1:10;
curve = fit(x',y','linearinterp');
plot(curve,x,y) % works fine
plot(handles.axis,curve,x,y) % doesn't work
plot(curve,x,y,'Parent',handles.axis) % doesn't work
You can paste this example into Matlab to try it out. How can the code be corrected in order to specify the axes?
plot in the curve fitting toolbox is not the same as MATLAB's base plot. Though there is a documented syntax for specifying the parent axes for sfit objects, there doesn't seem to be one for cfit objects, which would be returned by your fit call in this case.
However, from the documentation we see that:
plot(cfit) plots the cfit object over the domain of the current axes, if any
So if the current axis is set prior to the plot call it should work as desired. This can be done either by modifying the figure's CurrentAxes property or by calling axes with the handle of an axes object as an input.
% Set up GUI
h.f = figure;
h.ax(1) = axes('Parent', h.f, 'Units', 'Normalized', 'Position', [0.07 0.1 0.4 0.85]);
h.ax(2) = axes('Parent', h.f, 'Units', 'Normalized', 'Position', [0.55 0.1 0.4 0.85]);
% Set up curve fit
x = 1:10;
y = 1:10;
curve = fit(x', y', 'linearinterp'); % Returns cfit object
axes(h.ax(2)); % Set right axes as CurrentAxes
% h.f.CurrentAxes = h.ax(2); % Set right axes as CurrentAxes
plot(curve, x, y);
I refine my answer as follows:
It looks that the plot function in matlab does not like a fit object after an axis followed by two vectors. In such case, I would do something like this:
x = 1:10;
y = 1:10;
figure % new figure
ax1 = subplot(2,1,1);
ax2 = subplot(2,1,2);
curve = fit(x',y','linearinterp');
plot(ax1,x,curve(x));
hold on;plot(ax1,x,y,'o') % works fine
plot(ax2,x,curve(x));
hold on;plot(ax2,x,y,'o') % works fine
Actually the trick is to provide x and then curve(x) as two vectors without giving the whole fit-object to the plot function.
I run into the problem where Matlab 2015b expands the labels of new Xticks when the x-axis gets bigger by using incomplete label, zeros, in the thread No Gap Next to Axis Label in Matlab?
The dynamic expansion of incomplete labels of xticks is not possible because there is always cases of insufficient space but only one symbol is needed to mark half between two values.
The situation is problematic with zeros because I have several calibration points and several systems where the extra zeros are errorprone.
I would like to have there another symbol.
Example code how to create those incomplete labels of xticks
labels = arrayfun(#(x)sprintf('%.2g', x), xticks, 'uniform', 0);
ax2 = axes('OuterPosition', [0.51 0.5 0.5 0.5]); % anything here
xticks = get(ax2, 'xtick'); % https://stackoverflow.com/a/35776785/54964
set(ax2, 'xticklabels', labels); % here the point!
Without those incomplete labels of xticks but broader labelling which is worser
labels = arrayfun(#(x)sprintf('%.2g', x), xticks, 'uniform', 0);
ax2 = axes('OuterPosition', [0.51 0.5 0.5 0.5]);
xticks = get(ax2, 'xtick'); % https://stackoverflow.com/a/35776785/54964
set(ax2, 'xtick', xticks, 'xticklabels', labels);
Output of Suever's answer
Beautiful Small window in the original size with scientific numbering because of callback(); at the end of the code following
Medium window
Code
hFig=figure;
data=randi(513,513);
D=mat2gray(pdist(data, 'correlation'));
ax2 = axes('OuterPosition', [0.51 0.5 0.5 0.5]);
plot(D, 'Parent', ax2);
axis(ax2, 'square');
title('Corr pdist');
cbar2 = colorbar();
set(ax2, 'XLim', [0 size(D,2)]);
set(cbar2, 'Visible', 'off')
grid minor;
labelconverter = #(x)sprintf('%.2g', x); % https://stackoverflow.com/a/35780915/54964
callback = #(varargin)set(ax2, 'xticklabels', arrayfun(labelconverter, get(ax2, 'xtick'), 'uniform', 0));
set(hFig, 'SizeChangedFcn', callback);
callback(); % necessary for small window
How can you have another symbol for the incomplete labels of xticks in Matlab?
As I said in the other question, if you want the labels to be updated automatically when you resize things, you'll want to do the following.
fig = figure;
% Set large xlimits to demonstrate the issue at hand
ax2 = axes('xlim', [0 1e9]);
% Force a draw event to have the axes determine where the
labelconverter = #(x)sprintf('%.2g', x);
callback = #(varargin)set(ax2, 'xticklabels', arrayfun(labelconverter, get(ax2, 'xtick'), 'uniform', 0));
set(fig, 'SizeChangedFcn', callback);
% Be sure to execute the callback to get new labels prior to figure resize.
callback();
As you change the size of your figure, the labels will be changed automatically and the positions will be updated.
Small Window
Medium Window
Large Window
Note: Test this code in isolation to verify that it works, then adapt the idea to your solution. It seems like you're ending up with a lot of complications because your namespace is polluted (for example your examples don't even run because labels isn't defined).
I'm trying to limit x axis, i.e., frequency axis to 4 Hz in MatLab. This is the code I used:
subplot(3,1,2);
%Fse = 220;
time = 0:1/fse:secBuffer-1/fse;
%a = eegCounter;
c = eegBuffer;
wo = 50 / (1000/2);
bw = wo / 60;
[b,a] = iirnotch(wo,bw);
y = filter(b,a,c);
ydft = fft(c);
xdft = fft(y);
xlabel('Frequency');
ylabel('Signal');
xlim([1,4]);
ylim([1,4]);
plot(xdft,ydft);
However mine is live signal plotting and both x axis and y axis keep changing according to incoming packets. How to limit x axis to 4 Hz?
When plotting MATLAB automatically tries to fit the axis with the dynamic range of the data. Therefore if you want to make sure only a given range is plotted, you need to specify it AFTER the call to plot to force MATLAB to do it, otherwise it won't and you will be stuck with the whole data.
Here is a very simple code in which I call xlim either before or after the call to plot. See the difference?
clear
clc
close all
x = 1:50;
y = x.^2;
figure
subplot(1,2,1)
xlim([1 20])
plot(x,y)
title('xlim before call to plot')
subplot(1,2,2)
plot(x,y)
xlim([1 20])
title('xlim after call to plot')
Produces this:
You have to set the XLimMode (and YLimMode) properties of the axes to manual. But even if you do so every call to plot(...) will reset that to auto and mess up your axes limits.
The cleanest way is to first define your axes and your plots outside of any loop (not forgetting to get their handle), then when you update the data just update the XData and YData of the line objects, using the set method. The set method will only update the property you pass in parameters, so it will not modify the XLimMode property.
%// This part of the code should run only once
h.ax = subplot(3,1,2) ; %// get the handle of the axes
h.line = plot(0) ; %// create an empty line plot
set(h.ax , 'XLimMode','manual' , 'XLim',[1 4]) ; %// define the properties of the axes (X)
set(h.ax , 'YLimMode','manual' , 'YLim',[1 4]) ; %// define the properties of the axes (Y)
xlabel('Frequency');
ylabel('Signal');
%//
%// This part of the code is the loop where you calculate and update your plot
%// ...
%// now do your calculations
%// ...
%// when it is time to update, just call:
set( h.line, 'XData',xdft 'YData',ydft ) ;
You can use the function axis as defined there axis function matlab
I need to plot a x-y function, that shows the histograms at x-values. Something similar to the bottom plot of the next figure:
I tried to use matlab's "barh", but can't plot many in the same figure.
Any ideas?
Or, maybe displacing the origin (baseline, basevalue in barseries properties) of successive plots would work. How could I do that for barh?
thanks.
Using 'Position' of axes property
% generate "data"
m = rand( 40,10 );
[n x] = hist( m, 50 );
% the actual plotting
figure;
ma = axes('Position',[.1 .1 .8 .8] ); % "parent" axes
N = size(n,2); % number of vertical bars
for ii=1:N,
% create an axes inside the parent axes for the ii-the barh
sa = axes('Position', [0.1+(ii-1)*.8/N, 0.1, .8/N, .8]); % position the ii-th barh
barh( x, n(:,ii), 'Parent', sa);
axis off;
end