How to disable uicontrol 'mouseless' interaction

Dear community,
at the moment I'm creating a gui using normal figures and uicontrol elements. One side is a plotting window in which the user can interact using key press and mouse klick, the other half of the figure are some additional settings like visibility of certain elements etc using checkboxes for example
If I click on a checkbox and afterwards i press 'space' in the plotting window which is processed by ''WindowButtonDownFcn''.
My Problem is, that at the same time the 'space' changes the checkbox value
Similar things happen if I changed the value of a popupmenu and I want to navigate in the plotting window using the arrow keys.
The Callbacks of the 'WindowButtonDownFcn' are processed correctly, but at the same time i change the value of the uicontrol
How can I remove this behavior such that the user can not interact with uicontrol elements using keyboard button press (and also navigating using TAB once an uicontrol element was used) but only mouse clicks
I am using Win10 and Matlab 2021a
here a short "working" example
close all;
fig=figure;
% create plots and disable interactivity
s=subplot(1,2,1);
p=plot(rand(1,1000));
axtoolbar(s,'Visible','off');
disableDefaultInteractivity(s);
%% create buttons and set window key press function
pressedButton= uicontrol(fig,'Style','text');
pressedButton.Units='normalized';
pressedButton.Position = [0.7 0.7 0.2 0.04];
pressedButton.String = 'no key pressed yet';
fig.WindowKeyPressFcn=@(~,ev) set(pressedButton,'String',ev.Key);
colorPopup = uicontrol(fig,'Style','popupmenu');
colorPopup.Units='normalized';
colorPopup.Position = [0.7 0.5 0.2 0.04];
colorPopup.String = {'r','b','k','c'};
colorPopup.Value = 1;
colorPopup.Callback =@(~,~) set(p,'Color',colorPopup.String{colorPopup.Value});
visibilityCheckbox = uicontrol(fig,'Style','checkbox');
visibilityCheckbox.Units='normalized';
visibilityCheckbox.Position = [0.7 0.6 0.2 0.04];
visibilityCheckbox.String='Visibility';
visibilityCheckbox.Value=1;
visibilityCheckbox.Callback=@(~,~) set(p,'Visible',visibilityCheckbox.Value);
best regards
Jonas

 Accepted Answer

If you click on an uicontrol element, it gets the focus. Then following keyboard events are caught be the keyboard handling of the uicontrol. A solution is to give the focus back to the figure or the axes in the callback of the uicontrol. This should work by figure(fighandle) according to the documentation, but this was not successful at least for Matlab 6.5 to 2018b (the newest version I have).
Workarounds:
  • Insert code in the callbacks, which sets the "Enable" property of the uicontrol to 'off' temporarily:
function checkBoxCallback(ObjH, EventData)
set(ObjH, 'Enable', 'off');
drawnow;
set(ObjH, 'Enable', 'on');
pause(0.02); % Voodoo!
end
WindowAPI(figHandle, 'setFocus')

10 Comments

i will try this lateron, thanks for your advice, i will report
dear Jan, thanks for your advice, it works really good. since I have a ton of uicontrol elements and dont want to add this in each callback, I added a addlistener to my code which catches all events and dis- and reenables the uielements if necessary
in addition to this, I used two pause() statements to update the state of the uictrontrol elements to prevent high latency if my figure contains many plot elements (or is there a better reason to use drawnow?)
that's what i added at the end of my code:
allUiELements=findall(gcf,'type','uicontrol');
addlistener(allUiELements,'Value','PostSet',@setEnable)
function setEnable(~,ev)
if strcmp(ev.AffectedObject.Enable,'on')
set(ev.AffectedObject, 'Enable', 'off');
pause(0.02);
set(ev.AffectedObject, 'Enable', 'on');
pause(0.02);
end
end
Jonas
Jonas on 26 Apr 2022
Edited: Jonas on 27 Apr 2022
now i noticed another last case, where the problem persists: if we have a popmenu and the "new" value is the same as before, the popupmenu still gets the focus and the described interactivity, because the callback is only executed, if the value is changed!
@Jan, any idea to remove also this case?
@Jonas: Wow, you are right.
Using the Java GUI element allows to catch 30 events. So replace the popup menu by the corresponding Java element.
dear Jan,
thanks again for your advice. Unfortunately i was not able to create a visible JPopupmenu. In addition, the String property of the respective Matlab Object does not exist.
I tried the following on Matlab 2022a
fig=figure;
[colorPopup,~]=uicomponent(fig,'style','JPopupMenu');
colorPopup.MatlabHGContainer.Units='normalized';
colorPopup.MatlabHGContainer.Position = [0.7 0.5 0.2 0.04];
colorPopup.MatlabHGContainer.String = {'r','b','k','c'};
colorPopup.MatlabHGContainer.Value = 1;
colorPopup.MatlabHGContainer.Callback =@(~,~) set(p,'Color',colorPopup.MatlabHGContainer.String{colorPopup.MatlabHGContainer.Value});
%colorPopup.FocusGainedCallback=@setEnable; % probably the callback i could
%also use to remove the focus again
colorPopup.MatlabHGContainer.Focusable=0; % probably the actual setting a want to disable?
further i get the warning
"Warning: JAVACOMPONENT will be removed in a future release. For more information see UI Alternatives for MATLAB Apps on mathworks.com. "
but since this is only a warning, this should not be the problem
can someone try this in their own matlab and make some lines work?
@Jonas: I do not like my suggestion of uicomponent. The warning is serious and writing stable code running under all versions of Matlab is preferrable.
I tried to identify the current object, if the WindowsKeyPressFcn is triggered, but unfortunately gcbo points to the figure also the checkbox has the focus.
An ugly workaround could be to store the values of all gui elements redundantly e.g. in the UserData of the figure. Then the callback of the gui-elements set their value and updates a copy in the UserData. The WindowKeyPress fcn moves the focus to the figure (or axes or what you need), restores the saved values from the UserData back to the gui elements and triggers the command for the pressed key.
This should revert the changes made by a keypress inside an gui element.
Sorry, more elephant than elegant, but at least no Java hacks.
But with another Java hack, I could get it working: FEX: findjobj :
Create the popup menu and get the underlying java object:
PopH = uicontrol('Style', 'popupmenu', 'String', {'1', '2', '3'});
jPopH = findjobj(PopH);
jPopH.MouseReleasedCallback = @(H, E) KillFocus(H, E, HList);
function KillFocus(jPopH, EventData, HList)
disp('triggered')
set(HList, 'Enable', 'off');
drawnow;
set(HList, 'Enable', 'on');
drawnow;
end
There are many other callbacks available, see: get(jPopH).
HList contains a list of all handles of the GUI elements to be unfocussed.
dear Jan, thanks again for your detailed suggestion. i will try this later. if i remember correctly, there was also a java focusable attribute which can be changed and may shorten this process significantly. i will report tomorrow!
long question, short answer: the java objects have a focusable attribute which can be set to 0 and solve all the problems, no need for any listeners or callbacks. I just added the following after creation of all uicontrol elements
objs=findall(fig,'type','uicontrol'); % get the uicontrol elements
for nr=1:numel(objs)
jobj=findjobj(objs(nr),'persist'); % findjobj can hadnle only one obj at a time, the persistent argument speeds up the finding process of jobj
jobj.Focusable=false;
end
% instead of the for loop we could also use
% arrayfun(@(in) set(findjobj(in,'persist'),'Focusable',false),objs)
thanks again to @Jan for his help!
and another comment: i don't know why, but we can't set the Focusable attribute, if the UIControl element is Invisible and it seems to reset it's proeprty when made visible. for that reason i used @Jan's solution with the Release callback
Jan included an HList Object, but there was no such object available and i was too dumb to get the Matlab handle from the java jPopH handle
uiButtons=findall(fig,'type','uicontrol');
for count=1:numel(uiButtons)
if uiButtons(count).Visible
jobj=findjobj(uiButtons(count));
jobj.MouseReleasedCallback=@(H, E) KillFocus(uiButtons(count));
end
end
function KillFocus(obj)
set(obj, 'Enable', 'off');
drawnow;
set(obj, 'Enable', 'on');
end

Sign in to comment.

More Answers (1)

for better visiblity here the last state of our discussion. thanks @Jan!
long question, short answer: the java objects have a focusable attribute which can be set to 0 and solve all the problems, no need for any listeners or callbacks. I just added the following after creation of all uicontrol elements
objs=findall(fig,'type','uicontrol'); % get the uicontrol elements
for nr=1:numel(objs)
jobj=findjobj(objs(nr),'persist'); % findjobj can hadnle only one obj at a time, the persistent argument speeds up the finding process of jobj
jobj.Focusable=false;
end
% instead of the for loop we could also use
% arrayfun(@(in) set(findjobj(in,'persist'),'Focusable',false),objs)
thanks again to @Jan for his help!

1 Comment

and another comment: i don't know why, but we can't set the Focusable attribute, if the UIControl element is Invisible and it seems to reset it's proeprty when made visible. for that reason i used @Jan's solution with the Release callback
uiButtons=findall(fig,'type','uicontrol');
for count=1:numel(uiButtons)
if uiButtons(count).Visible
jobj=findjobj(uiButtons(count));
jobj.MouseReleasedCallback=@(H, E) KillFocus(uiButtons(count));
end
end
function KillFocus(obj)
set(obj, 'Enable', 'off');
drawnow;
set(obj, 'Enable', 'on');
end

Sign in to comment.

Categories

Find more on App Building in Help Center and File Exchange

Products

Release

R2021a

Asked:

on 21 Apr 2022

Edited:

on 3 May 2022

Community Treasure Hunt

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

Start Hunting!