Slider disappears from GUI after being used - matlab

I try to make a MATLAB GUI programmatically and face the problem that my slider disappears after using it. I isolated the problem to keep the code short. In this GUI I want to refresh the plotmatrix each time the slider is used (ignore the fact that the value of the slider is completely irrelevant to my program, as mentioned before I really wanted to keep the code clean that's why I also removed this functionality). Here's the code (you have to run it as function):
function StackOverflowQuestion_GUI()
% clear memory
close all; clc;
% initialize figure
f = figure;
% create main axes
AX_main = axes('Parent',f,...
'Units','normalized','Position',[.1 .2 .8 .7]);
% create slider
uicontrol('Parent',f,...
'Style','slider','Callback',{#sliderCallback,AX_main},...
'Units','normalized','Position',[0.05 0.05 0.9 0.05]);
plotmatrix(AX_main,randn(500,3));
title('Random Plotmatrix');
end
function sliderCallback(~,~,AX_main) % callback for slider
plotmatrix(AX_main,randn(500,3));
title('Random Plotmatrix NEW');
end
Any help is appreciated! I think I misunderstood the concept of AXES. When I plot to the AXES-handle I created, why are other parts of the figure affected as well? If someone could explain to me how this graphic-handle system basically works that would be very nice too!

While daren shan's answer is correct, it's bizarre enough behavior that I was curious to see what is behind it.
Stepping through the source of plotmatrix we can find the line that deletes our slider object:
% Create/find BigAx and make it invisible
BigAx = newplot(cax);
Nothing obvious here, what does newplot do?
Use newplot at the beginning of high-level graphics code to determine
which figure and axes to target for graphics output. Calling newplot
can change the current figure and current axes. Basically, there are
three options when you are drawing graphics in existing figures and
axes:
Add the new graphics without changing any properties or deleting any objects.
Delete all existing objects whose handles are not hidden before drawing the new objects.
Delete all existing objects regardless of whether or not their handles are hidden, and reset most properties to their defaults before
drawing the new objects (refer to the following table for specific
information).
Oh...
So newplot is deleting the slider object.
So why does hold prevent the slider from being deleted, despite it being an axis method and not a figure method? To start, take a look at the "Algorithms" topic in the documentation:
The hold function sets the NextPlot property of the Axes or PolarAxes
object to either 'add' or 'replace'.
So hold on sets this to 'add' for the current axes. However, for a reason I can't currently figure out, this also sets the NextPlot of the figure to add as well.
We can see this with a short snippet:
f = figure('NextPlot', 'replacechildren');
ax = axes;
fprintf('NextPlot Status, base:\nFig: %s, Ax(1): %s\n\n', f.NextPlot, ax.NextPlot)
hold on
fprintf('NextPlot Status, hold on:\nFig: %s, Ax(1): %s\n\n', f.NextPlot, ax.NextPlot)
Which prints:
NextPlot Status, base:
Fig: replacechildren, Ax(1): replace
NextPlot Status, hold on:
Fig: add, Ax(1): add
Weird behavior, but I won't dwell on that.
Why does this matter? Go back to the newplot documentation. First, newplot reads the figure's NextPlot property to determine what to do. By default, a figure's NextPlot property is set to 'add', so it would retain all of the present graphics objects but plotmatrix explicitly changes this:
if ~hold_state
set(fig,'NextPlot','replacechildren')
end
So newplot goes from:
Draw to the current figure without clearing any graphics objects already present.
To:
Remove all child objects whose HandleVisibility property is set to on
and reset figure NextPlot property to add.
This clears the current figure and is equivalent to issuing the clf
command.
Which explains why the slider disappears and why hold on fixes the problem.
Per the documentation for newplot we can also set the HandleVisibility of the slider UIcontrol to save it from being destroyed:
% create slider
uicontrol('Parent',f,...
'Style','slider','Callback',{#sliderCallback,AX_main},...
'Units','normalized','Position',[0.05 0.05 0.9 0.05], ...
'HandleVisibility', 'off');

When you call plotmatrix, the function completely redraws the figure,
to conserve other elements you should use hold on; hold off; statements:
function StackOverflowQuestion_GUI()
% clear memory
clear; close all; clc;
% initialize figure
f = figure;
% create main axes
AX_main = axes('Parent',f,...
'Units','normalized','Position',[.1 .2 .8 .7]);
% create slider
uicontrol('Parent',f,...
'Style','slider','Callback',{#sliderCallback,AX_main},...
'Units','normalized','Position',[0.05 0.05 0.9 0.05]);
plotmatrix(AX_main,randn(500,3));
title('Random Plotmatrix');
end
function sliderCallback(~,~,AX_main) % callback for slider
hold on;
plotmatrix(AX_main,randn(500,3));
hold off;
title('Random Plotmatrix NEW');
end

Related

How to use "ButtonDownFcn" in a populated GUI axes?

I have a very simple GUI made in guide where i have a plot function initiated by a pushbutton which plots a scatter plot in axes (called Method1axes1):
handles.plot = scatter(X,Y, 'parent', handles.Method1axes1);
Now I want the user to be able to click the axes (plot) to get en new larger figure. I tried the below code which is working if i DON'T plot in the axes first. As soon as I run the plot function the scatterplot appears in Method1axes1 but I can no longer click the figure.
% --- Executes on mouse press over axes background.
function Method1axes1_ButtonDownFcn(hObject, eventdata, handles)
figure
scatter(X,Y);
What am I doing wrong?
This is a kind of special case for MATLAB, and it is not extremely well documented.
There are 2 things you need to consider:
1) The most obvious part. When you plot something in your axes, the plot is on the foreground. So when you click on your axes, the top plot intercept that click and tries to process it. You need to disable the mouse click capture from the plot/scatter/image objects you have in your axes. For that, you have to set the HitTest property of your scatter object to 'off'. (recent MATLAB version have changed the name of this property, it is now called PickableParts).
2) Much less obvious and documented. It used to be in the doc for the axes ButtonDownFcn callback but it is not explained anymore (although the behaviour persist). This is what I could find on old forums:
When you call PLOT, if the axes NextPlot property is set to 'replace'
(which it is by default) most of the properties of the axes (including
ButtonDownFcn) are reset to their default values.
Change the axes NextPlot property to 'replacechildren' to avoid this,
or set the ButtonDownFcn after calling PLOT, or use the low-level LINE
function instead of the higher-level PLOT function.
This is also discussed and explained here: Why does the ButtonDownFcn callback of my axes object stop working after plotting something?
For your case, I tried set(axe_handle,'NextPlot','replacechildren') and it works ok to let the mouse click reach the ButtonDownFcn, but unfortunately it creates havoc with the axes limits and LimitModes ... so I opted for the second solution, which is to redefine the callback for ButtonDownFcn after every plot in the axes.
So in summary, your code for the pushbutton1_Callback should be:
function pushbutton1_Callback(hObject, eventdata, handles)
% Whatever stuff you do before plotting
% ...
% Plot your data
handles.plot = scatter(X,Y, 'parent', handles.Method1axes1);
% Disable mouse click events for the "scatterplot" object
set(handles.plot,'HitTest','off') ;
% re-set the "ButtonDownFcn" callback
set(handles.Method1axes1,'ButtonDownFcn',#(s,e) Method1axes1_ButtonDownFcn(s,e,handles) )
And for your axes mouse click event, you might as well keep the handle of the new generated objects:
function Method1axes1_ButtonDownFcn(hObject, eventdata, handles)
handles.newfig = figure ;
handles.axes1copy = copyobj( handles.Method1axes1 , handles.newfig ) ;
Note that instead of plotting a new set, I simply use the copyobj function, very handy when you need to reproduce a plot.
Illustration:
If you want to set the figure/graph to enlarge and shrink on mouse
scroll/click, then just set the zoom property of the required axes in
OpeningFcn within the m file.
For example within the OpeningFcn in the GUI's m file, put the below code. Please ensure that you put the below code within the OpeningFcn function.
h1 = zoom(handles.Method1axes1);
h1.Enable = 'on';
Now, on each mouse scroll/click, you would be able to zoom in/out the graphs.
A sample screenshot of openingFcn for a GUI named ZoomAxesDemo is given below.

Updating chart in a GUI with Timer creates a new figure & axes

I am trying to make a program that displays live chart data from a broker once in a period. The period could be for example 5 seconds or 15 minutes.
I have made a GUI and a Timer. When the program starts, the first plot goes to the axes in the GUI. However, all the updated plots (from the timer) come to a new figure (only one), but not into the figure in the GUI.
Attached is some code:
This is in the openingFcn of the GUI .m-file
handles.timer = timer(...
'ExecutionMode', 'fixedRate', ... % Run timer repeatedly
'Period', 5, ... % Initial period is 5 sec.
'TimerFcn', {#updateChart,hObject}); % Specify callback
guidata(hObject,handles)
axes(handles.axes1);
candle(highBid, lowBid, closeBid, openBid);
start(handles.timer);
And the function updateChart:
function updateChart(hObject,eventdata,hfigure)
% Get new data, one candle at a time
...
% How many times the chart has already updated
handles = guidata(hfigure);
k = handles.timer.TasksExecuted;
...
% Draw (update) the chart
hold on;
axes(handles.axes1);
candle(highBid, lowBid, closeBid, openBid); % this will be plotted in a new figure !
Any suggestions on how to update the chart at the GUI window?
I found the way to solve it. Indeed the same thing happens with any kind of high level plotting function. I had to reproduce your problem with the plot function and the behaviour was as you described.
Short answer:
you have to set the HandleVisibility of the figure to on (instead of the default setting callback). If you are working with GUIDE you have to set that directly in the figure property inspector from GUIDE (for some obscure reason it does not work if this is set later in the initialisation code):
This settings will let the timer callback have visibility of the figure children object so the plot command will not decide to create a new set of axes & figure when faced with invisible ones.
Note 1:
The plot was always refreshed in the right axes when the plot command was specified with the right target handle. For graphic functions which support passing the target axes in parameter, the syntax :
% infallible syntax (when allowed)
plot( data2plot , 'Parent',targetAxesHandle)
is always preferable to the one you were using (setting an axes active then plotting in the current active axes)
% this syntax may fail to plot in the right "axes" somtimes, as you observed
axes(targetAxesHandle);
plot( data2plot )
Now reading the documentation for your specific plotting function candle, I did not find clues that you can pass an axes handle to it, so for this type of function, you have to resort to the solution given on top of this post. However, if you ever give some feedback to the writers of the toolbox, I would strongly suggest to tell them about this important missing feature.
Note 2:
You do not have to call hold on before each plot. If you know you will always "add" to the plot, you can set it once and for all in the initialisation code:
set(handles.axes1,'Nextplot','add') % set "Hold on" permanently for "axes1"
And if you ever want to remove the locked hold, just set :
set(handles.axes1,'Nextplot','replace') % set "Hold off" permanently for "axes1"

How to make an axes current which is inside a figure created by GUIDE?

I created a bank figure using GUIDE and put an axes object inside it and saved the figure. Now I want to load the figure and set its axes as current axes object. This is my code:
close all; clear all; clc;
fh = openfig('test.fig');
ah = findobj(fh, 'tag', 'axes1');
figure(fh);
axes(ah);
plot(rand(10, 1));
But plot creates a new figure and plots in it! Am I missing something?
I know that I can solve it with plot(ah, ...), but I want to make gca to return this new axes. I have a lot of plotting codes that I want to be drawn in this new axes.
By default, HandleVisibility of GUIDE figures is set such that they aren't automatically detected. For example if you load the figure and then call gcf, you'll also create a new figure.
To have the plot be placed within the axes, you can specify the axes explicitly as the parent of the plot command.
plot(rand(10, 1), 'Parent', ah)
Alternately, you could specify that the HandleVisibility of the figure is 'on'. And then plot will be able to find it. This could be done by either setting the value of HandleVisibility using the property editor in GUIDE or calling the set function:
set(fh, 'HandleVisibility', 'on')
I recommend the first option as explicitly specifying the parent axes is always better than implicit.

Matlab axes resize in non interactive mode behaves unexpectedly

I am confused about how resizing works for plots with two axes in matlab. I am finding inconsistent behaviour in the result depending on whether I step through the graph generation code in a debugger (works properly) or if I run it all at once.
For example, in the following function, I am linking the Position property of my two axes:
if I put a breakpoint on the last line, and step through it, the position of the first axis is changed automatically as it should be
if I run it all at once, it does not change and gets misaligned with the second axis
What is the reason for this?
I am using 2015B if this matters.
function graph_test
% set up a horizontal bar plot with a scattre plot on a secondary x-axis
% at the top
barh(1:10)
ax1 = gca;
set(ax1,'Box','off');
ax2 = axes('XAxisLocation','top','Color','none','Position', ax1.Position);
linkaxes([ax1 ax2],'y');
linkprop([ax1 ax2],'Position');
set(ax2,'Ytick',[]);
hold on;
scatter(ax2,[1:-.1:.1],[1:10]);
xlabel(ax1,'bottom axis');
xlabel(ax2,'top axis');
% set title
% we need to set this on second axis so that it does
% not overlap with axis legend
% the secondary axis is not auto resized as per matlab docs
title(ax2,'My graph');
% resize ax2 manually
ax2.OuterPosition(4) = 1-ax2.OuterPosition(1);
end
The issue is that you are listening to changes in Position yet you are explicitly updating OuterPosition. The changes made in OuterPosition ultimately make their way back to changes in Position but if the processor or renderer is busy, it is unable to propagate the change back (and notify listeners) until the processor is idle.
To fix this, you can explicitly tell MATLAB to flush all queued events immediately after changing the OuterPosition using drawnow. This will cause Position to update and notify all potential listeners, which in your case will lead to the Position of ax1 being updated.
ax2.OuterPosition(4) = 1 - ax2.OuterPosition(1);
drawnow nocallbacks
The reason that you don't see this behavior when stepping through it with the debugger is that, at that point in time, the MATLAB interpreter is idle and able to process all graphics events in real-time.

In Matlab, own function which dynamically load/display images into Axes from a Gui in a loop?

With Guide I made a Matlab Gui that have 10 Axes in which i want to display images, in all of them at once, after i press a Button.
I made a separate .m file with function Load_Write_Img_Results(img_index) that i call from the Button Callback which have the following code:
for i = 1 : 10
handles_imgOut = findobj('Tag', ['imgOut' num2str(i)]);
set(handles_imgOut, 'HandleVisibility', 'ON');
axes(handles_imgOut);
image(imgs_data{img_index(i)});
...
end
Every time when i run the main Gui and press the button for the first time, images are displayed in all axes, so everything works ok.
The problem appear when i press the button for the second time and i get this error:
Error using axes
Invalid object handle
on this line:
axes(handles_imgOut);
When debugging, i saw that after handles_imgOut = findobj('Tag', ['imgOut' num2str(i)]); the handles_imgOut is not getting any value and is empty, so it's obvious that error.
Is there any chance i can't get handles for axes at the second press of the button?
Also, i want to know how can i solve this warning from Matlab:
Calling AXES(h) in a loop can be slow. Consider moving the call to AXES outside the loop.
Thanks in advance, any suggestions are welcome!
[SOLUTION]:
for i = 1 : 10
handles_imgOut = findobj('Tag', ['imgOut' num2str(i)]);
set(handles_imgOut, 'HandleVisibility', 'ON');
axes(handles_imgOut);
image(imgs_data{img_index(i)});
set(gca, 'Tag', ['imgOut' num2str(i)]); //! renew the tag
...
end
I'm fairly new to GUIDE and I've encountered similar problems, with the graph not being updated more than once / axes not found.
In the end, I used the following approach, which I hope can be useful to you too:
% I get my axes directly from the handles object, instead of using findObj:
graph1 = handles.axes1;
graph2 = handles.axes2;
% clear axes
cla(graph1);
cla(graph2);
% showTrial is my function that draws the graphs -
%notice that I used handles to store other variables as well (.data, .trials)
showTrial(handles.data, handles.trials(1), graph1, graph2)
To sum it up:
don't use findObj, get your axes from handles (it should automatically contain them as imgOut1,imgOut2, etc.)
pass the axes to your drawing function or pass directly the handles variable e.g. Load_Write_Img_Results(img_index, handles)
The fact that it works on the first button press, but not thereafter hints that
image(imgs_data{img_index(i)});
opens new axes instead of drawing into the existing ones. Since the new axes are not initialized with your tags, findobj will not find them. Either
(1) make sure to hold the original axes by placing a hold on command right after their creation,
or
(2) renew the tag right after the image command by
set(gca, 'Tag', ['imgOut' num2str(i)]);