How to collect and show answers captured by using key press function (keypressfnc)?

4 vues (au cours des 30 derniers jours)
I have a survey with n questions. Each question will can be answerd with yes or no. To caputure the answer of each individual questions I would like to use the key press function. If the subject's answer to the current question i is no, the subject presses the left arrow key and a "0" will be stored in an array at the position (i,1). If the subject's answer to the current question i is yes, the subject presses the right arrow key and a "1" will be stored in the array at the position (i,1). When all questions are answerd, the answer-array has n entries.
The desired output is to display the completely filled answer-array (size nx1).
I do not get the filled array and i can't display it, because of the following problems:
1) During the iteration I can give answers by using left or right arrow key, but the values are not assigned to i. Therefore the answers are not stored an the iteration does not stop after n answers.
2) Pressing any other key than left or right arrow key and then pressing left or right arrow key creates n copies of the given answer. Infinite answers can be given, but I can not use the command window anymore and the only way to stop the code is by closing Matlab.
So, please help me: How do I have to to change my code to get my individual answers, store them at the correct position into the array, stop the iteration when i have all n answers and end my function properly?
survey;
function survey() % outer function
hkey = figure;
set(hkey,'KeyPressFcn',@(src, evnt) UserFeedback(src, evnt));
waitfor(hkey);
function UserFeedback(~,evnt) % inner function
n = 5; % total number of questions
answers = zeros(n,1); % array where all n answers will be saved
for i = 1:n % answer all n questions individually until every question was answerd
if strcmpi(evnt.Key,'leftarrow') % answer is no
userresponse = 0; % write 0 into array at (i,1)
answers(i,1) = userresponse;
nope = ['The answer of question ', num2str(i), 'is: NO.'];
disp(nope) % display response
pause; % go to next question
elseif strcmpi(evnt.Key,'rightarrow') % answer is yes
userresponse = 1; % write 1 into array at (i,1)
answers(i,1) = userresponse;
yeah = ['The answer of question ', num2str(i), 'is: YES.'];
disp(yeah) % display response
pause; % go to next question
end % if-conditions
end % for-loop
disp('All answers have been captured.') % notification that survey in for-loop has been complete
disp(answers); % desired output - results of survey: all n given answers
end % inner function
end % outer function

Réponse acceptée

Voss
Voss le 13 Jan 2023
The main problem here is that the KeyPressFcn is executed every time the user hits a key while the figure has focus. That is, KeyPressFcn is intended to capture a single keystroke. Therefore, a for loop to capture multiple keystrokes inside the KeyPressFcn is not an appropriate code structure.
Another problem is that pause, with no arguments, waits for a keystroke before continuing execution of the code. Executing a pause statement inside a KeyPressFcn apparently (based on observations in R2016b) causes the KeyPressFcn to be executed again when the user hits the key intended to stop the pause and resume code execution. Maybe you can do away with the pause entirely (it's not clear what it's there for without knowing more about how the questions are presented).
Try something like this:
function survey() % outer function
hkey = figure;
set(hkey,'KeyPressFcn',@UserFeedback);
total_questions = 5; % total number of questions to ask
current_question = 1; % the question currently being asked/answered
answers = zeros(total_questions,1); % initialize the answers
waitfor(hkey);
function UserFeedback(~,evnt) % inner function
if current_question > total_questions
% the user already answered all the questions, don't do anything
return
end
if strcmpi(evnt.Key,'leftarrow') % answer is no
userresponse = 0; % write 0 into array at (current_question,1)
answers(current_question,1) = userresponse;
nope = ['The answer of question ', num2str(current_question), 'is: NO.'];
disp(nope) % display response
% pause(1); % go to next question
elseif strcmpi(evnt.Key,'rightarrow') % answer is yes
userresponse = 1; % write 1 into array at (current_question,1)
answers(current_question,1) = userresponse;
yeah = ['The answer of question ', num2str(current_question), 'is: YES.'];
disp(yeah) % display response
% pause(1); % go to next question
else % user hit another key: do nothing
return
end % if-conditions
% display the answers when all questions have been asked and answered
if current_question == total_questions
disp('All answers have been captured.') % notification that survey has been complete
disp(answers); % desired output - results of survey: all n given answers
end
% increment current_question
current_question = current_question+1;
end % inner function
end % outer function
Or this, which works the same but reduces the redundancy between the code in the if and elseif blocks:
function survey() % outer function
hkey = figure;
set(hkey,'KeyPressFcn',@UserFeedback);
total_questions = 5; % total number of questions to ask
current_question = 1; % the question currently being asked/answered
answers = zeros(total_questions,1); % initialize the answers
waitfor(hkey);
function UserFeedback(~,evnt) % inner function
if current_question > total_questions
% the user already answered all the questions, don't do anything
return
end
if strcmpi(evnt.Key,'leftarrow') % answer is no
userresponse = 0; % write 0 into array at (current_question,1)
response = 'NO';
elseif strcmpi(evnt.Key,'rightarrow') % answer is yes
userresponse = 1; % write 1 into array at (current_question,1)
response = 'YES';
else % user hit another key: do nothing
return
end % if-conditions
% store the answer
answers(current_question,1) = userresponse;
% display response
disp(sprintf('The answer of question %d is: %s.',current_question,response));
% display the answers when all questions have been asked and answered
if current_question == total_questions
disp('All answers have been captured.') % notification that survey has been complete
disp(answers); % desired output - results of survey: all n given answers
end
% increment current_question
current_question = current_question+1;
end % inner function
end % outer function
Also, regarding, "I can not use the command window anymore and the only way to stop the code is by closing Matlab.", you can usually cancel running code by first giving focus to the command window by clicking into it, then hitting Ctrl+C. You may have to hit Ctrl+C multiple times, or just hold it down until the code stops. Usually you'll get an error to the effect of, "Operation terminated by user ...", and then you can resume your work without having to close and restart MATLAB.
  3 commentaires
Voss
Voss le 13 Jan 2023
You're welcome!
If I understand your question, you're asking about nested functions, which is what you have here (UserFeedback is nested inside survey).
Technically, you don't have to end them all together. You can put more code after the "end" that ends UserFeedback but before the "end" that ends survey.
However, I think it's a good idea if the only code you put there is other nested function definitions. And I think it's a bad idea to have any code other than other nested function definitions in between those two "end"s. In other words, all the code in your outer function that's not in a nested function should be at the top of the outer function. This is for clarity, ease of debugging and maintenance, and better style and readability.
Here's an example of what I mean. Take a look at the four functions outer_1, outer_2, outer_3, and outer_4 below. They each have two nested functions in them; the only difference between the four outer functions is the placement of the code that is in the outer function but not in any "inner" function.
You can see all four do the same thing:
outer_1()
x=2,y=4 inner_1
outer_2()
x=2,y=4 inner_1
outer_3()
x=2,y=4 inner_1
outer_4()
x=2,y=4 inner_1
They work the same because the code that is in the outer function but not inside an inner (nested) function is what is executed when the outer function is run. If there are nested functions defined in between those lines of code, the code works the same, but it's just more difficult (potentially much more difficult) to read and make sense of.
[the recommended style] Code at the top of the outer function:
function outer_1()
% some initialization code
x = 2;
y = 4;
disp(sprintf('x=%d,y=%d',x,y));
% call inner_1:
inner_1()
% a nested function
function inner_1()
disp('inner_1');
end
% another nested function
function inner_2()
disp('inner_2');
end
end
[not recommended] Code at the bottom of the outer function. This works the same, but it "hides" important code at the bottom of the outer function - de-emphasizing it in favor of code in the inner nested functions that may or may not ever be executed.
function outer_2()
% a nested function
function inner_1()
disp('inner_1');
end
% another nested function
function inner_2()
disp('inner_2');
end
% some initialization code
x = 2;
y = 4;
disp(sprintf('x=%d,y=%d',x,y))
% call inner_1:
inner_1()
end
[not recommended] Code in between nested functions. Also works the same, but again important code is hidden.
function outer_3()
% a nested function
function inner_1()
disp('inner_1');
end
% some initialization code
x = 2;
y = 4;
disp(sprintf('x=%d,y=%d',x,y))
% call inner_1:
inner_1()
% another nested function
function inner_2()
disp('inner_2');
end
end
[not recommended] Code dispersed throughout. Also works the same, but again important code is hidden/obscured by the placement of the nested inner functions.
function outer_4()
% some initialization code
x = 2;
y = 4;
% a nested function
function inner_1()
disp('inner_1');
end
% more initialization code
disp(sprintf('x=%d,y=%d',x,y))
% another nested function
function inner_2()
disp('inner_2');
end
% call inner_1:
inner_1()
end
In small examples like these, it's easy to look at them and see that they all work the same, but consider: what if the outer function was hundreds or thousands of lines long, with dozens of nested functions in it, each of which might itself be dozens or hundreds of lines - you might have to look all over the place in between the various nested functions to find the code you need. If you didn't write the code (or if you wrote it long ago), you might not even realize there's code somewhere down there between some nested functions that's being executed.
Bottom line: while technically you're allowed to put your outer function code anywhere, it's better to have it all at the top, followed by the definitions of the nested (inner) functions.
Brezelbayer
Brezelbayer le 14 Jan 2023
@Voss, Thank you very much for the detailed explanation! I can clearly see the differences and the advantages of option 1 compared to 2, 3, 4. It really helped me a lot to understand nested functions better and it makes coding so much easier.

Connectez-vous pour commenter.

Plus de réponses (0)

Catégories

En savoir plus sur Matrix Indexing dans Help Center et File Exchange

Produits


Version

R2022b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by