I am writing a program in which at some point a graph is plotted and displayed on screen. The user then needs to press 'y' or 'n' to accept or reject the graph. My current solution uses the PsychToolbox (the actual solution doesn't need to), which includes a command called 'KbCheck' which checks at the time of calling the state of all the keyboard buttons. My code looks like this:
function [keyPressed] = waitForYesNoKeypress
keyPressed = 0; % set this to zero until we receive a sensible keypress
while keyPressed == 0 % hang the system until a response is given
[ keyIsDown, seconds, keyCode ] = KbCheck; % check for keypress
if find(keyCode) == 89 | find(keyCode) == 78 % 89 = 'y', 78 = 'n'
keyPressed = find(keyCode);
end
end
The problem is, that the system really does 'hang' until a key is pressed. Ideally, I would be able to scroll, zoom, and generally interact with the graphs that are plotted onscreen so that I can really decide whether or not I want to press 'y' or 'n'!
I have tried adding 'drawnow;' into the while loop above but that doesn't work: I still am unable to interact with the plotted graphs until after I've accepted or rejected them.
The solution doesn't have to use PsychToolbox; I assume there are plenty of other options out there?
Thanks
I'd use the input function:
a = input('Accept this graph (y/n)? ','s')
if strcmpi(a,'y')
...
else
...
end
Although admittedly it requires two keypresses (y then Enter) rather the one.
Wait for buttonpress opens up a figure, which may be unwanted. Use instead
pause('on');
pause;
which lets the user pause until a key is pressed.
Why not using waitforbuttonpress instead?
Documentation: http://www.mathworks.fr/help/techdoc/ref/waitforbuttonpress.html
You don't want to use waitforbuttonpress since it locks the figure gui (no zooming, panning etc).
pause can cause the command window to steal the focus from the figure.
The solution I find to work best is to open the figure with a null keyPressFcn in order to avoid focus problems:
figure('KeyPressFcn',#(obj,evt) 0);
and then wait for CurrentCharacter property change:
waitfor(gcf,'CurrentCharacter');
curChar=uint8(get(gcf,'CurrentCharacter'));
Wait for key press or mouse-button click:
Example:
w = waitforbuttonpress;
if w == 0
disp('Button click')
else
disp('Key press')
end
for more information visit:
http://www.mathworks.com/help/matlab/ref/waitforbuttonpress.html
The waitforbuttonpress command is good but is triggered by either a mouse click or a key press. If you want it to trigger only from a key press, you can use the following hack:
while ~waitforbuttonpress
end
Related
I'm using a programmatic GUI in MATLAB which uses multiple figure windows. When I press the button 'Redraw' in Figure A, a new figure appears (Figure B) with some data plotted. I want the focus to immediately switch back to Figure A because there are many hotkeys (WindowKeyPressFcn) that I use in that window to update the plots in Figure B.
There are two problems here:
1) The last line of the callback for the button 'Redraw' does switch focus back to Figure A, BUT only if Figure B exists already. That is, the first time Figure B is created, it remains in focus. If I then use Figure A to update the plots in Figure B, the focus correctly switches back to Figure A. I can't think of why it behaves differently during the first redraw and all subsequent calls.
2) The even bigger issue is that if I set a breakpoint anywhere in the code and then resume execution, the focus switches back to Figure A as I want. So, why does entering the debugger and doing nothing else fix the problem? How can I find the issue if everything works in the debugger?
Thanks in advance!
EDIT: To my great surprise, I was able to reproduce this "Heisenbug" by writing my first ever programmatic GUI. This should be the simplest example of my problem. To see it in action, simply run the code below and click on the push button. For some reason, when Window 2 is created for the first time, the focus does NOT switch back to Window 1 as intended. It works properly for all subsequent button presses. Try closing Window 2 and pushing the button again, the error will keep occurring.
As mentioned in the original post, setting a breakpoint in the code resolves the issue. Set a breakpoint at line 27, then resume execution and Window 1 will be in focus.
What is happening here?
function heisenbug
%% Main problem:
% After clicking the push button, I want the focus to
% always switch back to Window 1 (the one containing the button).
% However, this does not work when Window 2 is first created.
%%
%% Create and then hide the GUI as it is being constructed
f = figure('Visible','off','name','Window 1','units','normalized','Position',[0.1 0.1 0.5 0.5]);
%% Initialize handles structure
handles = guihandles(f);
handles.window2 = [];
guidata(f,handles)
%% Make a button
hbutton = uicontrol('Style','pushbutton','String','Push me','units','normalized',...
'Position',[0.1 0.1 0.8 0.8],...
'Callback',#button_Callback);
%% Make the GUI visible
f.Visible = 'on';
%% Button callback
function button_Callback(source,eventData)
handles = guidata(gcbo);
% If Window 2 already exists, plot a circle, then switch focus back to Window 1.
if ~isempty(handles.window2) && ishandle(handles.window2)
figure(handles.window2);
plot(1,1,'bo')
figure(f);
% Otherwise, create Window 2 and do the same thing.
else
handles.window2 = figure('Name','Window 2','units','normalized','position',[0.4 0.1 0.5 0.5]);
plot(1,1,'bo')
figure(f)
end
guidata(source,handles)
end
end
My post was quickly answered by Adam (thanks!) on the MathWorks site: http://se.mathworks.com/matlabcentral/answers/299607-simply-entering-the-debugger-during-execution-of-matlab-gui-fixes-error-that-persists-during-normal.
I needed to insert a pause(0.05) after Window 2 is created, before trying to switch the focus back with figure(f). Otherwise the focus is stolen back to Window 2 when it finishes plotting.
I have the following code as part of my MATLAB GUI code:
k = waitforbuttonpress;
if k==0
if strcmp(get(handles.YESNO,'String'),'Y')
hint = 1;
else
hint = 0;
end
else
hint = 0;
end
I wait for the user to press one of the two YES or NO buttons. Inside each of these callbacks I update the variable handles.YESNO as set(handles.YESNO,'String','Y'); or set(handles.YESNO,'String','N'); respectively.
When I execute my MATLAB GUI, I have to press the YES button twice for the value to take into effect. Any tips/hints to overcome this issue?
I believe that the waitforbuttonpress mask your callback.
Instead, block your executation with uiwait that waits for your figure to close or to uiresume called by the buttons callbacks.
I want to detect how many times the user click spacebar in 5 seconds
Is there any good way to fix this problem?
Thanks
One way to easily read user inputs from the keyboard is to create a new figure and specify a KeyPressFcn callback function, which is executed automatically if any key is pressed.
Lets start off by creating a new figure. As we don't need the figure to display anything, let's make it as small as possible (i.e. 1 by 1 pixel) and place it at the lower corner of the display:
f = figure('Position',[0,0,1,1]);
Now we'll set the UserData property of the figure - which we will use as counter - to zero:
set(f,'UserData',0);
Now let's see what to do when a key is pressed: We can create a small callback function which checks if the pressed button was a space and increases the UserData counter if that was the case. We'll call that function isspace:
function isspace(hObject,callbackData)
if get(hObject,'CurrentCharacter') == ' '
set(hObject,'UserData',get(hObject,'UserData')+1);
end
end
Now simply set up the figure to use this function as KeyPressFcn by
set(f,'KeyPressFcn',#isspace);
This already counts the number of times space is pressed. The current value of the counter is read by
get(f,'UserData');
Now we need the time measurement. This can be done using a timer. We'll configure it to go off after 5 seconds and then assing a new value in the base workspace. For that we need a callback function timerCallback.m
function timerCallback(hObj,eventData)
assignin('base','nSpace',get(gcf,'UserData'));
delete(gcf);
stop(hObj);
delete(hObj);
end
t = timer('StartDelay',5,'TimerFcn',#timerCallback);
start(t);
And that's it: First create the figure, create the timer and after 5 seconds you get the number of key presses in the variable nSpace in your workspace and the window is closed.
I have Matlab application where the user needs to click on a point in a graph, and then confirm the click in a message box. I'm using getCursorInfo to get the current clicked location. If the user doesn't confirm the point selection, another point should be selected.
The problem is that sometimes after the user choose 'No' in the message box, getCursorInfo returns a new position without requiring the user to click on the mouse. It is as if getCursorInfo thinks the mouse button is continuously down.
Here is a minimal sample of code that demonstrates this - after the 3rd or 4th time the message box is shown, Matlab thinks the mouse button is down and changes the cursor position without any clicks.
fig = figure;
data = peaks();
plot(data);
old_pos = [-2 -2]
cur_pos = old_pos
while 1
datacursormode on
dcm = datacursormode(fig);
while all(cur_pos==old_pos)
pause(0.25);
ci = getCursorInfo(dcm);
if ~isempty(ci)
cur_pos = ci.Position;
end
end
display(cur_pos);
old_pos = cur_pos
datacursormode off
questdlg('Question?');
end
Without the dialog this code allows the user to select as many points as needed without getting confused (simply comment the questdlg call to see).
Turns out the solution was here, I needed to delete the data cursors with the following line:
delete(findall(fig,'Type','hggroup'));
I'm trying to sort through some image data in matlab and want to prompt the user for input about a series of images. Each time I show an image I want to pause and wait for a keystroke and perform a different action depending on what was pressed.
Current Best Solution:
responses = zeros(length(images),1);
for i = 1:length(images)
im = imread(images{i}.fname);
h = figure(1);
imshow(im);
% instead of just pause, I want to get the keystroke (k) that was pressed
waitforbuttonpress;
k = get(h,'CurrentCharacter');
switch lower(k)
case 'a'
responses(i) = 1;
case 'b'
responses(i) = 2;
end
end
You could use the KeyPressFcn property of the figure environment which should be set to a handle of a callback function that receives an event structure containing the character pressed. See the Matlab documentation for details and example.
Also you can look at the CurrentKey property of the figure environment but it will not give you a way to know when the key is actually pressed.