diff --git a/@MyInstrument/MyInstrument.asv b/@MyInstrument/MyInstrument.asv index b50728a..df0d579 100644 --- a/@MyInstrument/MyInstrument.asv +++ b/@MyInstrument/MyInstrument.asv @@ -1,432 +1,440 @@ classdef MyInstrument < dynamicprops properties (SetAccess=protected, GetAccess=public) name=''; interface=''; address=''; - %Logical for whether gui is enabled - enable_gui=false; - %Contains the GUI handles - Gui; %Contains the device object. struct() is a dummy, as Device %needs to always support properties for consistency. Device=struct(); %Input parser for class constructor Parser; %Contains a list of the commands available for the instrument as %well as the default values and input requirements CommandList=struct(); %Parses commands using an inputParser object CommandParser; %Trace object for storing data Trace=MyTrace(); end properties (Constant=true) % Default parameters for VISA connection DEFAULT_INP_BUFF_SIZE = 1e7; % buffer size bytes DEFAULT_OUT_BUFF_SIZE = 1e7; % buffer size bytes DEFAULT_TIMEOUT = 10; % Timeout in s DEFAULT_VISA_BRAND = 'ni'; end properties (Dependent=true) command_names; command_no; write_commands; read_commands; end events NewData; end methods (Access=private) function createParser(this) p=inputParser(); addRequired(p,'interface',@ischar); addRequired(p,'address',@ischar); addParameter(p,'name','',@ischar); - addParameter(p,'gui','',@ischar); addParameter(p,'visa_brand',this.DEFAULT_VISA_BRAND,@ischar); this.Parser=p; end end methods (Access=public) function this=MyInstrument(interface, address, varargin) createParser(this); - parse(this.Parser,interface,address,varargin{:}); - + parse(this.Parser,interface,address,varargin{:}); %Loads parsed variables into class properties this.name=this.Parser.Results.name; this.interface=this.Parser.Results.interface; this.address=this.Parser.Results.address; - this.enable_gui=~ismember('gui',this.Parser.UsingDefaults); - - %If a gui input is given, load the gui - if this.enable_gui - %Loads the gui from the input gui string - this.Gui=guihandles(eval(this.Parser.Results.gui)); - %Sets figure close function such that class will know when - %figure is closed - ind=structfun(@(x) isa(x,'matlab.ui.Figure'),... - this.Gui); - names=fieldnames(this.Gui); - set(this.Gui.(names{ind}), 'CloseRequestFcn',... - @(hObject,eventdata) closeFigure(this, hObject, ... - eventdata)); - end end - function delete(this) - %Removes close function from figure, prevents infinite loop - if this.enable_gui - ind=structfun(@(x) isa(x,'matlab.ui.Figure'),... - this.Gui); - names=fieldnames(this.Gui); - set(this.Gui.(names{ind}), 'CloseRequestFcn',... - @(hObject,eventdata) closeFigure(this, hObject, ... - eventdata)); - %Deletes the figure handles - structfun(@(x) delete(x), this.Gui); - %Removes the figure handle to prevent memory leaks - this.Gui=[]; - end - + function delete(this) %Closes the connection to the device closeDevice(this); %Deletes the device object delete(this.Device); clear('this.Device'); end %Clears data from trace to save memory. function clearData(this) this.Trace.x=[]; this.Trace.y=[]; end %Writes properties to device. Can take multiple inputs. With the %option all, the function writes default to all the %available writeable parameters. function writeProperty(this, varargin) %Parses the inputs using the CommandParser parse(this.CommandParser, varargin{:}); if this.CommandParser.Results.all % If the 'all' is true, write all the commands exec=this.write_commands; else % Check which commands were passed values ind_val=cellfun(@(x)... (~ismember(x, this.CommandParser.UsingDefaults)),... this.write_commands); exec=this.write_commands(ind_val); end for i=1:length(exec) %Creates the write command using the right string spec write_command=[this.CommandList.(exec{i}).command,... ' ',this.CommandList.(exec{i}).str_spec]; %Gets the value to write to the device this.(exec{i})=this.CommandParser.Results.(exec{i}); command=sprintf(write_command, this.(exec{i})); %Sends command to device fprintf(this.Device, command); end end % Wrapper for writeProperty that opens and closes the device function writePropertyHedged(this, varargin) openDevice(this); try writeProperty(this, varargin{:}); catch warning('Error while writing the properties:'); disp(varargin); end readProperty(this, 'all'); closeDevice(this); end function result=readProperty(this, varargin) result = struct(); read_all_flag = any(strcmp('all',varargin)); if read_all_flag % Read all the commands with read access exec=this.read_commands; else ind_r=ismember(varargin,this.read_commands); exec=varargin(ind_r); if any(~ind_r) % Issue warnings for commands not in the command_names warning('The following are not valid read commands:'); disp(varargin(~ind_r)); end end - read_command=join(exec,'?;:'); + read_command=join(cellfun(... + @(cmd)this.CommandList.(cmd).command,exec,... + 'UniformOutput',false),'?;:'); read_command=[read_command{1},'?;']; - res_str = split(query(this.Device,read_command); - - % Iterate over the commands list -% for i=1:length(exec) -% %Creates the correct read command -% read_command=[this.CommandList.(exec{i}).command,'?']; -% %Reads the property from the device and stores it in the -% %correct place -% res_str = query(this.Device,read_command); -% result.(exec{i})=... -% sscanf(res_str,this.CommandList.(exec{i}).str_spec); -% %Assign the values to the MyInstrument properties -% this.(exec{i})=result.(exec{i}); -% end + res_str = query(this.Device,read_command); + % drop the end-of-the-string symbol and split + res_str = split(res_str(1:end-1),';'); + if length(exec)==length(res_str) + for i=1:length(exec) + result.(exec{i})=sscanf(res_str{i},... + this.CommandList.(exec{i}).str_spec); + %Assign the values to the MyInstrument properties + this.(exec{i})=result.(exec{i}); + end + else + warning(['Not all the properties could be read, ',... + 'no instrument class values are updated']); + end end % Wrapper for readProperty that opens and closes the device - function result = readPropertyHedged(this, varargin) + function result=readPropertyHedged(this, varargin) openDevice(this); try result = readProperty(this, varargin{:}); catch warning('Error while reading the properties:'); disp(varargin); end closeDevice(this); end + % Extend the property value based on val_list + function std_val = standardizeValue(this, cmd, varargin) + if ~ismember(cmd,this.command_names) + warning('%s is not a valid command',cmd); + std_val = ''; + return + end + vlist = this.CommandList.(cmd).val_list; + % The value to normalize can be explicitly passed as + % varargin{1}, otherwise use the property value + if isempty(varargin) + val = this.(cmd); + else + val = varargin{1}; + end + % find matching commands + ismatch = false(1,length(vlist)); + for i=1:length(vlist) + n = min([length(val), length(vlist{i})]); + % compare first n symbols disregarding case + ismatch(i) = strncmpi(val, vlist{i},n); + end + % out of matching names pick the longest + if any(ismatch) + mvlist = vlist(ismatch); + str = mvlist{1}; + for i=1:length(mvlist) + if length(mvlist{i})>length(str) + str = mvlist{i}; + end + end + std_val = str; + % set the class + if isempty(varargin) + this.(cmd) = std_val; + end + else + std_val = val; + end + end + % Connects to the device function connectDevice(this, interface, address) try % visa brand, DEFAULT_VISA_BRAND if not specified vb = this.Parser.Results.visa_brand; switch lower(interface) case 'constructor' % in this case the 'address' is a command % (ObjectConstructorName) as returned by the % instrhwinfo this.Device=eval(address); case 'visa' this.Device=visa(vb, address); case 'tcpip' this.Device= visa(vb, sprintf(... 'TCPIP0::%s::inst0::INSTR',this.address)); case 'usb' this.Device=visa(vb, sprintf(... 'USB0::%s::INSTR',address)); otherwise warning('Device is not connected: unknown interface'); end configureDeviceDefault(this); catch warning('Device is not connected'); end end % Opens the device if it is not open function openDevice(this) if ~isopen(this) try fopen(this.Device); catch % try to find and close all the devices with the same % VISA resource name try instr_list=instrfind('RsrcName',this.Device.RsrcName); fclose(instr_list); fopen(this.Device); warning('Multiple instrument objects of address %s exist',... this.address); catch error('Could not open device') end end end end % Closes the connection to the device function closeDevice(this) if isopen(this) fclose(this.Device); end end function configureDeviceDefault(this) dev_prop_list = properties(this.Device); if ismember('OutputBufferSize',dev_prop_list) this.Device.OutputBufferSize = this.DEFAULT_OUT_BUFF_SIZE; end if ismember('InputBufferSize',dev_prop_list) this.Device.InputBufferSize = this.DEFAULT_INP_BUFF_SIZE; end if ismember('Timeout',dev_prop_list) this.Device.Timeout = this.DEFAULT_TIMEOUT; end end %Triggers event for acquired data function triggerNewData(this) notify(this,'NewData') end %Checks if the connection to the device is open function bool=isopen(this) try bool=strcmp(this.Device.Status, 'open'); catch warning('Cannot verify device Status property'); bool=false; end end %Adds a command to the CommandList function addCommand(this, tag, command, varargin) p=inputParser(); addRequired(p,'tag',@ischar); addRequired(p,'command',@ischar); addParameter(p,'default','placeholder'); addParameter(p,'classes',{},@iscell); addParameter(p,'attributes',{},@iscell); addParameter(p,'str_spec','%e',@ischar); % list of the values the variable can take, {} means no % restriction addParameter(p,'val_list',{},@iscell); addParameter(p,'access','rw',@ischar); parse(p,tag,command,varargin{:}); %Adds the command to be sent to the device this.CommandList.(tag).command=command; this.CommandList.(tag).access=p.Results.access; this.CommandList.(tag).write_flag=contains(p.Results.access,'w'); this.CommandList.(tag).read_flag=contains(p.Results.access,'r'); this.CommandList.(tag).default=p.Results.default; this.CommandList.(tag).val_list=p.Results.val_list; % Adds the string specifier to the list. if the format % specifier is not given explicitly, try to infer if ismember('str_spec', p.UsingDefaults) this.CommandList.(tag).str_spec=... formatSpecFromAttributes(this,p.Results.classes... ,p.Results.attributes); elseif strcmp(p.Results.str_spec,'%b') % b is a non-system specifier to represent the % logical type this.CommandList.(tag).str_spec='%i'; else this.CommandList.(tag).str_spec=p.Results.str_spec; end % Adds the attributes for the input to the command. If not % given explicitly, infer from the format specifier if ismember('classes',p.UsingDefaults) [this.CommandList.(tag).classes,... this.CommandList.(tag).attributes]=... AttributesFromFormatSpec(this, p.Results.str_spec); else this.CommandList.(tag).classes=p.Results.classes; this.CommandList.(tag).attributes=p.Results.attributes; end % Adds a property to the class corresponding to the tag if ~isprop(this,tag) addprop(this,tag); end this.(tag)=p.Results.default; end %Creates inputParser using the command list function createCommandParser(this) %Use input parser %Requires input of the appropriate class p=inputParser; p.StructExpand=0; %Flag for whether the command should initialize the device with %defaults addParameter(p, 'all',false,@islogical); for i=1:length(this.write_commands) %Adds optional inputs for each command, with the %appropriate default value from the command list and the %required attributes for the command input. tag=this.write_commands{i}; % Create validation function based on properties: % class, attributes and list of values if ~isempty(this.CommandList.(tag).val_list) v_func = @(x) any(cellfun(@(y) isequal(y, x),... this.CommandList.(tag).val_list)); else v_func = @(x) validateattributes(x,... this.CommandList.(tag).classes,... this.CommandList.(tag).attributes); end addParameter(p, tag,... this.CommandList.(tag).default, v_func); end this.CommandParser=p; end function str_spec=formatSpecFromAttributes(~,classes,attributes) if ismember('char',classes) str_spec='%s'; elseif ismember('logical',classes)||... (ismember('numeric',classes)&&... ismember('integer',attributes)) str_spec='%i'; else %assign default value, i.e. double str_spec='%e'; end end function [class,attribute]=AttributesFromFormatSpec(~, str_spec) % find index of the first letter after the % sign ind_p=strfind(str_spec,'%'); ind=ind_p+find(isletter(str_spec(ind_p:end)),1)-1; str_spec_letter=str_spec(ind); switch str_spec_letter case {'d','f','e','g'} class={'numeric'}; attribute={}; case 'i' class={'numeric'}; attribute={'integer'}; case 's' class={'char'}; attribute={}; case 'b' class={'logical'}; attribute={}; otherwise class={}; attribute={}; end end %Close figure callback simply calls delete function for class function closeFigure(this,~,~) delete(this); end end % Get functions methods function command_names=get.command_names(this) command_names=fieldnames(this.CommandList); end function write_commands=get.write_commands(this) ind_w=structfun(@(x) x.write_flag, this.CommandList); write_commands=this.command_names(ind_w); end function read_commands=get.read_commands(this) ind_r=structfun(@(x) x.read_flag, this.CommandList); read_commands=this.command_names(ind_r); end function command_no=get.command_no(this) command_no=length(this.command_names); end end end \ No newline at end of file diff --git a/@MyInstrument/MyInstrument.m b/@MyInstrument/MyInstrument.m index 9780cdf..04606ab 100644 --- a/@MyInstrument/MyInstrument.m +++ b/@MyInstrument/MyInstrument.m @@ -1,399 +1,440 @@ classdef MyInstrument < dynamicprops properties (SetAccess=protected, GetAccess=public) name=''; interface=''; address=''; %Contains the device object. struct() is a dummy, as Device %needs to always support properties for consistency. Device=struct(); %Input parser for class constructor Parser; %Contains a list of the commands available for the instrument as %well as the default values and input requirements CommandList=struct(); %Parses commands using an inputParser object CommandParser; %Trace object for storing data Trace=MyTrace(); end properties (Constant=true) % Default parameters for VISA connection DEFAULT_INP_BUFF_SIZE = 1e7; % buffer size bytes DEFAULT_OUT_BUFF_SIZE = 1e7; % buffer size bytes DEFAULT_TIMEOUT = 10; % Timeout in s DEFAULT_VISA_BRAND = 'ni'; end properties (Dependent=true) command_names; command_no; write_commands; read_commands; end events NewData; end methods (Access=private) function createParser(this) p=inputParser(); addRequired(p,'interface',@ischar); addRequired(p,'address',@ischar); addParameter(p,'name','',@ischar); addParameter(p,'visa_brand',this.DEFAULT_VISA_BRAND,@ischar); this.Parser=p; end end methods (Access=public) function this=MyInstrument(interface, address, varargin) createParser(this); parse(this.Parser,interface,address,varargin{:}); %Loads parsed variables into class properties this.name=this.Parser.Results.name; this.interface=this.Parser.Results.interface; this.address=this.Parser.Results.address; end function delete(this) %Closes the connection to the device closeDevice(this); %Deletes the device object delete(this.Device); clear('this.Device'); end %Clears data from trace to save memory. function clearData(this) this.Trace.x=[]; this.Trace.y=[]; end %Writes properties to device. Can take multiple inputs. With the %option all, the function writes default to all the %available writeable parameters. function writeProperty(this, varargin) %Parses the inputs using the CommandParser parse(this.CommandParser, varargin{:}); if this.CommandParser.Results.all % If the 'all' is true, write all the commands exec=this.write_commands; else % Check which commands were passed values ind_val=cellfun(@(x)... (~ismember(x, this.CommandParser.UsingDefaults)),... this.write_commands); exec=this.write_commands(ind_val); end for i=1:length(exec) %Creates the write command using the right string spec write_command=[this.CommandList.(exec{i}).command,... ' ',this.CommandList.(exec{i}).str_spec]; %Gets the value to write to the device this.(exec{i})=this.CommandParser.Results.(exec{i}); command=sprintf(write_command, this.(exec{i})); %Sends command to device fprintf(this.Device, command); end end % Wrapper for writeProperty that opens and closes the device function writePropertyHedged(this, varargin) openDevice(this); try writeProperty(this, varargin{:}); catch warning('Error while writing the properties:'); disp(varargin); end readProperty(this, 'all'); closeDevice(this); end function result=readProperty(this, varargin) result = struct(); read_all_flag = any(strcmp('all',varargin)); if read_all_flag % Read all the commands with read access exec=this.read_commands; else ind_r=ismember(varargin,this.read_commands); exec=varargin(ind_r); if any(~ind_r) % Issue warnings for commands not in the command_names warning('The following are not valid read commands:'); disp(varargin(~ind_r)); end end read_command=join(cellfun(... @(cmd)this.CommandList.(cmd).command,exec,... 'UniformOutput',false),'?;:'); read_command=[read_command{1},'?;']; res_str = query(this.Device,read_command); % drop the end-of-the-string symbol and split res_str = split(res_str(1:end-1),';'); if length(exec)==length(res_str) for i=1:length(exec) result.(exec{i})=sscanf(res_str{i},... this.CommandList.(exec{i}).str_spec); %Assign the values to the MyInstrument properties this.(exec{i})=result.(exec{i}); end else warning(['Not all the properties could be read, ',... 'no instrument class values are updated']); end end % Wrapper for readProperty that opens and closes the device - function result = readPropertyHedged(this, varargin) + function result=readPropertyHedged(this, varargin) openDevice(this); try result = readProperty(this, varargin{:}); catch warning('Error while reading the properties:'); disp(varargin); end closeDevice(this); end + % Extend the property value based on val_list + function std_val = standardizeValue(this, cmd, varargin) + if ~ismember(cmd,this.command_names) + warning('%s is not a valid command',cmd); + std_val = ''; + return + end + vlist = this.CommandList.(cmd).val_list; + % The value to normalize can be explicitly passed as + % varargin{1}, otherwise use the property value + if isempty(varargin) + val = this.(cmd); + else + val = varargin{1}; + end + % find matching commands + ismatch = false(1,length(vlist)); + for i=1:length(vlist) + n = min([length(val), length(vlist{i})]); + % compare first n symbols disregarding case + ismatch(i) = strncmpi(val, vlist{i},n); + end + % out of matching names pick the longest + if any(ismatch) + mvlist = vlist(ismatch); + str = mvlist{1}; + for i=1:length(mvlist) + if length(mvlist{i})>length(str) + str = mvlist{i}; + end + end + std_val = str; + % set the property if value was not given explicitly + if isempty(varargin) + this.(cmd) = std_val; + end + else + std_val = val; + end + end + % Connects to the device function connectDevice(this, interface, address) try % visa brand, DEFAULT_VISA_BRAND if not specified vb = this.Parser.Results.visa_brand; switch lower(interface) case 'constructor' % in this case the 'address' is a command % (ObjectConstructorName) as returned by the % instrhwinfo this.Device=eval(address); case 'visa' this.Device=visa(vb, address); case 'tcpip' this.Device= visa(vb, sprintf(... 'TCPIP0::%s::inst0::INSTR',this.address)); case 'usb' this.Device=visa(vb, sprintf(... 'USB0::%s::INSTR',address)); otherwise warning('Device is not connected: unknown interface'); end configureDeviceDefault(this); catch warning('Device is not connected'); end end % Opens the device if it is not open function openDevice(this) if ~isopen(this) try fopen(this.Device); catch % try to find and close all the devices with the same % VISA resource name try instr_list=instrfind('RsrcName',this.Device.RsrcName); fclose(instr_list); fopen(this.Device); warning('Multiple instrument objects of address %s exist',... this.address); catch error('Could not open device') end end end end % Closes the connection to the device function closeDevice(this) if isopen(this) fclose(this.Device); end end function configureDeviceDefault(this) dev_prop_list = properties(this.Device); if ismember('OutputBufferSize',dev_prop_list) this.Device.OutputBufferSize = this.DEFAULT_OUT_BUFF_SIZE; end if ismember('InputBufferSize',dev_prop_list) this.Device.InputBufferSize = this.DEFAULT_INP_BUFF_SIZE; end if ismember('Timeout',dev_prop_list) this.Device.Timeout = this.DEFAULT_TIMEOUT; end end %Triggers event for acquired data function triggerNewData(this) notify(this,'NewData') end %Checks if the connection to the device is open function bool=isopen(this) try bool=strcmp(this.Device.Status, 'open'); catch warning('Cannot verify device Status property'); bool=false; end end %Adds a command to the CommandList function addCommand(this, tag, command, varargin) p=inputParser(); addRequired(p,'tag',@ischar); addRequired(p,'command',@ischar); addParameter(p,'default','placeholder'); addParameter(p,'classes',{},@iscell); addParameter(p,'attributes',{},@iscell); addParameter(p,'str_spec','%e',@ischar); % list of the values the variable can take, {} means no % restriction addParameter(p,'val_list',{},@iscell); addParameter(p,'access','rw',@ischar); parse(p,tag,command,varargin{:}); %Adds the command to be sent to the device this.CommandList.(tag).command=command; this.CommandList.(tag).access=p.Results.access; this.CommandList.(tag).write_flag=contains(p.Results.access,'w'); this.CommandList.(tag).read_flag=contains(p.Results.access,'r'); this.CommandList.(tag).default=p.Results.default; this.CommandList.(tag).val_list=p.Results.val_list; % Adds the string specifier to the list. if the format % specifier is not given explicitly, try to infer if ismember('str_spec', p.UsingDefaults) this.CommandList.(tag).str_spec=... formatSpecFromAttributes(this,p.Results.classes... ,p.Results.attributes); elseif strcmp(p.Results.str_spec,'%b') % b is a non-system specifier to represent the % logical type this.CommandList.(tag).str_spec='%i'; else this.CommandList.(tag).str_spec=p.Results.str_spec; end % Adds the attributes for the input to the command. If not % given explicitly, infer from the format specifier if ismember('classes',p.UsingDefaults) [this.CommandList.(tag).classes,... this.CommandList.(tag).attributes]=... AttributesFromFormatSpec(this, p.Results.str_spec); else this.CommandList.(tag).classes=p.Results.classes; this.CommandList.(tag).attributes=p.Results.attributes; end % Adds a property to the class corresponding to the tag if ~isprop(this,tag) addprop(this,tag); end this.(tag)=p.Results.default; end %Creates inputParser using the command list function createCommandParser(this) %Use input parser %Requires input of the appropriate class p=inputParser; p.StructExpand=0; %Flag for whether the command should initialize the device with %defaults addParameter(p, 'all',false,@islogical); for i=1:length(this.write_commands) %Adds optional inputs for each command, with the %appropriate default value from the command list and the %required attributes for the command input. tag=this.write_commands{i}; % Create validation function based on properties: % class, attributes and list of values if ~isempty(this.CommandList.(tag).val_list) v_func = @(x) any(cellfun(@(y) isequal(y, x),... this.CommandList.(tag).val_list)); else v_func = @(x) validateattributes(x,... this.CommandList.(tag).classes,... this.CommandList.(tag).attributes); end addParameter(p, tag,... this.CommandList.(tag).default, v_func); end this.CommandParser=p; end function str_spec=formatSpecFromAttributes(~,classes,attributes) if ismember('char',classes) str_spec='%s'; elseif ismember('logical',classes)||... (ismember('numeric',classes)&&... ismember('integer',attributes)) str_spec='%i'; else %assign default value, i.e. double str_spec='%e'; end end function [class,attribute]=AttributesFromFormatSpec(~, str_spec) % find index of the first letter after the % sign ind_p=strfind(str_spec,'%'); ind=ind_p+find(isletter(str_spec(ind_p:end)),1)-1; str_spec_letter=str_spec(ind); switch str_spec_letter case {'d','f','e','g'} class={'numeric'}; attribute={}; case 'i' class={'numeric'}; attribute={'integer'}; case 's' class={'char'}; attribute={}; case 'b' class={'logical'}; attribute={}; otherwise class={}; attribute={}; end end %Close figure callback simply calls delete function for class function closeFigure(this,~,~) delete(this); end end % Get functions methods function command_names=get.command_names(this) command_names=fieldnames(this.CommandList); end function write_commands=get.write_commands(this) ind_w=structfun(@(x) x.write_flag, this.CommandList); write_commands=this.command_names(ind_w); end function read_commands=get.read_commands(this) ind_r=structfun(@(x) x.read_flag, this.CommandList); read_commands=this.command_names(ind_r); end function command_no=get.command_no(this) command_no=length(this.command_names); end end end \ No newline at end of file diff --git a/GUIs/GuiScope.mlapp b/GUIs/GuiScope.mlapp index a87d4a5..47179ee 100644 Binary files a/GUIs/GuiScope.mlapp and b/GUIs/GuiScope.mlapp differ diff --git a/Utility functions/App utilities/linkControlElement.m b/Utility functions/App utilities/linkControlElement.asv similarity index 94% copy from Utility functions/App utilities/linkControlElement.m copy to Utility functions/App utilities/linkControlElement.asv index 8dd1cf1..439a9b3 100644 --- a/Utility functions/App utilities/linkControlElement.m +++ b/Utility functions/App utilities/linkControlElement.asv @@ -1,30 +1,31 @@ -function linkControlElement(app, elem, prop_tag, varargin) - p=inputParser(); - addRequired(p,'elem'); - addRequired(p,'prop_tag',@ischar); - addParameter(p,'input_presc',1,@isnumeric); - addParameter(p,'create_callback_fcn',@(x)0,@(f)isa(f,'function_handle')); - parse(p,elem,prop_tag,varargin{:}); - - % The property-control link is established by assigning the tag - % and adding the control to the list of linked elements - elem.Tag = prop_tag; - app.linked_elem_list = [app.linked_elem_list, elem]; - - % If the create_callback_fcn is set, assign it to the - % ValueChangedFcn which passes the field input to the instument - if ~ismember('create_callback_fcn',p.UsingDefaults) - elem.ValueChangedFcn = feval(p.Results.create_callback_fcn); - end - - % If the prescaler is indicated, add it to the element as a new property - if p.Results.input_presc ~= 1 - if isprop(elem, 'InputPrescaler') - warning('The InputPrescaler property already exists in the control element'); - else - addprop(elem,'InputPrescaler'); - end - elem.InputPrescaler = p.Results.input_presc; - end -end - +function linkControlElement(app, elem, prop_tag, varargin) + p=inputParser(); + addRequired(p,'elem'); + addRequired(p,'prop_tag',@ischar); + addParameter(p,'input_presc',1,@isnumeric); + addParameter(p,'out_proc_fcn',@(x)0,@(f)isa(f,'function_handle')); + addParameter(p,'create_callback_fcn',@(x)0,@(f)isa(f,'function_handle')); + parse(p,elem,prop_tag,varargin{:}); + + % The property-control link is established by assigning the tag + % and adding the control to the list of linked elements + elem.Tag = prop_tag; + app.linked_elem_list = [app.linked_elem_list, elem]; + + % If the create_callback_fcn is set, assign it to the + % ValueChangedFcn which passes the field input to the instument + if ~ismember('create_callback_fcn',p.UsingDefaults) + elem.ValueChangedFcn = feval(p.Results.create_callback_fcn); + end + + % If the prescaler is indicated, add it to the element as a new property + if p.Results.input_presc ~= 1 + if isprop(elem, 'InputPrescaler') + warning('The InputPrescaler property already exists in the control element'); + else + addprop(elem,'InputPrescaler'); + end + elem.InputPrescaler = p.Results.input_presc; + end +end + diff --git a/Utility functions/App utilities/linkControlElement.m b/Utility functions/App utilities/linkControlElement.m index 8dd1cf1..6eca53c 100644 --- a/Utility functions/App utilities/linkControlElement.m +++ b/Utility functions/App utilities/linkControlElement.m @@ -1,30 +1,41 @@ function linkControlElement(app, elem, prop_tag, varargin) p=inputParser(); addRequired(p,'elem'); addRequired(p,'prop_tag',@ischar); addParameter(p,'input_presc',1,@isnumeric); + addParameter(p,'out_proc_fcn',@(x)0,@(f)isa(f,'function_handle')); addParameter(p,'create_callback_fcn',@(x)0,@(f)isa(f,'function_handle')); parse(p,elem,prop_tag,varargin{:}); % The property-control link is established by assigning the tag % and adding the control to the list of linked elements elem.Tag = prop_tag; app.linked_elem_list = [app.linked_elem_list, elem]; % If the create_callback_fcn is set, assign it to the % ValueChangedFcn which passes the field input to the instument if ~ismember('create_callback_fcn',p.UsingDefaults) elem.ValueChangedFcn = feval(p.Results.create_callback_fcn); end % If the prescaler is indicated, add it to the element as a new property if p.Results.input_presc ~= 1 if isprop(elem, 'InputPrescaler') warning('The InputPrescaler property already exists in the control element'); else addprop(elem,'InputPrescaler'); end elem.InputPrescaler = p.Results.input_presc; end + + % add an arbitrary function for output processing + if ~ismember('out_proc_fcn',p.UsingDefaults) + if isprop(elem, 'OutputProcessingFcn') + warning('The OutputProcessingFcn property already exists in the control element'); + else + addprop(elem,'OutputProcessingFcn'); + end + elem.OutputProcessingFcn = p.Results.out_proc_fcn; + end end diff --git a/Utility functions/App utilities/updateGui.m b/Utility functions/App utilities/updateGui.m index 6e59e55..27fcd51 100644 --- a/Utility functions/App utilities/updateGui.m +++ b/Utility functions/App utilities/updateGui.m @@ -1,16 +1,18 @@ % update all the linked control elements according to their counterpart properties function updateGui(app) % Instrument is a MyInstrument object for i=1:length(app.linked_elem_list) tmpelem = app.linked_elem_list(i); if isprop(app.Instr, tmpelem.Tag) tmpval = app.Instr.(tmpelem.Tag); % scale the value if the control element has a prescaler - if isprop(tmpelem, 'InputPrescaler') + if isprop(tmpelem, 'OutputProcessingFcn') + tmpval = tmpelem.OutputProcessingFcn(tmpval); + elseif isprop(tmpelem, 'InputPrescaler') tmpval = tmpval*tmpelem.InputPrescaler; end tmpelem.Value = tmpval; end end end