Matlab GUI: override behaviour of the spacebar key - matlab

I have built a GUI in matlab using GUIDE, and added keyboard shortcuts to some actions using the WindowKeyPressFcn callback function of my figure. my problem is that I want to use the spacebar as one of the shortcut keys, but it already has a preset usage: it activates the currently selected control (i.e. same as clicking the selected button).
While I can trigger my intended action just fine on the spacebar key through the callback, I found no way to remove this additional unwanted functionality. The result is two actions being performed - the one I programmed and the unintended button press, which creates a mess. The bottom line is that I can't use the spacebar as a shortcut. Is there any way to turn this functionality off or bypass it somehow? Perhaps something that will stop the key-press from reaching the GUI after it is handled by my callback?
I'd prefer a documented Matlab way but if that's not possible, java hacks are welcome as well.

AFAIK the unwanted behaviour is more OS releated than Matlab related and I know of no wway to remove it, however you can work around it.
One way to do this is to check in your pushbutton callback what was the last key pressed, you do this by adding a KeyPressFcn to your pushbutton. I have created a simple example below:
function spacebarTest
% create a figure
hFig = figure ( 'KeyPressFcn', #KeyPress );
% create a uicontrol
uicontrol ( 'style', 'pushbutton', 'Callback', #PushButton, 'KeyPressFcn', #KeyPress );
% store the last key pressed information in the fiure handle
userData.lastKey = '';
set ( hFig, 'userData', userData );
end
function KeyPress ( obj, event )
% obtain the handle for the figure
hFig = ancestor ( obj, 'figure' );
% extract the user data
userData = get ( hFig, 'userData' );
% store the last key in the user data
userData.lastKey = event.Key;
% update the figure handle user data
set ( hFig, 'userData', userData );
disp ( 'spacebar shortcut' );
end
function PushButton ( obj, event )
% get the figure handle
hFig = ancestor ( obj, 'figure' );
% extract the user data
userData = get ( hFig, 'userData' );
% check if spacebar was the last key pressed
if strcmp ( userData.lastKey, 'space' ); return; end
disp ( 'running callback - push me' );
end
One problem with this is that it still "looks" like your button has been pressed, although the callback has not run. To resolve this you would need to stop the button ever gaining focus which you would need to replace your uicontrol button with a JButton (java) and use the FocusGainedCallback

Related

Disable progress bar Matlab [duplicate]

This question already has an answer here:
How to disable the progress bar when using the nlfilter function in Matlab?
(1 answer)
Closed 5 years ago.
I'm using a code in Matlab composed of a large number of nested functions. A large number of these functions show progressbars. Is there any Matlab command or any possibility to disable progressbars showing, without having to locate and comment/remove all the lines where they are called from?
I assume from your comments you mean you have lots of functions calling waitbar.
You could overload the 'waitbar' function with your own waitbar.m ensuring its higher on the searchpath. Although this is not usually a good idea and may cause problems in the future when you (or anyone else you uses your codes) do want to use the waitbar and it doesn't appear.
Another (preferable in my view) way to disable it is to create your own intermediate function where you can toggle on/off the waitbar:
function h = mywaitbar ( varargin )
% preallocate output
h = [];
% use an internal persistent variable
persistent active
% by default set to true
if isempty ( active ); active = true; end
% Check to see if its a control call
if nargin == 1 && ischar ( varargin{1} )
% is it a call to disable it?
if strcmp ( varargin{1}, '**disable**' )
active = false;
else
active = true;
end
return
end
if active
h = waitbar ( varargin{:} );
end
end
The downside to this is that you will need to find and replace all your waitbar commands with the new function 'waitbar', but this is a one time only action.
Then disable all future calls to waitbar by:
mywaitbar ( '**disable**' )
Run your codes and no waitbar will be shown. The use of a peristent variable will keep the status until you restart Matlab (or you invoke clear all). To stop 'clear all' resetting it you can use mlock in the function.
To reenable the waitbar:
mywaitbar ( '**enable**' )
To test it use the following code:
for ii=1:10
h = mywaitbar ( ii );
fprintf ( 'test with waitbar %i\n', ii);
end
Now disable the waitbar capability:
mywaitbar ( '**disable**' )
for ii=1:10
h = mywaitbar ( ii );
fprintf ( 'test with waitbar disabled %i\n', ii);
end
You will see that the code above runs with no waitbar being shown.

How to check if a uicontextmenu is visible or active

What I have:
In a Matlab-GUI I have a uicontextmenu connected to a plot (=axes). If I "activate" this via a mouse-click (right-button), I can use the usual "Callback" to do something, like highlighting the plot. If the user then selects one of the uimenu-elements of the menu, I can use the Callback of this uimenu-element and reset the highlighting.
But there is a problem, if the user does not select an element. The context-menu disappears and I cannot find a way to find out, if this happens. In my example, the highlighted plot stays highlighted.
What I tried so far:
Besides reading the docs, I appended listeners to the properties to some of the uimenu-elements, e.g.:
addlistener(mymenu_element, 'Visible', 'PostSet', #mytest);
but this property, as well as others, seems not to be changed or touched at any time - what suprises me a bit :o
So the question is:
Is there a way to execute a function after a uicontextmenu is executed (or however you call it, when a context-menu "disappears")? In other words: if the user does not select an element of a context-menu, how can this be identified?
Since you cant listen to these items (I've run a few tests and come to the same conclusion) you can work around this by creating and managing your uicontextmenu in a different way:
function yourFunction
% create a figure
hFig = figure;
% add a listener to the mouse being pressed
addlistener ( hFig, 'WindowMousePress', #(a,b)mouseDown(hFig) );
end
function mouseDown(hFig)
% react depening on the mouse selection type:
switch hFig.SelectionType
case 'alt' % right click
% create a uicontext menu and store in figure data
hFig.UserData.uic = uicontextmenu ( 'parent', hFig );
% create the menu items for the uicontextmenu
uimenu ( 'parent', hFig.UserData.uic, 'Label', 'do this', 'Callback', #(a,b)DoThis(hFig) )
% assign to the figure
hFig.UIContextMenu = hFig.UserData.uic;
% turn visible on and set position
hFig.UserData.uic.Visible = 'on';
hFig.UserData.uic.Position = hFig.CurrentPoint;
% uicontext menu will appear as desired
% the next mouse action is then to either select an item or
% we will capture it below
otherwise
% if the uic is stored in the userdata we need to run the clean up
% code since the user has not clicked on one of the items
if isfield ( hFig.UserData, 'uic' )
DoThis(hFig);
end
end
end
function DoThis(hFig)
% Your code
disp ( 'your code' );
% Clean up
CleanUp(hFig);
end
function CleanUp(hFig)
% delete the uicontextmenu and remove the reference to it
delete(hFig.UserData.uic)
hFig.UserData = rmfield ( hFig.UserData, 'uic' );
end

Real-time update of values inside a call_back function[the values are from external function(of another .m file)]

I have an external function say "external_func" (seperate .m file)
Inside this function a while loop is called, and this while loop update a variabl named "update_prog"
Now I will pass this value into the GUIDE using
assignin('base', 'update_prog', update_prog); % passing to workspace
I am making this
"update_prog" as global variable and calling it into GUIDE .m file
function pb1_Callback(hObject, eventdata, handles)
global update_prog
% hObject handle to pb1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% else
% set(handles.pb1,'enable','on');
% end
% update_prog first value prints, but it wont updates as the loop in external_func goes on.
drawnow;
set(handles.slider1,'Value',update_prog)
external_func;
so here in the GUIDE .m file I can get the value
"update_prog" but it wont keep up with the while loop. I used "drawnow" but its of no use.
How can I refine this value "update_prog" as the while loop in the "external_func" goes through multiple iterations. [Note: The updated values are there in the callback function of GUIDE, but unless there is a callback the callback function wont update the "update_prog"], so how can I acheive this real-time update inside a call_back function.
[Note: passing the variables through function input is not possible here in my case, so I am looking for alternatives]
Edit1: please consider this link, which has an exampleWhich may clarify you what I am trying to acheive
What I am doing here is
Passing the variable(which is being updated in the while loop of the externel function) into GUI.
I will use this variable to show the progress on the progress bar(Slider).
What is the problem?
1. The variable inside the GUI callback(Consdier I will press a push button and then it will call the function with while loop) will put the updated values into the set(handles.slider,'Value',variable)
By doing this I cant move the slider.
Why?
Callback updates the variable only when I press the push button, next all the updates to the variable will not be updated, so progress bar/slider wont move.
I wouldn't recommend to pass your variable in 3 steps with an intermediate workspace (external->base workspace->GUI). I would rather recommend to pass your variable directly (external->GUI).
Each Matlab figure offers a space to store variables (Application Data) of any type. I would suggest reading the article Share Data Among Callbacks and read the documentation for the 3 functions:
guidata
setappdata
getappdata
This way will offer you much more control over the scope of your variables and you won't need any global declaration.
Below is an example of a simple gui. The gui declare the variable in it's user space (with setappdata), then uses a timer to periodically read this variable (with getappdata).
The external function do whatever you want it to do (just a random number in the example), then to update the variable you use the same setappdata. The only thing you need for that is the handle of the main GUI figure, so in the example I give it as input of the external function.
The GUI also has two buttons to start and stop the update.
The code for the main example GUI 'theGui.m' is :
function h = theGui
%// basic GUI with 2 buttons and 1 slider
h.fig = figure('Position',[433 434 500 100],'Menubar','none','CloseRequestFcn',#my_closefcn) ;
h.sld = uicontrol('Style','Slider','Position',[20 20 460 20]) ;
h.btnStart = uicontrol('Style','pushbutton','String','Start updating','Callback',#btnStart_callback,'Position',[20 50 200 30]);
h.btnStop = uicontrol('Style','pushbutton','String','Stop updating','Callback',#btnStop_callback,'Position',[280 50 200 30],'Max',1,'Min',0);
%// Define the timer
h.t = timer ;
h.t.Period = 0.1 ; %// 0.1s refresh interval
h.t.TimerFcn = {#timer_callback,h.fig} ;
h.t.ExecutionMode = 'fixedSpacing' ;
%// initialise the variable to update in the GUI appdata
update_prog = 0 ;
setappdata( h.fig , 'update_prog' , update_prog ) ;
%// save handles
guidata( h.fig , h );
function btnStart_callback(hobj,~)
h = guidata( hobj ) ; %// retrieve handles
if strcmp('off',h.t.Running) %// Start timer (only if not already running)
start(h.t)
end
function btnStop_callback(hobj,~)
h = guidata( hobj ) ; %// retrieve handles
stop(h.t) %// Stop timer
function timer_callback(~,~,hfig)
update_prog = getappdata( hfig , 'update_prog' ) ; %// retrieve the 'update_prog' variable value
h = guidata( hfig ) ; %// retrieve handles
set(h.sld , 'Value' , update_prog) ; %// update the slider object with the retrieved value
function my_closefcn(hobj,~)
%// this function is only to clean up when the GUI will be closed.
%// It is recommended to delete the timer manually
h = guidata( hobj ) ; %// retrieve handles
stop(h.t) %// Stop timer (in case it is still running)
delete(h.t) ; %// delete the timer
delete(h.fig) ; %// destroy the figure
And the code for external_func.m
function external_func( guiMainFigureHandle )
%// This function will only generate random numbers and push them into the
%// variable 'update_prog' contained in the GUI appdata.
%// This is why this function NEEDS the handle of the gui to be able to
%// access the Application Data space of the gui.
for k = 1:100
randomValue = rand(1) ; %// generate a random value
hfig = ancestor( guiMainFigureHandle , 'figure' ) ; %// make sure the handle provided is the top level figure
setappdata( hfig , 'update_prog' , randomValue) ; %// update the variable value
pause(0.1) ;
end
Edit:
I place this in edit instead of changing the code above because I don't recommend messing with the root object if you don't need to. But in your case it can be a way round your problem.
If your external function doesn't have access to the GUI, it can always update a part of memory which is available for all the programs running in a given Matlab session, namely the root object. The handle for it is reserved and is the same for any program: 0 (although since v2014b there is another way to invoke it : groot, it is still always the same handle for all Matlab).
So in the example above, in theGui.m, use instead:
setappdata( 0 , 'update_prog' , update_prog ) ;
in the main routine, then in the subfunction function timer_callback(~,~,hfig), use:
update_prog = getappdata( 0 , 'update_prog' ) ; %// retrieve the 'update_prog' variable
And your function external_func() doesn't need any extra argument, the update only needs one line:
setappdata( 0 , 'update_prog' , update_prog) ; %// update the variable value
I "suspect" that your update_prog variable in the base workspace is not a global (you must define it to be global in every workspace that you want to use it).
Since your using globals (there are many better ways to do this - but thats not your question) - why don't you simply define the update_prog variable to be global in your external_func function (replace the assign call).
edit put a drawnow in your external_func function. That way when you click on the button it will update.
edit 3
I think I know what you want to do, try this example and see if it does what you want - updated to show how you find the slider object in your code and update inside your loop:
function mygui
% create a figure
f = figure;
% create a uicontrol slider - note that I give it a tag.
uicontrol ( 'style', 'slider', 'Position', [0 200 200 40], 'tag', 'MYSLIDER', 'backgroundcolor', 'white', 'parent', f );
% create a push button which we can press to update
uicontrol ( 'string', 'Press 2 start', 'callback', #(a,b)myLoop(), 'Position', [0 0 200 50] )
end
% This replicates your "external_func" function.
function myLoop()
% since you cant pass in any var -> you need to find the slider object
% you can do this by using findobj and search for the tag of the item
uic = findobj ( 0, 'tag', 'MYSLIDER' );
% find the figure handle (only needed for this demo)
f = ancestor ( uic, 'figure' );
% initialise the value which will be used to update the slider
count = 0;
% create your loop
while ishandle(f)
% incrememt the count variable -> this will end up on the slider value
count = count + 1e-5;
% reset count if > 1 -> so the slider cant go out of range.
if count >= 1
count = 0;
end
set ( uic, 'Value', count );
% initiate a drawnow -> this allows matlab to process GUI events
drawnow();
end
end
The downside of this is you insert a drawnow in your loop -> which could slow it down somewhat.
If this doesn't fix your problem you need to explain better what you want to do... (in my view)

BytesAvailableFcn callback not receiving updated handles

I created an interface which automatically reads in data through the serial port, hence the reason I implemented the BytesAvailableFcn Callback
handles.fileID.BytesAvailableFcnMode = 'terminator';
handles.fileID.BytesAvailableFcn = {#streamData_fastTrak, handles};
The data that is read is displayed in a table chosen by the user (through use of radio buttons in the GUI). When an option is chosen a callback occurs to save the selected radio button to a variable which is saved in the handles struct. I have followed the program step for step and I am sure this callback does occur and that the variable is saved. However when the serial callback occurs the handles struct still has the old option value.
Here is the serial callback code:
function handles = streamData_fastTrak(hObject, eventdata, handles)
handles.num = handles.num + 1;
%receive data through serial
line = transpose(fscanf(handles.fileID, ' %f ' ));
table_data = get(handles.uitable1, 'data');
table_data_style = get(handles.uitable4, 'data');
display(handles.butt_state);
display(handles.num);
if(fix(line(1)) == 1 && strcmp(handles.butt_state, 'style_button'))
table_data_style(handles.select_Indices(1), 2:(length(line)+1)) = num2cell(line);
set(handles.uitable4, 'data', table_data_style);
display(handles.select_Indices);
elseif(fix(line(1)) > 1 && strcmp(handles.butt_state, 'stat_button'))
table_data(line(1)-1, 1:length(line)) = num2cell(line);
set(handles.uitable1, 'data', table_data);
if(line(1) == countStates(handles))
streamSensor_1_2_3(hObject, handles);
handles.time_step = handles.time_step + 1;
end
end
And the radio button callback:
function uipanel2_SelectionChangeFcn(hObject, eventdata, handles)
handles.butt_state = get(get(handles.uipanel2,'SelectedObject'), 'tag');
display(handles.butt_state);
guidata(hObject, handles);
The way I see it there are 2 ways to approach the problem:
The first way (I don't recommend this as much as the second one) is to pass the data you want updates to a string control and have it read back by your serial port function.
The other way that i recommend is to include a dummy button with a call back that calls
handles.fileID.BytesAvailableFcn = {#streamData_fastTrak, handles};
Again - this will update the new "handles" data to the callback function
For example
Assuming a dummy push button with tag PB1
function handles = streamData_fastTrak(hObject, eventdata, handles)
%% do stuff here
%% update handles data
PB1_Callback (handles.PB1,event,dat)
guidata(handles.PB1,handles) %% function ends
%% dummy button callback function%%
function PB1_Callback(hObject,event,handles)
handles.fileID.BytesAvailableFcn = {#streamData_fastTrak, handles};
guidata(hObject,handles) %% dummy button function ends
You can make the dummy button invisible by making the background color of the button same as that of the UI.
When you first declare your callback function for the ByteAvailableFcn in the line:
handles.fileID.BytesAvailableFcn = {#streamData_fastTrak, handles};
Matlab assign the function handle to the event and also pass the handles stucture at this point of time. This is now frozen into the private workspace of the callback. If you change the handles structure later on in your code (as you do when you try to attach the variable handles.butt_state), the callback doesn't know it, it still use the handles structure that was passed when you declared the callback.
There are several ways of getting this value correctly but I'll give 2 of them:
1) get the value from the radio button when needed
in your streamData_fastTrak callback function, query the button state directly from the uicontrol (instead of checking for a saved value)
handles.butt_state = get(get(handles.uipanel2,'SelectedObject'), 'tag');
This way you are sure to get the latest state of the radio button.
2) Store value in appdata
Every time the button state is changed, store the value somewhere, but you still have to query this value when your callback want to execute. A good place to save values are in the appdata (accessed using setappdata and getappdata).
So in your button callback:
function uipanel2_SelectionChangeFcn(hObject, eventdata, handles)
butt_state = get(get(handles.uipanel2,'SelectedObject'), 'tag');
setappdata(handles.uipanel2, 'butt_state' , butt_state );
display(butt_state);
And in your streamData_fastTrak callback:
function handles = streamData_fastTrak(hObject, eventdata, handles)
butt_state = getappdata(handles.uipanel2, 'butt_state' );
%// and the rest of your code ...

How to create a GUI inside a function in MATLAB?

Is it possible to write a GUI from inside a function?
The problem is that the callback of all GUI-functions are evaluated in the global workspace. But functions have their own workspace and can not access variables in the global workspace. Is it possible to make the GUI-functions use the workspace of the function? For example:
function myvar = myfunc()
myvar = true;
h_fig = figure;
% create a useless button
uicontrol( h_fig, 'style', 'pushbutton', ...
'string', 'clickme', ...
'callback', 'myvar = false' );
% wait for the button to be pressed
while myvar
pause( 0.2 );
end
close( h_fig );
disp( 'this will never be displayed' );
end
This event-loop will run indefinitely, since the callback will not modify myvar in the function. Instead it will create a new myvar in the global workspace.
There are a number of ways to build a GUI, such as using the App Designer, GUIDE, or creating it programmatically (I'll illustrate this option below). It's also important to be aware of the different ways to define callback functions for your GUI components and the options available for sharing data between components.
The approach I'm partial to is using nested functions as callbacks. Here's a simple GUI as an example:
function make_useless_button()
% Initialize variables and graphics:
iCounter = 0;
hFigure = figure;
hButton = uicontrol('Style', 'pushbutton', 'Parent', hFigure, ...
'String', 'Blah', 'Callback', #increment);
% Nested callback function:
function increment(~, ~)
iCounter = iCounter+1;
disp(iCounter);
end
end
When you run this code, the counter displayed should increment by one every time you press the button, because the nested function increment has access to the workspace of make_useless_button and thus can modify iCounter. Note that the button callback is set to a function handle to increment, and that this function must accept two arguments by default: a graphics handle for the UI component that triggered the callback, and a structure of associated event data. We ignore them with the ~ in this case since we aren't using them.
Extending the above approach to your particular problem, you could add your loop and change the callback so it sets your flag variable to false:
function make_stop_button()
% Initialize variables and graphics:
keepLooping = true;
hFigure = figure;
hButton = uicontrol('Style', 'pushbutton', 'Parent', hFigure, ...
'String', 'Stop', 'Callback', #stop_fcn);
% Keep looping until the button is pressed:
while keepLooping,
drawnow;
end
% Delete the figure:
delete(hFigure);
% Nested callback function:
function stop_fcn(~, ~)
keepLooping = false;
end
end
The drawnow is needed here to give the button callback a chance to interrupt the program flow within the loop and modify the value of keepLooping.
You can declare a variable global in your function and global in the GUI code, certainly if the callback is in a separate function rather than inline. I've done this in a little skeleton GUI I use to make quick menu system.
In your code above you may be able to add the global keyword to your initial declaration and also to your inline callback i.e. 'global myvar = false'
I found a solution to the problem. The callback-function has to modify the handle-structure of the GUI. This structure can be accessed both from within the callback and from the function without introducing new variables to the global workspace:
function myfunc()
h_fig = figure;
% add continue_loop to the GUI-handles structure
fig_handles = guihandles( h_fig );
fig_handles.continue_loop = true;
guidata( h_fig, fig_handles );
% create a useless button
uicontrol( h_fig, 'style', 'pushbutton', ...
'string', 'clickme', ...
'callback', #gui_callback );
% wait for the button to be pressed
while fig_handles.continue_loop
fig_handles = guidata( h_fig ); % update handles
pause( 0.2 );
end
close( h_fig );
disp( 'callback ran successfully' );
end
% The arguments are the Matlab-defaults for GUI-callbacks.
function gui_callback( hObject, eventdata, handles )
% modify and save handles-Structure
handles.continue_loop = false;
guidata( hObject, handles );
end
note that since the while-loop will only update fig_handles when it is run, you will always have at least 0.2 seconds delay until the loop catches the modification of fig_handles.continue_loop