forked from optospinlab/modularControl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmcGUI.m
289 lines (238 loc) · 13 KB
/
mcGUI.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
classdef mcGUI < mcSavableClass
properties
% config = []; % Defined in mcSavableClass. All static variables (e.g. valid range) go in config.
controls = {};
f = [];
updated = 0;
finished = false;
pw = 300;
ph = 700; % Make variable...
end
methods (Static)
function config = defaultConfig()
config = mcGUI.exampleConfig();
end
function config = exampleConfig()
% Style String Variable TooltipString Optional: Limit [min max round] (only for edit)
config.controls = { { 'title', 'Title: ', NaN, 'This section is an example section' },...
{ 'edit', 'Number!: ' 0, 'Enter a number!', [01 234 1]},...
{ 'edit', 'Number!: ' 0, 'Enter a number!', [01 234 0]},...
{ 'edit', 'Number!: ' 0, 'Enter a number!', [-Inf Inf]},...
{ 'edit', 'Number!: ' 0, 'Enter a number!', [0 0]},...
{ 'push', 'Push this button', 'hello', 'Push to activate a generic config' },...
{ 'edit', 'Number!: ' 0, 'Enter another number!' } };
end
end
methods
function gui = mcGUI(varin)
switch nargin
case 0
gui.load(); % Attempt to load a previous config from configs/computername/classname/config.mat
if isempty(gui.config) % If the file did not exist or the loading failed...
gui.config = gui.defaultConfig(); % ...then use the defaultConfig() as a backup.
end
case 1
gui.config = varin; % Otherwise use the input as the config.
end
if strcmpi(class(gui), 'mcGUI')
gui.buildGUI();
end
end
function buildGUI(gui)
gui.f = mcInstrumentHandler.createFigure(gui, 'saveopen');
gui.f.Resize = 'off';
gui.f.CloseRequestFcn = @gui.closeRequestFcn;
gui.f.Tag = class(gui);
gui.f.UserData = gui;
if isfield(gui.config, 'Position') % If the position was saved in the config,...
gui.f.Position = gui.config.Position; % ...Then use these position settings.
gui.f.Position(3) = gui.pw; % But force the width to be the expected value (becuase it might mess up the margins and centering otherwise.
else
gui.f.Position = [100, 100, gui.pw, gui.ph]; % Otherwise, use the default settings.
end
bh = 20; % Button height
ii = 1.5; % Initial button
m = .1; % Margin
w = 1 - 2*m;
M = gui.pw*m;
W = gui.pw*w;
prevControl = ''; % The kind of the previous control. This allows us to put in nice formatting (e.g. larger space before titles, etc)
jj = 1;
for kk = 1:length(gui.config.controls)
control = gui.config.controls{kk};
switch control{1}
case 'title'
if ~isempty(prevControl)
ii = ii + 1; % Add a space after the last line.
end
uicontrol( 'Parent', gui.f,...
'Style', 'text',...
'String', control{2},...
'TooltipString', control{4},...
'HorizontalAlignment', 'left',...
'FontWeight', 'bold',...
'Position', [M, -ii*bh, W, bh]);
ii = ii + 1;
case 'text'
if strcmpi(prevControl, 'title')
ii = ii + .25;
end
if strcmpi(prevControl, 'push')
ii = ii + 1;
end
if strcmpi(prevControl, 'edit')
ii = ii + 1;
end
uicontrol( 'Parent', gui.f,...
'Style', 'text',...
'String', control{2},...
'TooltipString', control{4},...
'HorizontalAlignment', 'left',...
'Position', [M, -ii*bh, W, bh]);
gui.controls{jj} = uicontrol( 'Parent', gui.f,...
'Style', 'edit',...
'String', control{3},...
'Position', [M, -(ii+1)*bh, W, bh],...
'UserData', kk); % also save the line that spawned it so the final value can be saved.
gui.controls{jj}.Callback = @gui.update;
jj = jj + 1;
ii = ii + 2;
case 'edit'
if strcmpi(prevControl, 'title')
ii = ii + .25;
end
if strcmpi(prevControl, 'push')
ii = ii + 1;
end
if strcmpi(prevControl, 'text')
ii = ii + 1;
end
uicontrol( 'Parent', gui.f,...
'Style', 'text',...
'String', control{2},...
'TooltipString', control{4},...
'HorizontalAlignment', 'right',...
'Position', [M, -ii*bh, W/2, bh]);
gui.controls{jj} = uicontrol( 'Parent', gui.f,...
'Style', 'edit',...
'String', control{3},...
'Value', control{3},... % Also store number as value (used if string change is undesirable).
'Position', [M + W/2, -ii*bh, W/2, bh],...
'UserData', kk); % also save the line that spawned it so the final value can be saved.
if length(control) > 4
gui.controls{jj}.TooltipString = gui.getLimitString(control{5});
gui.controls{jj}.Callback = {@gui.limit control{5}};
else
gui.controls{jj}.TooltipString = gui.getLimitString([]);
gui.controls{jj}.Callback = {@gui.limit [-Inf Inf]};
end
jj = jj + 1;
ii = ii + 1;
case 'push'
if ~strcmpi(prevControl, 'push')
ii = ii + .25;
end
uicontrol( 'Parent', gui.f,...
'Style', 'push',...
'String', control{2},...
'TooltipString', control{4},...
'Position', [M, -ii*bh, W, bh],...
'Callback', {@gui.Callbacks, control{3}});
ii = ii + 1;
end
prevControl = control{1};
end
gui.ph = ii*bh;
gui.f.Position(4) = gui.ph;
for kk = 1:length(gui.f.Children)
child = gui.f.Children(kk);
if isprop(child, 'Position')
child.Position(2) = child.Position(2) + gui.ph;
end
end
gui.f.Visible = 'on';
end
function limit(gui, src, ~, lim)
val = str2double(src.String); % Try to interpret the edit string as a double.
if isnan(val) % If we don't understand.. (e.g. '1+1' was input), try to eval() it.
try
val = eval(src.String); % This would be an example of an exploit, if this was supposed to be a secure application. The user should never be able to execute his own code.
catch
val = src.Value; % If we still don't understand, revert to the previous value.
end
end
% Next, preform our checks on our value.
if length(lim) == 1 && lim % If we only should round...
val = round(val);
elseif length(lim) > 1 % If we have min/max bounds...
if val > lim(2)
val = lim(2);
end
if val < lim(1)
val = lim(1);
end
% Note that this will cause val = lim(1) if lim(1) > lim(2) instead of the expected lim(1) < lim(2)
if length(lim) > 2 && lim(3)
val = round(val);
end
end
src.String = val;
src.Value = val;
gui.update(src, 0);
end
function str = getLimitString(~, lim)
str = 'No requirements.';
if length(lim) == 1
if lim
str = 'Must be an integer.';
end
elseif length(lim) > 1
str = ['Bounded between ' num2str(lim(1)) ' and ' num2str(lim(2)) '.'];
if length(lim) > 2 && lim(3)
str = [str ' Must be an integer.'];
end
end
end
function update(gui, src, ~)
if ~isnumeric(src) % This saves the value that the edit box was changed to in the static config. This will allow this value to be saved and recovered.
if isempty(src.Value)
gui.config.controls{src.UserData}{3} = src.String;
else
gui.config.controls{src.UserData}{3} = src.Value;
end
end
gui.updated = gui.updated + 1;
end
function closeRequestFcn(gui, ~, ~)
delete(gui);
end
function delete(gui)
gui.save(); % Inherited from mcSavableClass...
delete(gui.f)
end
% function val = getEditValue(gui, jj) % Gets the value of the jj'th edit (change this eventually to look for the edit corresponding to a string? After all, this makes editing difficult)
% val = gui.controls{jj}.Value;
% end
end
methods
function Callbacks(gui, ~, ~, cbName)
switch cbName
case 'quit'
delete(gui);
case 'update'
gui.update(0,0);
case 'finish'
gui.update(0,0);
gui.finished = true;
case 'hello'
disp('Hello World!');
otherwise
if ischar(cbName)
disp([class(gui) '.Callbacks(s, e, cbName): No callback of name ' cbName]);
else
disp([class(gui) '.Callbacks(s, e, cbName): Did not understand cbName; not a string.']);
end
end
end
end
end