Why is the uieditfield ValueChangingFcn function called twice?

To get to know app designer I attempted to create an autocomplete thingy when using a uieditfield. The code is provided below as clear as I could. I cannot discover what's wrong with the code, but it's not working as expected and the callback function is called twice. When I put breakpoints in the callback function, it works as I expect it to (even though it's called twice), but without using breakpoints it doesn't. If I type anything in the uieditfield, then click somewhere else (shift focus), then continue typing in the uieditfield, that's when it gives the autocomplete for the previous value. I'm at a loss, does anyone understand?
clear, clc, close all
% Create figure
fig = uifigure;
% List of names
names = {'Bob';
'Eric';
'John';
'Phil';
'James';
'Anne';
'Suzanne';
'Karen';
'Eleanor';
'Janet';};
% Create table of names
namesTablePos = [28 28 504 203];
namesTable = uitable(fig, ...
'Position', namesTablePos, ...
'ColumnName', 'Names', ...
'Data', names);
% Create editfield
fieldPos = [224, 315, 150, 25];
field = uieditfield(fig, 'text', ...
'ValueChangingFcn', @(h,e) callbackAutoComplete(h,e), ...
'Position', fieldPos);
field.UserData = names;
% Create tree
tree = uitree(fig, ...
'SelectionChangedFcn', @(h,e) callbackSelectionChanged(h,e), ...
'Visible', 'off');
function callbackAutoComplete(src, ~)
% Get figure handle
tree = src.Parent.Children(1);
% Reset tree
tree.Visible = 'Off';
delete(tree.Children)
% Get field value
val = src.Value;
% If value is not empty, populate tree
if ~isempty(val)
% Get names
names = src.UserData;
% Filter names
indices = [];
for ix = 1:numel(names)
name = names{ix};
if contains(lower(name), lower(val))
indices = [indices ix];
end
end
% Set maximum visible suggestions
n = min(numel(indices), 3);
% If any, create nodes
if n > 0
% Set tree properties
fieldPos = src.Position;
treePos = fieldPos + fieldPos(4).*[0 -n 0 (n-1)];
tree.set(...
'Position', treePos, ...
'Visible', 'on');
% Add nodes
names = sort(names(indices));
for ix = 1 : numel(names)
uitreenode(tree, 'Text', names{ix});
end
end
end
end
function callbackSelectionChanged(src, ~)
% Get node value
val = src.SelectedNodes.Text;
% Set value to field
src.Parent.Children(2).Value = val;
end

 採用された回答

Mario Malic
Mario Malic 2021 年 4 月 24 日

1 投票

Hey Anais,
That's some nice usage of tree component, thanks for that!
Here are two lines that you need to change in your code. If you inspect the src.Value, it does not populate the value unless the focus is shifted away from the component, so it's better to use event.Value since it uses the values you expected.
function callbackAutoComplete(src, event)
val = event.Value;
Here's your code posted with these two lines so the answer is clear for someone else who may encounter this.
clear, clc, close all
% Create figure
fig = uifigure;
% List of names
names = {'Bob';
'Eric';
'John';
'Phil';
'James';
'Anne';
'Suzanne';
'Karen';
'Eleanor';
'Janet';};
% Create table of names
namesTablePos = [28 28 504 203];
namesTable = uitable(fig, ...
'Position', namesTablePos, ...
'ColumnName', 'Names', ...
'Data', names);
% Create editfield
fieldPos = [224, 315, 150, 25];
field = uieditfield(fig, 'text', ...
'ValueChangingFcn', @(h,e) callbackAutoComplete(h,e), ...
'Position', fieldPos);
field.UserData = names;
% Create tree
tree = uitree(fig, ...
'SelectionChangedFcn', @(h,e) callbackSelectionChanged(h,e), ...
'Visible', 'off');
function callbackAutoComplete(src, event)
% Get figure handle
tree = src.Parent.Children(1);
% Reset tree
tree.Visible = 'Off';
delete(tree.Children)
% Get field value
val = event.Value;
% If value is not empty, populate tree
if ~isempty(val)
% Get names
names = src.UserData;
% Filter names
indices = [];
for ix = 1:numel(names)
name = names{ix};
if contains(lower(name), lower(val))
indices = [indices ix];
end
end
% Set maximum visible suggestions
n = min(numel(indices), 3);
% If any, create nodes
if n > 0
% Set tree properties
fieldPos = src.Position;
treePos = fieldPos + fieldPos(4).*[0 -n 0 (n-1)];
tree.set(...
'Position', treePos, ...
'Visible', 'on');
% Add nodes
names = sort(names(indices));
for ix = 1 : numel(names)
uitreenode(tree, 'Text', names{ix});
end
end
end
end
function callbackSelectionChanged(src, ~)
% Get node value
val = src.SelectedNodes.Text;
% Set value to field
src.Parent.Children(2).Value = val;
end

6 件のコメント

Anais G
Anais G 2021 年 4 月 24 日
編集済み: Anais G 2021 年 4 月 24 日
Hi Mario, thanks for the answer, that's it! I didn't realize src.Value works like that because of course when I inspected the code the focus got shifted. I think I should read into the differences between src and event input.
Have a nice day!
EDIT: by any chance, do you know why the callback ran twice?
Mario Malic
Mario Malic 2021 年 4 月 24 日
編集済み: Mario Malic 2021 年 4 月 24 日
I think it's due to the callback being defined as an anonymous function but I am not sure. If you create an anonymous function and put a breakpoint on it, it'll ask you where do you want to set it IIRC.
I think there's one workspace where the arguments are sent in the anonymous function, and the second is the actual workspace of the function, but you should verify this yourself, as I haven't dug deeply into it.
Have a great day as well!
Subhajit
Subhajit 2023 年 9 月 27 日
Hi,
I donot think the problem is solved, I tried running the above code after suggestion from Mario.
If I select the any one option after 1st mouse click, it gives following error:
However, if I click somewhere else or hit enter, then select any one options, it correctly takes the value.
Do you know how to fix this?
Voss
Voss 2023 年 9 月 27 日
@Subhajit: Modify the function callbackSelectionChanged as follows:
function callbackSelectionChanged(src, event)
% return if not exactly one node selected
if ~isscalar(event.SelectedNodes)
return
end
% Get node value
val = event.SelectedNodes.Text;
% Set value to field
src.Parent.Children(2).Value = val;
end
Subhajit
Subhajit 2023 年 9 月 27 日
Hi @Voss,
That is a nice hack to get rid of error. However, i still have to click twice to get the correct answer. Thank you for the solution. Have a nice day.
Voss
Voss 2023 年 9 月 27 日
編集済み: Voss 2023 年 9 月 27 日
@Subhajit You're welcome!
I don't know why clicking twice is necessary; I noticed that too. If I were attempting to create this UI, I would probably try a uilistbox or uidropdown in place of the uitree (but maybe there's some reason a uitree is preferable - I don't know, I didn't try the uilistbox or uidropdown).
Anyway, you didn't ask about the clicking twice; you only asked about the error, so that's what I fixed.
Have a nice hack (and day)!

サインインしてコメントする。

その他の回答 (0 件)

カテゴリ

ヘルプ センター および File ExchangeData Type Identification についてさらに検索

製品

リリース

R2021a

質問済み:

2021 年 4 月 23 日

編集済み:

2023 年 9 月 27 日

Community Treasure Hunt

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

Start Hunting!

Translated by