Use the output of a callback further in the code - matlab

I have a subroutine in my code where I create a GUI for the user to chose a type of analysis:
%% Gives user the choice of replacement method
figure('Units','Normalized','Color','w','Position',[.3 .6 .4 .15],...
'NumberTitle','off','Name','Analysis choice','MenuBar','none');
uicontrol('Style','text','Units','Normalized',...
'Position',[.1 .7 .8 .2],'BackgroundColor','w','String','What replacement method do you want to use?');
uicontrol('Style','pushbutton','Units','Normalized',...
'Position',[.05 .3 .3 .3],'String','Cubic interpolation 24 points',...
'CallBack','cubic_choice'); % cubic_choice.m rotine must be on current folder
uicontrol('Style','pushbutton','Units','Normalized',...
'Position',[.4 .3 .3 .3],'String','Last good data value',...
'CallBack','lgv_choice'); % lgv_choice.m rotine must be on current folder
uicontrol('Style','pushbutton','Units','Normalized',...
'Position',[.75 .3 .2 .3],'String','Linear interpolation',...
'CallBack','li_choice'); % li_choice.m rotine must be on current folder
uiwait;
close;
Further in the code, I have a if loop that analysis the choice that the user made:
if strcmp(inp,'cubic') ...
The problem is, when I press the button "Cubic interpolation 24 points", the callback function doesn't give me the inp variable, i.e., it doesn't appear on the workspace.
The callback function is something like this:
%% Callback script for replacement method
% Cubic interpolation with 24 points method
function [inp] = cubic_choice
inp = 'cubic';
uiresume(gcbf); % resumes the button call
i know that I probably have to use setappdata and getappdata because I already read it in some other thread but I can't get it to work.
Can anyone help me?
Thanks in advance.
Kind regards,
Pedro Sanches

Rather than using a global variable, you imho should check the functions getappdata, setappdata and / or guidata.
Basically, from your callback, you'll have to set your choice at some place you can access it in the rest of the code.
One possibility e.g. is to use set/getappdata as follows:
function cubic_choice()
figHandle = ancestor(gcbf, 'figure');
setappdata(figHandle, 'choice', 'cubic');
uiresume(gcbf);
end
Right after your uiwait call you can then get this property, taking figHandle from the return-value of your figure-call in the first line of your example:
inp = getappdata(figHandle, 'choice');

This is an issue of variable scope and design. inp will not be visible outside the callback function. You are also not "returning it to the workspace" because it is the GUI that is calling you back; no assignment takes place in the workspace.
You could declare global inp both in your workspace and in the callback function, as in
function [inp] = cubic_choice
global inp
inp = 'cubic';
However, you may want to consider responding to the choice directly from within the callback handler. This means, whatever code you have in your if-statement may just as well go directly into your callback function.
Alternatively, if it is really about choices, why not use radio buttons? Then, you could hold on to he handle h returned by uicontrol and query it's state at any later time with get(h, 'value').

Related

Close uialert figure by button click

Based on a MATLAB example, I added a close command in order to close the uifigure after pressing the OK button. However, in its current state the figure is closed automatically, rather than on the button click. How can I alter the below to have the figure close on click?
Code:
fig = uifigure;
message = {'Fire hazard!','Consider reducing temperature.'};
uialert(fig,message,'Warning',...
'Icon','warning');
close(fig)
You should use a callback in uialert:
fig = uifigure;
message = {'Fire hazard!','Consider reducing temperature.'};
uialert(fig, message, 'Warning', 'Icon', 'warning', ...
'CloseFcn', #(~, ~)close(fig)); % This will be executed after Ok is pressed
The syntax # is the Matlab way to define an anonymous function (Matlab calls these function handles, other languages usually call these lambdas). It allows passing functions as parameters to other functions. If your anonymous function needs to receive parameters, the syntax #(p1, p2, p3) can be used.
In the case of callbacks for uifigures, the callbacks always expect two parameters: fig and event. fig is the figure where the event happened, event is a structure describing the event. In the case above, since all you want to do is close the figure (and you already know which figure you want to close), you can ignore both parameters. The syntax in Matlab to tell that you are receiving a parameter that you purposefully are ignoring is by using a tilde (~) in the position of the parameter. This can be used anywhere actually, including in the definition of normal functions or when unpacking the return value. For example:
[U,~,V] = svd(A)
tells that you are not interested in the singular values of your SVD, only in left and right singular vectors.
You can read more about function handles in the Matlab documentation.

How to plot several function calls in one figure

I made a matlab-function that plots a graph. When I call the function several times, I want it to plot all the graphs in one prepared figure. But instead my code opens with every function call the prepared figure in a new window with only one Graph in it.
My function looks like this
function myfunction(x,y)
if ~exist('myfigure')
myfigure = openfig('myfigure.fig')
assignin('base', 'myfigure',myfigure)
end
figure(myfigure);
plot(x,y)
end
With the if-function I tried to prevent it from opening a new figure-window, when myfigure is allready opened. But it seems like Matlab just ignores the if-function for my surprise. Even the Assignin didn't help out. Although checking in the command window, showed that exist('myfigure') changes its value.
I really don't know why the if-function is ignored by Matlab. Have you any suggestions how to fix this
The problem here is exist cannot see the previous figure, because it's handle is deleted when the previous call to the function was ended. My suggestion is as follow:
Pass the figure handle to the function, and also return it as output:
function myfigure = myfunction(x,y,myfigure)
if nargin<3 % if you pass 2 variables or less
myfigure = figure; % create a figure
else
figure(myfigure); % otherwise use the one in handle
end
plot(x,y)
end
Here a sample code for that:
x = 0:0.01:2*pi;
myfigure = myfunction(x,sin(x)); %first call
myfunction(x,cos(x),myfigure); % second call
myfunction(x,tan(x),myfigure); % third call...
Note that you only need to get myfunction output on the first call, then you can continue using it until you delete the figure.
The function figure that you used is probably why it opens a new figure.
What you might want to do is simply get the current axes and plot in it.
So your function would look like this
function myfunction(x,y)
myaxes = gca;
plot(myaxes,x,y)
end
This would work if you only have one active figure and axes, if you have more than you mihgt want to pass the axes handle to the function.

Make MATLAB accept KeyPress only once

I am currently trying to write an experiment in MATLAB. As part of it, it should accept and record a key response, either 1 or 0. The problem is that I only need the keys to be recorded at one specific time slot, and to be ignored in the other parts of the experiment. The response has to be recorded along with the time it took to respond, and it should be done once only, the earliest (so that once the user has pressed a key, the program does not record the subsequent ones).
So far, I have attempted numerous ways. These are probably nooby workarounds, but I am not good with object oriented programming.
One option is to use
set(gcf,'KeyPressFcn',#KeyDownListener)
where KeyDownListener is
function KeyUpListener(key_hand, key_obj, starting_time)
toc(starting_time)
key_pressed = key_obj.Key; return; end
However, there are two problems: 1) I am struggling trying to get the value from this function back to the calling script; 2) Once MATLAB reads this set(...) piece of code, it keeps capturing every single key pressed. So, basically, if there are 100 trials (each consisting of, say, 5 stages, of which the keypress should only be accepted at stage 4) in the experiment put in a loop, the set(...) will be ignored at the first run at stages 1-3 before it first appears, but will then be present in all the runs from the second, at every stage, 1-5.
Then I tried to put both the calling script and the called function into another function called from an outer script, so that once the control is returned to that higher level script, I put in another
set(gcf,'KeyPressFcn',#mute)
by which mute function performs no actions whatsoever. This seems to work for problem No 2, but it still does not allow me to obtain the value for the keypress callback, and, as I am using pause(..) to allow the user time for response, it does not interrupt with the first key pressed, it waits for the entire time allocated in the brackets for pause.
Passing variable around:
I recommend using setappdata and getappdata to pass variables around between callbacks of a GUI.
You can also read Share Data Among Callbacks for more informations about this aspect.
Assigning and disabling callbacks:
To disable a callback function, you do not need to re-assign the callback to a function that does nothing, you can simply assign an empty array:
% assign the 'KeyDownListener' function and pass one parameter ('var1') with it
set( h.fig, 'KeyPressFcn',{#KeyDownListener,var1})
% then later when you don't need it anymore:
% Disable the 'KeyPressFcn listener, assign 'empty' to it
set( h.fig, 'KeyPressFcn',[])
Full example:
Below is a minimal GUI which demonstrate how to capture a single key press (and the time), then save the data collected into the appdata space of the application, where they are collected again by a 'display' function (where you could also do whatever you want with the data collected).
code for SingleKeyPressDemo.m:
function h = SingleKeyPressDemo
% create a minimal figure with a button and 2 text fields
h.fig = figure ;
h.btn = uicontrol('Style','Pushbutton','String','Start capture',...
'units','Normalized','Position',[0.1 0.6 0.8 0.3],...
'Callback',#StartKeyCapture) ;
h.txtKey = uicontrol('Style','text','String','Key pressed:',...
'units','Normalized','Position',[0.1 0.4 0.8 0.1]) ;
h.txtTime = uicontrol('Style','text','String','Elapsed time:',...
'units','Normalized','Position',[0.1 0.3 0.8 0.1]) ;
% assign a callback to the KeyRelease event to intercept passing the
% eventdata to the command window
set(h.fig,'KeyReleaseFcn',#KeyReleased)
% initialise appdata variables to hold the captured key and the time
setappdata( h.fig , 'CapturedKey' , [] )
setappdata( h.fig , 'Elapsed_time' , [] )
% save the handle structure
guidata( h.fig , h)
function StartKeyCapture(hobj,~)
h = guidata( hobj ) ; % retrieve the handle structure
StartTime = tic ; % Start a stopwatch
% assigne the callback funtion, passing the stopwatch in parameter
set(h.fig,'KeyPressFcn',{#KeyDownListener,StartTime})
% disable the button until a key is pressed (also makes it loose the
% focus, which is handy otherwise the button keeps the focus and hides
% the 'KeyPressedFcn'
set( h.btn , 'Enable','off')
function KeyDownListener(hobj, key_obj, starting_time)
% Detect key pressed and time elapsed
Elapsed_time = toc(starting_time) ;
key_pressed = key_obj.Key;
h = guidata( hobj ) ; % retrieve the handle structure
setappdata( h.fig , 'CapturedKey' , key_pressed ) ; % save the captured key
setappdata( h.fig , 'Elapsed_time' , Elapsed_time ) ; % save the elapsed time
set(h.fig,'KeyPressFcn',[]) % remove listener so new key press will not trigger execution
set( h.btn , 'Enable','on') % re-enable the button for a new experiment
% (optional) do something after a key was pressed
display_results(h.fig) ;
function display_results(hobj)
h = guidata( hobj ) ; % retrieve the handle structure
% retrieve the saved data
CapturedKey = getappdata( h.fig , 'CapturedKey' ) ;
Elapsed_time = getappdata( h.fig , 'Elapsed_time' ) ;
% update display
set( h.txtKey , 'String', sprintf('Key pressed: %s',CapturedKey) ) ;
set( h.txtTime , 'String', sprintf('Elapsed time: %f ms',Elapsed_time) ) ;
function KeyReleased(~,~)
% do nothing
% The only purpose of this function is to intercept the KeyReleased event
% so it won't be automatically passed on to the command window.
Creating GUI programmatically is full of verbose in Matlab, focus on the code in the 2 functions StartKeyCapture and KeyDownListener to achieve what you were asking.
This example will produce the following GUI:
Additional note:
I would also recommend against the use of gcf in a GUI application. It is a handy shortcut when debugging or working in the console with figures opened around, but in a GUI the calls to its own elements should be as self-contained as possible. MATLAB offers way to save the handles of all your GUI elements (all the uicontrols, including the main figure), so you can call them explicitely when you need them. This reduces the risk of errors (imagine your user is also running other figures in the same MATLAB session, if your callback triggers and call gcf when the user was fiddling with another figure, you are going to try to execute code on a different figure than it was intended to ... which can easily result in errors).
Read the documentation on guidata for more information (and/or observe how I've used it in the example above).
Edit:
To avoid the focus coming back to the command window at every key pressed, you also have to also define a callback function to the corresponding KeyRelease event, otherwise the event will automatically be forwarded to the command window (which will take the focus).
A clean way to do is to simply add callback assignment
set(h.fig,'KeyReleaseFcn',#KeyReleased)
once and for all in the figure definition (you don't have to set/undo this callback at each experiment), and then define an empty function function KeyReleased(~,~) which does nothing. This way is implemented in the modified code example above.
Another way to do without the extra empty function, would be to simply define the callback in line at the assignment time:
set(h.fig,'KeyReleaseFcn','disp([])')
This way you do not need to have an empty KeyReleased function.
Note however that a callback function must be defined (either inline or later in the code). Simply assigning empty to the callback will not work. (e.g. both options below will fail to intercept the event and will forward it to the command window:
set(h.fig,'KeyReleaseFcn','') % not working
set(h.fig,'KeyReleaseFcn',[]) % not working either

pass value between two callback function in matlab GUI

I have 2 pushbutton. If i press one infinte loop will be running and if i press other loop must break. please some help in code.
Thanks in advance
Are you using GUIDE or a "programmatic" gui? The following is a little example for a programmatic gui; similar concepts may apply for GUIDE. (I personally like the added flexibility of the programmatic gui route, plus I always end up irrevocably breaking any GUIDE gui's I create...)
Anyway, a few things to note in this example:
use of the gui's figure handle UserData field to store "global" information. This is a way to pass data between callbacks.
the pause statement within the "infinite" loop is needed so that the interrupt from cb_button2 is processed. From Matlab help: "If the Interruptible property of the object whose callback is executing is on , the callback can be interrupted. However, it is interrupted only when it, or a function it triggers, calls drawnow, figure, getframe, pause, or waitfor."
function my_gui(varargin)
mainfig = figure;
out.h_button1 = uicontrol(mainfig,...
'Style','pushbutton',...
'Units','Normalized',...
'Position',[0,0.5,1,0.5],...
'String','Button 1',...
'Callback',#cb_button1);
out.h_button2 = uicontrol(mainfig,...
'Style','pushbutton',...
'Units','Normalized',...
'Position',[0,0,1,0.5],...
'String','Button 2',...
'Callback',#cb_button2);
out.button2_flag = 0; %flag indicating whether button 2 has been pressed yet
set(mainfig,'UserData',out);%store "global" data in mainfig's UserData (for use by callbacks)
function cb_button1(varargin)
out = get(gcbf,'UserData'); %gcbf: handle of calling object's figure
while ~out.button2_flag
disp('Aaaahhh, infinite loop! Quick press Button 2!');
out = get(gcbf,'UserData'); %reload "global" data
pause(0.1); %need this so this callback may be interrupted by cb_button2
end
disp('Thanks! I thought that would never end!');
function cb_button2(varargin)
out = get(gcbf,'UserData'); %gcbf: handle of calling object's figure
out.button2_flag = 1;
set(gcbf,'UserData',out); %save changes to "global" data

How to wrap an already existing function with a new function of the same name

Is it possible to create a wrapper around a function that has the exact same name as the original function?
This would be very useful in circumstances where the user wants to do some additional checks on input variables before they are passed on to the built in function How to interrupt MATLAB IDE when it hangs on displaying very large array?
Actually alternatively to slayton's answer you don't need to use openvar. If you define a function with the same name as a matlab function, it will shadow that function (i.e. be called instead).
To then avoid recursively calling your own function, you can call the original function from within the wrapper by using builtin.
e.g.
outputs = builtin(funcname, inputs..);
Simple example, named rand.m and in the matlab path:
function out = main(varargin)
disp('Test wrapping rand... calling rand now...');
out = builtin('rand', varargin{:});
Note that this only works for functions that are found by builtin. For those that are not, slayton's approach is likely necessary.
Yes this is possible but it requires a bit of hacking. It requires that you copy around some function handles.
Using the example provided in the question I will show how to wrap the function openvar in a user defined function that checks the size of the input variable and then allows the user to cancel any open operation for variables that are too large.
Additionally, this should work when the user double clicks a variable in the Workspace pane of the Matlab IDE.
We need to do three things.
Get a handle to the original openvar function
Define the wrapper function that calls openvar
Redirect the original openvar name to our new function.
Example Function
function openVarWrapper(x, vector)
maxVarSize = 10000;
%declare the global variable
persistent openVarHandle;
%if the variable is empty then make the link to the original openvar
if isempty(openVarHandle)
openVarHandle = #openvar;
end
%no variable name passed, call was to setup connection
if narargin==0
return;
end
%get a copy of the original variable to check its size
tmpVar = evalin('base', x);
%if the variable is big and the user doesn't click yes then return
if prod( size( tmpVar)) > maxVarSize
resp = questdlg(sprintf('Variable %s is very large, open anyway?', x));
if ~strcmp(resp, 'Yes')
return;
end
end
if ischar(x) && ~isempty(openVarHandle);
openVarHandle(x);
end
end
Once this function is defined then you simply need to execute a script that
Clears any variables named openvar
run the openVarWrapper script to setup the connection
point the original openVar to openVarWrapper
Example Script:
clear openvar;
openVarWrapper;
openvar = #openVarWrapper;
Finally when you want to clean everything up you can simply call:
clear openvar;
I prefer jmetz's approach using builtin() when it can be applied, because it is clean and to the point. Unfortunately, many many functions are not found by builtin().
I found that I was able to wrap a function using a combination of the which -all and cd commands. I suspect that this approach can be adapted to a wide variety of applications.
In my example case, I wanted to (temporarily) wrap the interp1 function so that I could check for NaN output values. (The interp1 function will, by default, return a NaN under some conditions, such as if a query point is larger than the largest sample point.) Here's what I came up with:
function Vq = interp1(varargin)
persistent interp1_builtin;
if (isempty(interp1_builtin)) % first call: handle not set
toolbox = 'polyfun';
% get a list of all known instances of the function, and then
% select the first such instance that contains the toolbox name
% in its path
which_list = which('interp1','-all');
for ii = 1:length(which_list)
if (strfind(which_list{ii}, [filesep, toolbox, filesep]))
base_path = fileparts(which_list{ii}); % path to the original function
current_path = pwd;
cd(base_path); % go to the original function's directory
interp1_builtin = #interp1; % create a function handle to the original function
cd(current_path); % go back to the working directory
break
end
end
end
Vq = interp1_builtin(varargin{:}); % call the original function
% test if the output was NaN, and print a message
if (any(isnan(Vq)))
dbstack;
disp('ERROR: interp1 returned a NaN');
keyboard
end
end
See also: How to use MATLAB toolbox function which has the same name of a user defined function