Detect a keystroke in matlab while viewing a figure? - matlab

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.

Related

datacursormode: spurious MouseMotion event processing when UpdateFcn takes time to return

I'm using DATACURSORMODE in Matlab 8.1.0.604 (R2013a) on Mac OS 10.11.6 (El Capitan).
In the simple example below, the data cursor only moves when a new location is clicked, not when the mouse is merely moved. This is exactly the behaviour I want. However, in my code I get a different behaviour: after the first click, the cursor moves whenever the mouse just moves, until I subsequently double-click. I did not knowingly ask for this latter behaviour and do not want it.
The key difference seems to be that my UpdateFcn callback code takes time to complete (and let's assume it always will: my callback is intended to perform fairly complex actions, and is already as vectorized and optimized as it can get). This is emulated by the pause statement in the example below, which should hopefully replicate the problem if uncommented (might have to fiddle with the duration, depending on platform/setup).
In datacursormode.m the "UNDOCUMENTED FUNCTIONALITY" comments mention that both MouseMotion and ButtonDown events are thrown by a data cursor mode object. This is clearly not the full story, because by default there's no response to mouse motion alone, but something happens, presumably due to the delay, to make it do so.
So my question is: is this cursor-moving-on-mouse-motion-until-you-doubleclick a known feature/mode, or just unintended "undefined behaviour" as a side effect of the delay? In either case, how can I (programmatically) prevent it from happening, assuming I can't actually speed up the callback code?
function demo
fig = 1;
figure(fig), close, figure(fig) % ensure virgin figure
image
h = datacursormode(fig);
datacursormode(fig, 'on')
set(h, 'UpdateFcn', #callback)
function txt = callback(cbo, event)
% pause(0.1) % uncomment this line (and/or lengthen the delay as necessary) to replicate the problem
txt = evalc('cbo,event,disp(get(event))');
I couldn't actually reproduce your problem on R2012a or R2016a on Windows, but it sounds like on your system MATLAB is failing to catch the ButtonUp event when you click. The reason MouseMotion events are processed is because you should be able to drag the cursor when you have the mouse button held down.
Assuming that it is indeed the slow response of the UpdateFcn that causes this, you may be able to resolve this by triggering the slow part in an asynchronous operation which then triggers another update when it is done. The most universal form of this, I think, is to use a timer with a very short StartDelay. In the example below I've used appdata to share handles between the datacursormode object and the timer, but you could approach this in many different ways in your specific implementation.
function demo
fig = 1;
figure(fig), close, figure(fig) % ensure virgin figure
image
h = datacursormode(fig);
datacursormode(fig, 'on')
set(h, 'UpdateFcn', #cursormodecallback)
setappdata(fig, 'cursormode',h);
setappdata(fig, 'timer',timer('StartDelay',0.001, 'TimerFcn',#timercallback));
setappdata(fig, 'lasttxt','Initial text');
function txt = cursormodecallback(cbo, event)
txt = getappdata(1,'lasttxt'); % Display the most recently generated text in the tooltip
t = getappdata(1,'timer');
if strcmp(get(t,'Running'),'off') % If we have already asked for an updated tooltip and haven't got one yet then don't ask for another one
set(t, 'UserData',{cbo,event}); % Store the data needed by the slow callback to generate the tooltip
start(t); % Ask the timer to generate new text
end
function timercallback(cbo, event)
cursordata = get(cbo,'UserData');
[cbo,event] = cursordata{:};
pause(1)
txt = evalc('cbo,event,disp(get(event))');
if ~isequal(txt,getappdata(1,'lasttxt'))
setappdata(1,'lasttxt',txt); % Store the latest text
updateDataCursors(getappdata(1,'cursormode')); % Update the cursor so the text is displayed
end
Here's an edited version of Will's solution that works nicely. The StartDelay value is critical: <=6ms fails to solve the mouse motion problem. 7ms solves it most of the time, but occasionally lapses. 10ms seems to work fairly reliably (except the very first time in a given new Matlab session, when things are slow to wake up). Typical Matlab implementation flakiness...
function demo
fig = 100;
figure(fig), close, figure(fig) % ensure virgin figure
img = image;
EnableDataCursor(img)
function EnableDataCursor(img)
ax = get(img, 'parent');
fig = get(ax, 'parent');
dcm = datacursormode(fig);
datacursormode(fig, 'on')
set(dcm, 'UpdateFcn', #CursorModeCallback)
setappdata(img, 'CursorModeObject', dcm);
setappdata(img, 'Timer', timer('StartDelay', 0.010, 'TimerFcn', #TimerCallback));
setappdata(img, 'LastText', 'Initial text');
function txt = CursorModeCallback(cbo, event)
img = get(event, 'Target');
t = getappdata(img, 'Timer');
if strcmp(get(t, 'Running'), 'off') % If we have already asked for an updated tooltip and haven't got one yet then don't ask for another one
txt = 'updating...';
set(t, 'UserData', {cbo, event}); % Store the data needed by the slow callback to generate the tooltip
start(t); % Ask the timer to generate new text
else
txt = getappdata(img, 'LastText'); % Display the most recently generated text in the tooltip
end
function TimerCallback(t, varargin)
ud = get(t, 'UserData');
[cbo, event] = deal(ud{:});
img = get(event, 'Target');
pause(1) % numbercrunch, numbercrunch, numbercrunch
txt = num2str(get(event, 'Position'));
if ~isequal(txt, getappdata(img, 'LastText'))
setappdata(img, 'LastText', txt); % Store the latest text
updateDataCursors(getappdata(img, 'CursorModeObject')); % Update the cursor so the text is displayed
end

Need to press button twice to update variable in MATLAB GUI

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.

Flip real time video and stop it with user input

I created a GUI using Matlab and inserted a live video in this GUI with 2 bush buttons, one for starting video and the other for capturing an image.
The problem is when I press start video, the video displays the picture flipped, it means when you wave right hand, it is displayed in the left side.
how can I solved this problem?
I wrote this code:
i=0;
while(i<=1000)
dataa=getsnapshot(vid);
data1 = dataa(:,:,1);
data1 = fliplr(data1);
data2 = dataa(:,:,2);
data2 = fliplr(data2);
data3 = dataa(:,:,3);
data3 = fliplr(data3);
data=cat(3,data1,data2,data3);
flushdata(vid);
imshow(data);
end
and it works, but now I want to stop the while statement when the user pushes a button. How can I do that?
Thank you.
You can use the fliplr() function to flip your frames when you capture them to flip them. As long as I know, this code is quite fast as it doesnt flip the positions but changes the matrix element memory directions.
For stoping the while loop I suggest the next thing (maybe a more experienced matlab "player" would do it something else)
Wherever you have the code do:
while (i<=1000)&&~buttonpushed
% your code
pause(0.01) % If you dont pause the loop the GUI cannot refresh
end
In the startup part of the code
global buttonpushed;
buttonpushed=false;
And In the callback of the button:
function pushbutton1_Callback(hObject, eventdata, handles)
if get(hObject,'Value')
buttonpushed=true;
else
buttonpushed=false;
end
end

Matlab adding KeyListener to existing button

I've written a Matlab program which counts different values when are particular button is pressed (so far just the numbers of "yes" and "no"). I'm trying to add a key listener so that when I press, for example, n on the keyboard the button is pressed, or the same actions are completed. I have tried the addListener and keyfunclistener functions but neither seems to be working.
Here is an example of the button:
no=uicontrol(h_fig,'callback',countnonerve,'position',[.65 .07 .1 .08],'string','No','style','pushbutton','Units','normalized');
Any suggestions? It would be very helpful I'm not familiar with MatLab
You could try using the KeyPressFcn property of the figure to record/capture individual key presses:
function keypress_Test
h = figure;
set(h,'KeyPressFcn',#keyPressCb);
function keyPressCb(src,evnt)
disp(['key pressed: ' evnt.Key]);
end
end
The above code can be pasted into a file and saved as keypress_Test.m. A figure is created and a callback assigned to the KeyPressFcn property.
It can be easily adapted for your GUI. Wherever you have access to the figure handles (probably the h_fig from above) just add set(h_fig,'KeyPressFcn',#keyPressCb); and paste the keyPressCb code into the same file.
If you are counting the number of times a certain key is pressed, then you will have to save this information somewhere..probably to the handles structure. Is that the case? If so, then you can easily access this from the callback
function keyPressCb(src,evnt)
disp(['key pressed: ' evnt.Key]);
% get the handles structure
handles = guidata(src);
if ~isempty(handles)
% do something here - increment count for evnt.Key
% save the updated count
guidata(src,handles);
end
end
Try it out and see what happens!

MATLAB: Pause program and await keypress

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