MATLAB callback function: undefined function? - matlab

I have a button group with 3 radio buttons inside, as well as a display section.
The effect I want is that once choosing a radio button, the display section changes as well.
I implement it using a class, and add the SelectionChangedFcn when creating those components:
app.ControlButtonGroup.SelectionChangedFcn = {#controlBtnGroupSelectionChanged, app}
I define the function controlBtnGroupSelectionChanged(obj, eventData, app) at the same file where the createComponents.m function file is saved.
But when I click a different radio, error messages are as following:
Undefined function 'controlBtnGroupSelectionChanged' for input
arguments of type 'matlab.ui.container.ButtonGroup'. Error while
evaluating ButtonGroup SelectionChangedFcn
I also declare the controlBtnGroupSelectionChanged function as a private method in my class.
The controlBtnGroupSelectionChanged function is as following:
function controlBtnGroupSelectionChanged(obj, eventData, app)
% Update display section as the radio button in control section is changed
%new = app.ControlButtonGroup.NewValue;
new = obj.SelectedObject.String;
switch new
case 'Transfer Path'
% display the transfer path
imagesc(app.curImage,'Parent',app.DisplayPath);
app.DisplayPath.Visible = 'off';
case 'Store'
% display the store text
text(curStore,'Parent',app.DisplayPath);
case 'Mill'
% Display the Mill text
text(curMill,'Parent',app.DisplayPath);
end
end
The part of that defined in class is as :
methods (Access = private)
% Create UIFigure and components
createComponents(app);
% Callback function for Control Button Group Selection Changed
controlBtnGroupSelectionChanged(obj, eventData, app);
end
And The part related to that in createComponents function is:
app.ControlButtonGroup = uibuttongroup(app.UIFigure);
app.ControlButtonGroup.Title = 'Control';
app.ControlButtonGroup.FontSize = 16;
app.ControlButtonGroup.Units = 'Normalized';
app.ControlButtonGroup.Position = [0.45 0.75 0.45 0.2];
app.ControlButtonGroup.SelectionChangedFcn = {#controlBtnGroupSelectionChanged,app};
Why does it say that my function is undefined?
UPDATE:
I have gone through some other demos, and changed the callback as app.ControlButtonGroup.SelectionChangedFcn = #(h, e)controlBtnGroupSelectionChanged(app). And define(also prototype) it just as controlBtnGroupSelectionChanged(app). It works then. so I guess the problem should be about the number of parameters, but I still don't find much detailed information on that. Any hints are appreciated!!

You actually need to supply the object as the first argument to the function so that MATLAB knows to look for it as a method of app.
app.ControlButtonGroup.SelectionChangedFcn = #(s,e)controlBtnGroupSelectionChanged(app, s, e);
% Or this way which implicitly passes the two input parameters
app.ControlButtonGroup.SelectionChangedFcn = #app.controlBtnGroupSelectionChanged;
Then your method would look like this
function controlBtnGroupSelectionChanged(obj, source, event)
If you don't need the source (the handle to the control button group) or event (the eventdata associated with the selection event), then you can have your callback "ignore" these second and third inputs by doing something like
app.ControlButtonGroup.SelectionChangedFcn = #(s,e)app.controlBtnGroupSelectionChanged();
And your method would look like
function controlBtnGroupSelectionChanged(app)

Related

Error: Missing multiplication operator, missing or unbalanced delimiters. To construct matrices, use brackets instead of parentheses

am trying to design an interface using the MATLAB APP DESIGNER. However, I am getting an error message.
This is for creating a user interface in using MATLAB APP DESIGNER
% Callbacks that handle component events
methods (Access = private)
% Code that executes after component creation
function startupFcn(app, varargin)
% Configure image axes
app.ImageAxes2.Visible = 'off';
app.ImageAxes.Colormap = gray(256);
axis(app.ImageAxes, 'image');
app.ImageAxes3.Visible = 'off';
app.ImageAxes.Colormap = gray(256);
axis(app.ImageAxes, 'image');
end
% Callback function
function DropDownValueChanged(app, event)
% Update the image and histograms
updateimage(app, app.DropDown.Value);
end
% Callback function
function LoadButtonPushed(app, event)
% Display uigetfile dialog
filterspec = {'*.jpg;*.tif;*.png;*.gif','All Image Files'};
[f, p] = uigetfile(filterspec);
% Make sure user didn't cancel uigetfile dialog
if (ischar(p))
fname = [p f];
updateimage(app, fname);
end
end
I expected the user interface to be displayed after running the simulation. But I am getting the following error message:
"Missing multiplication operator, missing or unbalanced delimiters. To construct matrices, use brackets instead of parentheses".
The errors point to the line that uses the METHODS function. However, that particular line in MATLAB APP DESIGNER code view is gray in color. As a result, I am unable to edit that particular line. Any suggestion would be appreciated.

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 ...

Growing a struct with GUI

I've been working on a little custom made database in MATLAB.
I have a GUI with a bunch of 'Edit Text' boxes and buttons.
The key is that I should be able to register an undefined number of students with some information like names, surnames, code etc. I've managed to store only one student (i.e the first time i push the 'Submit Button') but when i enter another student's information, MATLAB just overwrites the information from the previous registration.
Here's the Callback for the 'Submit' button
function Submit_Callback(hObject, eventdata, handles)
global n
n=n+1
% hObject handle to Submit (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
name1 = get(handles.name,'String'); %edit1 being Tag of ur edit box
name2=get(handles.name2,'String');
name3=get(handles.name3,'String');
major=get(handles.major,'String');
labavg=num2str(get(handles.labavg,'String'));
finalgrade=num2str(get(handles.finalgrade,'String'));
email=num2str(get(handles.email,'String'));
code=num2str(get(handles.code,'String'));
for ii=1:numel(n)
student_information(ii).name=name1
student_information(ii).surname1=name2
student_information(ii).surname2=name3
student_information(ii).code=code
student_information(ii).major=major
student_information(ii).final_grade=finalgrade
student_information(ii).laboratory_average=labavg
student_information(ii).email=email
end
assignin('base', 'student_information', student_information)
end
I've declared the counter 'n' as a global variable set to '0' in the workspace.
I'm not sure if my code isn't looping properly. Maybe the mistake is in there but I can't see how to fix it.
Can you please help me with my code?
Thanks!
I'm not sure what you were trying to achieve with your loop, but I don't see the need for it. Also, by using assignin, you are overriding the contents of student_information in your workspace. You are better off making student_information global in Submit_Callback in addition to n, then construct a new_student structure using your information and append it to student_information as follows:
name1 = get(handles.name,'String'); %edit1 being Tag of ur edit box
name2=get(handles.name2,'String');
name3=get(handles.name3,'String');
major=get(handles.major,'String');
labavg=num2str(get(handles.labavg,'String'));
finalgrade=num2str(get(handles.finalgrade,'String'));
email=num2str(get(handles.email,'String'));
code=num2str(get(handles.code,'String'));
new_student.name = name1;
new_student.surname1 = name2;
new_student.surname2 = name3;
new_student.major = major;
new_student.laboratory_average = labavg;
new_student.final_grade = finalgrade;
new_student.email = email;
new_student.code = code;
student_information(n) = new_student;
n = n + 1;
and that should append the new entry at the end of the struct array.

Within Matlab GUI, I want to transfer the selected data in listbox1 to listbox2

This case is, I know how to transfer all the data from one listbox to another one, and then clear listbox1. I did in this way:
function pushbutton1_Callback(hObject, eventdata, handles)
StringInLB1 = get(handles.listbox1,'string');
set(handles.listbox2,'string',StringInLB1);
set(handles.listbox1,'string','');
Now my question is: How can I transfer some selected data to listbox2 ? I "ctrl+ single click" multiple data in listbox1, but how can I use thoes data?
Thanks a lot.
It looks like you are looking to assign some function to the listbox callback. i.e. each time the selection is changed by the user, you want to do something with the data. Anyhow that's what I understood from your comment to #James answer.
If it's the case, here is a sample code generating a simple GUI in which the color of a plot is changed by the user directly by clicking in the listbox:
function DummyListBox
global hFig hListBox hPlot
ScreenSize = get(0,'ScreenSize');
hFig = figure('Visible','off','Position',[ScreenSize(3)/2,ScreenSize(4)/2,450,285]);
ColorString = {'Red';'Green';'Blue'}; % Define string populating the listbox
hListBox = uicontrol('Style','Listbox','String',ColorString,'Position',[315,150,70,50],'max',3,...
'min',1,'Callback',#ListBox_Callback); %%// added 'min' and 'max' properties to select multiple items
hText = uicontrol('Style','Text','Position',[315,220,70,35],'String','Empty now'); %%// Add text box
hAxes = axes('Units','Pixels','Position',[50,60,200,185]);
set(hFig,'Visible','on')
x = 1:5*pi;
hPlot = plot(x,sin(x),'-r','Parent',hAxes); %display some data
% Listbox callback: each time the selection changes, the color of the
% plot changes accordingly.
function ListBox_Callback(~,~)
SelectedValues = get(hListBox,'Value'); % Get the values selected
set(hText,'String',SelectedValues); % Uptdate the string in the textbox
NewColor = ColorString{get(hListBox,'Value')};
set(hPlot,'Color',NewColor)
end
end
The output now looks like this:
As you see the function in which the change in color takes place is the listbox's callback. Hope this is what you want. If not please be more specific as to what you would like. Thanks!
EDIT: As you can see, I added a text box to show you what it looks like when you select multilpe items in the listbox. In order to do so, you must add a 'min' and a 'max' property when you create the listbox. I set them to 1 and 3, respectively. The 'Value' property of the listbox corresponds to the actual number of the item selected from the list. So if you have 3 items as is my exmample,the Value can be 1,2,3 or any combination. They are displayed in the text box. You can get the corresponding data from those values, stored in vectors.
So to answer your question, you could write:
Data1 = ValuesVector(1)
Data2 = VectorValues(2)
... and so on
If you use a
tmp = get(handles.listbox1,'string');
You'll see what the variable is like. Then you can use something like
set(handles.listbox1,'string',tmp{2});

How to create an 'closure function' in Matlab as in python and js?

Background
In Python and JS we have closures, which returns a function with some of the variables pre-defined. e.g.
def make_printer(msg):
def printer():
print msg
return printer
Do we have similar stuff in Matlab?
Here I have a callback function
function show(object, eventdata)
that is to be set to the callback of my GUI
func = #show;
set(gcf, 'WindowButtonMotionFcn', func);
However, I want to add some additional parameters to this show function.
Currently I'm using global variables to do that. But I think it would be elegant if we have a 'closures function'.
About anonymous function
Yes we do have anonymous function in Matlab. However it seems to me that it is too simple to support 60-line procedures.
There was once a document floating about the internet by Sergey Simakov. It was very terse , not especially descriptive but covered the bases. This was in my experience the most authoritive text on matlab GUI's. I suspect it still is ...
You're solving two/three problems :
Closure Problem
Nested functions resolve this problem.
function iterator = count(initial)
% Initialize
if ~exist('initial','var')
counter = 0
else
counter = initial
end
function varargout = next() % [1]
% Increment
counter = counter + 1
varargout = {counter} % [1]
end
iterator = #next
end
Note(s) :
One may not simply return counter ! It must be wrapped in varargout or assigned to some other output variable.
Usage
counter = count(4) % Instantiate
number = counter() % Assignment
number =
5
Closed Scope + State problem
No ugly braces, worrying about scope, cell arrays of strings. If you need access to something it's under SELF no more FINDOBJ, USERDATA, SET/GETAPPDATA, GLOBAL nonsense.
classdef Figure < handle
properties
parent#double
button#double
label#double
counter#function_handle
end
methods
function self = Figure(initial)
self.counter = count(initial) % [1]
self.parent = figure('Position',[200,200,300,100])
self.button = uicontrol('String','Push', 'Callback', #self.up, 'Style', 'pushbutton', 'Units', 'normalized', 'Position', [0.05, 0.05, 0.9, 0.4])
self.label = uicontrol('String', self.counter(), 'Style', 'text', 'Units', 'normalized', 'Position', [0.05, 0.55, 0.9, 0.4])
end
function up(self,comp,data)
set(self.label,'String',self.counter())
end
end
end
Note(s):
One uses the function listed in the Closure problem above
Usage :
f = Figure(4) % Instantiate
number = get(f.label,'String') % Assign
number =
5
You may prefer :
f.label.get('String') % Fails (f.label is double not handle, go figure)
h = handle(f.label) % Convert Double to handle
number = h.get('String') % Works
number =
5
Since it looks like my comment was helpful, I'll explain what I mean about the struct thing. I think the way you are calling the callback functions is slightly different, so this may not work so well).
If you have a bunch of different callback functions, and a bunch of variables you want to pass to them, it is annoying and difficult to have a big list of input arguments for each function. Instead, I do something like this:
First create a bunch of UI components , but don't specify their callback functions, e.g
radio1_handle=uicontrol(panel1_handle,'Style','radiobutton',...
'Min',0,'Max',1,'Value',0,'Units','normalized','Position',[.8 .8 .2 .25]);
once you have made all the components, create a struct of variables you will be using
vars=struct('varName1',var1,'varName2',var2);
then update the UI components to include the callback functions
set(radio1_handle,'Callback',{#radio1_callback,vars});
now create the actual functions
function radio1_callback(object,eventData,vars)
So it's nothing fancy, just a potentially neater way than using multiple arguments.
Inspired by this question and #David's comment, I came up with the solution. (Maybe this is #David's answer, but not explicitly explained. So let me extend his comment into an answer.)
There is actually a way to add extra parameters to callbacks.
Just add the parameters to the end of the parameter list
function show(object, eventdata, extra)
and set the callback like this:
func = #show;
set(gcf, 'WindowButtonMotionFcn', func);
Reference
Mathworks
Passing Additional Input Arguments
You can define the callback function to accept additional input
arguments by adding them to the function definition:
function myCallback(src,eventdata,arg1,arg2)
When using additional
arguments for the callback function, you must set the value of the
property to a cell array (i.e., enclose the function handle and
arguments in curly braces):
figure('WindowButtonDownFcn',{#myCallback,arg1,arg2})