Why doesn't assignin work in this context?

I have a matlab function which creates a variable named 'duration'.
function doStuff()
duration = 5;
Inside the function I create a uicontrol edit box to allow changing the duration value.
uicontrol(panel, 'Style', 'edit', 'Callback', {@setEditBoxNumber, 'duration', 0, 15});
The callback is a local function.
function setEditBoxNumber(obj, ~, ivar, min, max)
value = str2double(get(obj, 'String'));
if isnan(value)
if ~isempty(ivar)
value = evalin('base', ivar);
end
else
value = clip(value, min, max);
end
set(obj, 'String', num2str(value));
if ~isempty(ivar)
assignin('base', ivar, value);
end
value = evalin('base', 'duration');
value = duration;
end
The last two lines are used for debugging purposes. If duration is set to 4 and I type '2' into the text box the string is properly converted to the number 2. The value returned by the bottom evalin call is 2, but the value of duration is still 4.
This is very confusing. If I type letters in the text box the str2double call returns NaN and the upper evalin call properly retrieves the value for duration. I tried using the caller workspace but there was no change in behavior.

1 Comment

I mistakenly said that setEditBoxNumber is a local function. It is a nested function, not a local function.

Sign in to comment.

Answers (1)

When you use
value = evalin('base', 'duration');
value = duration;
the first line does not change duration in the current workspace, only in the base workspace. The line after that uses the duration of the current workspace, where it has not been changed. Remove the semicolon from the end of the
value = evalin('base', 'duration');
line to see the value as fetched from the base workspace.

5 Comments

Tom
Tom on 13 Aug 2015
Edited: Tom on 13 Aug 2015
The evalin call at the bottom of the callback function is for debugging purposes only. The call shows the assignin set the value for duration in some context, just not the context I want. When I look at the value of duration inside the context of the function doStuff I see that the value is unchanged. I've tried different combinations of 'base' and 'caller' and none do what I want, change the value of duration as seen within the function doStuff.
You have done the assignin('base') and that affects the variable named by ivar in the base workspace. To check it you can pull the value back,
value = evalin('base', ivar)
What is the relationship between doStuff and setEditBoxNumber ? If doStuff is not calling setEditBoxNumber directly and setEditBoxNumber is not a nested function of doStuff then setEditBoxNumber cannot force doStuff's variables to change.
Consider having a handles.ivars structure and having setEditBoxNumber change the value in handles.ivars.(ivar) and then guidata(hObject, handles) to push the value upward. Then at at any point that doStuff suspects that the value might be changed and wants to fetch the new value, doStuff should
handles = guidata(TheFigureNumber);
and pull the values out of handles.ivars.duration
setEditBoxNumber is a nested function inside of doStuff. It is not called directly. It is a callback function called by a uicontrol that is created by function doStuff. It appears that the callback workspace is not the same workspace used when the uicontrol is created.
I am thinking of using a structure which is passed as an argument to the callback. Something like the following:
function setEditBoxNumber(obj, ~, aStruct, fieldName, min, max)
Callbacks are evaluated as if called from the base workspace. If the callback is defined as a nested routine then it can read and write variables in the nesting routine that are assigned values before the nested routine is defined.
function outer
a = 123;
function inner(src, event)
a = 456; %this changes outer's "a"
b = 789; %this changes a local variable "b"
end
b = -11111; %although this belongs to outer, it was not defined before inner() is defined so inner() does not have access to it
set(gcf, 'WindowButtonFcn', @inner)
end
Then the callback of inner() will change outer()'s value of "a", but outer() has already returned so outer() does not get to see the change. If you were to re-execute outer() then because it has the assignment to "a" it would change "a" back to 123. For this reason, functions that nest other functions often end up being "shells" that exist mostly to set something up, with all the real work being done by nested routines whose function handles get stored somewhere.

Sign in to comment.

Categories

Products

Asked:

Tom
on 12 Aug 2015

Edited:

on 16 Aug 2015

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!