www.gusucode.com > mbctools 工具箱 matlab 源码程序 > mbctools/+xregdatagui/SweepsetList.m

    classdef SweepsetList < mbcgui.widget.BasicContainer
    %xregdatagui.sweepsetlist class
    %   xregdatagui.sweepsetlist extends mbcgui.widget.BasicContainer.
    %
    %    xregdatagui.sweepsetlist properties:
    %       Parent - Property is of type 'MATLAB array'
    %       Position - Property is of type 'rect'
    %       Enable - Property is of type 'on/off'
    %       Visible - Property is of type 'on/off'
    %       Userdata - Property is of type 'MATLAB array'
    %       Tag - Property is of type 'string'
    %       Sweepset - Property is of type 'MATLAB array'
    %       ColumnFcns - Property is of type 'MATLAB array' (read only)
    %       ColumnHeaders - Property is of type 'string vector' (read only)
    %       Editable - Property is of type 'bool'
    %       ShowUnits - Property is of type 'bool'
    %       DisplayFormat - Property is of type 'string'
    %       UIContextMenu - Property is of type 'MATLAB array'
    %       MultiSelect - Property is of type 'bool'
    %       SelectedRows - Property is of type 'MATLAB array'
    %       Listview - Property is of type 'MATLAB array' (read only)
    %
    %    xregdatagui.sweepsetlist methods:
    %       editVariable - Edit a variable definition in the sweepset
    %       getStandardColumnFunctions - Provide standard set of column functions.
    %       pDisplaySweepset - Display the given sweepset
    %       pInitialiseListview - Listview initialiser
    %       pPostSetEditable - A short description of the function
    %       pPostSetMultiSelect - A short description of the function
    %       pSetSelectedRows - A short description of the function
    %       pSetSweepset - Set function to ensure that this property contains a sweepsepset
    %       pSweepsetChanged - update routine to call if the sweepset changes
    %       setColumnHeaders - set the list column headers
    
    %  Copyright 2015-2015 The MathWorks, Inc. and Ford Global Technologies, Inc.
    
    properties (SetObservable)
        %SWEEPSET Property is of type 'MATLAB array'
        Sweepset = sweepset;
        %EDITABLE Property is of type 'bool'
        Editable = false;
        %SHOWUNITS Property is of type 'bool'
        ShowUnits = true;
        %DISPLAYFORMAT Property is of type 'string'
        DisplayFormat = '%.3g';
        %MULTISELECT Property is of type 'bool'
        MultiSelect = false;
    end
    
    properties(Dependent)
        %UICONTEXTMENU Property is of type 'MATLAB array'
        UIContextMenu = [];
        %SELECTEDROWS Property is of type 'MATLAB array'
        SelectedRows = [];
    end
    
    properties (Access=protected, AbortSet)
        %COMLISTENERS Property is of type 'handle vector'
        COMListeners = [];
    end
    
    properties (SetAccess=protected, AbortSet, SetObservable)
        %COLUMNFCNS Property is of type 'MATLAB array' (read only)
        ColumnFcns = [];
        %COLUMNHEADERS Property is of type 'string vector' (read only)
        ColumnHeaders
        %LISTVIEW Property is of type 'MATLAB array' (read only)
        Listview = [];
    end
    
    
    events
        ButtonDown
        DoubleClick
        KeyUp
        SweepsetChanged
        SelectionChanged
    end  % events
    
    methods  % constructor block
        function [obj] = SweepsetList(varargin)
        %SWEEPSETLIST Construtor for the sweepsetlist object
        %
        %  OBJ = SWEEPSETLIST
        %  OBJ = SWEEPSETLIST(OBJ)
        %  OBJ = SWEEPSETLIST('Property', Value)
        
        % Call the inherited constructor
        obj@mbcgui.widget.BasicContainer(varargin{ : }); % converted super class constructor call
        
        % Create the listview
        obj.Listview = mbcwidgets.List('Parent',obj.Parent,...
            'Enable', obj.Enable,...
            'BorderType','beveledin');
        obj.ContentHandle = obj.Listview;
        % Set up property listeners
        obj.storeListeners([event.proplistener(obj,obj.findprop('MultiSelect'),'PostSet',@obj.pPostSetMultiSelect)
            event.proplistener(obj,obj.findprop('Editable'),'PostSet',@obj.pPostSetEditable)]);
        
        % Force an update of the listview
        obj.pInitialiseListview;
        
        end  % sweepsetlist
        
    end  % constructor block
    
    methods
        function set.Sweepset(obj,value)
        obj.Sweepset = obj.pSetSweepset(value);
        end
        
        function value = get.SelectedRows(obj)
        value = obj.Listview.getSelectedRows;
        end
        function set.SelectedRows(obj,value)
        obj.Listview.selectRows(value);
        end
        
        function set.UIContextMenu(obj,value)
        
        set(obj.Listview,'UIContextmenu',value);
        end
        
    end   % set and get functions
    
    methods (Static)
        function [ss,OK] = dialog(ss,Owner)
        %dialog edit variables in dialog
        %  [ss,OK]= xregdatagui.SweepsetList.dialog(ss,Owner);
        
        if nargin<2
            Owner = [];
        end
        dlg = mbcgui.container.Dialog('Name','Data Variable Editor',...
            'Size',[550 450],...
            'Owner',Owner,...
            'Buttons','OK_CANCEL');
        
        localData.newSweepset = ss;
        localData.mergeSweepset = [];
        localData.filename = get(ss,'filename');
        
        dlg.Content = xregdatagui.SweepsetList.createLayout(dlg.Figure,localData);
        
        closeAction = dlg.showDialog();
        if strcmp(closeAction,'OK')
            OK = true;
            % get updated sweepset from layout userdata
            layoutUD = get(dlg.Content, 'UserData');
            ss = layoutUD.lvVariables.Sweepset;
        else
            OK = false;
        end
        delete(dlg);
        
        end
        
        function layout = createLayout(Parent,localData)
        %createLayout create a layout for editing variables
        %  layout = xregdatagui.SweepsetList.createLayout(Parent,ss);

        if isa(Parent, 'xregcontainer')
            % allow reuse of layout within xregwizard
            layout = Parent;
            layoutUD = get(layout, 'UserData');
        else
            % create a new layout
            txRecords = uicontrol('Parent', Parent,...
                'Style', 'text',...
                'String', '',...
                'Visible', 'off',...
                'HorizontalAlignment', 'left');
            
            txFilename = uicontrol('Parent', Parent,...
                'Style', 'list',...
                'String', '',...
                'Visible', 'off',...
                'HorizontalAlignment', 'left');
            
            btEdit = uicontrol('Parent',Parent,...
                'String', 'Edit...',...
                'TooltipString', 'Edit variable name and units', ...
                'Enable', 'off',...
                'Tag','btEdit',...
                'Callback', @iEditClicked);
            btRemove = uicontrol('Parent',Parent,...
                'String', 'Remove',...
                'TooltipString', 'Remove selected variable(s)', ...
                'Enable', 'off',...
                'Tag','btRemove',...
                'Callback', @(h,evt) iRemoveClicked());
            
            lvVariables = xregdatagui.SweepsetList('Parent', Parent,...
                'MultiSelect',true,...
                'Editable', true);
            
            S = lvVariables.getStandardColumnFunctions;
            lvVariables.setColumnHeaders({S.Min S.Max S.Mean S.Std}, {'Min', 'Max', 'Mean', 'Std. Dev'}, [65 65 65 65]);
            
            layout = xreggridbaglayout(Parent,...
                'dimension', [3 4],...
                'elements', {txRecords  btEdit, lvVariables, ...
                [] btRemove,[],...
                [] [] [],...
                txFilename [] []},...
                'mergeblock', {[1 1] [1 3]},...
                'mergeblock', {[3 3] [1 4]},...
                'rowsizes', [40 25 -1],...
                'colsizes', [60 60 60 -1],...
                'border', [10 0 10 10],...
                'gapy', 10,...
                'gapx', 5);
            
            layoutUD.Listeners = [
                event.listener(lvVariables, 'KeyUp', @iListviewDelete);...
                event.listener(lvVariables, 'SelectionChanged', @iListviewSelectionChanged)];
            layoutUD.txRecords     = txRecords;
            layoutUD.txFilename    = txFilename;
            layoutUD.lvVariables   = lvVariables;
            layoutUD.btEdit = btEdit;
            layoutUD.btRemove = btRemove;
            layoutUD.mergeAvail    = false;

            set(layout,'UserData',layoutUD);
        end
        if nargin > 1
            % initialise user data from localData input
            layoutUD.mergeSweepset = localData.mergeSweepset;
            layoutUD.filename      = localData.filename;
            layoutUD.mergeAvail    = ~isempty(layoutUD.mergeSweepset);
            layoutUD.lvVariables.Sweepset = localData.newSweepset;
            set(layout,'UserData',layoutUD);
        end
        % initialise layout 
        initialise()
        
            function initialise()
            %initialise layout controls and info
            ssData = layoutUD.lvVariables.Sweepset;
            % Set text and variables information
            set(layoutUD.txRecords, 'String', ['Imported ' num2str(size(ssData,1)) ' records and ' num2str(size(ssData,2)) ...
                ' variables from files :']);
            set(layoutUD.txFilename, 'String', get(ssData,'filename'));
            set(layoutUD.btRemove,'Enable',mbconoff(~isempty(layoutUD.lvVariables.SelectedRows)))
            set(layoutUD.btEdit,'Enable',mbconoff(isscalar(layoutUD.lvVariables.SelectedRows)))
            end
        
            function iEditClicked(~,~)
            %iEditClicked click on edit button
            layoutUD.lvVariables.editVariable;
            end
        
            function iRemoveClicked(SelectedRows)
            %iRemoveClicked click on remove button
            
            if nargin==0
                SelectedRows = layoutUD.lvVariables.SelectedRows;
            end
            % Which rows are being kept
            rowsToKeep = setdiff(1:size(layoutUD.lvVariables.Sweepset, 2), SelectedRows);
            % Update the sweepset - NOTE it's important to make the sweepset empty if
            % there are no variables since it's the number of records that define an
            % empty sweepset.
            if isempty(rowsToKeep)
                ssData = sweepset;
            else
                ssData = layoutUD.lvVariables.Sweepset(:, rowsToKeep);
            end
            layoutUD.lvVariables.Sweepset = ssData;
            initialise();
            
            end
        
            function iListviewDelete(~, evt)
            %iListviewKeyUp delete rows on key event
            
            % First check no modifiers were pressed
            if evt.data.Modifiers == 0
                % Which key was pressed
                switch evt.data.KeyCode
                    case mbcgui.util.KeyCode.DELETE % DELETE_KEY
                        iRemoveClicked(evt.data.Rows);
                end
            end
            end

            function iListviewSelectionChanged(lvVariables,evt)
            %iListviewSelectionChanged enable edit and remove buttons based
            %on list selection state
            if nargin==2
                SelectedRows = evt.data.SelectedRows;
            else
                SelectedRows = lvVariables.SelectedRows;
            end
            
            % enable remove if some rows are selected
            set(layoutUD.btRemove,'Enable',mbconoff(~isempty(SelectedRows)))
            % enable edit if only one row is selected
            set(layoutUD.btEdit,'Enable',mbconoff(isscalar(SelectedRows)))
            end
        
        
        end
    end
    
    methods  % public methods
        %----------------------------------------
        function editVariable(obj, index)
        %EDITVARIABLE Edit a variable definition in the sweepset
        %
        %  EDITVARIABLE(OBJ, INDEX)
        
        % Get the current names and units from the sweepset
        if nargin<2
            index = obj.SelectedRows;
        end
        names = get(obj.Sweepset, 'Name');
        units = get(obj.Sweepset, 'Units');
        % Convert units to cell array
        for i = 1:length(units)
            units{i} = char(units{i});
        end
        % Create a dialog to display the name and unit
        [OK, newName, newUnit] = i_editNameAndUnit(obj.Parent, names{index}, units{index});
        % Did the user press OK
        if OK
            names{index} = newName;
            units{index} = newUnit;
            % And set the names and units
            obj.Sweepset = set(obj.Sweepset, 'Name', names, 'Units', units);
            % Tell the world the sweepset has changed
            notify(obj, 'SweepsetChanged');
        end
        end  % editVariable
        
        %----------------------------------------
        function pDisplaySweepset(obj, ss)
        %PDISPLAYSWEEPSET Display the given sweepset
        %
        %  PDISPLAYSWEEPSET(OBJ, SS)
        %
        %  Implicit assumption is that ss is different from the current ss and all
        %  functions need to be updated. Also that there are the correct number of
        %  rows and columns in the listview for displaying the information.
        
        data = double(ss);
        
        Names = get(ss, 'Name');
        
        Data= Names(:);
        if obj.ShowUnits
            Units = get(ss, 'Units');
            Data = [Data Units(:)];
        end
        
        % Initialise to NaN
        columns = NaN(size(data, 2), length(obj.ColumnFcns));
        % Compute the column data
        if ~isempty(data)
            for i = 1:length(obj.ColumnFcns)
                fcn = obj.ColumnFcns{i};
                try %#ok<TRYNC>
                    if isa(fcn,'function_handle')
                        columns(:, i) = feval(fcn, data)';
                    elseif iscell(fcn)
                        columns(:, i) = feval(fcn{1}, data, fcn{2:end})';
                    end
                end
            end
        end
        Data = [Data num2cell(columns)];
        selectedRows = obj.Listview.SelectedRows;
        obj.Listview.setData(Data);
        if ~isempty(selectedRows) && max(selectedRows)<=size(Data,1)
            % select existing rows
            obj.Listview.SelectedRows = selectedRows;
        else
            obj.Listview.SelectedRows = 1;
        end
        
        end  % pDisplaySweepset
        
       
        %----------------------------------------
        function pInitialiseListview(obj)
        %PINITIALISELISTVIEW Listview initialiser
        %
        %  PINITIALISELISTVIEW(OBJ)
        
         % Make the listview a detail view
        % Set a whole bunch of properties
        
        if obj.MultiSelect
            set(obj.Listview,'SelectionMode','MultiRow')
        else
            set(obj.Listview,'SelectionMode','SingleRow')
        end
        set(obj.Listview,'Editable', obj.Editable);
        if obj.Editable
            obj.Listview.Peer.setEditableColumns(0:1);
        end
        
        set(obj.Listview,...
            'SelectionChangedCallBack',{@i_click,obj},...
            'ActionPerformedCallback',{@i_doubleClick,obj},...
            'ValueChangedCallback',{@i_afterLabelEdit,obj},...
            'KeyTypedCallback',{@i_keyUp,obj});
        end  % pInitialiseListview
        
        %----------------------------------------
        function pPostSetEditable(obj, ~,~)
        %PPOSTSETEDITABLE A short description of the function
        %
        %  OUT = PPOSTSETEDITABLE(IN)
        
        obj.Listview.Editable = obj.Editable;
        if obj.Editable
            obj.Listview.Peer.setEditableColumns(0:1);
        else
            obj.Listview.Peer.setEditableColumns([]);
        end
        
        end  % pPostSetEditable
        
        %----------------------------------------
        function pPostSetMultiSelect(obj, ~,~)
        %PPOSTSETMULTISELECT A short description of the function
        %  PPOSTSETMULTISELECT(OBJ, EVENT)
        
        if obj.MultiSelect
            set(obj.Listview,'SelectionMode','MultiRow')
        else
            set(obj.Listview,'SelectionMode','SingleRow')
        end
        
        end  % pPostSetMultiSelect
        
        %----------------------------------------
        function [ss] = pSetSweepset(obj, ss)
        %PSETSWEEPSET Set function to ensure that this property contains a sweepsepset
        %  [SS] = PSETSWEEPSET(OBJ, SS)
        
        if ~isa(ss, 'sweepset')
            error(message('mbc:xregdatagui:sweepsetlist:InvalidPropertyValue'));
        end
        
        % Call the update routine with the old and new sweepsets
        obj.pSweepsetChanged(obj.Sweepset, ss);
        
        end  % pSetSweepset
        
        %----------------------------------------
        function pSweepsetChanged(obj, oldSS, newSS)
        %PSWEEPSETCHANGED update routine to call if the sweepset changes
        %  PSWEEPSETCHANGED(OBJ, OLDSS, NEWSS)
        
        % No point in doing this if we don't yet have a listview
        if isempty(obj.Listview) || ~ishandle(obj.Listview)
            return
        end
        % Get a handle to the listItems collection
        % Have the variable names changed
        oldNames = get(oldSS, 'Name');
        newNames = get(newSS, 'Name');
        oldUnits = get(oldSS, 'Units');
        newUnits = get(newSS, 'Units');
        
        NAMES_CHANGED = ~isequal(oldNames, newNames);
        UNITS_CHANGED = ~isequal(oldUnits, newUnits);
        
        
        % Has the data changed (treating NaN as equal)
        if NAMES_CHANGED || UNITS_CHANGED || ~isequaln(double(oldSS), double(newSS))
            obj.pDisplaySweepset(newSS);
        end
        
        end  % pSweepsetChanged
        
        %----------------------------------------
        function setColumnHeaders(obj, callbacks, names, widths)
        %SETCOLUMNHEADERS set the list column headers
        %  SETCOLUMNHEADERS(OBJ, CALLBACKS, NAMES)
        
        % Make sure that the callbacks and names are cell arrays
        if ~iscell(callbacks)
            callbacks = {callbacks};
        end

        if ~iscell(names)
            names = {names};
        end
        % Have we got widths - default to width 80
        if nargin < 4
            widths = 80*ones(size(names));
        end
        % Set the internal bits
        obj.ColumnFcns = callbacks(:);
        obj.ColumnHeaders = names(:);
        
        ColHeaders = {'Variable Name'};
        ColWidths = 150;
        if obj.ShowUnits
            ColHeaders = [ColHeaders ; {'Units'}];
            ColWidths = [ColWidths 80];
        end
        ColHeaders = [ColHeaders ; obj.ColumnHeaders];
        ColWidths = [ColWidths widths];
        
        obj.Listview.Peer.setColumnData(ColHeaders);
        obj.Listview.Peer.setColumnWidths(ColWidths);
        
        if size(obj.Sweepset, 1) > 0
            obj.pDisplaySweepset(obj.Sweepset);
        end
        
        end  % setColumnHeaders
        
    end  % public methods
    
    
    methods (Static) % static methods
        %----------------------------------------
        function S = getStandardColumnFunctions
        %GETSTANDARDCOLUMNFUNCTIONS Provide standard set of column functions.
        %
        %   S = GETSTANDARDCOLUMNFUNCTIONS creates and returns a structure that
        %   contains function handles suitable for use as column data generators.
        %   These functions are generally preferred over simple built-in maths
        %   functions because they are designed to balance memory usage and
        %   computational speed.
        %
        %   The structure will contain the following fields: Mean, Std, Min, Max
        
        S = struct('Mean', @(d) nanmean(d,1), ...
            'Std', @(d) nanstd(d,0,1), ...
            'Min', @(d) nanmin(d,[],1), ...
            'Max', @(d) nanmax(d,[],1));
        end  % getStandardColumnFunctions
        
    end  % static methods
    
end  % classdef

function [OK, name, unit] = i_editNameAndUnit(parent, name, unit)
%i_editNameAndUnit edit variable dialog

SC = xregGui.SystemColorsDbl;

% Create a figure in which to draw the dialog.
dlg = mbcgui.container.Dialog('Name', 'Edit Data Variable',...
	'Visible','off',...
    'Owner', ancestor(parent,'figure'),...
    'Size',[250 120],...
    'Buttons','OK_CANCEL',...
    'DefaultAction','CANCEL');
f = dlg.Figure;
% Center on the parent figure
xregcenterfigure(f, [250 120], ancestor(parent,'figure'));

nameEdit = uicontrol('Parent', f,...
    'Style', 'edit',...
    'HorizontalAlignment', 'left', ...
    'BackgroundColor', SC.WINDOW_BG,...
    'String', name);

nameLabel = xregGui.labelcontrol('parent', f,...
    'LabelSizeMode', 'Absolute',...
    'ControlSizeMode', 'Relative',...
    'LabelAlignment', 'left',...
    'ControlSize', 1,...
    'LabelSize', 45,...
    'Control', nameEdit,...
    'String', 'Name:');

unitEdit = uicontrol('Parent', f,...
    'Style', 'edit',...
    'HorizontalAlignment', 'left', ...
    'BackgroundColor', SC.WINDOW_BG,...
    'String', unit);

unitLabel = xregGui.labelcontrol('Parent', f,...
    'LabelSizeMode', 'Absolute',...
    'ControlSizeMode', 'Relative',...
    'LabelAlignment', 'left',...
    'ControlSize', 1,...
    'LabelSize', 45,...
    'Control', unitEdit,...
    'String', 'Units:');

f.LayoutManager = xreggridbaglayout(f, ...
    'dimension', [2 1],...
    'elements', {nameLabel unitLabel},...
    'gapy', 5,...
    'rowsizes', [20 20],...
    'border', [7 7 7 7]);

closeAction = dlg.showDialog();

OK = strcmpi(closeAction,'OK');
name = get(nameEdit, 'String');
unit = get(unitEdit, 'String');

delete(dlg);

end  % i_editNameAndUnit

%------------------------------------------------------------------------
function i_click(~, evt,obj)
% listview selection action
evt = xregEventData(evt.data);
notify(obj, 'ButtonDown',evt);
notify(obj,'SelectionChanged',evt)
end  % i_click

%------------------------------------------------------------------------
function i_doubleClick(~, evt,obj)
% double click listview action
notify(obj, 'DoubleClick',xregEventData(evt.data));
% Has the use requested the built-in editable behaviour?
selectedRows = evt.data.SelectedRows;
if obj.Editable && ~isempty(selectedRows)
    obj.editVariable(selectedRows(1));
end
end  % i_doubleClick

%------------------------------------------------------------------------
function i_keyUp(~, evt,obj)

% convert event data to MCOS event.eventdata
evt = xregEventData(evt.data);
% Send on the KeyUp
notify(obj, 'KeyUp', evt);
end  % i_keyUp

%------------------------------------------------------------------------
function i_afterLabelEdit(~, evt,obj)
%i_afterLabelEdit edit listview

index = evt.data.Rows;
% Only add a sensible name
if isscalar(index) && ~isempty(evt.data.NewValue)
    switch evt.data.Columns
        case 1
            % Get the current names from the sweepset
            names = get(obj.Sweepset, 'Name');
            % Modify the correct name
            names{index} = evt.data.NewValue;
            % And set the names
            obj.Sweepset = set(obj.Sweepset, 'Name', names);
        case 2
            % Get the current units from the sweepset
            units = get(obj.Sweepset, 'UNITS');
            % Modify the correct units
            units{index} = evt.data.NewValue;
            % And set the units
            obj.Sweepset = set(obj.Sweepset, 'UNITS', units);
        otherwise
            error('Invalid column');
    end
    % Tell the world the sweepset has changed
    notify(obj, 'SweepsetChanged');
end
end  % i_afterLabelEdit