diff --git a/@MyDaq/MyDaq.m b/@MyDaq/MyDaq.m index 46c152f..788d3fc 100644 --- a/@MyDaq/MyDaq.m +++ b/@MyDaq/MyDaq.m @@ -1,927 +1,948 @@ classdef MyDaq < handle properties %Contains GUI handles Gui; %Contains Reference trace (MyTrace object) Ref; %Contains Data trace (MyTrace object) Data; %Contains Background trace (MyTrace object) Background; %List of all the programs with run files ProgramList=struct(); % List of running programs RunningPrograms=struct(); %Struct containing Cursor objects Cursors=struct(); %Struct containing Cursor labels CrsLabels=struct(); %Struct containing MyFit objects Fits=struct(); %Input parser for class constructor ConstructionParser; %Struct for listeners Listeners=struct(); %Sets the colors of fits, data and reference fit_color='k'; data_color='b'; ref_color='r'; bg_color='c'; - %Properties for saving files - base_dir; - session_name; - file_name; - %Flag for enabling the GUI enable_gui; end properties (Dependent=true) save_dir; main_plot; open_fits; open_crs; - savefile; running_progs; + %Properties for saving files + base_dir; + session_name; + filename; end methods (Access=public) %% Class functions %Constructor function function this=MyDaq(varargin) p=inputParser; addParameter(p,'enable_gui',1); this.ConstructionParser=p; parse(p, varargin{:}); %Sets the class variables to the inputs from the inputParser. for i=1:length(p.Parameters) %Takes the value from the inputParser to the appropriate %property. if isprop(this, p.Parameters{i}) this.(p.Parameters{i})= p.Results.(p.Parameters{i}); end end - this.base_dir='M:\Measurement Campaigns\'; this.ProgramList = readRunFiles(); if this.enable_gui this.Gui=guihandles(eval('GuiDaq')); initGui(this); % Initialize the menu based on the available run files content = menuFromRunFiles(this.ProgramList,... 'show_in_daq',true); set(this.Gui.InstrMenu,'String',[{'Select the application'};... content.titles]); % Add a property to the menu for storing the program file % names if ~isprop(this.Gui.InstrMenu, 'ItemsData') addprop(this.Gui.InstrMenu, 'ItemsData'); end set(this.Gui.InstrMenu,'ItemsData',[{''};... content.tags]); set(this.Gui.BaseDir,'String',this.base_dir); hold(this.main_plot,'on'); end %Initializes empty trace objects this.Ref=MyTrace(); this.Data=MyTrace(); this.Background=MyTrace(); + + %Initializes saving locations + this.base_dir='M:\Measurement Campaigns\'; + this.session_name='placeholder'; + this.filename='placeholder'; end function delete(this) %Deletes the MyFit objects and their listeners cellfun(@(x) deleteListeners(this,x), this.open_fits); structfun(@(x) delete(x), this.Fits); %Close the programs and delete their listeners cellfun(@(x) deleteListeners(this, x), this.running_progs); structfun(@(x) delete(x), this.RunningPrograms); if this.enable_gui this.Gui.figure1.CloseRequestFcn=''; %Deletes the figure delete(this.Gui.figure1); %Removes the figure handle to prevent memory leaks this.Gui=[]; end end end methods (Access=private) %Sets callback functions for the GUI initGui(this) %Executes when the GUI is closed function closeFigure(this,~,~) delete(this); end %Updates fits function updateFits(this) %Pushes data into fits in the form of MyTrace objects, so that %units etc follow. Also updates user supplied parameters. for i=1:length(this.open_fits) switch this.open_fits{i} case {'Linear','Quadratic','Gaussian',... 'Exponential','Beta'} this.Fits.(this.open_fits{i}).Data=... getFitData(this,'VertData'); case {'Lorentzian','DoubleLorentzian'} this.Fits.(this.open_fits{i}).Data=... getFitData(this,'VertData'); if isfield(this.Cursors,'VertRef') ind=findCursorData(this,'Data','VertRef'); this.Fits.(this.open_fits{i}).CalVals.line_spacing=... range(this.Data.x(ind)); end case {'G0'} this.Fits.G0.MechTrace=getFitData(this,'VertData'); this.Fits.G0.CalTrace=getFitData(this,'VertRef'); end end end % If vertical cursors are on, takes only data %within cursors. %If the cursor is not open, it takes all the data from the selected %trace in the analysis trace selection dropdown function Trace=getFitData(this,varargin) %Parses varargin input p=inputParser; addOptional(p,'name','',@ischar); parse(p,varargin{:}) name=p.Results.name; %Finds out which trace the user wants to fit. trc_opts=this.Gui.SelTrace.String; trc_str=trc_opts{this.Gui.SelTrace.Value}; % Note the use of copy here! This is a handle %class, so if normal assignment is used, this.Fits.Data and %this.(trace_str) will refer to the same object, causing roblems. %Name input is the name of the cursor to be used to extract data. Trace=copy(this.(trc_str)); if isfield(this.Cursors,name) ind=findCursorData(this, trc_str, name); Trace.x=this.(trc_str).x(ind); Trace.y=this.(trc_str).y(ind); end end %Finds data between named cursors in the given trace function ind=findCursorData(this, trc_str, name) crs_pos=sort([this.Cursors.(name){1}.Location,... this.Cursors.(name){2}.Location]); ind=(this.(trc_str).x>crs_pos(1) & this.(trc_str).x %Prints the figure to the clipboard print(newFig,'-clipboard','-dbitmap'); %Deletes the figure delete(newFig); end function updateAxis(this) axis(this.main_plot,'tight'); end end methods (Access=public) %% Callbacks %Callback for copying the plot to clipboard function copyPlotCallback(this,~,~) copyPlot(this); end %Callback for centering cursors function centerCursorsCallback(this, ~, ~) if ~this.Gui.LogX.Value x_pos=mean(this.main_plot.XLim); else x_pos=10^(mean(log10(this.main_plot.XLim))); end if ~this.Gui.LogY.Value y_pos=mean(this.main_plot.YLim); else y_pos=10^(mean(log10(this.main_plot.YLim))); end for i=1:length(this.open_crs) switch this.Cursors.(this.open_crs{i}){1}.Orientation case 'horizontal' pos=y_pos; case 'vertical' pos=x_pos; end %Centers the position cellfun(@(x) set(x,'Location',pos), ... this.Cursors.(this.open_crs{i})); %Triggers the UpdateCursorBar event, which triggers %listener callback to reposition text cellfun(@(x) notify(x,'UpdateCursorBar'),... this.Cursors.(this.open_crs{i})); cellfun(@(x) notify(x,'EndDrag'),... this.Cursors.(this.open_crs{i})); end end %Callback for creating vertical data cursors function cursorButtonCallback(this, hObject, ~) name=erase(hObject.Tag,'Button'); %Gets the first four characters of the tag (Vert or Horz) type=name(1:4); if hObject.Value hObject.BackgroundColor=[0,1,0.2]; createCursors(this,name,type); else hObject.BackgroundColor=[0.941,0.941,0.941]; deleteCursors(this,name); end end %Callback for the instrument menu function instrMenuCallback(this,hObject,~) val=hObject.Value; if val==1 %Returns if we are on the dummy option ('Select instrument') return else tag = hObject.ItemsData{val}; end % Run-files themselves are supposed to prevent duplicated % instances, but let DAQ handle it as well for safety if ismember(tag, this.running_progs) && ... isvalid(this.RunningPrograms.(tag)) % Change focus to the instrument control window fig_handle = findfigure(this.RunningPrograms.(tag)); % If unable, try the same for .Gui object inside if isempty(fig_handle) && ... isprop(this.RunningPrograms.(tag),'Gui') fig_handle =... findfigure(this.RunningPrograms.(tag).Gui); end if ~isempty(fig_handle) fig_handle.Visible='off'; fig_handle.Visible='on'; return else warning('%s shows as open, but no open GUI was found',... tag); return end end try [~, fname, ~] = fileparts(this.ProgramList.(tag).fullname); prog = feval(fname); this.RunningPrograms.(tag) = evalin('base', prog); catch error('Cannot run %s', this.ProgramList.(tag).fullname) end % Add listeners to the NewData and ObjectBeingDestroyed events if contains('NewData',events(this.RunningPrograms.(tag))) this.Listeners.(tag).NewData=... addlistener(this.RunningPrograms.(tag),'NewData',... @(src, eventdata) acquireNewData(this, src, eventdata)); % Compatibility with apps, which have .Instr property elseif isprop(this.RunningPrograms.(tag),'Instr') && ... contains('NewData',events(this.RunningPrograms.(tag).Instr)) this.Listeners.(tag).NewData=... addlistener(this.RunningPrograms.(tag).Instr,'NewData',... @(src, eventdata) acquireNewData(this, src, eventdata)); else warning(['No NewData event found, %s cannot transfer',... ' data to the DAQ'],tag); end this.Listeners.(tag).Deletion=... addlistener(this.RunningPrograms.(tag),... 'ObjectBeingDestroyed',... @(src, eventdata) removeProgram(this, src, eventdata)); end %Select trace callback function selTraceCallback(this, ~, ~) updateFits(this) end %Saves the data if the save data button is pressed. function saveDataCallback(this, ~, ~) if this.Data.validatePlot - save(this.Data,'save_dir',this.save_dir,'name',... - this.savefile) + save(this.Data,'save_dir',this.save_dir,'filename',... + this.filename) else - errdlg('Data trace was empty, could not save'); + errordlg('Data trace was empty, could not save'); end end %Saves the reference if the save ref button is pressed. function saveRefCallback(this, ~, ~) if this.Data.validatePlot - save(this.Ref,'save_dir',this.save_dir,'name',... - this.savefile) + save(this.Ref,'save_dir',this.save_dir,'filename',... + this.filename) else - errdlg('Reference trace was empty, could not save') + errordlg('Reference trace was empty, could not save') end end %Toggle button callback for showing the data trace. function showDataCallback(this, hObject, ~) if hObject.Value hObject.BackgroundColor=[0,1,0.2]; setVisible(this.Data,this.main_plot,1); updateAxis(this); else hObject.BackgroundColor=[0.941,0.941,0.941]; setVisible(this.Data,this.main_plot,0); updateAxis(this); end end %Toggle button callback for showing the ref trace function showRefCallback(this, hObject, ~) if hObject.Value hObject.BackgroundColor=[0,1,0.2]; setVisible(this.Ref,this.main_plot,1); updateAxis(this); else hObject.BackgroundColor=[0.941,0.941,0.941]; setVisible(this.Ref,this.main_plot,0); updateAxis(this); end end %Callback for moving the data to reference. function dataToRefCallback(this, ~, ~) if this.Data.validatePlot this.Ref.x=this.Data.x; this.Ref.y=this.Data.y; this.Ref.plotTrace(this.main_plot,'Color',this.ref_color,... 'make_labels',true); this.Ref.setVisible(this.main_plot,1); updateFits(this); this.Gui.ShowRef.Value=1; this.Gui.ShowRef.BackgroundColor=[0,1,0.2]; else warning('Data trace was empty, could not move to reference') end end %Callback for ref to bg button. Sends the reference to background function refToBgCallback(this, ~, ~) if this.Ref.validatePlot this.Background.x=this.Ref.x; this.Background.y=this.Ref.y; this.Background.plotTrace(this.main_plot,... 'Color',this.bg_color,'make_labels',true); this.Background.setVisible(this.main_plot,1); else warning('Reference trace was empty, could not move to background') end end %Callback for data to bg button. Sends the data to background function dataToBgCallback(this, ~, ~) if this.Data.validatePlot this.Background.x=this.Data.x; this.Background.y=this.Data.y; this.Background.plotTrace(this.main_plot,... 'Color',this.bg_color,'make_labels',true); this.Background.setVisible(this.main_plot,1); else warning('Data trace was empty, could not move to background') end end %Callback for clear background button. Clears the background function clearBgCallback(this, ~, ~) this.Background.x=[]; this.Background.y=[]; this.Background.setVisible(this.main_plot,0); end %Callback for LogY button. Sets the YScale to log/lin function logYCallback(this, hObject, ~) if hObject.Value this.main_plot.YScale='Log'; hObject.BackgroundColor=[0,1,0.2]; else this.main_plot.YScale='Linear'; hObject.BackgroundColor=[0.941,0.941,0.941]; end updateAxis(this); updateCursors(this); end %Callback for LogX button. Sets the XScale to log/lin. Updates the %axis and cursors afterwards. function logXCallback(this, hObject, ~) if get(hObject,'Value') set(this.main_plot,'XScale','Log'); set(hObject, 'BackgroundColor',[0,1,0.2]); else set(this.main_plot,'XScale','Linear'); set(hObject, 'BackgroundColor',[0.941,0.941,0.941]); end updateAxis(this); updateCursors(this); end %Base directory callback. Sets the base directory. Also %updates fit objects with the new save directory. - function baseDirCallback(this, hObject, ~) - this.base_dir=hObject.String; + function baseDirCallback(this, ~, ~) for i=1:length(this.open_fits) - this.Fits.(this.open_fits{i}).save_dir=this.save_dir; + this.Fits.(this.open_fits{i}).base_dir=this.base_dir; end end %Callback for session name edit box. Sets the session name. Also %updates fit objects with the new save directory. - function sessionNameCallback(this, hObject, ~) - this.session_name=hObject.String; + function sessionNameCallback(this, ~, ~) for i=1:length(this.open_fits) - this.Fits.(this.open_fits{i}).save_dir=this.save_dir; + this.Fits.(this.open_fits{i}).session_name=this.session_name; end end %Callback for filename edit box. Sets the file name. Also %updates fit objects with the new file name. - function fileNameCallback(this, hObject,~) - this.file_name=hObject.String; + function fileNameCallback(this, ~,~) for i=1:length(this.open_fits) - this.Fits.(this.open_fits{i}).save_name=this.file_name; + this.Fits.(this.open_fits{i}).filename=this.filename; end end %Callback for the analyze menu (popup menu for selecting fits). %Opens the correct MyFit object. function analyzeMenuCallback(this, hObject, ~) analyze_ind=hObject.Value; %Finds the correct fit name by erasing spaces and other %superfluous strings analyze_name=hObject.String{analyze_ind}; analyze_name=erase(analyze_name,'Fit'); analyze_name=erase(analyze_name,'Calibration'); analyze_name=erase(analyze_name,' '); switch analyze_name case {'Linear','Quadratic','Lorentzian','Gaussian',... 'DoubleLorentzian'} openMyFit(this,analyze_name); case 'g0' openMyG(this); case 'Beta' openMyBeta(this); end end function openMyFit(this,fit_name) %Sees if the MyFit object is already open, if it is, changes the %focus to it, if not, opens it. if ismember(fit_name,fieldnames(this.Fits)) %Changes focus to the relevant fit window figure(this.Fits.(fit_name).Gui.Window); else %Gets the data for the fit using the getFitData function %with the vertical cursors DataTrace=getFitData(this,'VertData'); %Makes an instance of MyFit with correct parameters. this.Fits.(fit_name)=MyFit(... 'fit_name',fit_name,... 'enable_plot',1,... 'plot_handle',this.main_plot,... 'Data',DataTrace,... - 'save_dir',this.save_dir,... - 'save_name',this.file_name); + 'base_dir',this.base_dir,... + 'session_name',this.session_name,... + 'filename',this.filename); updateFits(this); %Sets up a listener for the Deletion event, which %removes the MyFit object from the Fits structure if it is %deleted. this.Listeners.(fit_name).Deletion=... addlistener(this.Fits.(fit_name),'ObjectBeingDestroyed',... @(src, eventdata) deleteFit(this, src, eventdata)); %Sets up a listener for the NewFit. Callback plots the fit %on the main plot. this.Listeners.(fit_name).NewFit=... addlistener(this.Fits.(fit_name),'NewFit',... @(src, eventdata) plotNewFit(this, src, eventdata)); %Sets up a listener for NewInitVal this.Listeners.(fit_name).NewInitVal=... addlistener(this.Fits.(fit_name),'NewInitVal',... @(~,~) updateCursors(this)); end end %Opens MyG class if it is not open. function openMyG(this) if ismember('G0',this.open_fits) figure(this.Fits.G0.Gui.figure1); else MechTrace=getFitData(this,'VertData'); CalTrace=getFitData(this,'VertRef'); this.Fits.G0=MyG('MechTrace',MechTrace,'CalTrace',CalTrace,... 'name','G0'); %Adds listener for object being destroyed this.Listeners.G0.Deletion=addlistener(this.Fits.G0,... 'ObjectBeingDestroyed',... @(~,~) deleteObj(this,'G0')); end end %Opens MyBeta class if it is not open. function openMyBeta(this) if ismember('Beta', this.open_fits) figure(this.Fits.Beta.Gui.figure1); else DataTrace=getFitData(this); this.Fits.Beta=MyBeta('Data',DataTrace); %Adds listener for object being destroyed, to perform cleanup this.Listeners.Beta.Deletion=addlistener(this.Fits.Beta,... 'ObjectBeingDestroyed',... @(~,~) deleteObj(this,'Beta')); end end %Callback for load data button function loadDataCallback(this, ~, ~) if isempty(this.base_dir) warning('Please input a valid folder name for loading a trace'); this.base_dir=pwd; end try [load_name,path_name]=uigetfile('.txt','Select the trace',... this.base_dir); load_path=[path_name,load_name]; dest_trc=this.Gui.DestTrc.String{this.Gui.DestTrc.Value}; loadTrace(this.(dest_trc),load_path); this.(dest_trc).plotTrace(this.main_plot,... 'Color',this.(sprintf('%s_color',lower(dest_trc))),... 'make_labels',true); updateAxis(this); updateCursors(this); catch error('Please select a valid file'); end end end methods (Access=public) %% Listener functions %Callback function for NewFit listener. Plots the fit in the %window using the plotFit function of the MyFit object function plotNewFit(this, src, ~) src.plotFit('Color',this.fit_color); updateAxis(this); updateCursors(this); end %Callback function for the NewData listener function acquireNewData(this, src, ~) hline=getLineHandle(this.Data,this.main_plot); this.Data=copy(src.Trace); if ~isempty(hline); this.Data.hlines{1}=hline; end clearData(src.Trace); this.Data.plotTrace(this.main_plot,'Color',this.data_color,... 'make_labels',true) updateAxis(this); updateCursors(this); updateFits(this); end %Deletes the object from the RunningPrograms struct function removeProgram(this, src, ~) % Find the object that is being deleted ind=cellfun(@(x) isequal(this.RunningPrograms.(x), src),... this.running_progs); tag=this.running_progs{ind}; this.RunningPrograms=rmfield(this.RunningPrograms,tag); %Deletes the listeners from the Listeners struct deleteListeners(this, tag); end %Callback function for MyFit ObjectBeingDestroyed listener. %Removes the relevant field from the Fits struct and deletes the %listeners from the object. function deleteFit(this, src, ~) %Deletes the object from the Fits struct and deletes listeners deleteObj(this,src.fit_name); %Clears the fits src.clearFit; %Updates cursors since the fits are removed from the plot updateCursors(this); end %Callback function for other analysis method deletion listeners. %Does the same as above. function deleteObj(this,name) if ismember(name,this.open_fits) this.Fits=rmfield(this.Fits,name); end deleteListeners(this, name); end %Listener update function for vertical cursor function vertCursorUpdate(this, src) %Finds the index of the cursor. All cursors are tagged %(name)1, (name)2, e.g. VertData2, ind is the number. ind=str2double(src.Tag(end)); tag=src.Tag(1:(end-1)); %Moves the cursor labels set(this.CrsLabels.(tag){ind},'Position',[src.Location,... this.CrsLabels.(tag){ind}.Position(2),0]); if strcmp(tag,'VertData') %Sets the edit box displaying the location of the cursor this.Gui.(sprintf('EditV%d',ind)).String=... num2str(src.Location); %Sets the edit box displaying the difference in locations this.Gui.EditV2V1.String=... num2str(this.Cursors.VertData{2}.Location-... this.Cursors.VertData{1}.Location); end end %Listener update function for horizontal cursor function horzCursorUpdate(this, src) %Finds the index of the cursor. All cursors are tagged %(name)1, (name)2, e.g. VertData2, ind is the number. ind=str2double(src.Tag(end)); tag=src.Tag(1:(end-1)); %Moves the cursor labels set(this.CrsLabels.(tag){ind},'Position',... [this.CrsLabels.(tag){ind}.Position(1),... src.Location,0]); if strcmp(tag,'HorzData') %Sets the edit box displaying the location of the cursor this.Gui.(sprintf('EditH%d',ind)).String=... num2str(src.Location); %Sets the edit box displaying the difference in locations this.Gui.EditH2H1.String=... num2str(this.Cursors.HorzData{2}.Location-... this.Cursors.HorzData{1}.Location); end end %Function that deletes listeners from the listeners struct, %corresponding to an object of name obj_name function deleteListeners(this, obj_name) %Finds if the object has listeners in the listeners structure if ismember(obj_name, fieldnames(this.Listeners)) %Grabs the fieldnames of the object's listeners structure names=fieldnames(this.Listeners.(obj_name)); for i=1:length(names) %Deletes the listeners delete(this.Listeners.(obj_name).(names{i})); %Removes the field from the structure this.Listeners.(obj_name)=... rmfield(this.Listeners.(obj_name),names{i}); end %Removes the object's field from the structure this.Listeners=rmfield(this.Listeners, obj_name); end end end methods - %% Set functions - function set.base_dir(this,base_dir) - if ~strcmp(base_dir(end),'\') - base_dir(end+1)='\'; - end - this.base_dir=base_dir; - end + %% Get functions %Get function from save directory function save_dir=get.save_dir(this) save_dir=createSessionPath(this.base_dir,this.session_name); end %Get function for the plot handles function main_plot=get.main_plot(this) if this.enable_gui main_plot=this.Gui.figure1.CurrentAxes; else main_plot=[]; end end %Get function for open fits function open_fits=get.open_fits(this) open_fits=fieldnames(this.Fits); end function running_progs=get.running_progs(this) running_progs=fieldnames(this.RunningPrograms); end - %Generates appropriate file name for the save file. - function savefile=get.savefile(this) - if get(this.Gui.AutoName,'Value') - date_time = datestr(now,'yyyy-mm-dd_HHMMSS'); - else - date_time=''; - end - - savefile=[this.file_name,date_time]; - end - %Get function that displays names of open cursors function open_crs=get.open_crs(this) open_crs=fieldnames(this.Cursors); end + + function base_dir=get.base_dir(this) + try + base_dir=this.Gui.BaseDir.String; + catch + base_dir=pwd; + end + end + + function set.base_dir(this,base_dir) + this.Gui.BaseDir.String=base_dir; + end + + function session_name=get.session_name(this) + try + session_name=this.Gui.SessionName.String; + catch + session_name=''; + end + end + + function set.session_name(this,session_name) + this.Gui.SessionName.String=session_name; + end + + function filename=get.filename(this) + try + filename=this.Gui.FileName.String; + catch + filename='placeholder'; + end + end + + function set.filename(this,filename) + this.Gui.FileName.String=filename; + end + + end end \ No newline at end of file diff --git a/@MyFit/MyFit.m b/@MyFit/MyFit.m index a6daf25..3edba53 100644 --- a/@MyFit/MyFit.m +++ b/@MyFit/MyFit.m @@ -1,727 +1,728 @@ classdef MyFit < dynamicprops %Note that dynamicprops classes are handle classes. properties (Access=public) Data; init_params=[]; scale_init=[]; lim_lower; lim_upper; enable_plot; plot_handle; %Calibration values supplied externally CalVals=struct(); init_color='c'; end properties (GetAccess=public, SetAccess=private) Fit; Gui; Fitdata; FitStruct; coeffs; fit_name='Linear' end properties (Access=private) %Structure used for initializing GUI of userpanel UserGui; Parser; enable_gui=1; hline_init; end properties (Dependent=true) fit_function; fit_tex; fit_params; fit_param_names; valid_fit_names; n_params; scaled_params; init_param_fun; x_vec; n_user_fields; user_field_tags; user_field_names; user_field_vals; fullpath; - save_name; - save_dir; + filename; + base_dir; save_path; session_name; end events NewFit; NewInitVal; end %Parser function methods (Access=private) %Creates parser for constructor function createParser(this) p=inputParser; addParameter(p,'fit_name','Linear',@ischar) addParameter(p,'Data',MyTrace()); addParameter(p,'Fit',MyTrace()); addParameter(p,'x',[]); addParameter(p,'y',[]); addParameter(p,'enable_gui',1); addParameter(p,'enable_plot',0); addParameter(p,'plot_handle',[]); - addParameter(p,'save_dir',pwd); - addParameter(p,'save_name','placeholder'); + addParameter(p,'base_dir',pwd); + addParameter(p,'session_name','placeholder'); + addParameter(p,'filename','placeholder'); this.Parser=p; end end %Public methods methods (Access=public) %Constructor function function this=MyFit(varargin) createFitStruct(this); createParser(this); parse(this.Parser,varargin{:}); parseInputs(this); initCalVals(this); if ismember('Data',this.Parser.UsingDefaults) &&... ~ismember('x',this.Parser.UsingDefaults) &&... ~ismember('y',this.Parser.UsingDefaults) this.Data.x=this.Parser.Results.x; this.Data.y=this.Parser.Results.y; end %Sets the scale_init to 1, this is used for the GUI. this.scale_init=ones(1,this.n_params); this.init_params=ones(1,this.n_params); %Creates the structure that contains variables for calibration %of fit results createUserGuiStruct(this); if this.enable_gui; createGui(this); end %If the data is appropriate, generates initial %parameters if validateData(this); genInitParams(this); end end %Deletion function of object function delete(this) if this.enable_gui %Avoids loops set(this.Gui.Window,'CloseRequestFcn',''); %Deletes the figure delete(this.Gui.Window); %Removes the figure handle to prevent memory leaks this.Gui=[]; end if ~isempty(this.hline_init); delete(this.hline_init); end if ~isempty(this.Fit.hlines); delete(this.Fit.hlines{:}); end end %Close figure callback simply calls delete function for class function closeFigure(this,~,~) delete(this); end %Saves the metadata function saveParams(this) assert(~isempty(this.coeffs) && ... length(this.coeffs)==this.n_params,... ['The number of calculated coefficients (%i) is not',... ' equal to the number of parameters (%i).', ... ' Perform a fit before trying to save parameters.'],... length(this.coeffs),this.n_params); %Creates combined strings of form: Linewidth (b), where %Linewidth is the parameter name and b is the parameter tag param_headers=cellfun(@(x,y) sprintf('%s (%s)',x,y),... this.fit_param_names, this.fit_params,'UniformOutput',0); user_field_headers=cellfun(@(x,y) ... sprintf('%s. %s',this.UserGui.Fields.(x).parent,y),... this.user_field_tags,this.user_field_names,... 'UniformOutput',0)'; headers=[param_headers user_field_headers]; n_columns=length(headers); %Sets the column width. Pads 2 for legibility. col_width=cellfun(@(x) length(x), headers)+2; %Min column width of 24 col_width(col_width<24)=24; - if ~exist(this.save_dir,'dir') - mkdir(this.save_dir) + if ~exist(this.base_dir,'dir') + mkdir(this.base_dir) end if ~exist(this.save_path,'dir') mkdir(this.save_path) end if exist(this.fullpath,'file') fileID=fopen(this.fullpath,'a'); else fileID=fopen(this.fullpath,'w'); pre_fmt_str=repmat('%%%is\\t',1,n_columns); fmt_str=sprintf([pre_fmt_str,'\r\n'],col_width); fprintf(fileID,fmt_str,headers{:}); end pre_fmt_str_nmb=repmat('%%%i.15e\\t',1,n_columns); nmb_fmt_str=sprintf([pre_fmt_str_nmb,'\r\n'],col_width); fprintf(fileID,nmb_fmt_str,[this.coeffs,this.user_field_vals']); fclose(fileID); end %Initializes the CalVals structure. function initCalVals(this) switch this.fit_name case 'Lorentzian' %Line spacing is the spacing between all the lines, %i.e. number of lines times the spacing between each %one this.CalVals.line_spacing=1; case 'DoubleLorentzian' this.CalVals.line_spacing=1; end end %Fits the trace using currently set parameters, depending on the %model. function fitTrace(this) this.Fit.x=this.x_vec; switch this.fit_name case 'Linear' %Fits polynomial of order 1 this.coeffs=polyfit(this.Data.x,this.Data.y,1); this.Fit.y=polyval(this.coeffs,this.Fit.x); case 'Quadratic' %Fits polynomial of order 2 this.coeffs=polyfit(this.Data.x,this.Data.y,2); this.Fit.y=polyval(this.coeffs,this.Fit.x); case {'Exponential','Gaussian'} doFit(this) case {'Lorentzian','DoubleLorentzian'} doFit(this); otherwise error('Selected fit is invalid'); end calcUserParams(this); %Sets the new initial parameters to be the fitted parameters this.init_params=this.coeffs; %Resets the scale variables for the GUI this.scale_init=ones(1,this.n_params); %Updates the gui if it is enabled if this.enable_gui; updateGui(this); end %Plots the fit if the flag is on if this.enable_plot; plotFit(this); end %Triggers new fit event triggerNewFit(this); end function calcUserParams(this) switch this.fit_name case 'Lorentzian' this.mech_lw=this.coeffs(2); %#ok this.mech_freq=this.coeffs(3); %#ok this.Q=this.mech_freq/this.mech_lw; %#ok this.opt_lw=convOptFreq(this,this.coeffs(2)); %#ok this.Qf=this.mech_freq*this.Q; %#ok case 'DoubleLorentzian' this.opt_lw1=convOptFreq(this,this.coeffs(2)); %#ok this.opt_lw2=convOptFreq(this,this.coeffs(5)); %#ok splitting=abs(this.coeffs(6)-this.coeffs(3)); this.mode_split=convOptFreq(this,splitting); %#ok otherwise end end function real_freq=convOptFreq(this,freq) real_freq=freq*this.spacing*this.line_no/this.CalVals.line_spacing; end function createUserGuiStruct(this) this.UserGui=struct('Fields',struct(),'Tabs',struct()); switch this.fit_name case 'Lorentzian' %Parameters for the tab relating to mechanics this.UserGui.Tabs.Mech.tab_title='Mech.'; this.UserGui.Tabs.Mech.Children={}; addUserField(this,'Mech','mech_lw','Linewidth (Hz)',1,... 'enable_flag','off') addUserField(this,'Mech','Q',... 'Qualify Factor (x10^6)',1e6,... 'enable_flag','off','conv_factor',1e6) addUserField(this,'Mech','mech_freq','Frequency (MHz)',1e6,... 'conv_factor',1e6, 'enable_flag','off') addUserField(this,'Mech','Qf','Q\times f (10^{14} Hz)',1e14,... 'conv_factor',1e14,'enable_flag','off'); %Parameters for the tab relating to optics this.UserGui.Tabs.Opt.tab_title='Optical'; this.UserGui.Tabs.Opt.Children={}; addUserField(this,'Opt','spacing',... 'Line Spacing (MHz)',1e6,'conv_factor',1e6,... 'Callback', @(~,~) calcUserParams(this)); addUserField(this,'Opt','line_no','Number of lines',10,... 'Callback', @(~,~) calcUserParams(this)); addUserField(this,'Opt','opt_lw','Linewidth (MHz)',1e6,... 'enable_flag','off','conv_factor',1e6); case 'DoubleLorentzian' this.UserGui.Tabs.Opt.tab_title='Optical'; this.UserGui.Tabs.Opt.Children={}; addUserField(this,'Opt','spacing',... 'Line Spacing (MHz)',1e6,'conv_factor',1e6,... 'Callback', @(~,~) calcUserParams(this)); addUserField(this,'Opt','line_no','Number of lines',10,... 'Callback', @(~,~) calcUserParams(this)); addUserField(this,'Opt','opt_lw1','Linewidth 1 (MHz)',1e6,... 'enable_flag','off','conv_factor',1e6); addUserField(this,'Opt','opt_lw2','Linewidth 2 (MHz)',1e6,... 'enable_flag','off','conv_factor',1e6); addUserField(this,'Opt','mode_split',... 'Modal splitting (MHz)',1e6,... 'enable_flag','off','conv_factor',1e6); otherwise %Do nothing if there is no defined user parameters end end %Parent is the parent tab for the userfield, tag is the tag given %to the GUI element, title is the text written next to the field, %initial value is the initial value of the property and change_flag %determines whether the gui element is enabled for writing or not. function addUserField(this, parent, tag, title, ... init_val,varargin) %Parsing inputs p=inputParser(); addRequired(p,'Parent'); addRequired(p,'Tag'); addRequired(p,'Title'); addRequired(p,'init_val'); addParameter(p,'enable_flag','on'); addParameter(p,'Callback',''); addParameter(p,'conv_factor',1); parse(p,parent,tag,title,init_val,varargin{:}); tag=p.Results.Tag; %Populates the UserGui struct this.UserGui.Fields.(tag).parent=p.Results.Parent; this.UserGui.Fields.(tag).title=p.Results.Title; this.UserGui.Fields.(tag).init_val=p.Results.init_val; this.UserGui.Fields.(tag).enable_flag=... p.Results.enable_flag; this.UserGui.Fields.(tag).conv_factor=p.Results.conv_factor; this.UserGui.Fields.(tag).Callback=... p.Results.Callback; this.UserGui.Tabs.(p.Results.Parent).Children{end+1}=tag; %Adds the new property to the class addUserProp(this, tag); end function addUserProp(this,tag) prop=addprop(this,tag); if this.enable_gui prop.GetMethod=@(this) getUserVal(this,tag); prop.SetMethod=@(this, val) setUserVal(this, val, tag); prop.Dependent=true; end end function val=getUserVal(this, tag) conv_factor=this.UserGui.Fields.(tag).conv_factor; val=str2double(this.Gui.([tag,'Edit']).String)*conv_factor; end function setUserVal(this, val, tag) conv_factor=this.UserGui.Fields.(tag).conv_factor; this.Gui.([tag,'Edit']).String=num2str(val/conv_factor); end % Callbacks %Save fit function callback function saveFitCallback(this,~,~) - assert(~isempty(this.save_dir),'Save directory is not specified'); - assert(ischar(this.save_dir),... + assert(~isempty(this.base_dir),'Save directory is not specified'); + assert(ischar(this.base_dir),... ['Save directory is not specified.',... ' Should be of type char but is %s.'], ... - class(this.save_dir)) + class(this.base_dir)) try - this.Fit.save('name',this.save_name,... - 'save_dir',this.save_dir) + this.Fit.save('name',this.filename,... + 'base_dir',this.base_dir) catch error(['Attempted to save to directory %s',... - ' with file name %s, but failed'],this.save_dir,... - this.save_name); + ' with file name %s, but failed'],this.base_dir,... + this.filename); end end function saveParamCallback(this,~,~) saveParams(this); end %Callback functions for sliders in GUI. Uses param_ind to find out %which slider the call is coming from, this was implemented to %speed up the callback. function sliderCallback(this, param_ind, hObject, ~) %Gets the value from the slider scale=get(hObject,'Value'); %Updates the scale with a new value this.scale_init(param_ind)=10^((scale-50)/50); %Updates the edit box with the new value from the slider set(this.Gui.(sprintf('Edit_%s',this.fit_params{param_ind})),... 'String',sprintf('%3.3e',this.scaled_params(param_ind))); if this.enable_plot; plotInitFun(this); end end %Callback function for edit boxes in GUI function editCallback(this, hObject, ~) init_param=str2double(get(hObject,'String')); tag=get(hObject,'Tag'); %Finds the index where the fit_param name begins (convention is %after the underscore) fit_param=tag((strfind(tag,'_')+1):end); param_ind=strcmp(fit_param,this.fit_params); %Updates the slider to be such that the scaling is 1 set(this.Gui.(sprintf('Slider_%s',fit_param)),... 'Value',50); %Updates the correct initial parameter this.init_params(param_ind)=init_param; if this.enable_plot; plotInitFun(this); end %Triggers event for new init values triggerNewInitVal(this); end %Callback function for analyze button in GUI. Checks if the data is %ready for fitting. function analyzeCallback(this, ~, ~) assert(validateData(this),... ['The length of x is %d and the length of y is',... ' %d. The lengths must be equal and greater than ',... 'the number of fit parameters to perform a fit'],... length(this.Data.x),length(this.Data.y)) fitTrace(this); end %Callback for clearing the fits on the axis. function clearFitCallback(this,~,~) clearFit(this); end %Callback function for generate init parameters button. Updates GUI %afterwards function initParamCallback(this,~,~) genInitParams(this); updateGui(this); end %Generates model-dependent initial parameters, lower and upper %boundaries. function genInitParams(this) assert(validateData(this), ['The data must be vectors of',... ' equal length greater than the number of fit parameters.',... ' Currently the number of fit parameters is %d, the',... ' length of x is %d and the length of y is %d'],... this.n_params,length(this.Data.x),length(this.Data.y)); %Cell for putting parameters in to be interpreted in the %parser. Element 1 contains the init params, Element 2 contains %the lower limits and Element 3 contains the upper limits. params={}; switch this.fit_name case 'Exponential' [params{1},params{2},params{3}]=... initParamExponential(this.Data.x,this.Data.y); case 'Gaussian' [params{1},params{2},params{3}]=... initParamGaussian(this.Data.x,this.Data.y); case 'Lorentzian' [params{1},params{2},params{3}]=... initParamLorentzian(this.Data.x,this.Data.y); case 'DoubleLorentzian' [params{1},params{2},params{3}]=... initParamDblLorentzian(this.Data.x,this.Data.y); end %Validates the initial parameters p=createFitParser(this.n_params); parse(p,params{:}); %Loads the parsed results into the class variables this.init_params=p.Results.init_params; this.lim_lower=p.Results.lower; this.lim_upper=p.Results.upper; %Plots the fit function with the new initial parameters if this.enable_plot; plotInitFun(this); end end %Plots the trace contained in the Fit MyTrace object. function plotFit(this,varargin) this.Fit.plotTrace(this.plot_handle,varargin{:}); end %Clears the plots function clearFit(this) cellfun(@(x) delete(x), this.Fit.hlines); delete(this.hline_init); this.hline_init=[]; this.Fit.hlines={}; end %Function for plotting fit model with current initial parameters. function plotInitFun(this) %Substantially faster than any alternative - generating %anonymous functions is very cpu intensive. input_cell=num2cell(this.scaled_params); y_vec=feval(this.FitStruct.(this.fit_name).anon_fit_fun,... this.x_vec,input_cell{:}); if isempty(this.hline_init) this.hline_init=plot(this.plot_handle,this.x_vec,y_vec,... 'Color',this.init_color); else set(this.hline_init,'XData',this.x_vec,'YData',y_vec); end end end %Private methods methods(Access=private) %Creates the GUI of MyFit, in separate file. createGui(this); %Creates a panel for the GUI, in separate file createTab(this, tab_tag, bg_color, button_h); %Creats two vboxes (from GUI layouts) to display values of %quantities createUnitBox(this, bg_color, h_parent, name); %Creates edit box inside a UnitDisp for showing label and value of %a quantity. Used in conjunction with createUnitBox createUnitDisp(this,varargin); %Sets the class variables to the inputs from the inputParser. function parseInputs(this) for i=1:length(this.Parser.Parameters) %Takes the value from the inputParser to the appropriate %property. if isprop(this,this.Parser.Parameters{i}) this.(this.Parser.Parameters{i})=... this.Parser.Results.(this.Parser.Parameters{i}); end end end %Does the fit with the currently set parameters function doFit(this) %Fits with the below properties. Chosen for maximum accuracy. this.Fitdata=fit(this.Data.x,this.Data.y,this.fit_function,... 'Lower',this.lim_lower,'Upper',this.lim_upper,... 'StartPoint',this.init_params, .... 'MaxFunEvals',2000,'MaxIter',2000,'TolFun',1e-9); %Puts the y values of the fit into the struct. this.Fit.y=this.Fitdata(this.Fit.x); %Puts the coeffs into the class variable. this.coeffs=coeffvalues(this.Fitdata); end %Triggers the NewFit event such that other objects can use this to %e.g. plot new fits function triggerNewFit(this) notify(this,'NewFit'); end function triggerNewInitVal(this) notify(this,'NewInitVal'); end %Creates the struct used to get all things relevant to the fit %model function createFitStruct(this) %Adds fits addFit(this,'Linear','a*x+b','$$ax+b$$',{'a','b'},... {'Gradient','Offset'}) addFit(this,'Quadratic','a*x^2+b*x+c','$$ax^2+bx+c$$',... {'a','b','c'},{'Quadratic coeff.','Linear coeff.','Offset'}); addFit(this,'Gaussian','a*exp(-((x-c)/b)^2/2)+d',... '$$ae^{-\frac{(x-c)^2}{2b^2}}+d$$',{'a','b','c','d'},... {'Amplitude','Width','Center','Offset'}); addFit(this,'Lorentzian','1/pi*a*b/2/((x-c)^2+(b/2)^2)+d',... '$$\frac{a}{\pi}\frac{b/2}{(x-c)^2+(b/2)^2}+d$$',{'a','b','c','d'},... {'Amplitude','Width','Center','Offset'}); addFit(this,'Exponential','a*exp(b*x)+c',... '$$ae^{bx}+c$$',{'a','b','c'},... {'Amplitude','Rate','Offset'}); addFit(this,'DoubleLorentzian',... '1/pi*b/2*a/((x-c)^2+(b/2)^2)+1/pi*e/2*d/((x-f)^2+(e/2)^2)+g',... '$$\frac{a}{\pi}\frac{b/2}{(x-c)^2+(b/2)^2}+\frac{d}{\pi}\frac{e/2}{(x-f)^2+(e/2)^2}+g$$',... {'a','b','c','d','e','f','g'},... {'Amplitude 1','Width 1','Center 1','Amplitude 2',... 'Width 2','Center 2','Offset'}); end %Updates the GUI if the edit or slider boxes are changed from %elsewhere. function updateGui(this) %Converts the scale variable to the value between 0 and 100 %necessary for the slider slider_vals=50*log10(this.scale_init)+50; for i=1:this.n_params set(this.Gui.(sprintf('Edit_%s',this.fit_params{i})),... 'String',sprintf('%3.3e',this.scaled_params(i))); set(this.Gui.(sprintf('Slider_%s',this.fit_params{i})),... 'Value',slider_vals(i)); end end %Adds a fit to the list of fits function addFit(this,fit_name,fit_function,fit_tex,fit_params,... fit_param_names) this.FitStruct.(fit_name).fit_function=fit_function; this.FitStruct.(fit_name).fit_tex=fit_tex; this.FitStruct.(fit_name).fit_params=fit_params; this.FitStruct.(fit_name).fit_param_names=fit_param_names; %Generates the anonymous fit function from the above args=['@(x,', strjoin(fit_params,','),')']; anon_fit_fun=str2func(vectorize([args,fit_function])); this.FitStruct.(fit_name).anon_fit_fun=anon_fit_fun; end %Checks if the class is ready to perform a fit function bool=validateData(this) bool=~isempty(this.Data.x) && ~isempty(this.Data.y) && ... length(this.Data.x)==length(this.Data.y) && ... length(this.Data.x)>=this.n_params; end end % Get and set functions methods % Set functions %Set function for fit_name. function set.fit_name(this,fit_name) assert(ischar(fit_name),'The fit name must be a string'); %Capitalizes the first letter fit_name=[upper(fit_name(1)),lower(fit_name(2:end))]; %Checks it is a valid fit name ind=strcmpi(fit_name,this.valid_fit_names);%#ok assert(any(ind),'%s is not a supported fit name',fit_name); this.fit_name=this.valid_fit_names{ind}; %#ok end % Get functions for dependent variables %Generates the valid fit names function valid_fit_names=get.valid_fit_names(this) valid_fit_names=fieldnames(this.FitStruct); end %Grabs the correct fit function from FitStruct function fit_function=get.fit_function(this) fit_function=this.FitStruct.(this.fit_name).fit_function; end %Grabs the correct tex string from FitStruct function fit_tex=get.fit_tex(this) fit_tex=this.FitStruct.(this.fit_name).fit_tex; end %Grabs the correct fit parameters from FitStruct function fit_params=get.fit_params(this) fit_params=this.FitStruct.(this.fit_name).fit_params; end %Grabs the correct fit parameter names from FitStruct function fit_param_names=get.fit_param_names(this) fit_param_names=this.FitStruct.(this.fit_name).fit_param_names; end %Calculates the scaled initial parameters function scaled_params=get.scaled_params(this) scaled_params=this.scale_init.*this.init_params; end %Calculates the number of parameters in the fit function function n_params=get.n_params(this) n_params=length(this.fit_params); end %Generates a vector of x values for plotting function x_vec=get.x_vec(this) x_vec=linspace(min(this.Data.x),max(this.Data.x),1000); end function n_user_fields=get.n_user_fields(this) n_user_fields=length(this.user_field_tags); end function user_field_tags=get.user_field_tags(this) user_field_tags=fieldnames(this.UserGui.Fields); end function user_field_names=get.user_field_names(this) user_field_names=cellfun(@(x) this.UserGui.Fields.(x).title,... this.user_field_tags,'UniformOutput',0); end function user_field_vals=get.user_field_vals(this) user_field_vals=cellfun(@(x) this.(x), this.user_field_tags); end function fullpath=get.fullpath(this) - fullpath=[this.save_path,this.save_name,'.txt']; + fullpath=[this.save_path,this.filename,'.txt']; end function save_path=get.save_path(this) - save_path=createSessionPath(this.save_dir,this.session_name); + save_path=createSessionPath(this.base_dir,this.session_name); end - function save_dir=get.save_dir(this) + function base_dir=get.base_dir(this) try - save_dir=this.Gui.SaveDir.String; + base_dir=this.Gui.BaseDir.String; catch - save_dir=pwd; + base_dir=pwd; end end - function set.save_dir(this,save_dir) - this.Gui.SaveDir.String=save_dir; + function set.base_dir(this,base_dir) + this.Gui.BaseDir.String=base_dir; end function session_name=get.session_name(this) try session_name=this.Gui.SessionName.String; catch session_name=''; end end function set.session_name(this,session_name) this.Gui.SessionName.String=session_name; end - function save_name=get.save_name(this) + function filename=get.filename(this) try - save_name=this.Gui.FileName.String; + filename=this.Gui.FileName.String; catch - save_name='placeholder'; + filename='placeholder'; end end - function set.save_name(this,save_name) - this.Gui.SaveName=save_name; + function set.filename(this,filename) + this.Gui.FileName.String=filename; end end end \ No newline at end of file diff --git a/@MyFit/createGui.m b/@MyFit/createGui.m index b42b61f..75fca2f 100644 --- a/@MyFit/createGui.m +++ b/@MyFit/createGui.m @@ -1,246 +1,246 @@ function createGui(this) %Makes the fit name have the first letter capitalized fit_name=[upper(this.fit_name(1)),this.fit_name(2:end)]; %Defines the colors for the Gui rgb_blue=[0.1843,0.4157,1]; rgb_white=[1,1,1]; %Width of the edit boxes in the GUI edit_width=140; %Height of buttons in GUI button_h=25; %Minimum height of the four vboxes composing the gui. title_h=40; equation_h=100; savebox_h=100; slider_h=100; min_fig_width=560; %Finds the minimum height in button heights of the user field panel. This %is used to calculate the height of the figure. tab_fields=fieldnames(this.UserGui.Tabs); max_fields=max(cellfun(@(x) length(this.UserGui.Tabs.(x).Children),tab_fields)); if max_fields>3 min_user_h=max_fields+2; else min_user_h=5; end userpanel_h=min_user_h*button_h; fig_h=title_h+equation_h+slider_h++savebox_h+userpanel_h; %Sets a minimum width if this.n_params<4; edit_width=min_fig_width/this.n_params; end fig_width=edit_width*this.n_params; %Name sets the title of the window, NumberTitle turns off the FigureN text %that would otherwise be before the title, MenuBar is the menu normally on %the figure, toolbar is the toolbar normally on the figure. %HandleVisibility refers to whether gcf, gca etc will grab this figure. this.Gui.Window = figure('Name', 'MyFit', 'NumberTitle', 'off', ... 'MenuBar', 'none', 'Toolbar', 'none', 'HandleVisibility', 'off',... 'Units','Pixels','Position',[500,500,fig_width,fig_h]); %Sets the close function (runs when x is pressed) to be class function set(this.Gui.Window, 'CloseRequestFcn',... @(hObject,eventdata) closeFigure(this, hObject,eventdata)); %The main vertical box. The four main panes of the GUI are stacked in the %box. We create these four boxes first so that we do not need to redraw %them later this.Gui.MainVbox=uix.VBox('Parent',this.Gui.Window,'BackgroundColor',rgb_white); %The title box this.Gui.Title=annotation(this.Gui.MainVbox,'textbox',[0.5,0.5,0.3,0.3],... 'String',fit_name,'Units','Normalized',... 'HorizontalAlignment','center','VerticalAlignment','middle',... 'FontSize',16,'BackgroundColor',rgb_white); %Displays the fitted equation this.Gui.Equation=annotation(this.Gui.MainVbox,'textbox',[0.5,0.5,0.3,0.3],... 'String',this.fit_tex,... 'Units','Normalized','Interpreter','LaTeX',... 'HorizontalAlignment','center','VerticalAlignment','middle',... 'FontSize',20,'BackgroundColor',rgb_white); %Creates an HBox for extracted parameters and user interactions with GUI this.Gui.UserHbox=uix.HBox('Parent',this.Gui.MainVbox,... 'BackgroundColor',rgb_white); %Creates the HBox for saving parameters this.Gui.SaveHbox=uix.HBox('Parent',this.Gui.MainVbox,... 'BackgroundColor',rgb_white); %Creates the HBox for the fitting parameters this.Gui.FitHbox=uix.HBox('Parent',this.Gui.MainVbox,'BackgroundColor',... rgb_white); %Sets the heights and minimum heights of the five vertical boxes. -1 means %it resizes with the window set(this.Gui.MainVbox,'Heights',[title_h,-1,userpanel_h,savebox_h,slider_h],... 'MinimumHeights',[title_h,equation_h,userpanel_h,savebox_h,slider_h]); %Here we create the fit panel in the GUI. this.Gui.FitPanel=uix.BoxPanel( 'Parent', this.Gui.UserHbox,... 'Padding',0,'BackgroundColor', rgb_white,... 'Title','Fit Panel','TitleColor',rgb_blue); %Here we create the panel for the useful parameters this.Gui.UserPanel=uix.BoxPanel( 'Parent', this.Gui.UserHbox,... 'Padding',0,'BackgroundColor', 'w',... 'Title','Calculated parameters','TitleColor',rgb_blue); %Sets the widths of the above set(this.Gui.UserHbox,'Widths',[-1,-2],'MinimumWidths',[0,375]); %This makes the buttons that go inside the FitPanel this.Gui.FitVbox=uix.VBox('Parent',this.Gui.FitPanel,'BackgroundColor',... rgb_white); %Creates the button for analysis inside the VBox this.Gui.AnalyzeButton=uicontrol('Parent',this.Gui.FitVbox,... 'style','pushbutton','Background','w','String','Analyze','Callback',... @(hObject, eventdata) analyzeCallback(this, hObject, eventdata)); %Creates button for generating new initial parameters this.Gui.InitButton=uicontrol('Parent',this.Gui.FitVbox,... 'style','pushbutton','Background','w',... 'String','Generate Init. Params','Callback',... @(hObject, eventdata) initParamCallback(this, hObject, eventdata)); %Creates button for clearing fits this.Gui.ClearButton=uicontrol('Parent',this.Gui.FitVbox,... 'style','pushbutton','Background','w','String','Clear fits','Callback',... @(hObject, eventdata) clearFitCallback(this, hObject, eventdata)); set(this.Gui.FitVbox,'Heights',[button_h,button_h,button_h]); this.Gui.TabPanel=uix.TabPanel('Parent',this.Gui.UserPanel,... 'BackgroundColor',rgb_white); %Creates the user values panel with associated tabs. The cellfun here %creates the appropriately named tabs. To add a tab, add a new field to the %UserGuiStruct. usertabs=fieldnames(this.UserGui.Tabs); if ~isempty(usertabs) cellfun(@(x) createTab(this,x,rgb_white,button_h),usertabs); this.Gui.TabPanel.TabTitles=... cellfun(@(x) this.UserGui.Tabs.(x).tab_title, usertabs,... 'UniformOutput',0); end this.Gui.SavePanel=uix.BoxPanel( 'Parent', this.Gui.SaveHbox,... 'Padding',0,'BackgroundColor', rgb_white,... 'Title','Save Panel','TitleColor',rgb_blue); this.Gui.DirPanel=uix.BoxPanel('Parent',this.Gui.SaveHbox,... 'Padding',0,'BackgroundColor',rgb_white,... 'Title','Directory Panel','TitleColor',rgb_blue); set(this.Gui.SaveHbox,'Widths',[-1,-2],'MinimumWidths',[0,375]); %Here we create the buttons and edit boxes inside the save box this.Gui.SaveButtonBox=uix.VBox('Parent',this.Gui.SavePanel,... 'BackgroundColor',rgb_white); this.Gui.DirHbox=uix.HBox('Parent',this.Gui.DirPanel,... 'BackgroundColor',rgb_white); -this.Gui.SaveNameLabelBox=uix.VBox('Parent',this.Gui.DirHbox,... +this.Gui.FileNameLabelBox=uix.VBox('Parent',this.Gui.DirHbox,... 'BackgroundColor',rgb_white); -this.Gui.SaveNameBox=uix.VBox('Parent',this.Gui.DirHbox,... +this.Gui.FileNameBox=uix.VBox('Parent',this.Gui.DirHbox,... 'BackgroundColor',rgb_white); set(this.Gui.DirHbox,'Widths',[-1,-2]); %Buttons for saving the fit and parameters this.Gui.SaveParamButton=uicontrol('Parent',this.Gui.SaveButtonBox,... 'style','pushbutton','Background','w','String','Save Parameters',... 'Callback', @(hObject, eventdata) saveParamCallback(this, hObject, eventdata)); this.Gui.SaveFitButton=uicontrol('Parent',this.Gui.SaveButtonBox,... 'style','pushbutton','Background','w','String','Save Fit',... 'Callback', @(hObject, eventdata) saveFitCallback(this, hObject, eventdata)); set(this.Gui.SaveButtonBox,'Heights',[button_h,button_h]) %Labels for the edit boxes -this.Gui.SaveDirLabel=annotation(this.Gui.SaveNameLabelBox,... +this.Gui.BaseDirLabel=annotation(this.Gui.FileNameLabelBox,... 'textbox',[0.5,0.5,0.3,0.3],... 'String','Save Directory','Units','Normalized',... 'HorizontalAlignment','Left','VerticalAlignment','middle',... 'FontSize',10,'BackgroundColor',rgb_white); -this.Gui.SessionNameLabel=annotation(this.Gui.SaveNameLabelBox,... +this.Gui.SessionNameLabel=annotation(this.Gui.FileNameLabelBox,... 'textbox',[0.5,0.5,0.3,0.3],... 'String','Session Name','Units','Normalized',... 'HorizontalAlignment','Left','VerticalAlignment','middle',... 'FontSize',10,'BackgroundColor',rgb_white); -this.Gui.FileNameLabel=annotation(this.Gui.SaveNameLabelBox,... +this.Gui.FileNameLabel=annotation(this.Gui.FileNameLabelBox,... 'textbox',[0.5,0.5,0.3,0.3],... 'String','File Name','Units','Normalized',... 'HorizontalAlignment','Left','VerticalAlignment','middle',... 'FontSize',10,'BackgroundColor',rgb_white); -set(this.Gui.SaveNameLabelBox,'Heights',button_h*ones(1,3)); +set(this.Gui.FileNameLabelBox,'Heights',button_h*ones(1,3)); %Boxes for editing the path and filename -this.Gui.SaveDir=uicontrol('Parent',this.Gui.SaveNameBox,... - 'style','edit','String',this.save_dir,'HorizontalAlignment','Left',... +this.Gui.BaseDir=uicontrol('Parent',this.Gui.FileNameBox,... + 'style','edit','String',this.base_dir,'HorizontalAlignment','Left',... 'FontSize',10); -this.Gui.SessionName=uicontrol('Parent',this.Gui.SaveNameBox,... +this.Gui.SessionName=uicontrol('Parent',this.Gui.FileNameBox,... 'style','edit','String',this.session_name,'HorizontalAlignment','Left',... 'FontSize',10); -this.Gui.FileName=uicontrol('Parent',this.Gui.SaveNameBox,... - 'style','edit','String',this.save_name,'HorizontalAlignment','Left',... +this.Gui.FileName=uicontrol('Parent',this.Gui.FileNameBox,... + 'style','edit','String',this.filename,'HorizontalAlignment','Left',... 'FontSize',10); -set(this.Gui.SaveNameBox,'Heights',button_h*ones(1,3)); +set(this.Gui.FileNameBox,'Heights',button_h*ones(1,3)); %We first make the BoxPanels to speed up the process. Otherwise everything %in the BoxPanel must be redrawn every time we make a new one. panel_str=cell(1,this.n_params); for i=1:this.n_params %Generates the string for the panel handle panel_str{i}=sprintf('Panel_%s',this.fit_params{i}); %Creates the panels this.Gui.(panel_str{i})=uix.BoxPanel( 'Parent', this.Gui.FitHbox ,... 'Padding',0,'BackgroundColor', 'w',... 'Title',sprintf('%s (%s)',this.fit_param_names{i},this.fit_params{i}),... 'TitleColor',rgb_blue,... 'Position',[1+edit_width*(i-1),1,edit_width,slider_h],... 'Visible','off'); end %Loops over number of parameters to create a fit panel for each one for i=1:this.n_params %Generates the string for the vbox handle vbox_str=sprintf('Vbox_%s',this.fit_params{i}); %Generates the string for the slider handle slider_str=sprintf('Slider_%s',this.fit_params{i}); %Generates string for edit panels edit_str=sprintf('Edit_%s',this.fit_params{i}); %Creates the vbox inside the panel that allows stacking this.Gui.(vbox_str) =uix.VBox( 'Parent', ... this.Gui.(panel_str{i}),'Padding',0,'BackgroundColor', 'w'); %Generates edit box for fit parameters this.Gui.(edit_str)=uicontrol('Parent',this.Gui.(vbox_str),... 'Style','edit','String',sprintf('%3.3e',this.init_params(i)),... 'FontSize',14,'Tag',edit_str,'HorizontalAlignment','Right',... 'Position',[1,48,edit_width-4,30],'Units','Pixels','Callback',... @(hObject,eventdata) editCallback(this, hObject, eventdata)); %Generates java-based slider. Looks nicer than MATLAB slider this.Gui.(slider_str)=uicomponent('Parent',this.Gui.(vbox_str),... 'style','jslider','Value',50,'Orientation',0,... 'MajorTickSpacing',20,'MinorTickSpacing',5,'Paintlabels',0,... 'PaintTicks',1,'Background',java.awt.Color.white,... 'pos',[1,-7,edit_width-4,55]); %Sets up callbacks for the slider this.Gui.([slider_str,'_callback'])=handle(this.Gui.(slider_str),... 'CallbackProperties'); this.Gui.([slider_str,'_callback']).StateChangedCallback = .... @(hObject, eventdata) sliderCallback(this,i,hObject,eventdata); this.Gui.([slider_str,'_callback']).MouseReleasedCallback = .... @(~, ~) triggerNewInitVal(this); %Sets heights and minimum heights for the elements in the fit vbox set(this.Gui.(vbox_str),'Heights',[30,55],'MinimumHeights',[30,55]) end %Makes all the panels at the bottom visible at the same time cellfun(@(x) set(this.Gui.(x),'Visible','on'),panel_str); end \ No newline at end of file diff --git a/Required packages/uicomponent/HyperlinkListenerImp.class b/Required packages/uicomponent/HyperlinkListenerImp.class new file mode 100644 index 0000000..ccfd301 Binary files /dev/null and b/Required packages/uicomponent/HyperlinkListenerImp.class differ diff --git a/Required packages/uicomponent/HyperlinkListenerImp.java b/Required packages/uicomponent/HyperlinkListenerImp.java new file mode 100644 index 0000000..a840933 --- /dev/null +++ b/Required packages/uicomponent/HyperlinkListenerImp.java @@ -0,0 +1,17 @@ +// HyperlinkListener - class for handling JEditorPane hyperlink events +// see: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JEditorPane.html +import java.io.*; +import javax.swing.*; +import javax.swing.event.*; +public class HyperlinkListenerImp implements HyperlinkListener { + public void hyperlinkUpdate(HyperlinkEvent evt) { + if (evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + JEditorPane pane = (JEditorPane)evt.getSource(); + try { + // Show the new page in the editor pane. + pane.setPage(evt.getURL()); + } catch (IOException e) { + } + } + } +} diff --git a/Required packages/uicomponent/createScreenshot.m b/Required packages/uicomponent/createScreenshot.m new file mode 100644 index 0000000..99fd5fe --- /dev/null +++ b/Required packages/uicomponent/createScreenshot.m @@ -0,0 +1,68 @@ +function [p,ep,st]=createScreenshot + clf + clear java all + set(gcf,'Position',[200,200,630,420],'NumberTitle','off','Name','UICOMPONENT examples'); + h=uipanel; + %p=handle([]); +% { + p =uicomponent('style','filechooser','position',[5,5,400,400]); + p(end+1)=uicomponent('style','JLabel',{'JFileChooser:'},'position',[10,400,100,15],'foreground',java.awt.Color.red); + p(end+1)=uicomponent('style','jslider',{},'pos',[410,5,70,120],'Value',72,'Orientation',1,'MajorTickSpacing',20,'MinorTickSpacing',5,'Paintlabels',1,'PaintTicks',1); + p(end+1)=uicomponent('style','jslider',{},'pos',[480,5,130,40],'Value',57,'Orientation',0); + p(end+1)=uicomponent('style','jslider',{},'pos',[480,40,130,40],'Value',22,'Orientation',0,'MajorTickSpacing',20,'PaintTicks',1); + p(end+1)=uicomponent('style','jslider',{},'pos',[480,80,130,40],'Value',84,'Orientation',0,'MajorTickSpacing',20,'PaintLabels',1); + p(end+1)=uicomponent('style','JLabel',{'JSlider (several options):'},'position',[430,120,150,15],'foreground',java.awt.Color.red); + p(end+1)=uicomponent('style','JComboBox',{'Option 1','Option 2','Option 3'},'position',[450,140,150,20],'editable',true,'SelectedItem',' I can edit this...'); + p(end+1)=uicomponent('style','JLabel',{'JComboBox (editable):'},'position',[430,160,150,15],'foreground',java.awt.Color.red); + p(end+1)=uicomponent('style','JSpinner','position',[520,180,80,20],'value',7); + p(end+1)=uicomponent('style','JLabel',{'JSpinner:'},'position',[430,180,50,20],'foreground',java.awt.Color.red); + p(end+1)=uicomponent('style','JPasswordField','position',[520,210,80,20],'Text','testing'); + p(end+1)=uicomponent('style','JLabel',{'JPasswordField:'},'position',[430,210,80,20],'foreground',java.awt.Color.red); + p(end+1)=uicomponent('style','JProgressBar','position',[520,240,80,20],'StringPainted',1,'Value',77.5,'Indeterminate',0); + % Note: without 'StringPainted' or 'StringPainted',0 for green blocks, not continuous blue... +%} + p(end+1)=uicomponent('style','JLabel',{'JProgressBar:'},'position',[430,240,80,20],'foreground',java.awt.Color.red); + + %url = java.net.URL(['jar:file:///' strrep(matlabroot,'\','/') '/help/techdoc/help.jar!/ref/plot.html']); + %url = java.net.URL(['file:///' strrep(matlabroot,'\','/') '/help/matlab/matlab_external/userdata.html']); + %url = java.net.URL('http://google.com'); + url = java.net.URL('http://java.sun.com/docs/books/tutorial/uiswing/components/examples/TextSamplerDemoHelp.html'); + url = java.net.URL('http://docs.oracle.com/javase/tutorial/uiswing/examples/components/TextSamplerDemoProject/src/components/TextSamplerDemoHelp.html'); + ep = javaObjectEDT(handle(javax.swing.JEditorPane(url), 'CallbackProperties')); + pause(1); + try + text = char(ep.getText); + st = regexprep(evalc('type ..\docstyle.css'),'/\*.*?\*/',''); + ep.setText(regexprep(text,{'