Why is the uieditfield ValueChangingFcn function called twice?
7 Ansichten (letzte 30 Tage)
Ältere Kommentare anzeigen
Anais G
am 23 Apr. 2021
Bearbeitet: Voss
am 27 Sep. 2023
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
0 Kommentare
Akzeptierte Antwort
Mario Malic
am 24 Apr. 2021
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 Kommentare
Voss
am 27 Sep. 2023
Bearbeitet: Voss
am 27 Sep. 2023
@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)!
Weitere Antworten (0)
Siehe auch
Kategorien
Mehr zu Develop uifigure-Based Apps finden Sie in Help Center und File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!