Code covered by the BSD License  

Highlights from
Image Morphology

image thumbnail

Image Morphology

by

 

14 Apr 2009 (Updated )

An interactive environment for morphologically operating on images.

Editor's Notes:

This file was selected as MATLAB Central Pick of the Week

sliderPanel(parent,PanelPVs,SliderPVs,EditPVs,LabelPVs,numFormat,varargin)
function [sliderHandle,panelHandle,editHandle] = sliderPanel(parent,PanelPVs,SliderPVs,EditPVs,LabelPVs,numFormat,varargin)
% [sliderHandle,panelHandle,editHandle] = sliderPanel(parent,PanelPVs,SliderPVs,EditPVs,LabelPVs,numFormat)
%
% Creates a slider in a separate uipanel, with an associated
% interactive EditBox, and left and right labels showing the
% minimum and maximum values of the slider, respectively.
% Moving the slider automatically updates the textbox, and vice
% versa. Both slider movement and text edits will trigger
% (non-recursively) the callback of the slider.
%
% The EditBox automatically disallows the entry of
% non-numeric values, or of values outside of [min,max].
% Attempts to enter disallowed values will be ignored.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SYNTAXES:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% 1) The main syntax for sliderPanel allows full control of
% all elements of the uitool. (See INPUT ARGUMENTS below for
% details).
%
% [sliderHandle,panelHandle,editHandle] =
%     sliderPanel(parent,PanelPVs,SliderPVs,EditPVs,LabelPVs,numFormat);
%
% 2)The following syntax captures a small subset of the
% sliderPanel functionality;
%
% hFig = gcf;
% sliderPanel(...
% 		'Parent'  , hFig, ...
% 		'Title'   , 'Slider Panel', ...
% 		'Position', [0.3 0.5 0.4 0.2], ...
%       'Backgroundcolor', 'r',...
% 		'Min'     , 0, ...
% 		'Max'     , 100, ...
% 		'Value'   , 50, ...
% 		'FontName', 'Verdana', ...
% 		'Callback', @myCallback);
%
% Note: This simplified syntax supports a small subset of
% the available PV pairs that can be controlled via the
% primary syntax. Supported parameters, and the object to
% which they assigned, are shown below:
%
% Parameter       Object(s) affected
%____________________________________
% Parent            uipanel
% Title             uipanel
% Position          uipanel
% Backgroundcolor   uipanel
% Bordertype        uipanel
% Tag               uipanel
% Fontname          uipanel, edit box, labels
% Fontweight        uipanel, edit box, labels
% Fontsize          uipanel, edit box, labels
% Min               slider
% Max               slider
% Value             slider
% Sliderstep        slider
% Callback          slider (and, by extension, edit box)
% Units             uipanel, slider, edit box, labels
% Visible           uipanel (as a parent)
% Numberformat      edit box
%
% (The code is easily modifiable to add new PV support.)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% INPUT ARGUMENTS (ALL OPTIONAL):
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%    PARENT:    the handle of the parent object for the
%               uipanel. Default is the current figure.
%
%    PANELPVS:  a cell array of any parameter-value pairs
%               valid for the uipanel object, or a structure
%               of same. (Defaults are those used by
%               UIPANEL.)
%
%    SLIDERPVS: a cell array of any parameter-value pairs
%               valid for the slider object, or a structure
%               of same. (Defaults are those used by
%               UICONTROL('STYLE','SLIDER').)
%
%    EDITPVS:   a cell array of any parameter-value pairs
%               valid for the UICONTROL EDIT object, or a
%               structure of same. (Defaults are those used
%               by UICONTROL('STYLE','EDIT').)
%
%    LABELPVS:  a cell array of any parameter-value pairs
%               valid for the UICONTROL TEXT objects used to
%               label the slider, or a structure of same.
%               (Defaults are those used by
%               UICONTROL('STYLE','TEXT').)
%               NOTE: The following are valid syntaxes for LABELPVS:
%               1) Applied to both (l/r) labels:
%                  {param1, val1, param2, val2,...}
%               2) First array applied to left label, second
%                  array applied to right label:
%                  {{P-V array},{P-V array}}
%               3) First array applied to left label, second
%                  array applied to right label, third array
%                  applied to both (l/r) labels:
%                  {{P-V array},{P-V array},{P-V array}}
%
%    NUMFORMAT: a format string accepted by SPRINTF, which
%               controls the formatting of the EditBox when
%               the slider is dragged. (Typing directly in
%               the EditBox does not trigger the formatting
%               constraint.)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% OUTPUT ARGUMENTS:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%    SLDRHNDL:  handles of slider object.
%
%    PNLHNDL:   handle of uipanel object.
%
%    EDITHNDL:  handle of edit box.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% EXAMPLES:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 1) Create a single-slider sliderPanel using cell-array inputs.
%
% [sldr,pnl,edt] = ...
%                   sliderPanel(gcf,...
%                   {'title','Threshold','pos',[0.1 0.2 0.8 0.15],'fontweight','b','units','pixels'},...
%                   {'max',80,'value',60,'callback','disp(''Slid'')'},...
%                   {},...
%                   {{'string','Low','foregroundcolor','b'},...
%                      {'string','High','foregroundcolor','r'},...
%                      {'fontweight','b'}},...
%                   '%0.1f');
%
% 2) Create a single-slider sliderPanel as a child of a
% uipanel, using struct and cell-array inputs. Moving the
% slider or updating the edit box will immediately refresh
% the value of variable sliderVal in the base workspace.
%
% hFig = figure;
% hPanel = uipanel(hFig,'title','MASTER','pos',[0.3 0.1 0.4 0.6]);
% PnlOpt.title = 'Threshold';
% PnlOpt.position = [0.05 0.2 0.9 0.4];
% PnlOpt.fontsize = 10;
% SldrOpt.min = 10;
% SldrOpt.max = 100;
% SldrOpt.value = 50;
% SldrOpt.callback = 'assignin(''base'',''sliderVal'',get(gcbo,''value''));';
% sliderPanel(hPanel,PnlOpt,SldrOpt,{'fontsize',12},{},'%0.0f')
%
% 3) Create multiple sliderPanels as children of a UIPANEL.
%
% figure;
% h = uipanel(gcf,'title','MAIN','units','normalized','pos',[0.2 0.1 0.6 0.8]);
% PnlOpt.title = 'Parameter Tool';
% PnlOpt.bordertype = 'none';
% PnlOpt.titleposition = 'centertop';
% PnlOpt.fontweight = 'bold';
% SldrOpt.min = 0;
% SldrOpt.max = 255;
% SldrOpt.value = 50;
% EditOpts = {'fontsize',10};
% LabelOpts = {'fontsize',9,'fontweight','b'};
% numFormat = '%0.0f';
% titleStrings = {'Slider 1','Slider 2', 'Slider 3', 'Slider 4'};
% startPos = {[0.1 0.05 0.8 0.21];
% 	        [0.1 0.28 0.8 0.21];
% 	        [0.1 0.51 0.8 0.21];
% 	        [0.1 0.74 0.8 0.21]};
% 		sldrCallbacks = {'disp(''Slider 1 moved'')';
% 			'disp(''Slider 2 moved'')';
% 			'disp(''Slider 3 moved'')';
% 			'disp(''Slider 4 moved'')'};
% for ii = 1:4
% 	PnlOpt.position = startPos{ii};
% 	PnlOpt.title = titleStrings{ii};
% 	SldrOpt.callback = sldrCallbacks{ii};
% 	sliderPanel(h,PnlOpt,SldrOpt,EditOpts,LabelOpts,numFormat);
% end
%
% 4) Demonstrate the use of sliderPanel in a function that
% interactively thresholds an image. (To try this example,
% paste the following code (both TEST and THRESH) into a new
% mfile, save it, and run it.)
%
% function test
% figure;
% ax1 = axes('units','normalized','pos',[0.1 0.25 0.8 0.7]);
% myimg = im2double(imread('cameraman.tif'));
% imobj = imshow(myimg);
% sliderPanel(gcf,{'pos',[0.1 0.05 0.8 0.15]},{'callback',{@thresh,myimg,imobj}},{},{},'%0.1f')
%
% % SUBFUNCTION:
% function thresh(varargin)
% sldr = varargin{1};
% newval = get(sldr,'value');
% myimg = varargin{3};
% imobj = varargin{4};
% set(imobj,'cdata',myimg > newval);
%
% 5) SIMPLIFIED SYNTAX
%
% hFig = gcf;
% [a,b]=sliderPanel(...
% 'Title'   , 'Slider Panel', ...
%   'Position', [0.3, 0.5, 0.4, 0.2], ...
%   'Min'     , 0, ...
%   'Max'     , 100, ...
%   'Value'   , 50, ...
%   'String'  , 'Slider 1', ...
%   'FontName', 'Verdana', ...
%   'units','pixels',...
%   'numformat','%0.0f',...
%   'Callback', 'disp(''slid'')')

% REVISIONS:
% 06/07/2010
% Modified simple syntax to specify label colors the same as background
% colors, unless otherwise set.
%
% 10/10/2012
% Implemented right-click resetting to default (initial) value. Right-click
% anywhere on the slider itself, and the slider and text reset
% automatically.
%
% 1/18/2013 Right-clicking to reset default now (appropriately) triggers
% slider's callback.
%
% 8/14/2014 Modified to facilitate disabling of slider. (Clicking on trough
% had been triggering reset even if 'enable' was set to 'off'. This is
% fixed now.) Re-wrote cellfind to be more efficient. Wrapped handles in
% curly brackets to accommodate change in support of cell concatenation in
% R2014b.  

% Written by Brett Shoelson, PhD
% brett.shoelson@mathworks.com
% 01/21/07
% Copyright 2007 - 2014 MathWorks, Inc.


if nargin < 6, numFormat = []; end
if nargin < 5, LabelPVs = {}; end
if nargin < 4, EditPVs = {}; end
if nargin < 3, SliderPVs = {}; end
if nargin < 2, PanelPVs = {}; end
if nargin == 0, parent = gcf; end

% OPTIONAL CALLING SYNTAX:
% Subset of functionality available for this calling syntax
if nargin > 1 && ~iscell(PanelPVs) && ~isa(PanelPVs,'struct')
	%NOTE: This was added to accommodate R2014b's change in support of
	%combining data and handles. I believe it will not adversely affect
	%anything:
	PanelPVs = {PanelPVs};
	%
	allargs = [parent,PanelPVs,SliderPVs,EditPVs,LabelPVs,numFormat,varargin];
	PanelPVs = {};SliderPVs = {};EditPVs = {};LabelPVs = {};numFormat = [];
	if ishandle(allargs{1}),
		parent = allargs{1};
	end
	loc = cellfind(allargs,'parent');
	if ~isempty(loc)
		parent = allargs{loc+1};
	end
	if ~ishandle(parent)
		parent = gcf;
	end
	loc = cellfind(allargs,'numformat');
	if ~isempty(loc)
		numFormat = allargs{loc+1};
	end
	loc = cellfind(allargs,'numberformat');
	if ~isempty(loc)
		numFormat = allargs{loc+1};
	end
	
	PanelPVs = validate(allargs,PanelPVs,...
		{'title','pos','position','fontname','fontname','fontsize',...
		'fontweight','backgroundcolor','bordertype','tag','units','visible','userdata'});
	SliderPVs = validate(allargs,SliderPVs,...
		{'min','max','value','callback','sliderstep','units'});
	EditPVs = validate(allargs,EditPVs,...
		{'fontname','fontsize','fontweight'});
	LabelPVs = validate(allargs,LabelPVs,{'fontname','fontsize',...
		'fontweight','units'});
	if isfield(PanelPVs,'backgroundcolor') && ~isfield(LabelPVs,'backgroundcolor')
		LabelPVs.backgroundcolor = PanelPVs.backgroundcolor;
	end
end

% CREATE PANEL (DEFAULT)
% Uipanels can be children of figures, uipanels, or
% uibuttongroups; the latter two are of 'type' 'uipanel'

% CREATE UIPANEL AS PARENT
panelHandle = uipanel('parent',parent);

% CREATE SLIDER (DEFAULT)
sliderHandle = uicontrol(panelHandle,'style','slider','units','normalized',...
	'pos',[0.05 0.5 0.9 0.40]);
% CREATE EDIT BOX (DEFAULT)
editHandle = uicontrol(panelHandle,'style','edit','units','normalized',...
	'pos',[0.35 0.05 0.3 0.35]);
% CREATE LABELS (DEFAULT)
labelHandle(1) = uicontrol(panelHandle,'style','text','units','normalized',...
	'pos',[0.05 0.05 0.25 0.25],'horizontalalignment','l',...
	'Backgroundcolor',get(panelHandle,'Backgroundcolor'));
labelHandle(2) = uicontrol(panelHandle,'style','text','units','normalized',...
	'pos',[0.7 0.05 0.25 0.25],'horizontalalignment','r',...
	'Backgroundcolor',get(panelHandle,'Backgroundcolor'));

% CUSTOMIZE PER USER-DEFINED PV PAIRS
applyPVs(panelHandle,PanelPVs);
applyPVs(sliderHandle,SliderPVs);

% EXTRACT SLIDER PARAMETERS
sldr.minval = get(sliderHandle,'min');
sldr.maxval = get(sliderHandle,'max');
sldr.value = get(sliderHandle,'value');
%Ensure that slider's value is in  acceptable range of
%[min,max]
if sldr.value < sldr.minval || sldr.value > sldr.maxval
	disp('Out-of-range value ignored for slider object.');
	sldr.value = sldr.minval;
	set(sliderHandle,'value',sldr.value);
end
sldr.callback = get(sliderHandle,'callback');

set(labelHandle(1),'string',sldr.minval);
set(labelHandle(2),'string',sldr.maxval);
set(editHandle,'string',sldr.value);

applyPVs(editHandle,EditPVs);
% If LabelPVs is a 1x2 array of cells, then cell 1 is
% applied to label 1, and cell 2 is applied to label 2.
% Otherwise,
if numel(LabelPVs)>1 && iscell(LabelPVs{1}) && iscell(LabelPVs{2})
	applyPVs(labelHandle(1),LabelPVs{1});
	applyPVs(labelHandle(2),LabelPVs{2});
else
	applyPVs(labelHandle,LabelPVs);
end
% If a third array of PVs is provided, use it for both
% labels.
if numel(LabelPVs)>2 && iscell(LabelPVs{3})
	applyPVs(labelHandle,LabelPVs{3});
end

%USERDATA IS A CHAR
set(editHandle,'userdata',get(editHandle,'string'));

%GIVE ADDHNDLEVENT/EVALHNDLEVENT A TRY
addHndlEvent(sliderHandle,'callback',@updateText);
addHndlEvent(editHandle,'callback',@updateSlider);

	function applyPVs(obj,pvarray)
		if isstruct(pvarray)
			set(obj,pvarray);
		else %Cell
			if ~isempty(pvarray)
				for ii = 1:2:numel(pvarray)
					set(obj,pvarray{ii},pvarray{ii+1});
				end
				%set(obj,pvarray{:});
			end
		end
		% NEW: 10/10/2012: implement right-click resetting of default
		%      initial value
		if strcmp(get(obj,'type'),'uicontrol')
			if strcmp(get(obj,'style'),'slider')
				val = get(obj,'value');
				set(obj,'buttondownfcn',{@resetDefault,gcbo,val});
			end
		end
	end

	function resetDefault(varargin)
		% fprintf('Within SliderPanel: SelectionType = %s\n',get(gcf,'selectiontype'))
		if strcmp(get(sliderHandle,'enable'),'off')
			return
		end
		currFun = sldr.callback;
		set(varargin{1},'value',varargin{4});
		updateText;
		if ~isempty(currFun)
			try
				if isa(currFun,'function_handle')
					feval(currFun,varargin{1});
				elseif isa(currFun,'cell')
					try
						updateSlider;
						%feval(currFun{:});
					catch
						disp('Unable to trigger reset-action for callbacks that use cell arrays.');
					end
				else
					eval(currFun)
				end
			catch
				disp('Unable to trigger callback; must use a function handle for that capability.')
			end
		end
	end

	function updateText(varargin)
		%Triggered by slider move
		newVal = get(sliderHandle,'value');
		if ~isempty(numFormat)
			newVal = sprintf(numFormat,newVal);
		end
		set(editHandle,'string',newVal,'userdata',newVal);
		drawnow;
	end

	function updateSlider(varargin)
		%Triggered by text change
		newVal = get(editHandle,'string');
		if isnan(str2double(newVal)) || str2double(newVal) < get(sliderHandle,'min') || str2double(newVal) > get(sliderHandle,'max')
			set(editHandle,'string',get(editHandle,'userdata'));
			return
		end
		set(sliderHandle,'value',str2double(newVal));
		set(editHandle,'userdata',newVal);
		set(editHandle,'value',str2double(newVal));%BUG FIX, 7/1/10
		drawnow;
		currFun = sldr.callback;
		if isempty(currFun)
			%Do nothing
		elseif isa(currFun,'function_handle')
			%REPLACE OBJECT HANDLE WITH THAT OF SLIDER
			varargin{1} = sliderHandle;
			currFun(varargin{:});
			%feval(sldr.callback,varargin{:});
		elseif isa(currFun,'char')
			%Just in case the callback specifies GCBO (which
			%will initially point to the edit box, rather
			%than the slider:
			currFun = strrep(currFun,'gcbo','sliderHandle');
			eval(currFun);
		elseif isa(currFun,'cell');
			%REPLACE OBJECT HANDLE WITH THAT OF SLIDER
			varargin{1} = sliderHandle;
			currFun{1}(varargin{:},currFun{2:end});
			%updateSlider(currFun{1})
			%feval(currFun{1},newVal);
		else
			%Shouldn't ever get here...but if you do, I
			%would like to know about it.
			error('Unrecognized event registered in eventid %d.',ii);
		end
	end

	function addHndlEvent(hndl,eventtype,newevent)
		hndlevent = getappdata(hndl,[eventtype 'hndlevent']);
		if isempty(hndlevent)
			%Initialize to original event comand
			hndlevent.cmdset = get(hndl,eventtype);
		end
		if isempty(hndlevent.cmdset)
			numevents = 0;
		else
			numevents = numel(hndlevent);
		end
		hndlevent(numevents+1).cmdset = newevent;
		
		set(hndl,eventtype,@evalHndlEvent);
		setappdata(hndl,[eventtype 'hndlevent'],hndlevent);
		
		function evalHndlEvent(varargin)
			hndlevent = getappdata(hndl,[eventtype 'hndlevent']);
			if nargin < 4
				eventList = 1:numel(hndlevent);
			end
			for ii = eventList
				currFun = hndlevent(ii).cmdset;
				if isempty(currFun)
					continue
				elseif ischar(currFun)
					eval(currFun);
				elseif iscell(currFun)
					currFun{1}(varargin{:},currFun{2:end});
				else
					currFun(varargin{:});
				end
			end
		end
	end

	function PVarray = validate(allargs, PVarray, parameterStrings)
		for ii = 1:numel(parameterStrings)
			parameter = parameterStrings{ii};
			loc = cellfind(allargs, parameter);
			if ~isempty(loc)
				eval(['PVarray.' parameter ' = allargs{loc(1)+1};']);
			end
		end
	end

	function posns = cellfind(cellarray, searchval)
		posns = find(strcmpi(cellarray,searchval));
% 		posns = zeros(numel(cellarray),1);
% 		if ischar(searchval)
% 			searchval = lower(searchval);
% 			for ii = 1:numel(cellarray)
% 				tmp = cellarray{ii};
% 				if ischar(tmp)
% 					tmp = lower(tmp);
% 				end
% 				if isequal(searchval,tmp)
% 					posns(ii) = 1;
% 				end
% 			end
% 			posns = find(posns);
% 		end
	end

end

Contact us