www.gusucode.com > mbctools 工具箱 matlab 源码程序 > mbctools/+xregdatagui/Editor.m
classdef Editor < mbcgui.Application %Editor Data Editor application object. % % XREGDATAGUI.Editor creates an Application object for the Data % Editor. In addition to the standard Application properties, this adds % properties, methods and events that are specific to the Data Editor. % Copyright 2008-2015 The MathWorks, Inc. properties(SetAccess=private) %MessageService data editor message service MessageService %MultiView multiview panel for plots and tables MultiView %Toolbar figure toolbar Toolbar %FileMenu main file menu FileMenu %ViewMenu main view menu ViewMenu %ToolsMenu main tools menu ToolsMenu %TestSnapPanel global test selector panel TestSnapPanel %TestSelector test number list for two-stage data selection TestSelector %AcceptDataAction action to process data for MBCMODEL % AcceptDataAction closes the data editor AcceptDataAction %StorageActions import and export storage actions StorageActions %GenerateReportAction generate report for data editor GenerateReportAction end properties (Dependent) %NewData set and get data for data editor NewData end % Miscellaneous storage properties(SetAccess=private, GetAccess=private) StatusInfoListeners StatusInfoID = -1; TitleListeners PointerStack = cell(0,2); end events ViewsChanged end methods function obj = Editor(varargin) %Editor editor constructor obj@mbcgui.Application(... 'Name', 'Data Editor',... 'Visible','off',... varargin{:}); obj.SleepOnClose = true; end function delete(obj) % Save the view layout if isgraphics(obj.Figure) delete(obj.Figure) end end function newData = get.NewData(hData) newData = hData.MessageService.DataObject; % Ensure the new data is correctly unhooked from the editor newData = removeMessageService(newData); end function set.NewData(hData,ssf) % Ensure the object has it's data message service set ssf = addMessageService(ssf, hData.MessageService); % Set the new data object to a deep copy of the new data hData.MessageService.setDataObject(ssf); end end methods (Static) function obj = create() %create create or restore data editor Tag = 'dataEditor'; obj = xregdatagui.Editor.find(Tag); if isempty(obj) % initial default size deSize = [50 100 800 600]; obj = xregdatagui.Editor('Tag',Tag,... 'Position',deSize); obj.Figure.Visible = 'on'; else restore(obj); end end function hData = open(pSSF,EditData,IS_TESTPLAN,fCloseEditor) %open open data editor with data % hData = xregdatagui.Editor.open(pSSF,EditData,IS_TESTPLAN,fCloseEditor) % pSSF pointer to sweepsetfilter % EditData allow editing of data % IS_TESTPLAN test plan data being edited % fCloseEditor callback to call on close if nargin<3 IS_TESTPLAN = isa(info(pSSF),'testplansweepsetfilter'); end if nargin<4 fCloseEditor = []; end % Make a copy of the object ssf = pSSF.copy; % Some legacy issues with data if IS_TESTPLAN if ~isempty( get(ssf,'reordersweeps') ) % legacy issue as old tps used reordersweeps to define match ssf = reorderSweeps(ssf,Inf); end % Restore the object cache to speed up evaluation in the data editor ssf = restoreCachedInfo(ssf); [ssf,OK] = setupGroups(getTestplan(ssf),ssf); if ~OK hData = []; return end end % Turn on the object's internal cacheing ssf = setCacheState(ssf, true); % setup groups if necessary before going into the data editor % Open data edit facility hData = xregdatagui.Editor.create(); hData.NewData = ssf; % Set the read-only status of the data hData.MessageService.isReadOnly = ~EditData; % restore data editor layout from data if available displayLayout = getDisplayLayout(ssf); if ~isempty(displayLayout) replaceDisplayLayout(hData,displayLayout); end %make modifications to cluster views if required setupClusterViews(hData) % Set the userdata of the figure S.ObjectBeingEdited = pSSF; if ~isempty(fCloseEditor) % setup user-defined close callback S.Close = event.listener(hData, 'Close', fCloseEditor); end %close down activities, including saving layout S.PostClose = event.listener(hData, 'PostClose', @onPostClose); % store close listeners hData.UserData = S; end function s = convertStorageToStruct(ViewInfo,vl) %convertStorageToStruct convert legacy cell array storage to structure % s = xregdatagui.Editor.convertStorageToStruct(ViewInfo,vl) if nargin<2 app = xregdatagui.Editor.find('dataEditor'); vl = app.MultiView.ViewList; end if strcmp(ViewInfo{1},'xregsplitlayout') s.Type = 'split'; s.Left = xregdatagui.Editor.convertStorageToStruct(ViewInfo{3},vl); s.Right = xregdatagui.Editor.convertStorageToStruct(ViewInfo{5},vl); s.Orientation = ViewInfo{7}; s.Split = ViewInfo{9}; %collapse views (list views are now invalid) if isempty(s.Left) s = s.Right; elseif isempty(s.Right) s = s.Left; end else % individual views Index = vl.findViewClass(ViewInfo{1}); if ~isempty(Index) s.Type = 'view'; s.ViewLabel = vl.Labels{Index}; s.ViewClass = func2str(vl.ConstructorFcns{Index}); s.ViewData = ViewInfo(2:end); else % view no longer valid (list views) s = []; end end end end % mbcgui.Application overrides methods(Access=protected) function C = createContent(obj) %CREATECONTENT Create content layout. % % C = CREATECONTENT(OBJ) constructs the basic content layout for the Data % Editor. ms = xregdatagui.MessageService; obj.MessageService = ms; obj.Listeners = [obj.Listeners(:); event.listener(ms,'Busy',@obj.onBusy) event.listener(ms,'Idle',@obj.onIdle) event.listener(ms,'ssDataChanged',@obj.onTestsChanged) event.listener(ms,'ssTestsChanged',@obj.onTestsChanged)]; hFig = obj.Figure; % Create the menuview object to generate and maintain the menus and toolbar % Top level menus obj.FileMenu = uimenu('Parent', hFig, 'Label', '&File'); obj.ViewMenu = uimenu('Parent', hFig, 'Label', '&View'); obj.ToolsMenu = uimenu('Parent', hFig, 'Label', '&Tools'); % Add a window and help menu xregwinlist(hFig); mv_helpmenu(hFig, {'&Data Editor Help', 'xreg_dataEditor'}); toolbarPanel = mbcgui.container.layoutpanel(... 'Parent', obj.Figure, ... 'BorderType', 'beveledin'); % Create the toolbar toolbar = xregGui.uitoolbar('Parent', toolbarPanel,... 'resourcelocation', xregrespath); obj.Toolbar = toolbar; set(toolbarPanel, 'LayoutComponent', toolbar); % Link the visible components together C = xreggridbaglayout(obj.Figure,... 'dimension', [2 1],... 'elements', {toolbarPanel []},... 'rowsizes', [31 -1],... 'gapy', 2); obj.setStatusProgress(0.25, 0, 1); msgID = obj.setStatusMessage('Creating information views...'); % Create the specific data and testnumber views that will always be added % to the data editor dataInfoView = xregdatagui.DataInfoView('Parent', hFig, ... 'MessageService', obj.MessageService); obj.setStatusProgress(0.4); % test selector panel obj.TestSelector = xregdatagui.TestSelector('Parent', hFig, ... 'MessageService', obj.MessageService); createMultiView(obj) % Create the layouts for the specific data views to sit in infoSnapPanel = xregsnapsplitlayout(hFig,... 'packstatus', 'off', ... 'barstyle',1,... 'orientation', 'ud',... 'style','totop',... 'split', [0 1],... 'minwidth', [150 100],... 'top', dataInfoView,... 'bottom', obj.MultiView); testSnapPanel = xregsnapsplitlayout(hFig,... 'packstatus', 'off',... 'barstyle',1,... 'orientation', 'lr',... 'style','toleft',... 'split', [0 1],... 'minwidth', [100 100],... 'left', obj.TestSelector,... 'right', infoSnapPanel); obj.setStatusMessage(msgID, 'Regenerating saved views...'); obj.setStatusProgress(0.6); obj.TestSnapPanel = testSnapPanel; % Get the layout preferences obj.setStatusMessage(msgID, 'Redrawing...'); obj.setStatusProgress(0.9); el = get(C, 'elements'); el{2} = testSnapPanel; set(C, 'elements', el, 'packstatus', 'on'); set(testSnapPanel, 'Visible', 'on'); % How far through are we? obj.setStatusMessage(msgID, 'Completing...'); obj.setStatusProgress(1); % Listen to changes in the size of the data info view to ensure it is % always 110 pixels high, except when it's snapped shut l = mbcgui.hgclassesutil.listener(hFig, 'SizeChanged', ... mbcutils.callback(@i_setInfoHeight, infoSnapPanel)); set(C, 'UserData', l); % Install listeners to maintain the window title and statusbar information obj.createStatusInfo(); obj.createTitle(); createControls(obj) % Tell the user everything is ready obj.setStatusProgress(0); obj.setStatusMessage(msgID, 'Ready'); end function sleep(obj) %SLEEP Take action when the window is made invisible % % SLEEP(OBJ) % % Set the viewed data object to a null one to ensure the data editor % doesn't keep data copies alive obj.MessageService.setDataObject([]); end end methods function restore(obj) %RESTORE Make window visible again % RESTORE(OBJ) will re-open an application that has been put into a % "sleep" mode when closed. if strcmp(get(obj.Figure, 'Visible'), 'on') % Fake a close event before restoring the application obj.close(true); end obj.restore@mbcgui.Application(); end end % Construction helper methods and callbacks methods(Access=private) function createControls(obj) %createControls create data editor controls ms = obj.MessageService; obj.StorageActions = mbcgui.actions.ActionGroup; obj.StorageActions.MenuType = 'separate'; obj.StorageActions.Actions = [mbcgui.actions.StatefulAction(@obj.onStorage,... '&Import Expressions...','Import variables, filters and editor layout',xregrespath('storageIcon.bmp')); mbcgui.actions.StatefulAction(@obj.onExportStorage,... 'Export Expressions...','Export variables, filters and editor layout',xregrespath('exportStorage.bmp'))]; f = obj.Figure; obj.AcceptDataAction= mbcgui.actions.StatefulAction(@(h,evt) close(f),... '&Save && Close','Save data and close data editor',xregrespath('Confirm_16.bmp')); hFig = obj.Figure; % process data on closing figure hFig.CloseRequestFcn = @(h,evt) obj.AcceptDataAction.execute(); % file menu createFileMenu(ms.Actions,obj.FileMenu); % create report generation if present doReport = license('test', 'MATLAB_Report_Gen') && ms.HasExtras && false; if doReport obj.GenerateReportAction= mbcgui.actions.StatefulAction(@obj.onGenerateReport,... 'Generate &Report...','Generate report',xregrespath('Report.bmp')); hReport = createMenuItem(obj.GenerateReportAction,obj.FileMenu); hReport.Separator = 'on'; end hClose = createMenuItem(obj.AcceptDataAction,obj.FileMenu); hClose.Separator = 'on'; % tools menu createToolsMenu(ms.Actions,obj.ToolsMenu); createMenuItem(obj.StorageActions,obj.ToolsMenu); % create toolbar items obj.Toolbar.setRedraw(false); createToolbar(ms.Actions,obj.Toolbar); createToolbutton(obj.StorageActions,obj.Toolbar); % create multiview toolbar AG = mbcgui.actions.ActionGroup('','Toggle'); AG.MenuType = 'separate'; AG.Actions = obj.MultiView.Actions.ChangeView; btns = createToolbutton(AG,obj.Toolbar); set(btns(1),'Separator','on'); if doReport % report generator toolbar tbReport = createToolbutton(obj.GenerateReportAction,obj.Toolbar); tbReport.Separator = 'on'; end tbExport = createToolbutton(obj.MessageService.Actions.ExportWorkspace,obj.Toolbar); if ~doReport tbExport.Separator = 'on'; end createToolbutton(obj.AcceptDataAction,obj.Toolbar); % Add a help toolbar button mv_helptoolbutton(obj.Toolbar, 'xreg_dataEditor'); obj.Toolbar.setRedraw(true); if ms.HasExtras e = mbcextensions.Extensions.Model; mbccreateaddonmenus(e.DataEditorTools, obj.ToolsMenu) end end function createMultiView(obj) %createMultiView create data editor multiview area vlist = mbcgui.multiview.ViewList; % standard data editor views add(vlist,xregdatagui.SweepsetPlotView.ViewInfo); add(vlist,xregdatagui.DataPlot3DView.ViewInfo); add(vlist,xregdatagui.DataTableView.ViewInfo); add(vlist,xregdatagui.MonitorPlotView.ViewInfo); % cluster views only relevant to test plan data add(vlist,xregdatagui.TssfClusterView.ViewInfo); if obj.MessageService.HasExtras % add extra views e = mbcextensions.Extensions.Model; for i=1:length(e.DataEditorViews) add(vlist,e.DataEditorViews(i).Constructor); end end obj.MultiView = mbcgui.multiview.MultiViewPanel('Parent', obj.Figure, ... 'Visible','off',... 'AlwaysAllowPrintToFigure',false,... 'MessageService', obj.MessageService, ... 'ViewLayoutName', 'DataEditorViews', ... 'ViewList', vlist); % include a table splitView(obj.MultiView,'lr',3); obj.MultiView.Visible = 'on'; % add multiview menus to main view menu addViewMenuItems(obj.MultiView,obj.ViewMenu); end function createStatusInfo(obj) %CREATESTATUSINFO Set up the update mechanism for displaying status % % CREATESTATUSINFO(obj) % Listen to changes in the DataMessageService.DataObject size dms = obj.MessageService; obj.StatusInfoListeners = [... event.proplistener(dms, dms.findprop('IsReadOnly'), 'PostSet', @obj.pPostSetDmsIsReadOnly);... event.listener(dms, 'ssRecordsChanged', @obj.updateStatusMessage);... event.listener(dms, 'ssNamesChanged', @obj.updateStatusMessage);... event.listener(dms, 'ssTestsChanged', @obj.updateStatusMessage);... ]; end function updateStatusMessage(obj,~, ~) % Get a temp pointer to the dms dms = obj.MessageService; % Get the size of the data sizeSS = size(dms.sweepset); % Do something depending on the class of the data object if dms.isaSS messageString = sprintf('Data has %d Records, %d Variables, and %d Tests.', ... sizeSS(1), sizeSS(2), sizeSS(3)); elseif dms.isaSSF % Get the sweepsetfilter ssf = dms.SweepsetFilter; % Get the parent sweepset parentSSSize = size(sweepset(info(dataptr(ssf)))); % Create an appropriate message messageString = sprintf('Data has %d/%d Records, %d + %d Variables, and %d Tests.',... sizeSS(1), parentSSSize(1), sizeSS(2), sizeSS(2)-parentSSSize(2), sizeSS(3)); else messageString = 'Unknown data type ... Ready'; end if obj.StatusInfoID > 0 % Just change the message obj.setStatusMessage(obj.StatusInfoID, messageString); else % Add the new message obj.StatusInfoID = obj.setStatusMessage(messageString); end end function createTitle(obj) %CREATESTATUSINFO Set up the update mechanism for displaying status % % CREATESTATUSINFO(obj) % Listen to changes in the DataMessageService dms = obj.MessageService; obj.TitleListeners = [... event.listener(dms, 'ssfNameChanged', @obj.updateTitle);... event.proplistener(dms, dms.findprop('IsReadOnly'), 'PostSet', @obj.updateTitle);... ]; end function updateTitle(obj,~, ~) dms = obj.MessageService; defaultTitle = 'Data Editor - '; % Name depending on the type of the object switch class(dms.DataObject) case {'sweepsetfilter', 'testplansweepsetfilter'} if dms.IsReadOnly title = [defaultTitle dms.ObjectName ' [Read Only]']; else title = [defaultTitle dms.ObjectName]; end case 'sweepset' title = [defaultTitle dms.ObjectName ' [Read Only]']; otherwise title = [defaultTitle 'Other']; end % Set the title of the figure set(obj.Figure, 'Name', title); end function onPostClose(hData,~) %onPostClose post close action for data editor pSSF = hData.UserData.ObjectBeingEdited; if isvalid(pSSF) % save layout for data whether or not the data has changed pSSF.info = setDisplayLayout(pSSF.info,hData.serializeDisplayLayout); end % Free the inputs to the DataMessageService freeInternalPtrs(hData.MessageService.DataObject); % Clear close listeners hData.UserData = []; end function onBusy(obj,~,evt) %onBusy listen for MessageService.Busy and change the figure pointer ptrID = setPointer(obj,'watch'); msgID = setStatusMessage(obj,evt.Data.Message); obj.PointerStack(end+1,:) = {ptrID,msgID}; end function onIdle(obj,~,~) %onIdle listen for MessageService.Idle and change the figure pointer if ~isempty(obj.PointerStack) removePointer(obj,obj.PointerStack{end,1}) removeStatusMessage(obj,obj.PointerStack{end,2}) obj.PointerStack(end,:) = []; end end function onGenerateReport(obj,~,~) %onGenerateReport generate report callback busy(obj.MessageService,'Generating report...'); [docFile,pth] = uiputfile({'*.docx';'*.pdf'},'Data Report'); if ischar(docFile) [fname,ext] = fileparts(docFile); rpt = mlreportgen.dom.Document(fullfile(pth,fname),'docx'); generateReport(obj,rpt) close(rpt); if strcmpi(ext,'.pdf') rptview(rpt.OutputPath,'pdf'); else rptview(rpt.OutputPath); end end idle(obj.MessageService); end function onStorage(obj,~,~) %onStorage access storage functionality % Create the storage dialog % Try getting the storage handle f = mvf('storage'); % Do we already have a valid storage handle? if isgraphics(f) figure(f); return end % Indicate this task might take a little time busy(obj.MessageService,'Opening Storage Window...'); f = xregdatagui.StorageView.createFigure(obj); % Register as a figure to delete later obj.addSubFigure(f); % Remove the message and the pointer idle(obj.MessageService); end function onExportStorage(obj,~,~) %onExportStorage export storage settings to MAT file % Open the save file dialog [filename, pathname] = uiputfile({'*.mat' 'MAT-files (*.mat)';... '*.*' 'All files (*.*)'}, 'Save Variables, Filters and Editor Layout'); % Did the user cancel if isequal(pathname, 0) || isequal(filename, 0) return end % store current layout in sweepsetfilter ViewInfo = serializeDisplayLayout(obj); ssf = obj.MessageService.SweepsetFilter; ssf = setDisplayLayout(ssf,ViewInfo); storage.data = getStorage(ssf); storage.version = 2; save(fullfile(pathname,filename), 'storage'); end function pPostSetDmsIsReadOnly(obj,~,~) %pPostSetDmsIsReadOnly disable storage actions if data editor is read-only obj.StorageActions.Actions(2).Enabled = ~obj.MessageService.IsReadOnly; end function onTestsChanged(obj,~,~) %onTestsChanged hide the test number panel for one-stage data ms = obj.MessageService; if ~isempty(ms.DataObject) % only toggle state if there is some data if ms.isOneStage % don't show the test selector list set(obj.TestSnapPanel,'State','left','SplitEnable','off'); else % show the test selector list set(obj.TestSnapPanel,'State','center','SplitEnable','on'); if isempty(ms.SelectedTests) % select a test if required setSelectedTests(ms,1); end end end end end % View manipulation methods methods function ViewInfo = serializeDisplayLayout(obj) %serializeDisplayLayout serialize data editor layout layoutdata = saveViewLayout(obj.MultiView); ViewInfo.Layout = layoutdata; % store selected tests ViewInfo.SelectedTests = obj.MessageService.SelectedTests; if isempty(ViewInfo.SelectedTests) ViewInfo.SelectedTests = 1; end end function replaceDisplayLayout(obj, ViewInfo) %replaceDisplayLayout replace data editor layout % replaceDisplayLayout(obj, ViewInfo) if iscell(ViewInfo) % update from pre-R2016a form ViewInfo = xregdatagui.Editor.convertStorageToStruct(ViewInfo,obj.MultiView.ViewList); end if isfield(ViewInfo,'SelectedTests') SelectedTests = ViewInfo.SelectedTests; lytStruc = ViewInfo.Layout; else SelectedTests = obj.MessageService.SelectedTests; lytStruc = ViewInfo; end restoreViewLayout(obj.MultiView,'dataeditor',lytStruc); % flush a data changed event updateViews(obj.MessageService,SelectedTests); end function setupClusterViews(obj) %setupClusterViews setup cluster views ms = obj.MessageService; % design matching MatchDesign = ms.isaTSSF && ~isExpData( designdev(getTestplan(ms.TestplanSweepsetFilter)) ); enableView(obj.MultiView,'xregdatagui.TssfClusterView',MatchDesign); if ~MatchDesign % remove cluster view removeViews(obj.MultiView,'xregdatagui.TssfClusterView'); end end function generateReport(obj,rpt,lvl) %generateReport generate report for view % generateReport(obj,rpt,lvl) import mlreportgen.dom.* if nargin<3 lvl = 1; end % Use object name for heading ms = obj.MessageService; h = Heading(lvl,ms.ObjectName); rpt.append(h); % generate report for SweepSetFilter data manipulation ssf = ms.SweepsetFilter; generateReport(ssf,rpt,lvl+1) % generate report for multiview plots obj.MultiView.generateReport(rpt,lvl+1) end function views = enumerateViews(obj) %enumerateViews get list of views viewContainers = getViewContainers(obj.MultiView); views = [viewContainers.View]; end end end function i_setInfoHeight(~, ~, snapPanel) % Push all the height change into the bottom split, keeping the minimum % size of the top part 110 pixels. Note that the floor will ensure that % these (110 and currentWidths(2) + deltaH) will not sum to more than % the new possible height of the figure. snapPos = get(snapPanel, 'Position'); currentWidths = get(snapPanel, 'minwidth'); newWidths = [currentWidths(1) snapPos(4)-currentWidths(1)]; if ~isequal(newWidths, currentWidths) set(snapPanel, 'split', [0 1]); end end