I have a strange problem in my Matlab GUI. The GUI contains uipanel and icontrol objects, some of which are buttons. Usually, the GUI is controlled with the directional arrow keys.
However, once I click one of my buttons, the keyboard events are not recorded any more. I've set breakpoints in the keypress callback to find out what's happening and it turns out the callback is never called. If I manage to click the GUI background, it works once again, which makes me think it's related to the active control. But how can I give control back to the main window? uicontrol(hFigure) doesn't work, neither does figure(hFigure).
The following code snippet reproduces the problem. Copy it into a new file (ideally called test.m, otherwise Code Analyzer will complain) and run it to open a GUI window that shows this behaviour. Once the button is clicked, the arrow keys aren't recorded any more unless the user clicks the area outside the text uicontrol.
function test
figure('KeyPressFcn',#key)
clf
p = uipanel('position',[0 0 1 1],'BackgroundColor',[.7 .7 .7]);
uicontrol('Style','push','String','Click me','Units','norm',...
'Position',[0.43 0.91 0.14 0.06],'Callback',#button);
t = uicontrol(p,'Style','text','String','Use arrow keys','Units','norm',...
'Position',[0.2 0.4 0.6 0.2],'FontSize',20);
function button(~,~)
set(t,'String','Button pressed.');
end
function key(~,e)
set(t,'String',['Key ' e.Key ' pressed.']);
end
end
You are right about why this doesn't work. When you click on the button, the figure is no longer the active control. The best way to fix this is to additionally set the KeyPressFcn property of the button to be the same as the KeyPressFcn of the figure.
function test
figure('KeyPressFcn',#key)
clf
p = uipanel('position',[0 0 1 1],'BackgroundColor',[.7 .7 .7]);
uicontrol('Style','push','String','Click me','Units','norm',...
'Position',[0.43 0.91 0.14 0.06],'Callback',#button, ...
'KeyPressFcn', #key);
t = uicontrol(p,'Style','text','String','Use arrow keys','Units','norm',...
'Position',[0.2 0.4 0.6 0.2],'FontSize',20);
function button(~,~)
set(t,'String','Button pressed.');
end
function key(~,e)
set(t,'String',['Key ' e.Key ' pressed.']);
end
end
You could also set the WindowKeyPressFcn instead of KeyPressFcn.
For more information see my answer here:
matlab: difference between KeyPressFcn and WindowKeyPressFcn
Related
I have a matlab gui made with guide that has a checkbox uicontroll. When that checkbox is focused, pressing spacebar (un)checks that checkbox.
I don't want this behaviour - how can I turn this off?
I want to turn it off because I have defined a keypressFcn for the spacebar and I want something else to happen when the user presses the spacebar. atm that 'something else' is working. If spacebar is hit, my keypressFcn runs and does what it should do and additionally the checkbox (un)checks. I only want it to execute my keypressFcn, though..
I don't really know where to start solving this problem.. Just some generall direction instructions would already be helpfull!
when I had a similar problem I hacked the KeyPressFcn to bypass the spacebar:
function test_KeyPressFcn
% Create a figure
figure();
% Add a check box with a KeyPressFcn callback, which will be called when the user preses a key
uicontrol('Style' , 'checkBox','KeyPressFcn' , #KeyPressed);
function KeyPressed(src , event)
if strcmpi(event.Key , 'space')
% Pressing the spacebar changed the value of the checkbox to
% new_value
new_value = get(src , 'Value');
% Let's revert it to its old value
set(src , 'Value' , ~new_value)
end
The space bar is still working but you set the checkbox back to its original value!
I had the similar issue. My solution was to set up a dummy uicontrol (like a text Style with empty String), and in any uicontrol CallBack, I always call uicontrol(dummy) to have the dummy uicontrol focused, so spacebar press will have no effect. It doesn't sound a good solution, but it works well for me.
dummy = uicontrol(gcf, 'Style', 'text'); % use this for focus
ckbox = uicontrol(gcf, 'Style', 'CheckBox', 'String', 'myCheckBox', ...
'Callback', #(h,e)uicontrol(dummy), 'Position', [100 200 100 32]);
If you now click the checkbox, it will change its value, and the callback will move focus to the dummy text, so spacebar won't change its value anymore.
If user may press TAB key, it will cycle eligible uicontrols, and if focus lies on the checkbox, spacebar will change its value again. My solution for this is to do uicontrol(dummy) in the KeypressFcn so dummy will be on focus after TAB press.
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 am running the drawnow statement from a callback function within a MATLAB GUI to update the state of a button. At the beginning of the callback (which has high runtime) I alter the properties of the button and force an update with drawnow. While updateing properly, the button remains rendered 'pushed down' instead of 'disabled'. After the callback is finished, the button is updated again and now rendered 'disabled'.
Take following minmal (not) working example:
function simple_example()
h = figure();
% add a button, give it some properties and a callback
uicontrol(h,...
'Style','pushbutton',...
'String','I am enabled',...
'Units','normalized',...
'Position',[0.5 0.5 0.4 0.4],...
'Callback',#btn_callback);
end
function btn_callback(hObject, ~)
set(hObject,'Enable','off');
set(hObject,'String','I am disabled');
drawnow;
pause(3);
end
Is there a way to change this behavior and have the button appear disabled while the callback is still executing?
As you are asking about appearance here's a workaround using uibuttongroup:
function simple_example()
h = figure();
b = uibuttongroup('Position',[0.5 0.5 0.4 0.4]);
bgcolor = b.BackgroundColor;
% add a button, give it some properties and a callback
uicontrol(b,...
'Style','pushbutton',...
'String','I am enabled',...
'Units','normalized',...
'Position',[-0.05 -0.05 1.1 1.1],...
'Callback',#btn_callback);
end
function btn_callback(hObject, ~)
set(hObject,'Enable','off');
set(hObject,'String','I am disabled');
drawnow;
pause(3);
end
Here, you fit the button within a uibuttongroup, which normally groups several uibuttons and then set the button size bigger than the actual uibuttongroup, so the borders don't appear.
However, this let's you lose the button down rendering. You could get that back by altering the uicontrolgroup's border properties.
Update:
This seems to be OS-specific. On OS X your code works just fine, as far as I can see. Windows, I don't know, but according to your comment neither my version, nor yours seems to fix the issue. On Ubuntu, on the other hand, my answer solves the problem.
Quite simply, I'm trying to create a borderless button in a MATLAB GUI. The reasons are mostly aesthetics, so no need for a debate on why it should be borderless.
I already know that this cannot be done using the built-in MATLAB uicontrol alone, since the border of the button is not an accessible property in MATLAB. Thus, the underlying JAVA code (upon which MATLAB is written), must be accessed in order to manipulate the border. This is where I get lost, since I've only ever programmed in MATLAB.
I followed an example from here:
http://undocumentedmatlab.com/blog/borderless-button-used-for-plot-properties
But I'm still not getting a borderless button.
Here is a simple code example (NOTE the use of Yair Altman's findjobj which is available on the MATLAB file exchange):
f=figure('Menubar','none', 'Position',[200 200 300 200]);
p=uipanel(f, 'BackgroundColor', [0 0 1]);
h = uicontrol('parent', p, ...
'Style','pushbutton', ...
'String','click', ...
'TooltipString', 'you should click this' ...
'Units','normalized', ...
'Position',[0.3 0.3 0.5 0.5], ...
'BackgroundColor', [0 0 1]);
jh = findjobj(h);
jh.setBorder(javax.swing.BorderFactory.createEmptyBorder());
%attempt 1 does not remove border
jh.border=[];
%attempt 2 does not remove border
jh.setBorder([]);
%attempt 3 does not remove border
jh.border=javax.swing.BorderFactory.createEmptyBorder();
%attempt 4 does not remove border
Any thoughts on where I've gone wrong? Thanks!
You shoud add two lines:
jh.setBorderPainted(false);
jh.setContentAreaFilled(false);
It is not clear to me what you mean with "borderless".
Looking at the example on the Web page you posted, I assume you are looking for something like an "invisible" pushbutton.
If so, you might consider this alternative way:
insted of having a pushbutton you might have a static text uicontrol
make its backgroundcolor the same of the GUI backgroundcolor (it will become "invisible" and without any border)
do not set any string in the static text uicontrol
set the enable property of the static text uicontrol to off
define, for the static text uicontrol the ButtonDownFcn
write the code you want to execute by pushing the pushbutton in the ButtonDownFcn
When you press the mouse button on the "invisible" static text uicontrol, its ButtonDownFcn will be executed.
You just have to remember ... were the "invisible" static text uicontrol is.
Hope this helps.
The border is affected by the fly-over appearance feature. http://undocumentedmatlab.com/blog/undocumented-button-highlighting
You need to add
jh.setFlyOverAppearance(true);
Worked for me.
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