keyPressFcn on a figure: wait untill the user is done annotating.

6 次查看(过去 30 天)
Hi everyone one,
I am trying to annotated spectrogram generated myself. Spectrograms are already compute to save time during annotation. Than a MATLAB script open up the matfile containing spectrograms and plot it on the screen using the keyPressFcn while craeting the figure. This allow me to enter value like start time, end time, lowest freq, highest freq, comment related to the figure shown ubt also use ginput to directly get pixel of the spectrogram to calculate values. When all is done, I want to press the rightarrow so the next spectrogram shows up. But here is my trouble, I can I use the keyPressFcn, make sure it is waiting for the user to terminate annotating, then return the right command to the main script can undderstand that it is the time to show the next figure.
I also use setappdata to retreive value from keyPressFcn. I didn't really like this idea but it seems the only option I have. If you have better suggestion I will take it.
Here is a simple example of what I have so fare with comments directly in the script :
% Here is the simpliest example for figure annotation
clear all
% I want to plot figure one at the time, I want to figure to stay standby
% until the user have finishing pressing key and have press the key to
% either exit, next or back.
for i=1:2
% Some calculation for each loop element.
x = 1:10;
y = x*i;
disp(['Plotting figure : ', num2str(i)])
% PLotting figure
plotFigure(x,y)
% Waiting for the user to annotated the figure.
% I need kind of a waitfor here, so when the figure in still on,
% the reste of the script is not execute. Otherwise, the next disp is
% Is shown in the command windows without any keypress by the user.
% Maybe the wainting line should be in mykeypressfucntion
% waitfor()
% Retreiving the value pressed.
% The only way I found to retreive key pressed during keyPressFnc from
% figure is to use getappdata and setappdata. Is there any better way?
disp(['Retreiving pressed value.'])
keyInfo{i} = getappdata(0,'keyPressInfo');
% What is the next figure?
% If keyInfo{i}.status or otherway around is set to exit, back, next..
% Then the for or while loop will change accordingly.
% Free appdata memory
if ~isempty(getappdata(0,'keyPressInfo'))
rmappdata(0,'keyPressInfo');
end
end
% Here is the reste of the script post figure
% Some operation will be here after figure annotation
% For example, it will do operation on each keyInfo element.
% for i=1:nb
% time = keyInfo{i}.val
% end
% End of the script
% ------------------------------------------------------------------
function plotFigure(x,y)
fig = figure(figure( 'KeyPressFcn', @mykeyPressCallback))
plot(x,y);
end
% ------------------------------------------------------------------
function mykeyPressCallback(hObj,event)
ii=1;
% Callback function for key press event
if strcmp(event.Key,'rightarrow') % next
% Here I will like the callback fonction to exit and the next figure in
% the for loop to showup
info.status = 'next';
elseif strcmp(event.Key,'rightarrow') % Back
% Here I will like the callback fonction to exit and the previous figure in
% the for loop to showup if i ~= 1
info.status = 'back';
elseif isletter(event.Key)
switch event.Key
case 't' % Enter time range for the current validation
val = enterVal(['Enter start time: ']);
info.type{ii} = 'startT';
info.val{ii} = val;
ii=ii+1;
case 'x' % Start entering validation information
[x] = ginput(1);
info.type{ii} = 'startT';
info.val{ii} = x;
ii=ii+1;
case 'e' % Exit
% Here I will like the for loop to terminate and get to the
% end of the script directly
otherwise
warning(['There is no action for the key pressed : ', event.Key])
end
end
% Should I had a wait hear instead?
% So the code do not execute anyother command until any exit, next, or back
% key have been press? How can I do it?
% Saving data
setappdata(0, 'keyPressInfo' ,info);
end
% ------------------------------------------------------------------
function answer = enterVal(message)
% Open an input dialog to enter a value
answer = inputdlg(message, 'Input Required');
end

回答(1 个)

Voss
Voss 2024-8-27
Here is a code structure that you can run and add to.
n_fig = 2;
keyInfo = cell(1,n_fig);
ii = 1;
while ii <= n_fig
% Some calculation for each loop iteration
x = 1:10;
y = x*ii;
fprintf('Plotting figure : %d\n',ii)
% Plotting figure
fig = plotFigure(x,y);
% Waiting for the user to annotate the figure
uiwait(fig)
% if the figure was deleted, create it again and wait again
if ~ishandle(fig)
continue
end
% Retrieving the info about the key(s) pressed
disp('Retrieving pressed value.')
keyInfo{ii} = getappdata(fig,'keyPressInfo');
% the last key pressed on this figure was either 'rightarrow',
% 'leftarrow', or 'e' (corresponding to status 'next', 'back',
% or 'exit', respectively)
switch keyInfo{ii}{end}.status
case 'next'
% increment ii. then iterate again
ii = ii+1;
case 'back'
% if ii > 1, decrement ii. then iterate again
if ii > 1
ii = ii-1;
end
case 'exit'
% exit the while loop
break
end
end
% iterate over the key sequences, one sequence per figure
for ii = 1:n_fig
% iterate over the key(s) in each sequence
for jj = 1:numel(keyInfo{ii})
if isfield(keyInfo{ii}{jj},'val')
time = keyInfo{ii}{jj}.val
% ...
end
end
end
% End of the script
% ------------------------------------------------------------------
function fig = plotFigure(x,y)
fig = figure( 'KeyPressFcn', @mykeyPressCallback);
plot(x,y);
end
% ------------------------------------------------------------------
function mykeyPressCallback(hObj,event)
% Callback function for key press event
switch event.Key
case 'rightarrow' % next
info.status = 'next';
uiresume(hObj)
case 'leftarrow' % Back
info.status = 'back';
uiresume(hObj)
case 'e' % Exit
info.status = 'exit';
uiresume(hObj)
case 't' % Enter time range for the current validation
val = enterVal('Enter start time: ');
info.type = 'startT';
info.val = val;
case 'x' % Start entering validation information
x = ginput(1);
info.type = 'startT';
info.val = x;
otherwise % irrelevant key pressed
if isletter(event.Key)
warning(['There is no action for the key pressed : ', event.Key])
end
return
end
% need to store the entire sequence of (relevant) keys pressed, so append
% this new info struct onto the end of whatever is already stored in
% 'keyPressInfo' appdata for this figure
old_info = getappdata(hObj,'keyPressInfo');
setappdata(hObj,'keyPressInfo',[old_info {info}]);
end
% ------------------------------------------------------------------
function answer = enterVal(message)
% Open an input dialog to enter a value
answer = inputdlg(message, 'Input Required');
end
There are alternatives to setappdata. One alternative is to make your script into a function with the other functions (plotFigure, mykeyPressCallback, enterVal) nested inside it. All nested functions have access to any variable in the workspace of the nesting function (in this case the top-level/main function). I didn't do that here; I kept setappdata, but I changed it to be associated with each figure instead of the root graphics object (0).

类别

Help CenterFile Exchange 中查找有关 Maintain or Transition figure-Based Apps 的更多信息

产品


版本

R2024a

Community Treasure Hunt

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

Start Hunting!

Translated by