Skip to content

Commit 697f6c9

Browse files
committed
Better testing for "Did figure finish loading?"
- Added the private methods `waitTillFigureLoaded` and `waitTillWebwindowLoaded`. - Added the public method `waitForFigureReady(hUIFig)` - this blocking method returns only after determining that the UIFigure was fully loaded, or if 2*timeout was reached. - Refactored a couple of functions (getWebWindow, getWidgetID) to make their inner workings much clearer. - TODO: add the new public method to Readme.md. - TODO: test on older MATLAB versions.
1 parent 289d608 commit 697f6c9

File tree

1 file changed

+81
-55
lines changed

1 file changed

+81
-55
lines changed

mlapptools.m

Lines changed: 81 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
% setStyle - Modify a specified style property.
1717
% setTimeout - Override the default timeout for dojo commands, for a specific uifigure.
1818
% textAlign - Modify text alignment.
19+
% waitForFigureReady - A blocking method that only returns after the uifigure is fully loaded.
1920
%
2021
% See README.md for detailed documentation and examples.
2122

@@ -107,41 +108,20 @@ function fontWeight(uiElement, weight)
107108

108109
function [win] = getWebWindow(hUIFig)
109110
warnState = mlapptools.toggleWarnings('off');
110-
% Make sure we got a valid handle
111-
assert(mlapptools.isUIFigure(hUIFig),...
112-
'mlapptools:getWebWindow:NotUIFigure',...
113-
'The provided window handle is not of a UIFigure.');
114111

115-
to = mlapptools.getTimeout(hUIFig);
116-
tic
117-
while true && (toc < to)
118-
try
119-
hController = struct(struct(hUIFig).Controller);
120-
% Check for Controller version:
121-
switch subsref(ver('matlab'), substruct('.','Version'))
122-
case {'9.0','9.1'} % R2016a or R2016b
123-
win = hController.Container.CEF;
124-
otherwise % R2017a onward
125-
win = struct(hController.PlatformHost).CEF;
126-
end
127-
break
128-
catch err
129-
if strcmp(err.identifier, 'MATLAB:nonExistentField')
130-
pause(0.01)
131-
else
132-
warning(warnState); % Restore warning state
133-
rethrow(err)
134-
end
135-
end
136-
end
137-
warning(warnState); % Restore warning state
112+
mlapptools.waitTillFigureLoaded(hUIFig);
113+
% Since the above checks if a Controller exists, the below should work.
138114

139-
if toc >= to
140-
msgID = 'mlapptools:getWidgetID:QueryTimeout';
141-
error(msgID, ...
142-
'WidgetID query timed out after %u seconds, UI needs more time to load', ...
143-
to);
115+
hController = struct(struct(hUIFig).Controller);
116+
% Check for Controller version:
117+
switch subsref(ver('matlab'), substruct('.','Version'))
118+
case {'9.0','9.1'} % R2016a or R2016b
119+
win = hController.Container.CEF;
120+
otherwise % R2017a onward
121+
win = struct(hController.PlatformHost).CEF;
144122
end
123+
124+
warning(warnState); % Restore warning state
145125

146126
end % getWebWindow
147127

@@ -267,6 +247,23 @@ function textAlign(uiElement, alignment)
267247
win.executeJS(alignSetStr);
268248
end % textAlign
269249

250+
function win = waitForFigureReady(hUIFig)
251+
% This blocking method waits until a UIFigure and its widgets have fully loaded.
252+
%% Make sure that the handle is valid:
253+
assert(mlapptools.isUIFigure(hUIFig),...
254+
'mlapptools:getWebWindow:NotUIFigure',...
255+
'The provided window handle is not of a UIFigure.');
256+
assert(strcmp(hUIFig.Visible,'on'),...
257+
'mlapptools:getWebWindow:FigureNotVisible',...
258+
'Invisible figures are not supported.');
259+
%% Wait for the figure to appear:
260+
mlapptools.waitTillFigureLoaded(hUIFig);
261+
%% Make sure that Dojo is ready:
262+
% Get a handle to the webwindow
263+
win = mlapptools.getWebWindow(hUIFig);
264+
mlapptools.waitTillWebwindowLoaded(win, hUIFig);
265+
end % waitForFigureReady
266+
270267
end % Public Static Methods
271268

272269
methods (Static = true, Access = private)
@@ -350,29 +347,14 @@ function textAlign(uiElement, alignment)
350347

351348
function [widgetID] = getWidgetID(win, data_tag)
352349
widgetquerystr = sprintf('dojo.getAttr(dojo.query("[data-tag^=''%s''] > div")[0], "widgetid")', data_tag);
353-
354-
to = mlapptools.getTimeout(mlapptools.figFromWebwindow(win));
355-
tic
356-
while true && (toc < to)
357-
try
358-
widgetID = win.executeJS(widgetquerystr);
359-
widgetID = widgetID(2:end-1);
360-
break
361-
catch err
362-
if ~isempty(strfind(err.message, 'JavaScript error: Uncaught ReferenceError: dojo is not defined')) || ...
363-
~isempty(strfind(err.message, 'Cannot read property ''widgetid'' of null'))
364-
pause(0.01)
365-
else
366-
rethrow(err)
367-
end
368-
end
369-
end
370-
371-
if toc >= to
372-
msgID = 'mlapptools:getWidgetID:QueryTimeout';
373-
error(msgID, ...
374-
'widgetID query timed out after %u seconds, UI needs more time to load', ...
375-
to);
350+
hFig = mlapptools.figFromWebwindow(win);
351+
mlapptools.waitTillFigureLoaded(win, hFig);
352+
try % should work for most UI objects
353+
widgetID = win.executeJS(widgetquerystr);
354+
widgetID = widgetID(2:end-1);
355+
catch % fallback for problematic objects
356+
warning('Problematic control encountered with no fallback implemented yet. Please ')
357+
% TODO
376358
end
377359
end % getWidgetID
378360

@@ -460,7 +442,51 @@ function validateAlignmentStr(alignment)
460442
error(msgID, 'Invalid font weight specified: ''%s''', weight);
461443
end
462444
end % validateFontWeight
445+
446+
function waitTillFigureLoaded(hFig)
447+
% A blocking method that ensures a UIFigure has fully loaded.
448+
warnState = mlapptools.toggleWarnings('off');
449+
to = mlapptools.getTimeout(hFig);
450+
tic
451+
while (toc < to) && isempty(struct(hFig).Controller)
452+
pause(0.01)
453+
end
454+
if toc > to
455+
msgID = 'mlapptools:waitTillFigureLoaded:TimeoutReached';
456+
error(msgID, ...
457+
['Waiting for the figure to load has timed out after %u seconds. ' ...
458+
'Try increasing the timeout. If the figure clearly loaded in time, yet '...
459+
'this error remains - it might be a bug in the tool! ' ...
460+
'Please let the developers know through GitHub.'], ...
461+
to);
462+
end
463+
warning(warnState);
464+
end % waitTillFigureLoaded
463465

466+
function waitTillWebwindowLoaded(hWebwindow, hFig)
467+
% A blocking method that ensures a certain webwindow has fully loaded.
468+
if nargin < 2
469+
hFig = mlapptools.figFromWebwindow(hWebwindow);
470+
end
471+
472+
to = mlapptools.getTimeout(hFig);
473+
tic
474+
while (toc < to) && ~jsondecode(hWebwindow.executeJS(...
475+
'this.hasOwnProperty("require") && require !== undefined && typeof(require) === "function"'))
476+
pause(0.01)
477+
end
478+
if toc > to
479+
msgID = 'mlapptools:waitTillWebwindowLoaded:TimeoutReached';
480+
error(msgID, ...
481+
['Waiting for the webwindow to load has timed out after %u seconds. ' ...
482+
'Try increasing the timeout. If the figure clearly loaded in time, yet '...
483+
'this error remains - it might be a bug in the tool! ' ...
484+
'Please let the developers know through GitHub.'], ...
485+
to);
486+
else
487+
hWebwindow.executeJS('require(["dojo/ready"], function(ready){});');
488+
end
489+
end % waitTillWebwindowLoaded
464490

465491
end % Private Static Methods
466492

0 commit comments

Comments
 (0)