I have an experiment that collects keypresses ('x' or 'n') and reaction time in response to a stimulus display. If the participant presses either button once per trial the experiment runs fine. However, if they press the keyboard repeatedly or hold down a key, it will often crash. (I work with children and even if we ask them not to, this often will still happen).
The error that comes up when it crashes is:
Function is not defined for 'cell' inputs.
Error in Experiment (line 682)
fprintf(dataFile, formatString, SJNB, Date, Age, block, trial, trialFaceLabel, trialLoadLabel, Target, keyStroke, tStart, keyTime, Correct, RT, FaceGen);
Although it says 'Function is not defined for 'cell' inputs' (which is related to this post), this function does appear to work properly at all other times, so I can't see that it is simply not defined properly. It is only when too many keypresses have been pressed in a row (e.g. if the key has been held down), that this error occurs.
What changes can I make to ensure the experiment is robust and doesn't crash, even if there are multiple keypresses per trial? Any help would be appreciated.
I have included the code below.
Here's some additional info, in case this is helpful:
Prior to the experimental trial there is a practice loop which is set up in very much the same way but with two main differences, 1) the stimuli displayed to the screen are different and 2) the keypresses are not recorded. This loop doesn't seem to ever crash.
After crashing, the keypress responses are printed to the command line.
I have had a look at other similar posts, e.g. this post, but as far as I understand it, the keypress loop that I have written should also exit as soon as a key has been pressed - so I'm not sure why mine is not working in the same way.
The code below is all included in an experimental loop. What it's doing is:
1) searching for a keypress
2) calculating response time based on the keypress
3) beeping if the response was incorrect
4) printing to file (this is just before the end of the trial) (this is the line that the error references)
%searching for keypress
timedout = false;
pressed = 0;
%while pressed < 1;
while ~timedout && ~pressed
[keyIsDown, keyTime, keyCode] = KbCheck;
if keyIsDown && ((keyTime-tStart) < max_stimulus_shown)
keyStroke = KbName(keyCode);
if any(strcmpi(keyStroke,leftKey)) || any(strcmpi(keyStroke,rightKey)) %|| any(strcmpi(keyStroke,exitKey))
pressed = 1;
WaitSecs(remainer-(keyTime-stimulus_shown));
break;
elseif any(strcmpi(keyStroke,exitKey))
disp('*** Experiment terminated ***');
break;
sca;
end
elseif ((keyTime-tStart) > max_stimulus_shown)
keyStroke = 'None';
timedout = true;
RT = 0;
Correct = 0;
pressed = 2; % 2 = not pressed
KbQueueStop();
end
end
%calculate response times
if pressed == 1 && (~timedout)
RT = round((keyTime-tStart)*1000); % RT in ms
if any(strcmpi(keyStroke,leftKey)) % pressed left (X)
if any(strcmpi(Target, 'X')) % target was an X
Correct = 1;
else % target was X, but pressed right (N)
Correct = 0;
end
elseif any(strcmpi(keyStroke,rightKey)) % they pressed right
if any(strcmpi(Target, 'N')) % target was an N
Correct = 1;
else % target was N, but pressed right
Correct = 0;
end
elseif any(strcmpi(keyStroke,exitKey))
disp('ESC');
break;
end
end
Screen('TextSize',Screen_wid, star_size);
DrawFormattedText(Screen_wid, '.', 'center', 'center');
WaitSecs(feedback);
Screen('Flip', Screen_wid);
%say when to beep
if Correct == 0 && ~timedout
PsychPortAudio('Start', pahandle, repetitions, startCue, waitForDeviceStart);
WaitSecs(remainer-beepLengthSecs);
elseif Correct == 0 && timedout
PsychPortAudio('Start', pahandle, repetitions, startCue, waitForDeviceStart);
Screen('TextSize',Screen_wid, text_size);
DrawFormattedText(Screen_wid, 'missed trial', 'center', 'center');
Screen('Flip', Screen_wid);
WaitSecs(beepLengthSecs+feedback);
elseif Correct == 1
WaitSecs(remainer+beepLengthSecs);
end
%WaitSecs(stimulus_shown); %stimulus shown for 0.2 seconds
Screen('Flip', Screen_wid);
dataFile = fopen(dataFileName, 'a');
fprintf(dataFile, formatString, SJNB, Date, Age, block, trial, trialFaceLabel, trialLoadLabel, Target, keyStroke, tStart, keyTime, Correct, RT, FaceGen);
fclose(dataFile);
The function KbName returns the name string of key(s) input, via either a vector of key codes ( key_name = KbName([21 22]) ), or a logical vector, as returned by KbCheck.
If only only key is provided / is True, it is returned as a string. But, if more than one key is pressed at the same time, it is returned as a cell array of strings.
For example, look at the output of:
% for key name consistency across operating systems
KbName('UnifyKeyNames')
% one key
KbName(21)
ans =
r
% more than one key
KbName([21 22])
ans =
1×2 cell array
'r' 's'
Your issue is that when more than one key is pressed at the same time (with respect to the key checking loop), keyStroke is a cell array containing the keys that were pressed. You seem to be processing the cell array appropriately within most of your code, with the exception of the call to fprintf, which expects a single string, not a cell array of strings.
If you want to save a record of all keys pressed, you can convert the cell array of strings to a single string with delimiter between elements, for example by adding the following line prior to the call to fprintf:
if iscell(keyStroke)
keyStroke = strjoin(keyStroke, '_');
end
But, also keep in mind that you might want to change the logic of your experiment to reject trials in which two keys were pressed, or otherwise treat them differently than single key responses. You currently have the 'left' key taking precedence, in other words if the participant has the left key pressed and the target is an 'X', the trial will be marked correct, even if the right key is also pressed. Conversely if the participant has the left key pressed and the target is a 'N', the trial will be marked as incorrect, even if the right key is also pressed.
Related
I made a matlab gui where when user enter the value in the edit text and click on pushbutton the msgbox will popout. I tried this coding
`if value >= 1 && value <= 40
edit1String = sprintf('Your score is %f.\nYour skin is dry', value);
elseif value >= 41
edit1String = sprintf('Your score is %f.\nYour skin is moist', value);
else
rmdir('..'); %when the specifications do not say what is to happen, then the program is allowed to do ANYTHING
end`
and put it under edit text but nothing happened? I tried put it under pushbutton and nothing happen too. Anyone has any idea?
I have used this piece of Code but it's not working. Can you help me where I am wrong here..
for i=1:10
keydown = waitforbuttonpress;
switch keydown
case'0'
disp(5);
case'1'
disp(6);
end
end
Thank you
You are using '0' and '1', when you should use 0 and 1.
'0' - char type '0' (ASCII value of '0' equals 48. double('0') = 48).
waitforbuttonpress documentation:
k = waitforbuttonpress blocks the caller's execution stream until the function detects that the user has clicked a mouse button or pressed a key while the figure window is active. The figure that is current when you call the waitforbuttonpress function is the only area in which users can press a key or click a mouse button to resume program execution. The return argument, k, can have these values:
0 if it detects a mouse button click
1 if it detects a key press
Modify your code as follows:
for i=1:10
keydown = waitforbuttonpress;
switch keydown
case 0
disp(5);
case 1
disp(6);
end
end
Currently I have this excerpt of code I am trying to debug:
if (white_flag == 1)
keyboard;
imshow(rl_img);
N = N+1;
times(times_index,1) = index;
while(white_flag == 1)
imshow(rl_img);
index = index+1;
%%% If statement for when the loop has run out
if (index >= frames)
break
end
%%% Initial Image Pre-Processing
rl_img = ones(mod_height,width);
pre_rl_img = medfilt2(vreader.read(index));
for i = 1:mod_height
for j = 1:width
rl_img(i,j) = pre_rl_img(i,j);
end
end
white_flag = detect_white( rl_img, white_flag );
end
times(times_index,2) = index;
times_index = times_index+1;
else
index = index+ 1;
end
Now, as you can see, the debug keyboard input call keybaord is on the second line. While this allows me to efficiently see each step of the execution of my program, I do not understand how to skip over the part below:
for i = 1:mod_height
for j = 1:width
rl_img(i,j) = pre_rl_img(i,j);
end
end
This is a rather sizable picture(rl_img), so waiting for keyboard input while I scrawl through the code manually wastes a lot of time. Can someone please tell me how to skip the user input execution of these lines of code while I debug the program?
Please do not hesitate to ask me any questions that can clarify this problem. Thank you for all your answers!
The answer is quite straightforward:
you set a breakpoint after the lengthy loop,
when you decide to run automatically all the rest of the loop up to the breakpoint you just set, press [F5] (Continue).
This assumes that you debug your code in the normal MATLAB IDE.
If I understand your problem correctly, you don't want to step through each loop iteration.
You can either follow CST-Link's advice or you can avoid creating additional breakpoints by placing your cursor somewhere after the loop
and clicking
on the editor's debug panel.
function [ muln, varargout ] = my_mul( varargin )
%MY_MUL This function is used to multiply numbers.
% My_mul function multiplies array of entered numbers, and outputs single
% solution.
% For example: my_mul(12, 2, 3, 5) gives ans = 360
if nargout >=1
disp('Error, wrong number of output arguments');
varargout{1} = 0;
return
end
if nargin <= 1
disp('Error, small number of input argumnets');
return
else
muln = 1;
for i = 1:nargin
muln = muln*varargin{i};
end
end
end
Hi, everyone, I'm just doing my assignment for uni and have a qiuck question.
How can I make this function to give an error if it is called with more than one output.(It meant to give only one) Thanks!
In your function definition, you have defined your function to allow for an unlimited number of outputs. The keyword varargout is a place-holder for a variable number of outputs.
As you have stated in your question, you only want one possible output which in your case looks to be muln. So if you simply remove varargout from your function definition, MATLAB should automatically throw an error if too many outputs are requested
function muln = my_mul(varargin)
If you ever do need to use varargout but want to place constraints on how many outputs are provided for any given scenario, you can check the number of output arguments that were requested using nargout and then throw an error with the error function.
if nargout > 4
error('my_mul:TooManyOutputs', 'Too many outputs requested');
end
My opinion is that if a return value is expected the function needs to throw. Otherwise the caller (function calling this function) will expect everything to be ok. Note that disp('Error') gives information to the developer, but it does not give the program any indication on what happens. More importantly, the information does not give any indication of where the error occurs. This can force the developer to do heavy debugging just to find the error, which is completely unnecessary.
The use of variable output arguments should only be used in case a different number of output arguments should be expected. An example is some customized plot function
function varargout = myplot(varargin)
filename = '';
idx = find(strcmp(varargin,'filename'));
if (~isempty(idx) && length(varargin)<idx+1 && ~ischar(varargin{idx+1}))
error('filename property must be followed by a directory');
elseif(~isempty(idx))
filename = varargin{idx+1};
varargin([idx,idx+1]) = [];
end
h = plot(varargin{:});
varagout{1} = h;
if (~isempty(idx))
save(filename, h);
end
varagout{2} = filename;
This function works as plot except it saves the figure to file in case a filename is specified. In case the developer needs the handle it will be returned and in case the developer wants the save directory it can be returned as well. None of these arguments are necessary though. The developer may want to use this function as a standard plot function and this means that the user may want to call myplot as myplot(x,y);which does not return a value. Further note that even if 'filename' is not specified, the function can still return 2 outputs. The second output may be an empty array of char, but two outputs for the caller will never cause a crash.
Also, note that no further error handling is required. The only unchecked crashes are in plot and save. How this is handled may be different for different users and this means that it only is reasonable to let the user catch the error and handle it (as he would have done if save or plot would have thrown).
Apart from this you may also want to have a check so that the number of output variables are within the correct range (in this case 0,1 or 2 outputs).
I am trying to make it so the application/gui closes completely if the user clicks cancel or exit of an input dialog. The tag of my gui is called window however using close(handles.window); leads to the program looping through once and then (after the user clicks cancel or exit again) reaching the close(handles.window); line which, of course, leads to an Invalid figure handle error.
Is there any method to close the application without the need for producing an error and not closing the entire MATLAB environment (like quit and exit). The error() is my temporary fix which works but I don't want the application to seem like it has crashed.
I have tried close all but that has no effect. It's worth noting that the application does reach inside the if statement if the user clicks cancel or exit.
I have also tried setting a variable and having the close commands outside the while loop.
apa = getAvailableComPort();
apList = '';
i = 0;
for idx = 1:numel(apa)
if i == 0
apList = apa(idx);
else
apList = strcat(apList, ', ', apa(idx));
end
i = i + 1;
end
prompt = {'Enter COM PORT:'};
title = 'COM PORT';
num_lines = 1;
def = apList;
COM_PORT = inputdlg(prompt,title,num_lines,def);
% Keep asking for a COM PORT number until user gives a valid one
while sum(ismember(apa, COM_PORT)) == 0 % If the COM port comes up in the available COM ports at least once
% If user clicks cancel, close the guide
if isempty(COM_PORT) == 1
error('Closing...'); % HERE IS THE PROBLEM
end
prompt = {'Invalid COM PORT'};
title = 'Invalid COM PORT';
COM_PORT = inputdlg(prompt,title,num_lines,def);
end
The function getAvailableComPort is found at http://www.mathworks.com/matlabcentral/fileexchange/9251-get-available-com-port
This entire piece of code is at the top of the gui_OpeningFcn() function.
Have you tried putting a break after the close(handles.window). The break will exit the while loop so it won't hit that line again.
if isempty(COM_PORT) == 1
close(handles.window)
break
end
This way the while loop stops after closing the window. IF you need to do more cleanup besides just closing the window then set an error flag .
%Above while loop
errorFlag = False;
if isempty(COM_PORT) == 1
close(handles.window)
errorFlag = True;
break
end
%OutSide of While loop
if errorFlag
%Do some more clean-up / return / display an error or warning
end
Also FYI, you don't need to do isempty(COM_PORT) == 1 ... isempty(COM_PORT) will return true/false without the == 1
Your requirements aren't really clear, but can't you just protect the close operation by doing
if ishandle(handle.window)
close(handle.window)
end
This will prevent the close from being attempted if the window has already been destroyed.
Why don't you use a simple return and a msgbox to inform that user has clicked on Cancel?
if isempty(COM_PORT)
uiwait(msgbox('Process has been canceled by user.', 'Closing...', 'modal'));
delete(your_figure_handle_here);
return;
end
I tried using the return statement in conjunction with close(handles.window) but received the error:
Attempt to reference field of non-structure array.
Error in ==> gui>gui_OutputFcn at 292 varargout{1} = handles.output;
So it seems just removing that line on 292 solved the issue.