How do I make a function run again on a key press? - matlab

I have a function that displays a graph with some random elements. I want to have it so that when the user presses a certain key the function runs again, redistributing these random elements on the graph. What is the best way to go about doing this?

You can wrap this within a while loop and using ginput. Put your function call within a while loop, use ginput and poll for a keystroke, and while this key is being pushed, keep going. Something like this, assuming that your figure is open after each call to the function:
while true
%// Generate random data
%// Call function
%// Open figure
%// Get a key from the user
[~,~,b] = ginput(1);
%// If you push C or c, then continue
if b == 67 || b == 99
continue;
else %// Else, get out
break;
end
end

You want to set a key press callback for a figure you're using in a script by using the KeyPressFcn like so:
h = figure('KeyPressFcn',#testcallback);
Then place the following in a function testcallback.m file (you can also use a function handle):
function testcallback(hObject,callbackdata)
% Check to make sure key pressed is the escape key
if (strcmp(callbackdata.Key,'escape'))
% Do whatever processing you want
imshow(rand(40));
end
end
When you run the script, a figure will appear. Everytime you press escape, the function will fire:

using the input() function in MATLAB allows you to prompt and ask a user for input. You can use this function to prompt the user to enter a key, when this key is entered, your function can be called and re-run.
Documentation on this can be found on the Mathworks website

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.

Is there any function for getpts() of MATLAB in Octave?

I load a data file and plot it in octave. But in the plot, I want to mark the periodic appearance of points on the plot. I used ginput() function for marking. But the problem I see is, if I mark a different point which was not supposed to mark and then immediately realise that i made mistake, now I want to delete my last marked point and then mark the correct point. I'm not able to do it. I found out that there is MATLAB function getpts() which does the same but octave version of getpts() is not there. Can anyone help me out please?
Example:
The sequence i want to mark is 1,2,3,4,5,6,7,8,9,10.
But accidently I mark 1,2,3,5 And realise that I did a mistake and then press delete button on the keyboard which deletes 5 and then I mark 4 and then 5.
While getpts is not implemented per se, producing a small function which gets inputs one by one via ginput and vets them to get the desired behaviour is fairly easy. E.g.
X = []; Y = [];
while true
[x, y, b] = ginput(1);
if b == 8 , X(end)=[]; Y(end)=[]; % backspace key pressed
elseif isempty(b), break; % enter key pressed
else , X(end+1)=x; Y(end+1)=y; % any other key
end
disp([X;Y]); fprintf('\n'); fflush(1); % Optional terminal output
end
This is a very flexible approach which allows you to modify and add functionality as you desire (e.g., add different markers based on specific key pressed, plot as you go, etc).

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

MATLAB - identification of every typesetted symbol

I have the following code in MATLAB, where the user has to enter the word "precipitations" letter by letter. After typing one letter, the user has to press Enter and the program checks, whether the typed letter was correct.
Now I would like to change the program, such that the user does not have to press Enter after typing a letter. Is there any operator or function in MATLAB which reacts to every pushed button, so one does not have to press Enter?
disp('Please enter "precipitations" without errors')
target=('precipitations');
n=size(target); n=n(2); % Characters number
for i=1:n;
YourInput(i)=input('','s');
if YourInput(i)==target(i)
disp('OK. Please, input the next symbol')
i=i+1;
else
disp('Error. Please try again.')
break
end
end
As far as I know, there is no built-in MATLAB function to do this. There is however a function getkey on MATLAB File Exchange.
You can download this function and change your code to use
YourInput(i) = getkey();
--
I of course wondered how this can be achieved, and it does the following: They create a new figure with a window size of 0,0 at position (1,1). You'll notice the new figure at the bottom left of the screen.
Then, a callback function KeypressFcn which is executed whenever a key is pressed, is created. The pressed key is saved in the UserData field of the figure, and returned as variable. The interesting parts of the function (and a minimal example) are:
fh = figure(...
'keypressfcn','set(gcbf,''Userdata'',double(get(gcbf,''Currentcharacter''))) ; uiresume ', ...
'position',[0 0 1 1] ...
);
uiwait ;
key = get(fh,'Userdata') ;
delete(fh) ;

How to determine the last time a user interacted with the matlab GUI?

In a Matlab function, I would like to know the last time a user interacted with the Matlab GUI. By matlab GUI, I mean basically, a user typing in the command window, or in the editor.
The algorithm I wish to implement is essentially:
If it's been a while, the function will not grab focus, but operate in the background.
If the user has recently interacted, presumably he/she is interested "right now" in the results, and the function will grab focus.
This is a tough one ! Here is a proposition to do what you want with the command window only, based on this undocumented code and persitent variables.
I used two functions: CW_listen and CW_callback. A call to CW_listen (or CW_listen(true)) starts to listen to the command window, while a call to CW_listen(false) stops listening. While listening is on, any action performed on the command window trigs a call toCW_callback.
Here are the two functions:
function CW_listen(b)
% Default value
if ~exist('b', 'var'), b = true; end
% Get the reference handle to the Command Window text area
jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance;
try
cmdWin = jDesktop.getClient('Command Window');
jTextArea = cmdWin.getComponent(0).getViewport.getComponent(0);
catch
commandwindow;
jTextArea = jDesktop.getMainFrame.getFocusOwner;
end
% Instrument the text area's callback
if b
set(jTextArea,'CaretUpdateCallback',#CW_callback);
else
set(jTextArea,'CaretUpdateCallback',[]);
end
and
function CW_callback(varargin)
% Define a persistent variable
persistent last_call;
if isempty(last_call)
last_call = clock;
else
ts = clock;
Dt = etime(ts, last_call);
% Update the status bar
dt = javaMethod('getInstance', 'com.mathworks.mde.desk.MLDesktop');
if dt.hasMainFrame
dt.setStatusText(['Ellapsed time: ' num2str(Dt) 's']);
end
if Dt>5
fprintf('So long !\n');
last_call = ts;
else
% Do nothing
end
end
I also dislayed the ellapsed time in the status bar, it was useful for developping the code and adds a quite cool feature.
You can replace the time in seconds (here 5s) and the fprintf('So long !\n'); by any action of your choice. Be aware that inserting any kind of display outside of this if statement will result in an infinite display loop ...
For the moment I don't see how one could transpose this to the editor, but if you search in Undocumented Matlab you may find how to do it ;)