MATLAB: Matlab GUI seems to be lagging

programmatic gui

Hello,
I am creating a questionnaire using the programmatic GUI, but I would like participants to use the numbers on the keyboard to respond instead of a mouse. In the questionnaire, participants would select the appropriate radio button by pressing the corresponding numbers on the keyboard and to lock in their responses, they press the spacebar. This would then bring them to the next question, and so on.
To do this, I used the WindowsKeyPressedFcn to check which key was pressed, and then call a callback function depending on which element was visible. My key pressed callback function looks like this
function keyPressCallback(source,eventdata)
% determine the key that was pressed
keyPressed = eventdata.Key;
if strcmpi(keyPressed,'space')
if strcmp(get(Next1, 'Visible'), 'on') == 1
% the key that was pressed was the space bar so set focus to
% the Next button
uicontrol(Next1);
% invoke the callback for the Next button
Next1_callback(Next1,[]);
elseif strcmp(get(Next2, 'Visible'), 'on') == 1
uicontrol(Next2);
Next2_callback(Next2, []);
elseif strcmp(get(Next3, 'Visible'), 'on') == 1
uicontrol(Next3);
Next3_callback(Next3, []);
elseif strcmp(get(Next4, 'Visible'), 'on') == 1
uicontrol(Next4);
Next4_callback(Next4, []);
elseif strcmp(get(CompNext, 'Visible'), 'on') == 1
uicontrol(CompNext);
CompNext_callback(CompNext, []);
elseif strcmp(get(RRStart, 'Visible'), 'on') == 1
uicontrol(RRStart);
RRStart_callback(RRStart, []);
elseif strcmp(get(RRNext, 'Visible'), 'on') == 1
uicontrol(RRNext);
RRNext_callback(RRNext, []);
end
end
if strcmpi(keyPressed, '1') || strcmpi(keyPressed, '2') || strcmpi(keyPressed, '3') || strcmpi(keyPressed, '4') || strcmpi(keyPressed, '5') || strcmpi(keyPressed, '6') || strcmpi(keyPressed, '7')
guidata(handles.Questionnaire, keyPressed);
if strcmp(get(curr_feel_ans, 'Visible'), 'on') == 1
uicontrol(curr_feel_ans);
feel_callback(curr_feel_ans, []);
elseif strcmp(get(Motiv_ans, 'Visible'), 'on') == 1
uicontrol(Motiv_ans);
Motiv_callback(Motiv_ans, []);
elseif strcmp(get(MC_ans, 'Visible'), 'on') == 1
uicontrol(MC_ans);
MC_callback(MC_ans, []);
elseif strcmp(get(Comp_ans, 'Visible'), 'on') == 1
uicontrol(Comp_ans);
Comp_callback(Comp_ans, []);
elseif strcmp(get(RR_ans, 'Visible'), 'on') == 1
uicontrol(RR_ans);
RR_callback(RR_ans, []);
end
end
clear keyPressed
end
The buttons here are listed in the order in which the questions appear. This works until I get to the RRStart. RRStart is a Next button that appears below some instructions to participants on how to answer the next few questions. After participants read the instructions, they have to press the spacebar to proceed to the actual questions. At this point, the program seems to freeze, and pressing the spacebar does nothing. However, if I minimize the window and maximize it again, pressing the spacebar brings me to the first question. When I'm on the first question, pressing one of the response numbers does nothing until I once again minimize and maximize the window. After this, the questionnaire seems to work properly again. For reference, here is code for this set of questions including the instructions
handles.REW_QNS.Reward_responsiveness = [];
RR_idx = 1;
handles.Questionnaire = figure('Name', 'Questionnaire', 'Color', [.8 .8 .8], 'NumberTitle', 'off', 'OuterPosition',[0 0 1920 1200],'Units','Pixels');
space_start = uicontrol('Visible', 'on', 'Style','Text', 'BackgroundColor', [.8 .8 .8], 'ForegroundColor', [0 0 0], 'FontSize', 14, 'FontUnits', 'points', 'Position',[650 600 640 70],'String','Press the spacebar to start');
RR_instruct = uicontrol('Visible', 'on', 'Parent', handles.Questionnaire, 'Style','Text', 'BackgroundColor', [.8 .8 .8], 'ForegroundColor', [0 0 0], 'FontSize', 14, 'FontUnits', 'points', 'Position',[650 700 640 70],'String','For the following 8 items, please indicate to what extent you agree with each statement by pressing the corresponding number on the keyboard.');
RR_ans = uibuttongroup('Visible', 'off', 'BackgroundColor', [.8 .8 .8]);
RRans1 = uicontrol('Style', 'radiobutton', 'Parent', RR_ans, 'FontSize', 12, 'FontUnits', 'points', 'BackgroundColor', [.8 .8 .8], 'Position', [600 660 200 50], 'String', '1. Strongly disagree', 'HandleVisibility', 'off', 'Tag', '1');
RRans2 = uicontrol('Style', 'radiobutton', 'Parent', RR_ans, 'FontSize', 12, 'FontUnits', 'points', 'BackgroundColor', [.8 .8 .8], 'Position', [795 660 200 50], 'String', '2. Somewhat disagree', 'HandleVisibility', 'off', 'Tag', '2');
RRans3 = uicontrol('Style', 'radiobutton', 'Parent', RR_ans, 'FontSize', 12, 'FontUnits', 'points', 'BackgroundColor', [.8 .8 .8], 'Position', [1005 660 200 50], 'String', '3. Somewhat agree', 'HandleVisibility', 'off', 'Tag', '3');
RRans4 = uicontrol('Style', 'radiobutton', 'Parent', RR_ans, 'FontSize', 12, 'FontUnits', 'points', 'BackgroundColor', [.8 .8 .8], 'Position', [1200 660 200 50], 'String', '4. Strongly agree', 'HandleVisibility', 'off', 'Tag', '4');
set(RR_ans, 'SelectionChangeFcn', @RR_callback);
set(RR_ans, 'SelectedObject', []);
RR(1) = uicontrol('Visible', 'off', 'Parent', RR_ans, 'Style', 'Text', 'BackgroundColor', [.8 .8 .8], 'ForegroundColor', [0 0 0], 'FontSize', 14, 'FontUnits', 'points', 'Position',[605 800 700 48],'String','I am someone who goes all-out.');
RR(2) = uicontrol('Visible', 'off', 'Parent', RR_ans, 'Style', 'Text', 'BackgroundColor', [.8 .8 .8], 'ForegroundColor', [0 0 0], 'FontSize', 14, 'FontUnits', 'points', 'Position',[600 800 700 48],'String','If I discover something new I like, I usually continue doing it for a while.');
RR(3) = uicontrol('Visible', 'off', 'Parent', RR_ans, 'Style', 'Text', 'BackgroundColor', [.8 .8 .8], 'ForegroundColor', [0 0 0], 'FontSize', 14, 'FontUnits', 'points', 'Position',[605 800 700 48],'String','I would do anything to achieve my goals.');
RR(4) = uicontrol('Visible', 'off', 'Parent', RR_ans, 'Style', 'Text', 'BackgroundColor', [.8 .8 .8], 'ForegroundColor', [0 0 0], 'FontSize', 14, 'FontUnits', 'points', 'Position',[610 800 700 48],'String','When I am successful at something, I continue doing it.');
RR(5) = uicontrol('Visible', 'off', 'Parent', RR_ans, 'Style', 'Text', 'BackgroundColor', [.8 .8 .8], 'ForegroundColor', [0 0 0], 'FontSize', 14, 'FontUnits', 'points', 'Position',[610 800 700 48],'String','When I go after something I use a “no holds barred” approach.');
RR(6) = uicontrol('Visible', 'off', 'Parent', RR_ans, 'Style', 'Text', 'BackgroundColor', [.8 .8 .8], 'ForegroundColor', [0 0 0], 'FontSize', 14, 'FontUnits', 'points', 'Position',[600 800 700 48],'String','When I see an opportunity for something I like, I get excited right away.');
RR(7) = uicontrol('Visible', 'off', 'Parent', RR_ans, 'Style', 'Text', 'BackgroundColor', [.8 .8 .8], 'ForegroundColor', [0 0 0], 'FontSize', 14, 'FontUnits', 'points', 'Position',[600 800 700 48],'String','When I am doing well at something, I love to keep at it.');
RR(8) = uicontrol('Visible', 'off', 'Parent', RR_ans, 'Style', 'Text', 'BackgroundColor', [.8 .8 .8], 'ForegroundColor', [0 0 0], 'FontSize', 14, 'FontUnits', 'points', 'Position',[600 800 700 48],'String','If I see a chance to get something I want, I move on it right away.');
RRStart = uicontrol('Parent',handles.Questionnaire,'Visible', 'on', 'Style','pushbutton', 'Position',[0.1 0.1 1 1], 'Callback',{@RRStart_callback});
RRNext = uicontrol('Parent',handles.Questionnaire,'Visible', 'off', 'Style','pushbutton', 'Position',[0.1 0.1 1 1], 'Callback',{@RRNext_callback});
handles.temp_data = 0;
set(handles.Questionnaire,'WindowKeyPressFcn',@keyPressCallback);
function keyPressCallback(source,eventdata)
keyPressed = eventdata.Key;
if strcmpi(keyPressed,'space')
if strcmp(get(RRStart, 'Visible'), 'on') == 1
uicontrol(RRStart);
RRStart_callback(RRStart, []);
elseif strcmp(get(RRNext, 'Visible'), 'on') == 1
uicontrol(RRNext);
RRNext_callback(RRNext, []);
end
end
if strcmpi(keyPressed, '1') || strcmpi(keyPressed, '2') || strcmpi(keyPressed, '3') || strcmpi(keyPressed, '4') || strcmpi(keyPressed, '5') || strcmpi(keyPressed, '6') || strcmpi(keyPressed, '7')
guidata(handles.Questionnaire, keyPressed);
if strcmp(get(RR_ans, 'Visible'), 'on') == 1
uicontrol(RR_ans);
RR_callback(RR_ans, []);
end
end
end
function RRStart_callback(~,~)
set(RR_instruct, 'Visible', 'off');
set(space_start, 'Visible', 'off');
set(RRStart, 'Visible', 'off');
set(RR(1), 'Visible', 'on');
set(RRNext, 'Visible', 'on');
set(RR_ans, 'Visible', 'on');
end
function RR_callback(~,~)
handles.temp_data = str2num(guidata(handles.Questionnaire));
if handles.temp_data == 1
set(RR_ans, 'SelectedObject', RRans1);
elseif handles.temp_data == 2
set(RR_ans, 'SelectedObject', RRans2);
elseif handles.temp_data == 3
set(RR_ans, 'SelectedObject', RRans3);
elseif handles.temp_data == 4
set(RR_ans, 'SelectedObject', RRans4);
end
guidata(handles.Questionnaire, handles.temp_data);
end
function RRNext_callback(~,~)
if (handles.temp_data ~= 0) && (handles.temp_data < 5)
handles.REW_QNS.Reward_responsiveness(RR_idx) = handles.temp_data;
set(RR_ans, 'SelectedObject', []);
set(RR(RR_idx), 'Visible', 'off');
RR_idx = RR_idx + 1;
if RR_idx <= 8
set(RR(RR_idx), 'Visible', 'on');
else
set(RRNext, 'Visible', 'off');
set(RR_ans, 'Visible', 'off');
end
end
handles.temp_data = 0;
end
Running this questionnaire on its own (without the questions that come before this) works properly. The problem seems to be that when more questions are added, Matlab slows down. Am I correct in thinking this? Or is something else the problem here? If I am right and my main function is too big (all my callbacks are nested within the main function), how can I prevent the program from lagging? Should I split my callback functions into separate .m files and call them within my main function? Thank you in advance!

Best Answer

Actually
figure(FigH)
should give the focus to the figure according to the documentation. But this did not work from Matlab 5.3 to at least 2016b. (@Mathworks: ???)
Therefore I'm using this function:
function FocusToFig(ObjH, EventData) %#ok<INUSD>
% Move focus to figure
% Actually the command "figure(FigureHandle)" should set this figure to the
% currently active object, as described in the documentation. Anyhow, under
% Matlab 6.5 to 2016b this does not work.
% Defining the root property "CurrentFigure" does activate the figure for the
% creation of new objects, but this does not activate the keyboard focus e.g.
% for the KeyPressFcn (and WindowsKeyPressFcn in Matlab 7.?).
% There is a method to gain the focus using a Java method for the underlying
% Java frame, but in Matlab 6.5 I cannot get the corresponding handle securely.
%


% This function offers a workaround which moves the focus to the specified
% figure. It works at least under under Matlab 6.5.1, 2008b and 2009a. It fails
% under at least 2008a for unknown reasons.
%
% FocusToFig(ObjH, [DummyEventData])
% INPUT:
% ObjH: Handle of a uicontrol object. It is tried to move the focus to the
% parent figure and making it the CurrentFigure of the root object.
% DummyEventData: The 2nd input is optional and ignored, such that FocusToFig
% can be called as function handle callback directly.
%
% Tested: Matlab 6.5, 7.7, 7.8, 7.13, WinXP/32, Win7/64
% Author: Jan Simon, Heidelberg, (C) 2009-2018
% License: Creative Commons Attribution Share Alike 3.0
if any(ishandle(ObjH)) % Catch no handle and empty ObjH
FigH = ancestor(ObjH, 'figure');
% Work-around
if strcmpi(get(ObjH, 'Type'), 'uicontrol')
set(ObjH, 'Enable', 'off');
drawnow;
set(ObjH, 'Enable', 'on');
pause(0.02); % Give the re-enabled control a chance to be rendered
end
% Methods according to the documentation (does not move the focus for
% keyboard events under Matlab 5.3, 6.5, 2008b, 2009a):
figure(FigH);
set(0, 'CurrentFigure', FigH);
end
Call this in all callbacks of uicontrol objects to care for keeping the focus on the figure.
By the way:
strcmp(get(RRStart, 'Visible'), 'on') == 1
is nicer without the "== 1": strcmp replies a logical already, so there is no need to compare it to 1. I'd create a tiny subfunction:
function L = isVisible(H)
L = strcmp(get(H, 'Visible'), 'on');
end
Compare:
if strcmp(get(Next1, 'Visible'), 'on') == 1
...
elseif strcmp(get(Next2, 'Visible'), 'on') == 1
...
elseif strcmp(get(Next3, 'Visible'), 'on') == 1
...
with
if isVisible(Next1)
...
elseif isVisible(Next2)
...
elseif isVisible(Next3)
...