Skip to content

Commit 57ba45c

Browse files
committed
Bug fixes for numRepeats behaviour, more tests
1 parent ad16e6f commit 57ba45c

File tree

6 files changed

+323
-111
lines changed

6 files changed

+323
-111
lines changed

+eui/ConditionPanel.m

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
'MenuSelectedFcn', fcn, 'Checked', 'on', 'Tag', 'randomize button');
4242
obj.ContextMenus(3) = uimenu(c, 'Label', 'Sort by selected column', ...
4343
'MenuSelectedFcn', @(~,~)disp('feature not yet implemented'), ...
44-
'Tag', 'sort by', 'Enable', 'off');
44+
'Tag', 'sort by', 'Enable', 'off'); % TODO Implement sort by column
4545
% Create condition table
4646
p = uix.Panel('Parent', obj.UIPanel, 'BorderType', 'none');
4747
obj.ConditionTable = uitable('Parent', p,...
@@ -118,25 +118,19 @@ function delete(obj)
118118
end
119119

120120
function onSelect(obj, ~, eventData)
121-
obj.SelectedCells = eventData.Indices;
122-
if size(eventData.Indices, 1) > 0
123-
% cells selected, enable buttons
124-
set(obj.MakeGlobalButton, 'Enable', 'on');
125-
set(obj.DeleteConditionButton, 'Enable', 'on');
126-
set(obj.SetValuesButton, 'Enable', 'on');
127-
set(obj.ContextMenus(1), 'Enable', 'on');
128-
set(obj.ContextMenus(3), 'Enable', 'on');
129-
else
130-
% nothing selected, disable buttons
131-
set(obj.MakeGlobalButton, 'Enable', 'off');
132-
set(obj.DeleteConditionButton, 'Enable', 'off');
133-
set(obj.SetValuesButton, 'Enable', 'off');
134-
set(obj.ContextMenus(1), 'Enable', 'off');
135-
set(obj.ContextMenus(3), 'Enable', 'off');
136-
end
121+
% If at least one cell is selected, ensure buttons and menu items are
122+
% enabled, otherwise disable them.
123+
if nargin > 2; obj.SelectedCells = eventData.Indices; end
124+
controls = ...
125+
[obj.MakeGlobalButton, ...
126+
obj.DeleteConditionButton, ...
127+
obj.SetValuesButton, ...
128+
obj.ContextMenus([1,3])];
129+
set(controls, 'Enable', iff(size(obj.SelectedCells, 1) > 0, 'on', 'off'));
137130
end
138131

139132
function makeGlobal(obj)
133+
% FIXME Don't allow only numRepeats to remain
140134
if isempty(obj.SelectedCells)
141135
disp('nothing selected')
142136
return
@@ -146,6 +140,11 @@ function makeGlobal(obj)
146140
rows = num2cell(obj.SelectedCells(iu,1)); %get rows of unique selected cols
147141
PE = obj.ParamEditor;
148142
cellfun(@PE.globaliseParamAtCell, names, rows);
143+
% If only numRepeats remains, globalise it
144+
if isequal(PE.Parameters.TrialSpecificNames, {'numRepeats'})
145+
PE.Parameters.Struct.numRepeats(1,1) = sum(PE.Parameters.Struct.numRepeats);
146+
PE.globaliseParamAtCell('numRepeats', 1)
147+
end
149148
end
150149

151150
function deleteSelectedConditions(obj)
@@ -158,7 +157,7 @@ function deleteSelectedConditions(obj)
158157
% See also EXP.PARAMETERS, GLOBALISESELECTEDPARAMETERS
159158
rows = unique(obj.SelectedCells(:,1));
160159
names = obj.ConditionTable.ColumnName;
161-
numConditions = size(obj.ConditionTable.Data,2);
160+
numConditions = size(obj.ConditionTable.Data,1);
162161
% If the number of remaining conditions is 1 or less...
163162
if numConditions-length(rows) <= 1
164163
remainingIdx = find(all(1:numConditions~=rows,1));
@@ -169,10 +168,10 @@ function deleteSelectedConditions(obj)
169168
%... globalize them
170169
obj.makeGlobal;
171170
else % Otherwise delete the selected conditions as usual
172-
obj.ParamEditor.Parameters.removeConditions(rows); %FIXME: Should be in ParamEditor
171+
obj.ParamEditor.Parameters.removeConditions(rows);
173172
end
174-
% Refresh the table of conditions FIXME: Should be in ParamEditor
175-
obj.ParamEditor.fillConditionTable();
173+
% Refresh the table of conditions
174+
obj.fillConditionTable();
176175
end
177176

178177
function setSelectedValues(obj) % Set multiple fields in conditional table
@@ -244,19 +243,19 @@ function fillConditionTable(obj)
244243
obj.ButtonPanel.Visible = 'on';
245244
obj.UIPanel.Visible = 'on';
246245
obj.ParamEditor.Parent.Widths = [-1, -1];
247-
data = reshape(struct2cell(trialParams), numel(titles), [])';
248-
data = mapToCell(@(e) obj.ParamEditor.paramValue2Control(e), data);
249-
set(obj.ConditionTable, 'ColumnName', titles, 'Data', data,...
250-
'ColumnEditable', true(1, numel(titles)));
251246
end
247+
data = reshape(struct2cell(trialParams), numel(titles), [])';
248+
data = mapToCell(@(e) obj.ParamEditor.paramValue2Control(e), data);
249+
set(obj.ConditionTable, 'ColumnName', titles, 'Data', data,...
250+
'ColumnEditable', true(1, numel(titles)));
252251
end
253252

254253
function newCondition(obj)
255254
disp('adding new condition row');
256255
PE = obj.ParamEditor;
257256
cellfun(@PE.addEmptyConditionToParam, ...
258257
PE.Parameters.TrialSpecificNames);
259-
obj.ParamEditor.fillConditionTable();
258+
obj.fillConditionTable();
260259
end
261260

262261
end

+eui/FieldPanel.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,11 @@ function onEdit(obj, src, id)
6060
switch get(src, 'style')
6161
case 'checkbox'
6262
newValue = logical(get(src, 'value'));
63-
obj.ParamEditor.updateGlobal(id, newValue);
63+
obj.ParamEditor.update(id, newValue);
6464
case 'edit'
6565
% if successful update the control with default formatting and
6666
% modified colour
67-
newValue = obj.ParamEditor.updateGlobal(id, get(src, 'string'));
67+
newValue = obj.ParamEditor.update(id, get(src, 'string'));
6868
set(src, 'String', obj.ParamEditor.paramValue2Control(newValue));
6969
end
7070
changed = strcmp(id,{obj.Labels.String});

+eui/ParamEditor.m

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,19 +60,21 @@ function delete(obj)
6060
end
6161

6262
function set.Enable(obj, value)
63+
% Disable all UI elements
64+
% Render the GUI view-only by disabling all UI elements. Used for
65+
% viewing parameters during an active experiment when the parameters
66+
% can no longer be adjusted.
67+
% See also EUI.EXPPANEL, EUI.CONDITIONPANEL/ONSELECT
6368
cUI = obj.ConditionalUI;
69+
contextMenus = [cUI.ContextMenus obj.GlobalUI.ContextMenu.Children]';
6470
parent = obj.Parent; % FIXME: use tags instead?
6571
if value == true
66-
arrayfun(@(prop) set(prop, 'Enable', 'on'), findobj(parent,'Enable','off'));
67-
if isempty(cUI.SelectedCells)
68-
set(cUI.MakeGlobalButton, 'Enable', 'off');
69-
set(cUI.DeleteConditionButton, 'Enable', 'off');
70-
set(cUI.SetValuesButton, 'Enable', 'off');
71-
end
72-
obj.Enable = true;
72+
arrayfun(@(prop) set(prop, 'Enable', 'on'), ...
73+
[contextMenus; findobj(parent,'Enable','off')]);
74+
cUI.onSelect() % Re-disable buttons if no cells were selected
7375
else
74-
arrayfun(@(prop) set(prop, 'Enable', 'off'), findobj(parent,'Enable','on'));
75-
obj.Enable = false;
76+
arrayfun(@(prop) set(prop, 'Enable', 'off'), ...
77+
[contextMenus; findobj(parent,'Enable','on')]);
7678
end
7779
end
7880

@@ -117,7 +119,7 @@ function setRandomized(obj, value)
117119
description = 'Whether to randomise the conditional paramters or present them in order';
118120
obj.Parameters.set('randomiseConditions', false, description, 'logical')
119121
elseif ismember('randomiseConditions', obj.Parameters.Names)
120-
obj.updateGlobal('randomiseConditions', logical(value));
122+
obj.update('randomiseConditions', logical(value));
121123
end
122124
menu = obj.ConditionalUI.ContextMenus(2);
123125
if value == false
@@ -163,7 +165,7 @@ function addEmptyConditionToParam(obj, name)
163165
obj.Parameters.Struct.(name) = cat(2, obj.Parameters.Struct.(name), newValue);
164166
end
165167

166-
function newValue = updateGlobal(obj, name, value, row)
168+
function newValue = update(obj, name, value, row)
167169
if nargin < 4; row = 1; end
168170
currValue = obj.Parameters.Struct.(name)(:,row);
169171
if iscell(currValue)
@@ -188,8 +190,8 @@ function globaliseParamAtCell(obj, name, row)
188190
obj.ConditionalUI.fillConditionTable;
189191
% Add new global parameter to field panel
190192
if islogical(value) % If parameter is logical, make checkbox
191-
ctrl = uicontrol('Parent', obj.GlobalUI.UIPanel, 'Style', 'checkbox', ...
192-
'Value', value, 'BackgroundColor', 'white');
193+
ctrl = uicontrol('Parent', obj.GlobalUI.UIPanel, ...
194+
'Style', 'checkbox', 'Value', value);
193195
addField(obj.GlobalUI, name, ctrl);
194196
else
195197
[~, ctrl] = addField(obj.GlobalUI, name);
@@ -306,7 +308,8 @@ function onResize(obj)
306308
% Convert the values displayed in the UI ('control values') to
307309
% parameter values. String representations of numrical arrays and
308310
% functions are converted back to their 'native' classes.
309-
if nargin < 4
311+
% TODO Implement string support
312+
if nargin < 3
310313
allowTypeChange = false;
311314
end
312315
switch class(currParam)
@@ -340,6 +343,11 @@ function onResize(obj)
340343
error('Cannot update unimplemented type ''%s''', class(currParam));
341344
end
342345
end
346+
% If necessary, assert that type didn't change
347+
if ~allowTypeChange
348+
assert(strcmp(class(currParam), class(data)), ...
349+
sprintf('Type change from %s to %s not allowed', class(currParam), class(data)))
350+
end
343351
end
344352

345353
end

+exp/Parameters.m

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,9 @@ function makeGlobal(obj, name, newValue)
220220
% We also remove the numRepeats parameter from the globalParams
221221
% since the trial elements are literally repeated now
222222
nreps = globalParams.numRepeats;
223-
trialParams = repmat(trialParams, 1, nreps);
223+
trialParams = iff(isempty(trialParams), ...
224+
@()repelems(struct, nreps),...
225+
@()repmat(trialParams, 1, nreps));
224226
globalParams = rmfield(globalParams, 'numRepeats');
225227
end
226228
if randomOrder
@@ -229,29 +231,6 @@ function makeGlobal(obj, name, newValue)
229231
cs = exp.PresetConditionServer(globalParams, trialParams);
230232
end
231233

232-
function [ctrl, label] = ui(obj, name, parent)
233-
% FIXME method not used at all(?)
234-
% Seems to reuse code put into indivTitle method
235-
% Doesn't return uicontrols if units aren't '°' or 's'
236-
ctrl = [];
237-
label = [];
238-
unitField = [name 'Units'];
239-
if isfield(obj.Params, unitField)
240-
value = obj.Params.(name);
241-
units = obj.Params.(unitField);
242-
words = lower(regexprep(name, '([a-z])([A-Z])', '$1 $2'));
243-
words(1) = upper(words(1));
244-
title = sprintf('%s (%s)', words, units);
245-
description = obj.Params.([name 'Description']);
246-
if any(strcmp(units, {'°', 's'}))
247-
label = uicontrol('Parent', parent, 'Style', 'text', 'String', title);
248-
ctrl = uicontrol('Parent', parent,...
249-
'Style', 'edit',...
250-
'String', num2str(value),...
251-
'TooltipString', description);
252-
end
253-
end
254-
end
255234
end
256235

257236
methods (Access = protected)

0 commit comments

Comments
 (0)