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.
Related
I would like to register a callback when a ROI, returned by drawpolygon is clicked. The docs (https://de.mathworks.com/help/images/ref/images.roi.polygon-class.html) say that there is an event ROIClicked for a ROI. However, I do not find a way to register a callback for this event.
f = figure();
imagesc(rand(64,64));
roi = drawpolygon();
Now, trying
roi.ROIClicked = #(~,~)disp('clicked');
fails with
Unrecognized property 'ROIClicked' for class 'images.roi.Polygon'.
and also
addlistener(roi, 'ROIClicked', 'PostSet', #(~,~)disp('clicked'));
fails with
Error using images.roi.Polygon/addlistener
The name 'ROIClicked' is not an accessible property for an instance of class 'images.roi.Polygon'.
I might have missed something in the docs, but I'd like to know how to register a callback that is executed when a ROI i clicked?
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.
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 would be very grateful for help, advice or suggestion. I have an application to control geodetic instrument using synchoronous interface. However some commands are asynchronous by its nature, e.g. GetReflectors. After this command is triggered I receive as many server answers as is the number of available reflectors. So I have registered an COM event and associate handler function. So far so good. I can display the data coming but I do not know how to pass some variable to the main function. I tried to save variable as .mat file or in .txt file and read it. Actually it works in Matlab but it does not works in compiled .exe aplication (Error firing event). Even disp command does not work in compiled aplication (display nothing). So the main question is: how to pass variables from handler to main function. Is there a way? Global variables? Thank you Filip
Edit: I am adding an code to demostrate the problem... I need to save Reflector Name and Reflector ID so as the user can choose one (because there are multiple events coming with different Reflectors).
function pushbutton_GetReflectors_Callback(hObject, eventdata, handles)
ltsync = actxserver ('LTControl.LTCommandSync2'); %Act as server: LTConnect2
ltsync.events() %List of all COM events
ltsync.registerevent({'ReflectorsData' 'ReflectorsHandler'}) %Register event
ltsync.GetReflectors() %Ask instrument for reflectors
pause(3) %Time to receive answers
end
function ReflectorsHandler(varargin) %Handler of the event ReflectorsData
%var1,var2,reflectorID,reflectorName,var5,surfaceOffset,reflectorsTotal,var8,var9
disp('Reflector Data:');
disp(varargin{3}) %Reflector ID
disp(varargin{4}) %Reflector name
end
I believe you can solve this problem by passing a function handle to registerevent instead of just the string function name, and creating a class to allow you to pass the data back. First, the class:
classdef ReflectorsResponse < handle
properties (SetAccess = private)
reflectors
responseComplete = false;
reflectorsTotal = NaN;
end
methods
function respond(obj, varargin)
% Create a struct for each reflector (or you could define
% another class, but let's keep it simple for the time being)
newRefl = struct();
newRefl.ID = varargin{3};
newRefl.name = varargin{4};
newRefl.surfaceOffset = varargin{6};
% ... store other properties in struct
% Store this reflector
obj.reflectors = [obj.reflectors; newRefl];
% Store total reflector count and check for completion
if isnan(obj.reflectorsTotal)
obj.reflectorsTotal = varargin{7};
end
if length(obj.reflectors) == obj.reflectorsTotal
obj.responseComplete = true;
end
end
end
end
You can then use this by making the respond method your handler:
function pushbutton_GetReflectors_Callback(hObject, eventdata, handles)
% Create the response object and associated handler function handle
response = ReflectorsResponse();
handlerFun = #(varargin)response.respond(varargin{:});
ltsync = actxserver ('LTControl.LTCommandSync2'); %Act as server: LTConnect2
ltsync.events() %List of all COM events
ltsync.registerevent({'ReflectorsData' handlerFun}) %Register event
ltsync.GetReflectors() %Ask instrument for reflectors
% Wait for request to complete
while ~response.responseComplete
pause(0.1);
drawnow update;
% NOTE: Should add a timeout to this loop
end
% Do stuff with response.reflectors
end
This will wait until there has been a response for each of the reflectors, the number of which is determined from the first response. Note that you should add a timeout to the while loop, otherwise you risk waiting infinitely.
If there are lots of these asynchronous requests that you have to handle, I would suggest encapsulating the entire request/response sequence (including the creation of the ActiveX server, setup of the event handler, and waiting for the response(s)) in a generalised base class, and deriving specific subclasses for each different request.
I have just recently started to use MATLAB to acquire data off of a data acquisition board and was in need of a function to acquire data continuously (i.e. until I ctrl^C out of the function). To do this I am using the data acquisition toolbox on a 32-bit windows OS.
Based on the documentation in matlab help and a few of the answers on this site, I found that after adding channels to my input handle I should:
set my 'SamplesPerTrigger' to Inf
set the 'TimerPeriod' to some value to trigger the 'TimerFcn'
set the 'TimerFcn' to some subfunction callback which appends data to a persistent variable
Is this a correct way to do this?
My code is as follows:
function acquire_arena_test(samprate,daq_device ,device_ID ,channels, saveroot)
setup.SampleRate = samprate;
setup.DAQdevice = {daq_device, device_ID};
setup.AIChannels = channels;
setup.SaveRoot = {saveroot};
ai = analoginput(setup.DAQdevice{1},setup.DAQdevice{2});
addchannel(ai,[setup.AIChannels]);
set(ai,'SamplesPerTrigger',Inf);
set(ai,'TimerPeriod',0.5);
set(ai,'TimerFcn',{#AcquireData,ai});
start(ai);
while(strcmpi(get(ai,'Running'),'On'))
pause(1)
end
stop(ai);
time = datestr(now,30);
save([saveroot time], 'data');
delete(ai);
clear ai;
function AcquireData(hObject, ~)
persistent totalData;
data = getdata(hObject);
if isempty(totalData)
totalData =data;
else
totalData = [totalData; data];
end
The initial analog input is definitely working properly. I have tried many permutations of giving the AcquireData callback to 'TimerFcn'. The error I receive is
`??? Error using ==> acquire_arena_test>AcquireData
Too many input arguments.
Warning: The TimerFcn callback is being disabled.
To enable the callback, set the TimerFcn property. `
Thanks in advance for any help.
I think the syntax you use for setting up your TimerFcn is wrong. You write
set(ai,'TimerFcn',{#AcquireData,ai});
but this means that your function AcquireData will be called with tree parameters: AcquireData(ai, event, ai) as explained here, which then of course triggers the error message since your AcquireData function only accepts two parameters. Just change your code to
set(ai,'TimerFcn',#AcquireData);
and it should work; the ai object is automatically passed as the first parameter (see the link to the MATLAB documentation above).
Sorry about answering my own question, but I figured it out. The trigger was not needed after all. Using a national instruments board (or a sound card, as it turns out) you can just change the LoggingMode to 'disk' and specify a file to save the .daq (data acquisition toolbox) file to save as with LogFileName. If you want to use the memory on your board, change the mode to disk&Memory. Helpful document:
http://www.mathworks.com/help/toolbox/daq/f12-16658.html
The script below acquires data during the pause, which is as long as you want it to be..
daqreset;
clear all;
ai = analoginput('nidaq','Dev1');
chans = addchannel(ai,0:6);
set(ai,'SamplesPerTrigger',Inf);
set(ai,'SampleRate',1000)
set(ai,'LogToDiskMode','Overwrite')
set(ai,'LogFileName','log.daq')
set(ai,'LoggingMode', 'disk')
start(ai)
pause()
stop(ai)
data = daqread('log.daq');
delete(ai);
Note that you still need to set 'SamplesPerTrigger' to Inf for this to work properly. Thank you to Jonas for his help as well.