Skip to content

Commit f8efea0

Browse files
committed
Update dialogues.m docstrings & comments.
1 parent d6b0d3c commit f8efea0

File tree

1 file changed

+236
-11
lines changed

1 file changed

+236
-11
lines changed

+Program/+GUI/dialogues.m

Lines changed: 236 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,82 @@
11
classdef dialogues
2+
%DIALOGUES This class is in charge of coordinating progress dialogues
3+
% within NeuroPAL_ID and across wrapped python scripts.
4+
%
5+
% Glossary:
6+
% - Task: This describes an active operation as represented in the
7+
% progress dialogue. Use this for any task that features several
8+
% steps. The dialogues class supports multiple nested tasks. If
9+
% you add a task while there is no active progress dialogue, one
10+
% will be created.
11+
% - Step: This describes a step within a tasks and are best used
12+
% within iterative loops. Adding any task or step will clear the
13+
% last step, and if you add a step while no progress dialogue is
14+
% active, we do not create one.
15+
% - Level filler: The progress dialogue system implemented through
16+
% the dialogues class supports nesting, meaning that child tasks
17+
% and steps are displayed as nested underneath their parent
18+
% tasks. Throughout this class, we use "level filler" to describe
19+
% the string that represents this nesting visually in the
20+
% progress dialogue. Basically, if, while performing Task A, you
21+
% start Task B, the progress dialogue would display as such:
22+
%
23+
% Task A
24+
% └🢒 Task B
25+
%
26+
% If you then add Another Task C underneath Task B, and then add
27+
% a step D, it will display as such:
28+
%
29+
% Task A
30+
% ├🢒 Task B
31+
% ├─🢒 Task C
32+
% └──🢒 { Step D }
33+
%
34+
% Here, Task B has no level filler, while Task C's level filler
35+
% is ─ and Step D's level filler is ──.
236

3-
properties (Constant)
4-
identifiers = struct( ...
5-
'task', {"🢒 "}, ...
6-
'step', {"{ "});
7-
37+
properties (Constant)
38+
% Text patterns used for sprintf() calls. The first %s describes
39+
% the nesting level of the given task/step, and the second %s
40+
% describes the task/step itself.
841
patterns = struct( ...
942
'task', {"└%s🢒 %s"}, ...
1043
'step', {"└%s{ %s }"});
44+
45+
identifiers = struct( ...
46+
'task', {"🢒 "}, ...
47+
'step', {"{ "});
1148
end
1249

1350
methods (Static, Access = public)
1451
function obj = active(input)
52+
%ACTIVE This function returns of the active dialogue if one
53+
% exists.
54+
%
55+
% Inputs:
56+
% - input: A handle referencing a uiprogressdlg instance.
57+
%
58+
% Outputs:
59+
% - obj: An active uiprogressdlg object. Empty if no progress
60+
% dialogue is active.
61+
62+
% Initialize a persistent variable.
1563
persistent handle
1664

65+
% If a dialogue handle was passed...
1766
if nargin > 0
67+
% Assign it to the persistent variable.
1868
handle = input;
1969
end
2070

71+
% If the persistent variable does not reference a uiprogressdlg
72+
% object or is not a valid graphical object, clear the
73+
% persistent variable. This is to ensure that references to
74+
% deleted graphics objects are detected and cleared.
2175
if (isa(handle, "handle") && any(~isvalid(handle)))
2276
handle = [];
2377
end
2478

79+
% Return the persistent variable.
2580
obj = handle;
2681
end
2782

@@ -36,10 +91,34 @@
3691
end
3792

3893
function handle = add_task(label)
94+
%ADD_TASK This function adds a new task to the progress
95+
% dialogue.
96+
%
97+
% Inputs:
98+
% - label: String/char representing text describing the task.
99+
% This is what will appear in the progress dialogue.
100+
%
101+
% Outputs:
102+
% - handle: A handle referencing the active progress
103+
% dialogue.
104+
105+
% Pass the label and task argument to update_message().
39106
handle = Program.GUI.dialogues.update_message(label, 'task');
40107
end
41108

42109
function handle = step(label)
110+
%STEP This function adds a new step to the progress
111+
% dialogue.
112+
%
113+
% Inputs:
114+
% - label: String/char representing text describing the step.
115+
% This is what will appear in the progress dialogue.
116+
%
117+
% Outputs:
118+
% - handle: A handle referencing the active progress
119+
% dialogue.
120+
121+
% Pass the label and step argument to update_message().
43122
handle = Program.GUI.dialogues.update_message(label, 'step');
44123
end
45124

@@ -78,68 +157,146 @@ function switch_to(mode, options)
78157
end
79158

80159
function handle = update_message(addl, template)
160+
%UPDATE_MESSAGE This function updates the active progress
161+
% dialogue with a passed task or step. If no progress
162+
% dialogue is active, this function calls the create()
163+
% function instead.
164+
%
165+
% Inputs:
166+
% - addl: String/char to be added to the progress dialogue.
167+
% - template: String/char specifying whether this is a task
168+
% or a step.
169+
%
170+
% Outputs:
171+
% - handle: Reference to the active progress dialogue.
172+
173+
% Get the active progress dialogue.
81174
handle = Program.GUI.dialogues.active();
82175

176+
% Check whether an active progress dialogue was returned...
83177
if ~isempty(handle)
178+
179+
% If so, split its current text into individual lines.
84180
arr_task = splitlines(handle.Message);
181+
182+
% Calculate the current number of lines.
85183
n_tasks = length(arr_task);
86184

185+
% If there is more than one line, update previous lines.
87186
if n_tasks > 1
187+
% Get the last line in the progress dialogue message.
88188
lline = arr_task{end};
89189

90190
if Program.GUI.dialogues.is_task(lline)
191+
% If the last line was a task, update its string
192+
% identifier to indicate that a nested operation is
193+
% in progress.
194+
195+
% Split the last line at its identifier.
91196
to_preserve = split(lline, "🢒");
197+
198+
% Calculate its level filler (i.e. one hyphen per
199+
% nested task).
92200
level_filler = repmat('', 1, n_tasks-2);
201+
202+
% Construct the updated string.
93203
arr_task{end} = sprintf("├%s🢒%s", ...
94204
level_filler, to_preserve{2});
95205

96206
elseif Program.GUI.dialogues.is_step(lline)
207+
% If the last line was a step, delete it.
97208
arr_task(end) = [];
209+
210+
% Update the number of lines in the progress
211+
% dialogue message accordingly.
98212
n_tasks = n_tasks - 1;
99213
end
100214
end
101215

216+
% Construct the level filler.
102217
level_filler = repmat('', 1, n_tasks-1);
218+
219+
% Get the appropriate text pattern for this template.
103220
pattern = Program.GUI.dialogues.patterns.(template);
221+
222+
% Construct the new line and append it to the existing
223+
% message.
104224
arr_task{end+1} = sprintf(pattern, level_filler, addl);
105225

226+
% Update the progress dialogue's message property.
106227
handle.Message = sprintf(join(string(arr_task), '\n'));
107228

108229
elseif strcmpi(template, 'task')
230+
% If no progress dialogue exists, create a new one and
231+
% initialize it with the passed task.
109232
handle = Program.GUI.dialogues.create('progress', ...
110233
'Message', addl);
111234
end
112235
end
113236

114237
function set_value(new_value)
238+
%SET_VALUE This function updates the value of the active
239+
% progress dialogue.
240+
%
241+
% Inputs:
242+
% - new_value: Non-negative numerical value to which the
243+
% progress dialogue's "Value" property will be updated.
244+
245+
% Get the active progress dialogue.
115246
handle = Program.GUI.dialogues.active();
247+
248+
% Check whether an active progress dialogue was found...
116249
if ~isempty(handle)
117250
if strcmp(handle.Indeterminate, 'on')
251+
% If a progress dialogue was found but it is currently
252+
% set to indeterminate mode, change that.
118253
handle.Indeterminate = 'off';
254+
119255
elseif new_value == 1
256+
% If a progress dialogue was found but the passed value
257+
% is equal to 1 (suggesting a completed loop), switch
258+
% it to indeterminate mode.
120259
handle.Indeterminate = 'on';
260+
121261
else
262+
% If a progress dialogue was found and it is not
263+
% indeterminate, update its "Value" property with our
264+
% new value.
122265
handle.Value = new_value;
123266
end
124267
end
125268
end
126269

127270
function resolve()
271+
%RESOLVE This function completes the last task/step passed to
272+
% the progress dialogue.
273+
274+
% Get the active progress dialogue.
128275
handle = Program.GUI.dialogues.active();
129276

277+
% Check to ensure that this is a valid progress dialogue.
130278
if isa(handle, "matlab.ui.dialog.ProgressDialog")
131-
task_arr = splitlines(Program.GUI.dialogues.clear_nlines(handle.Message));
279+
% If it is, split its message into a cell array.
280+
task_arr = splitlines( ...
281+
Program.GUI.dialogues.clear_nlines(handle.Message));
132282

133283
if length(task_arr) < 2
284+
% If the line to be deleted is the only one left in the
285+
% message, delete the progress dialogue.
134286
delete(handle)
135287

136288
elseif Program.GUI.dialogues.is_step(task_arr{end})
289+
% If the last line was a step, delete it and then call
290+
% resolve() again.
291+
137292
task_arr(end) = [];
138293
handle.Message = sprintf(join(string(task_arr), '\n'));
139294
Program.GUI.dialogues.resolve();
140295
return
141296

142297
else
298+
% If the last line was a step, delete it and update the
299+
% the level filler of any parent tasks.
143300
task_arr{end-1} = strrep(task_arr{end-1}, '', '');
144301
new_message = join(task_arr(1:end-1), '\n');
145302
handle.Message = sprintf(new_message{1});
@@ -156,19 +313,37 @@ function resolve()
156313
end
157314

158315
function handle = create(mode, varargin)
316+
%CREATE This function creates a new dialogue with the passed
317+
% parameters.
318+
%
319+
% Inputs:
320+
% - mode: String/char describing the type of dialogue to be
321+
% created. We currently only support "progress".
322+
% - varargin: A variable-size cell array representing
323+
% key/value input arguments to be parsed by an
324+
% inputParser object.
325+
326+
% Create an inputParser.
159327
p = inputParser;
160-
addOptional(p, 'Message', '');
161-
addOptional(p, 'Title', Program.ProgramInfo.window().Name);
162-
addOptional(p, 'Indeterminate', 'on');
163-
addOptional(p, 'Options', ["OK", "Cancel"])
164-
addOptional(p, 'Cancelable', 'off');
328+
329+
% Define various optional arguments.
330+
addOptional(p, 'Message', ''); % This is the message our new dialogue will display. Empty by default.
331+
addOptional(p, 'Title', Program.ProgramInfo.window().Name); % This is the title our new dialogue will use. Active window name by default.
332+
addOptional(p, 'Indeterminate', 'on'); % This is a switch describing whether the new dialogue will have indeterminate progress ("on") or discrete progress ("off"). On by default.
333+
addOptional(p, 'Options', ["OK", "Cancel"]) % The options to be passed to a hypothetical choice dialogue to be created.
334+
addOptional(p, 'Cancelable', 'off'); % Whether the created dialogue can be canceled.
335+
336+
% Parse the described optional arguments using our inputParser.
165337
parse(p, varargin{:});
166338

339+
% Check which kind of dialogue should be created. We've only
340+
% implemented support for progress dialogues thus far.
167341
switch mode
168342
case 'instruction'
169343
handle = Program.GUI.dialogues.create_instruction(p.Results);
170344

171345
case 'progress'
346+
% Call create_progress() and pass the parsed arguments.
172347
handle = Program.GUI.dialogues.create_progress(p.Results);
173348

174349
case 'choice'
@@ -185,18 +360,47 @@ function resolve()
185360
return
186361
end
187362

363+
% Set the newly created dialogue to be the active dialogue.
188364
Program.GUI.dialogues.active(handle);
189365
end
190366
end
191367

192368
methods (Static, Access = private)
193369
function bool = is_task(str)
370+
%IS_TASK This function checks whether a passed text matches
371+
% the pattern we use for tasks.
372+
%
373+
% Inputs:
374+
% - str: String/char.
375+
%
376+
% Outputs:
377+
% - bool: Boolean describing whether the input text describes
378+
% a task or not. True if yes, false if no.
379+
380+
% Get the identifiers property.
194381
identifiers = Program.GUI.dialogues.identifiers;
382+
383+
% Check whether the passed text matches what we expect to find
384+
% in a task string.
195385
bool = contains(str, identifiers.task);
196386
end
197387

198388
function bool = is_step(str)
389+
%IS_TASK This function checks whether a passed text matches
390+
% the pattern we use for steps.
391+
%
392+
% Inputs:
393+
% - str: String/char.
394+
%
395+
% Outputs:
396+
% - bool: Boolean describing whether the input text describes
397+
% a step or not. True if yes, false if no.
398+
399+
% Get the identifiers property.
199400
identifiers = Program.GUI.dialogues.identifiers;
401+
402+
% Check whether the passed text matches what we expect to find
403+
% in a step string.
200404
bool = contains(str, identifiers.step);
201405
end
202406

@@ -218,9 +422,30 @@ function resolve()
218422
end
219423

220424
function cleared_string = clear_nlines(input)
425+
%CLEAR_NLINES This function replaces all empty lines from a
426+
% passed text and replaces it with a newline character ("\n").
427+
% Having this function avoids repeating this boilerplate code
428+
% in various places, mostly because the "Message" property of
429+
% certain dialogue objects includes empty lines.
430+
%
431+
% Inputs:
432+
% - input: String/char to be filtered.
433+
%
434+
% Outputs:
435+
% - cleared_string: The filtered input string.
436+
437+
% Split the input text into a cell array of individual lines.
221438
cleared_string = splitlines(input);
439+
440+
% Remove all elements of the resulting cell array that are
441+
% empty.
222442
cleared_string = cleared_string(~cellfun('isempty', cleared_string));
443+
444+
% Join the remaining elements with the newline character ("\n")
445+
% as delimiter.
223446
cleared_string = join(cleared_string, '\n');
447+
448+
% Call sprintf on the joined text to parse these new lines.
224449
cleared_string = sprintf(cleared_string{1});
225450
end
226451
end

0 commit comments

Comments
 (0)