% paramOut = ParamEditor(param)
%
% a GUI that simplifies the construction of Property windows.
% The program pops up a window that displays all parameters in structure
% <param> using a tabbed interface. Complicated hierarchies of parameters
% can be build using a two-level tabbed interface.
%
% In detail, <param> has the following structure:
%
% param.highTab*.lowTab*.Parameter*
% param.highTab*_.InfoTxt
% param.highTab*_.lowTab*.InfoTxt
% param.highTab*_.lowTab*.Parameter*.InfoTxt
% param.highTab*_.lowTab*.Parameter*.Range
%
% where <highTab*> are the names of the high-level tabs and <lowTab*>
% are the names of the low-level tabs associated with the corresponding
% high-level tabs. For example, we could generate a result like the following
% ('highTab1'->'lowTab2' tabs are selected):
%
% ||=[highTab1][highTab2]=============={highTab1_.InfoTxt}=============||
% || ||
% || |-------------------------------------------------------------| ||
% || | | ||
% || | | ||
% || | Order: [______] | ||
% || | | ||
% || | WindowType: [_________] | ||
% || | | ||
% || | | ||
% || |---{highTab1_.lowTab2.InfoTxt}---------[lowTab1][lowTab2]----| ||
% || ||
% ||===================================================================||
%
% Additionaly to the fields 'highTab*', there are the so-called hidden
% fields 'highTab*_' which are not displayed, and which contain the following
% additional information:
%
% param.highTab*_.InfoTxt :
% [string] a small description text of 'highTab*'. The string
% is displayed at the right of the high-level tab.
% param.highTab*_.lowTab*.InfoTxt :
% [string] a small description text of 'highTab*.lowtab*'.
% The string is displayed at the left of the low-level tab.
% param.highTab*_.lowTab*.Parameter*.InfoTxt :
% [string] the text that will be displayed on the GUI. This
% is to avoid displaying quirky parameter names.
% The string is displayed at the left of the low-level tab.
% param.highTab*_.lowTab*.Parameter*.Range :
% [2x1] the minimum and the maximum accepted value (the
% range of these values). This is when the parameter
% value is a number. Use [ -Inf Inf ] to disable
% range checking.
% [cell array] of strings, doubles, integers, etc. This is
% used when the the parameter picks up it's value
% from a set of acceptable values. The cell array
% contains the acceptable values.
%
% Examples: an example can be found in: sigplot\test\test_ParamEditor.m
%
% Input Arguments:
% param: [struct] special structure that contains the parameters
% under definition plus extra information for their range of
% values.
%
% Output Arguments:
% paramOut: [struct] the modified structure
% cancel_flag: [boolean] true if the editor was closed using cancel, false if closed using ok
%
function [paramOut, cancel_flag] = ParamEditor(param)
paramOut = param;
cancel_flag = true; % Default value if the editor is closed using window controls.
paramDefault = param;
all_params = struct('Name',[],'hcontrol',[]);
tabStyle = 'lefttop';
subtabStyle = 'rightbottom';
width = 0;
hfig = figure(...
'Name','Properties',...
'NumberTitle','off', ...
'Resize','on',...
'Menubar','none',...
'Toolbar','none',...
'WindowStyle','normal'); %TEST%
tabNames = fieldnames(param);
Ntab = length(tabNames);
% remove hidden entries
I = [];
for n = 1:Ntab
name = char(tabNames(n));
if name(end)~='_'
I = [ I n ];
end
end
tabNames = tabNames(I);
%--------------------------------------------------------------------------
% Creating the tabs
%--------------------------------------------------------------------------
htab = uitabpanel(...
'Parent',hfig,...
'Style',tabStyle,...
'Units','normalized',...
'Position',[0 ,0.1 ,1, 0.9],...
'Margins',{[0,-1,1,0],'pixels'},...
'PanelBorderType','line',...
'Title', tabNames,...
'CreateFcn',@CreateTabs);
%--------------------------------------------------------------------------
% Creating the buttons: OK, DEFAULT, CANCEL
%--------------------------------------------------------------------------
buttonWidth = 0.25;
buttonHeight = 0.06;
buttonDist = (1 - 3*buttonWidth)/4;
hbutton = uicontrol(...
'Parent',hfig,...
'Units','normalized',...
'Position',[buttonDist,0.02,buttonWidth,buttonHeight],...
'BackgroundColor',get(htab,'BackgroundColor'),...
'ForegroundColor','k',...
'HitTest','off',...
'Style','pushbutton',...
'Callback',@button_OK_Callback,...
'String','OK');
hbutton(2) = uicontrol(...
'Parent',hfig,...
'Units','normalized',...
'Position',[2*buttonDist+buttonWidth,0.02,buttonWidth,buttonHeight],...
'BackgroundColor',get(htab,'BackgroundColor'),...
'ForegroundColor','k',...
'HitTest','off',...
'Style','pushbutton',...
'Callback',@button_DEFAULT_Callback,...
'String','DEFAULT');
hbutton(3) = uicontrol(...
'Parent',hfig,...
'Units','normalized',...
'Position',[3*buttonDist+2*buttonWidth,0.02,buttonWidth,buttonHeight],...
'BackgroundColor',get(htab,'BackgroundColor'),...
'ForegroundColor','k',...
'HitTest','off',...
'Style','pushbutton',...
'Callback',@button_CANCEL_Callback,...
'String','CANCEL');
updateParam();
uiwait(hfig);
%--------------------------------------------------------------------------
function CreateTabs(htab,evdt,hpanel,hstatus)
Ntab = length(tabNames);
hsubtab = [];
%
% create UI for each tab
%
for tabInd = 1:Ntab
%
% create the internal tab-panel (sub-tab)
%
subtabNames = eval(sprintf('fieldnames(param.%s);',char(tabNames(tabInd))));
hsubtab(tabInd) = uitabpanel(...
'Parent',hpanel(tabInd),...
'TabPosition',subtabStyle,...
'Units','normalized',...
'Position',[0.01 ,0.01 , 0.98, 0.98],...
'Margins',{[0,-1,1,0],'pixels'},...
'PanelBorderType','line',...
'Title', subtabNames,...
'CreateFcn',@CreateSubTabs);
setappdata(hsubtab(tabInd),'TabIndex',tabInd);
setappdata(hsubtab(tabInd),'TabName',char(tabNames(tabInd)));
%
% create & initialize the associated status text
%
hstatus_sub = getappdata(hsubtab(tabInd),'hstatus');
try
if isfield(param,[ char(tabNames(tabInd)) '_' ])
InfoTxt = eval(sprintf('param.%s_.%s.InfoTxt;',char(tabNames(tabInd)),char(subtabNames(1))));
else
InfoTxt = char(subtabNames(1));
end
catch
error('tab <%s>, subtab <%s>: InfoTxt is not found ',char(tabNames(tabInd)),char(subtabNames(1)));
end
uicontrol(...
'Parent',hstatus_sub,...
'Units','normalized',...
'Position',[0,0.1,1,.68],...
'BackgroundColor',get(hstatus_sub,'BackgroundColor'),...
'ForegroundColor','k',...
'HitTest','off',...
'Style','text',...
'String',InfoTxt);
%
% create UI for each sub-tab
%
hsubpanel = getappdata(hsubtab(tabInd),'panels');
for subtabInd = 1:length(hsubpanel)
subtabParamNames = eval(sprintf('fieldnames(param.%s.%s);',char(tabNames(tabInd)),char(subtabNames(subtabInd))));
charLen = 0.019;
charHeight = 0.06;
N = length(subtabParamNames);
htxt = [];
hinput = [];
%
% create UI items for each entry of the 'param'
% structure
%
for n = 1:N
if isfield(param,[ char(tabNames(tabInd)) '_' ])
txt = eval(sprintf('param.%s_.%s.%s.InfoTxt;',char(tabNames(tabInd)),char(subtabNames(subtabInd)),char(subtabParamNames(n))));
else
txt = char(subtabParamNames(n));
end
htxt(n) = uicontrol(...
'Parent',hsubpanel(subtabInd),...
'Units','normalized',...
'Position',[0,n/(N+1),length(txt)*charLen,charHeight],...
'BackgroundColor',get(hsubpanel(subtabInd),'BackgroundColor'),...
'HorizontalAlignment','left',...
'ForegroundColor','k',...
'HitTest','off',...
'Style','text',...
'FontSize',12,...
'String', [ txt ':' ]);
if isfield(param,[ char(tabNames(tabInd)) '_' ])
Range = eval(sprintf('param.%s_.%s.%s.Range;',char(tabNames(tabInd)),char(subtabNames(subtabInd)),char(subtabParamNames(n))));
else
Range = [ -Inf Inf ];
end
Value = eval(sprintf('param.%s.%s.%s;',char(tabNames(tabInd)),char(subtabNames(subtabInd)),char(subtabParamNames(n))));
if iscell(Range)
hinput(n) = uicontrol(...
'Parent',hsubpanel(subtabInd),...
'Units','normalized',...
'Position',[length(txt)*charLen, n/(N+1)+0.01, 20*charLen, charHeight],...
'BackgroundColor','white',...
'HorizontalAlignment','left',...
'ForegroundColor','k',...
'HitTest','off',...
'Style','popupmenu',...
'FontSize',12,...
'Callback',@popupmenu_Callback,...
'FontName','FixedWidth',...
'String', Range);
elseif strcmp(Range,'multiline_script')
tooltip_string = eval(sprintf('param.%s_.%s.%s.TooltipString;',char(tabNames(tabInd)),char(subtabNames(subtabInd)),char(subtabParamNames(n))));
hinput(n) = uicontrol(...
'Parent',hsubpanel(subtabInd),...
'Units','normalized',...
'Position',[length(txt)*charLen,(n-1)/(N+1)+1.5*charHeight,1.0-(length(txt)+1)*charLen,2/(N+1)-3*charHeight],...
'BackgroundColor','white',...
'HorizontalAlignment','left',...
'ForegroundColor','k',...
'HitTest','off',...
'Style','edit',...
'FontSize',12,...
'Callback',@edit_Callback,...
'FontName','FixedWidth',...
'Max',10,...
'Min',1,...
'TooltipString',tooltip_string,...
'String', Value);
else
hinput(n) = uicontrol(...
'Parent',hsubpanel(subtabInd),...
'Units','normalized',...
'Position',[length(txt)*charLen,n/(N+1),15*charLen,charHeight],...
'BackgroundColor','white',...
'HorizontalAlignment','left',...
'ForegroundColor','k',...
'HitTest','off',...
'Style','edit',...
'FontSize',12,...
'Callback',@edit_Callback,...
'FontName','FixedWidth',...
'String', Value);
end
setappdata(hinput(n),'ParamName',sprintf('param.%s.%s.%s',char(tabNames(tabInd)),char(subtabNames(subtabInd)),char(subtabParamNames(n))));
setappdata(hinput(n),'RangeName',sprintf('param.%s_.%s.%s.Range',char(tabNames(tabInd)),char(subtabNames(subtabInd)),char(subtabParamNames(n))));
Nparam = length(all_params);
all_params(Nparam+1).Name = sprintf('param.%s.%s.%s',char(tabNames(tabInd)),char(subtabNames(subtabInd)),char(subtabParamNames(n)));
all_params(Nparam+1).hcontrol = hinput(n);
end
end
end
if isfield(param,[ char(tabNames(1)) '_' ])
InfoTxt = eval(sprintf('param.%s_.InfoTxt;',char(tabNames(1))));
else
InfoTxt = '';
end
uicontrol(...
'Parent',hstatus,...
'Units','normalized',...
'Position',[0,0.1,1,.68],...
'BackgroundColor',get(hstatus,'BackgroundColor'),...
'ForegroundColor','k',...
'HitTest','off',...
'Style','text',...
'String',InfoTxt);
setappdata(htab,'SelectionChangeFcn',@TabStatus);
%TEST%set(htab,'ResizeFcn',{@TabResize,[0.5,0.5]});
set(htab,'Units','normalized'); %TEST%
all_params = all_params(2:end);
end
%--------------------------------------------------------------------------
function CreateSubTabs(htab,evdt,hpanel,hstatus)
setappdata(htab,'SelectionChangeFcn',@SubTabStatus);
setappdata(htab,'hstatus',hstatus);
end
%--------------------------------------------------------------------------
function TabResize(hobj,evdt,ysiz)
figpos = get(hfig,'Position');
tabpos = get(hobj,'Position');
tabpos([1,3]) = [width,1-width]*figpos(3)+[1,0];
tabpos([2,4]) = ysiz*figpos(4)+[1,0];
set(hobj,'Position',tabpos);
end
%--------------------------------------------------------------------------
function TabStatus(hobj,evdt)
if isfield(param,[ char(tabNames(1)) '_' ])
InfoTxt = eval(sprintf('param.%s_.InfoTxt;',char(tabNames(evdt.NewValue))));
else
InfoTxt = '';
end
set(get(evdt.Status,'Children'),'String',InfoTxt);
end
%--------------------------------------------------------------------------
function SubTabStatus(hobj,evdt)
htab = get(hobj,'Parent');
tabInd = getappdata(htab,'TabIndex');
subtabInd = evdt.NewValue;
subtabNames = eval(sprintf('fieldnames(param.%s);',char(tabNames(tabInd))));
if isfield(param,[ char(tabNames(tabInd)) '_' ])
InfoTxt = eval(sprintf('param.%s_.%s.InfoTxt;',char(tabNames(tabInd)),char(subtabNames(subtabInd))));
else
InfoTxt = char(subtabNames(subtabInd));
end
set(get(evdt.Status,'Children'),'String',InfoTxt);
end
%--------------------------------------------------------------------------
function popupmenu_Callback(hObject, eventdata, handles)
% hObject handle to popupmenu (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Hints: contents = get(hObject,'String') returns popupmenu contents as cell array
% contents{get(hObject,'Value')} returns selected item from
% popupmenu
contents = get(hObject,'String');
val = contents{get(hObject,'Value')};
paramName = getappdata(hObject,'ParamName');
valclass = eval(sprintf('class(%s);',paramName));
eval(sprintf('%s = %s(val);',paramName,valclass));
end
%--------------------------------------------------------------------------
function edit_Callback(hObject, eventdata, handles)
% hObject handle to edit1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Hints: get(hObject,'String') returns contents of edit1 as text
% str2double(get(hObject,'String')) returns contents of edit1 as a double
contents = get(hObject,'String');
paramName = getappdata(hObject,'ParamName');
rangeName = getappdata(hObject,'RangeName');
valclass = eval(sprintf('class(%s);',paramName));
Range = eval(sprintf('%s(%s);',valclass,rangeName));
switch valclass
case { 'double', 'single' }
cmd = 'sscanf(contents,''%f'',[ 1 1 ]);';
val = eval(cmd);
val = max(Range(1),min(val,Range(2)));
txt = sprintf('%g',val);
case { 'int32', 'int16', 'int8', 'uint32', 'uint16', 'uint8', 'boolean' }
cmd = 'sscanf(contents,''%d'',[ 1 1 ]);';
val = eval(cmd);
val = max(Range(1),min(val,Range(2)));
txt = sprintf('%d',val);
case { 'char' }
val = contents;
txt = char(val);
otherwise
warning('unknown type of values');
end
eval(sprintf('%s = val;',paramName));
set(hObject,'String',txt);
end
%--------------------------------------------------------------------------
function button_OK_Callback(hObject, eventdata, handles)
paramOut = param;
cancel_flag = false;
hfig = get(hObject,'Parent');
close(hfig);
end
%--------------------------------------------------------------------------
function button_DEFAULT_Callback(hObject, eventdata, handles)
param = paramDefault;
updateParam();
end
%--------------------------------------------------------------------------
function button_CANCEL_Callback(hObject, eventdata, handles)
cancel_flag = true;
close(hfig);
end
%--------------------------------------------------------------------------
function updateParam()
Nparam = length(all_params);
for n=1:Nparam
hObject = all_params(n).hcontrol;
val = eval(sprintf('%s;',all_params(n).Name));
valclass = eval(sprintf('class(%s);',all_params(n).Name));
switch valclass
case { 'double', 'single' }
txt = sprintf('%g',val);
case { 'int32', 'int16', 'int8', 'uint32', 'uint16', 'uint8', 'boolean' }
txt = sprintf('%d',val);
case { 'char' }
txt = val;
otherwise
warning('unknown type of values');
end
Style = get(hObject,'Style');
switch Style
case 'popupmenu'
contents = get(hObject,'String');
ind = find(ismember(contents, txt)==1);
set(hObject,'Value',ind);
case 'edit'
set(hObject,'String',txt);
otherwise
warning('unknown Style');
end
end
end
end