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

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"

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.

Set figure to not be target for next plot

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

Slider disappears from GUI after being used

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

Matlab opens new figure in Callback despite hold

I am creating a GUI with MATLAB's GUIDE. Say, the GUI consists of an axis called axis1
and a slider called slider1. Further say I wanted to plot something (e.g. a box) into axis1 and change the box's height with the slider.
I tried doing this by adding a listener to the slider like so:
hListener = addlistener(handles.slider1,'Value','PostSet',#(src,evnt)boxHeightChange(src,evnt, handles.figure1));
in the GUI's opening function. I further defined:
function boxHeightChange(src, event, figname)
handles = guidata(figname);
% delete "old" box
delete(handles.plottedHandle);
% bring axis in focus
axes(handles.axes1);
% plot the new box (with changed size)
hold on; boxHandle = plotTheBox(event.AffectedObject.Value); hold off
handles.plottedHandle = boxHandle;
% update saved values
guidata(figname, handles);
end
This works, but always opens a new figure to plot the resizable box into instead of drawing into handles.axes1. I do not understand why, since I call axes(handles.axes1); and hold on;
Any idea that might explain the behavior?
I will post a solution to my own question.
Apparently the Callback of a listener is not declared as a "GUI Callback" which is why the GUI can not be accessed from within boxHeightChange if the GUI option "command-line accessibility" is not set to "On".
That means: In GUIDE go to Tools -> GUI options and set "Command-line accessibility" to "On".
Most plotting functions let you pass a name value pair 'Parent', ah where ah specifies the axes to plot on. I think this is the best way to handle your problem. Your actual plotting command seems to be wrapped in the plotTheBox function, so you will have to pass the axes handle in somehow.
Your plot command will look something like this:
plot(a,'Parent',handles.axes1)
You solved the problem a different way on your own, but I think you should do it my way because it's more explicit and it's less likely to lead to unforeseen problems.

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)]);