AppDesinger Expand UI Tree Node not always working
26 views (last 30 days)
Show older comments
Using AppDesinger we are creating a tree node with checks. In our startup function of the AppDesigner we want the node to be expanded. However, the framework due to loading issues sometimes expands the tree node, sometimes it does not. This is frustrating and customer pain.
Example: (left bad behaviour, right correct behaviour)
Initalized, but Run All node did not expand Initialized and fully expaneded, as desired


As I understood the GUI initialization is somehow asynchronous, hence we added a timer in the end of the startup function. Yet, the problem persists SOMETIMES.
End of the startupFcn in AppDesinger
% Refresh the display of tree nodes to reflect initial counts and status icons.
reload_nodes(app, app.Tree)
drawnow;
% ------------------------------------------------------------
% ASYNC: Expand tree AFTER UI created all nodes
% ------------------------------------------------------------
% Expand the main 'Run All' node to show all sub-checks by default.
app.ExpansionTimer = timer( ...
'StartDelay', 1.0, ... % short delay so UI event queue finishes
'ExecutionMode', 'singleShot', ... % only run once
'BusyMode', 'queue', ... % ensures execution is queued
'TimerFcn', @(~,~) expand_tree_async(app) ...
);
start(app.ExpansionTimer);
The expand_tree_async Function that is called.
function expand_tree_async(app)
% expand_tree_async Expand the main tree node after UI rendering completes.
%
% This function is called asynchronously from a timer after the tree nodes
% have been created and rendered. Some UI components in App Designer are
% constructed asynchronously, so expanding tree nodes immediately after
% reload does not always work on the first try. By running this function
% on a short delay, the function ensures that all tree nodes exist before
% calling EXPAND on the main 'Run All' node.
%
% The function also performs basic validity checks and cleans up the timer
% handle used to invoke the expansion.
try
% Make sure the node exists
if isvalid(app.RunAllNode)
expand(app.RunAllNode, 'all'); % Expand all categories
drawnow; % Force UI update
end
catch
% ignore errors (e.g. app closed)
end
% Clean up the timer so MATLAB doesn't leak handles
try
if ~isempty(app.ExpansionTimer) && isvalid(app.ExpansionTimer)
stop(app.ExpansionTimer);
delete(app.ExpansionTimer);
end
catch
end
app.ExpansionTimer = [];
% Clean up the Progress Dialog
try
if isvalid(app.StartUpProgressBar)
drawnow;
delete(app.StartUpProgressBar);
end
catch
end
app.StartUpProgressBar = [];
end
Is there something I am missing, or how can consistent loading and expanding of the UI Interface be safely done? Thank you.
1 Comment
dpb
about 3 hours ago
Edited: dpb
31 minutes ago
try
% Make sure the node exists
if isvalid(app.RunAllNode)
expand(app.RunAllNode, 'all'); % Expand all categories
drawnow; % Force UI update
end
catch
% ignore errors (e.g. app closed)
end
Your try-catch block doesn't even tell you if isvalid() fails -- if, for some reason that is not True on these odd occurrences, then you would see the symptoms you do.
Why that might be so, I don't know, but at least while you're trying to track this down have some way of knowing that the expand() call didn't occur. As a patch, I'd add a MsgBox warning popup and then probably a waitfor or just a pause and try again.
Similarily, if the catch block executes for some reason and you ignore everything, then a valid error that prevented the expansion is being ignored and you don't know about it -- again, until you can discover what is actually happening here you need to be able to know what occurs on every possible path in order to be able to take whatever action(s) is(are) needed.
Answers (1)
dpb
about 22 hours ago
Edited: dpb
about 3 hours ago
app.ExpansionTimer = timer( ...
'StartDelay', 1.0, ... % short delay so UI event queue finishes
'ExecutionMode', 'singleShot', ... % only run once
'BusyMode', 'queue', ... % ensures execution is queued
'TimerFcn', @(~,~) expand_tree_async(app) ...
);
is your assignment of the callback function, but expand_tree_async expects the app object argument.
The timer event passes the timer object and which event id; not the app and having app in the argument list doesn't refer to the global app struct; you need to pass it as an additional argument so the callback function has access to it.
Use
function expand_tree_async(~,~,app)
as the callback function prototype and then
app.ExpansionTimer = timer( ...
'StartDelay', 1.0, ... % short delay so UI event queue finishes
'ExecutionMode', 'singleShot', ... % only run once
'BusyMode', 'queue', ... % ensures execution is queued
'TimerFcn', {@expand_tree_async,app} ...
);
instead so the app object is actually passed.
See Also
Categories
Find more on Programming Utilities in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!