I want to have a ui button that will start and pause a simulation. The script I want to run is a for loop simulation, say the script name is simulation.m.
I set the push button as follows.
start.button = uicontrol('Style','pushbutton','units','normalized',...
'String','Start','Position',[0.1,0.93,0.1,0.05], ...
'Callback',#start_call);
I can't figure out what to write in the callback function (either for running the script or for pausing it
function [] = start_call()
simulation.m;
end
You've basically got it right, you just need to add two things: the callback always takes two input arguments, so even if you don't use them, the function definition needs them. A script is run using the run command. Just change your callback to
function [] = start_call(source, eventdata)
run('simulation.m');
end
Remark: arguments that are not used are commonly replaced by the shorthand ~, which would then read
function start_call(~, ~)
You can also obviously drop the square brackets, if there is no output.
Related
I'm writing a function that does some work to provide a result to the caller; but once this work is done there exists some followup work that is not required by the caller but is known is going to be useful in the future. I'd like this followup work to be executed when the MATLAB process is no longer busy doing what is immediately required of it so it doesn't slow down, or worse block execution of, other priority tasks.
On the front end interface, MATLAB has a clear sense of whether code is currently executing. When there is, the status bar reads "Busy" and the Editor ribbon strip updates to show a "Pause" button to interrupt execution. You can enter new commands to the command window, but they aren't read and executed until the currently executing code completes. Once execution is completed, the status bar is cleared of text, the ribbon "Pause" button is replaced with a "Run" button, and the command window displays a >> indicating that new commands entered there will be executed immediately.
Effectively I'd like code running in the middle of a function to be able to have the equivalent effect of entering new commands into the command window, to be run after currently-executing code is done.
For example, consider the function:
function testfun(x)
disp("Starting main execution with input " + x);
pause(1)
disp("Completed main execution with input " + x);
disp("Starting followup execution with input " + x);
pause(1)
disp("Completed followup execution with input " + x);
end
A function or script that invokes this a couple of times (and where the extra disp calls are, there could be other unspecified time-consuming activities):
testfun(1)
disp("Done testfun(1).")
testfun(2)
disp("Done testfun(2).")
would result in the output:
Starting main execution with input 1
Completed main execution with input 1
Starting followup execution with input 1
Completed followup execution with input 1
Done testfun(1).
Starting main execution with input 2
Completed main execution with input 2
Starting followup execution with input 2
Completed followup execution with input 2
Done testfun(2).
and take four seconds in total. An alternative solution would result in the output:
Starting main execution with input 1
Completed main execution with input 1
Done testfun(1).
Starting main execution with input 2
Completed main execution with input 2
Done testfun(2).
Starting followup execution with input 1
Completed followup execution with input 1
Starting followup execution with input 2
Completed followup execution with input 2
The output Done testfun(2), which is the last of the higher-priority output results to appear, would only have taken 2 seconds to appear, rather than the original 4.
Is this possible? Alternatively, if I could allow execution to clear the current execution stack (as I could if interrupting the code with the debugger, then using dbstep out all the way up to the Base workspace, then calling new code from the command window) this would be a useful compromise even if it leaves open the possibility there are more function calls queued in the Base workspace. (The actual choice of workspace the code executes in doesn't matter too much, but stepping out of deeper nested workspaces would at least allow the remaining code queued within those workspaces to be completed before the workspace is destroyed.)
The best part-solution to this I'm aware of, with thanks to Jan Simon at MathWorks, is to observe the actual status bar of the MATLAB window. This is accessible with:
statusbar = com.mathworks.mde.desk.MLDesktop.getInstance.getMainFrame.getStatusBar;
The text contained in the status bar is then retrievable with statusbar.getText. When code is executing, the text contains the word "Busy", and when nothing is executing it doesn't, so I can use logic from this to decide MATLAB is currently busy (with careful attention to the possibility that other things, such as the profiler, will also modify the contents of this text).
A timer can poll this text so a callback will fire a short time (though not instantly, and dependent on how aggressively I'm willing to poll the interface) after MATLAB's Busy status ends.
This is not ideal because it only indirectly infers the busy status from a UI element that isn't designed to be involved in application control, and may be unreliable, and because it depends on repeated polling, but in some circumstances it will be better than nothing.
Applying this to the simplified example, we can break the task into two functions, with the main one taking an extra argument t to receive a timer object to interact with:
function testmain(x,t)
disp("Starting main execution with input " + x);
pause(1)
disp("Completed main execution with input " + x);
t.UserData.followups(end+1) = x;
end
function testfollowup(x)
disp("Starting followup execution with input " + x);
pause(1)
disp("Completed followup execution with input " + x);
end
A callback for the timer could then be:
function followupcallback(t,~)
if isempty(t.UserData.followups)
return
end
status = t.UserData.statusbar.getText;
if ~isempty(status) && contains(string(status),"Busy")
return
end
arrayfun(#testfollowup,t.UserData.followups);
t.UserData.followups = [];
t.stop
end
and the polling timer can then be set up:
t = timer( ...
"TimerFcn",#followupcallback, ...
"Period",0.25, ...
"ExecutionMode","fixedRate", ...
"UserData",struct( ...
"statusbar",com.mathworks.mde.desk.MLDesktop.getInstance.getMainFrame.getStatusBar, ...
"followups",[]));
t.start;
With the example script modified to pass the timer through:
testfun(1,t)
disp("Done testfun(1).")
testfun(2,t)
disp("Done testfun(2).")
The resulting output is reorded as intended.
I think you could build an actual work queue. I'm using a global variable here for that, so that multiple different functions can add to the work queue. I'm also using nested functions to encapsulate arbitrary code and variables into a function handle that can be stored and run later on.
I'm still hazy as to why this would be useful, but it does process things in the order given by the example in the OP.
initialize_work_queue
testfun(1)
disp("Done testfun(1).")
testfun(2)
disp("Done testfun(2).")
process_work_queue
disp("Done process_work_queue.")
function testfun(x)
disp("Starting main execution with input " + x);
pause(1)
disp("Completed main execution with input " + x);
function follow_up_work
disp("Starting followup execution with input " + x);
pause(1)
disp("Completed followup execution with input " + x);
end
global my_work_queue
my_work_queue{end+1} = #follow_up_work;
end
function initialize_work_queue
global my_work_queue
my_work_queue = {};
end
function process_work_queue
global my_work_queue
while ~isempty(my_work_queue)
func = my_work_queue{1};
my_work_queue(1) = [];
func();
end
end
It's my first post here, so hello everyone!
My question is about MATLAB GUI interface. In my code, there is a function callback from a pushbutton and I would like to disable every push/slide-able element in my GUI during processing this callback. Unfortunatelly, when I set 'enable' property of these elements to 'off' at the beginning of a callback and then I set it back to 'on' at the end, property doesn't change.
I think I know why it happens. Probably because if callback changes anything, it happens just after the function is finished and every change inside it does not affect any element outside of the function until the processing is done. That's why I don't see all these elements disabled - because at the end of the function I set everything 'on' and that's the only thing which takes place at all.
Regarding to this - is there any option I can change 'enable' property DURING executing a function? Code is shown below:
function [] = mListLaunchButton_call(varargin)
// Some global declarations
global a phi launchBlanker
global servoNumber servoZeroPosition servoDegreePerDegree servoDirection
// Assigning a class
Manual = varargin{3};
// Enabling "Stop" button and disabling everything else
set(Manual.listStopButton,'enable','on');
set(Manual.listSaveButton,'enable','off');
set(Manual.listDeleteButton,'enable','off');
set(Manual.listClearButton,'enable','off');
set(Manual.listLaunchButton,'enable','off');
set(Manual.closeButton,'enable','off');
for i = 1 : 5
set(Manual.sliderDOF(i),'enable','off');
end
%%%%%%%%%%%%%%%%%%%% HERE FUNCTION DOES SOME STUFF %%%%%%%%%%%%%%%%
// Disabling "Stop" button and enabling eveything else
set(Manual.listStopButton,'enable','off');
set(Manual.listSaveButton,'enable','on');
set(Manual.listDeleteButton,'enable','on');
set(Manual.listClearButton,'enable','on');
set(Manual.listLaunchButton,'enable','on');
set(Manual.closeButton,'enable','on');
for i = 1 : 5
set(Manual.sliderDOF(i),'enable','on');
end
Try using the drawnow command after your initial enabling/disabling of GUI controls and before the line:
%%%%%%%%%%%%%%%%%%%% HERE FUNCTION DOES SOME STUFF %%%%%%%%%%%%%%%%
This should cause MATLAB to flush the queued GUI events and update your screen before moving onto the meat of the function.
I'm new in Matlab and I have created a GUI with some push button.
Now, in my current folder I have 4 files:
init.m
example.mdl (simulink)
gui.fig
gui.m
In the gui.fig I have two push buttons:
Init button
start_simulink button
and I would like that when I push on this button the respective action :
start init
start simulink
but I would like that after pressing a button the m file and simulink are performed in a base workspace and not only in a Callback Workspace.
How can I achieve that?
Not exactly what you want, but with the same result. Finally all variables will be in your base workspace, in my eyes it doesn't matter in this case where your scripts initially got called.
For the initialization I assume you're just loading parameters into the base workspace, so you can use a little function in your callback:
function assign2workspace( scriptname )
eval( scriptname );
temp = who;
for iv = 1:length(temp)
assignin('base',temp{iv},eval(temp{iv}));
end
end
where scriptname is you initialization script.
For Simulink you could to do it analogue (untested though)
function startSimulink( modelname )
sim( modelname );
% do what has to be done
temp = who;
for iv = 1:length(temp)
assignin('base',temp{iv},eval(temp{iv}));
end
end
I think my problem was similar to yours. Here's my solution.
Any variable that your script needs from your GUI, be sure to assign it to the base workspace using assignin('base','variablename'). In the callback function for your button, run your script using evalin('base','scriptname')
i have written a code which creates a figure with 3 buttons and a text box. the program complains about my callback function when i press a button though.
function game(states)
fig=figure('position',[200 150 500 370]);
face.B1=uicontrol('parent',fig,'style','pushbutton','string','start!','visible','on','position',[20 160 460 50]);
face.B2=uicontrol('style','pushbutton','parent',fig,'string','B2','visible','off','position',[20 90 460 50]);
face.B3=uicontrol('style','pushbutton','parent',fig,'string','B3','visible','off','position',[20 20 460 50]);
face.txtbx=uicontrol('style','text','parent',fig,'string','welcome to my game. press start to begin','position',[20 230 460 120]);
%set the callback function of the button
%when the button is pressed, i want to initiate the changestate function
set(face.B1,'callback','changestate(face,states,1);');
% while 1
uiwait(fig)
% end
end
this is the function that i want to call when the button is pressed. the contents of this function are not important to my question, but i include it just in case
function face = changestate(face,states,nextstate)
disp('enter changestate')
face.B1=set(face.B1,'string',states{nextstate}.B1str,'callback','changestate(face,states,states{nextstate}.B1next)');
if ~isnan(states(nextstate).B2str)
face.B2=set(face.B2,'string',states{nextstate}.B2str,'callback','changestate(face,states,states{nextstate}.B2next)','visible','on');
else face.B2=set(face.B2,'visible','off');
end
if ~isnan(states(nextstate).B3str)
face.B3=set(face.B3,'string',states{nextstate}.B3str,'callback','changestate(face,states,states{nextstate}.B3next)','visible','on');
else face.B3=set(face.B3,'visible','off');
end
face.txtbx=set(face.txtbx,'string',states{nextstate}.txtbxstr);
% uiresume(fig)
end
the error that i am receiving is:
Error using waitfor
Undefined function or variable 'face'.
Error using waitfor
Error while evaluating uicontrol Callback
this error occurs when i press the button B1. I want the button to initiate the changestate function. can someone explain to me why i am getting this error?
When you use string declaration for a callback, it will be evaluated at the workspace callback scope. If you want your function to be evaluated with the variables at the current scope you should use one of the following:
…,'callback',#(~,~) changestate(face,states,states{nextstate}.B1next),...
…,'callback',#(hObj,evt) changestate(hObj,evt,face,states,states{nextstate}.B1next),...
…,'callback',{#changestate,face,states,states{nextstate}.B1next),...
instead of:
...,'callback','changestate(face,states,states{nextstate}.B1next),...
Where in the second and third callbacks, your function should be able to retrieve two more arguments, that are the button handle (hObj), and event data (evt), which will probably be empty.
The reason is the following, quoting here:
When MATLAB evaluates function handles, the same variables are in
scope as when the function handle was created. (In contrast, callbacks
specified as strings are evaluated in the base workspace.) This
simplifies the process of managing global data, such as object
handles, in a GUI.
Whereas when you use string:
Setting a callback property to a string causes MATLAB to evaluate that
string in the base workspace when the callback is invoked.
As you used uiwait, execution stops inside uiwait (line 82) (for my matlab version), which has a waitfor command, giving the following error:
Error using waitfor
Undefined function or variable 'face'.
If you don't use a uiwait, it would evaluate the string callback at the global workspace, and the error would look like:
>> Undefined function or variable 'face'.
Error while evaluating uicontrol Callback
This discussion may also be of your interest.
1-The code below displays the properties of the pressed key.Try it by pressing a key and observe the results.
figure('Name','Press keys to put event data in Command Window',...
'KeyPressFcn',#(obj,evt)disp(evt));
you will see outputs like this( e.g upon pressing space bar)
Character: ' '
Modifier: {1x0 cell}
Key: 'space'
2-Now simply add the following line of code to above ( or simply execute it before clearing the workspace)
cameratoolbar('SetMode','orbit');
Now press any key and nothing happens! the control will no longer be transferred to your costume call back function! ( here:#(obj,evt)disp(evt)).
same thing happens for WindowButtonDownFcn, WindowButtonUpFcn too.
How can I get around this? I wanna be able to handle KeyPressFcn or WindowButtonDownFcn after executing cameratoolbar('SetMode','orbit').
I found the answer: Once the cameratoolbar('SetMode','orbit') is called one of these two happens:the handle to the figure is lost or the event handler gets its default value. I am not sure which one though. Therefore we can add the following code to re-assign the lost handler back to our own call back function:
set(gcf,'KeyPressFcn',#(obj,evt)disp(evt))