www.gusucode.com > mbctools 工具箱 matlab 源码程序 > mbctools/+xregdatagui/MonitorPlotView.m
classdef MonitorPlotView < xregdatagui.AbstractDataView %xregdatagui.MonitorPlotView class % xregdatagui.MonitorPlotView extends xregdatagui.AbstractDataView. % % xregdatagui.MonitorPlotView 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' % MessageService - Property is of type 'handle' (read only) % Container - Property is of type 'handle' % UIContextMenu - Property is of type 'MATLAB array' % Listeners - Property is of type 'handle vector' (read only) % ListControl - Property is of type 'MATLAB array' (read only) % AxesHandles - Property is of type 'MATLAB array' (read only) % PlotProperties - Property is of type 'MATLAB array' % % xregdatagui.MonitorPlotView methods: % canPrint - Check whether component can be printed % gettitle - returns string describing this view % serializeView - A short description of the function % Copyright 2015 The MathWorks, Inc. and Ford Global Technologies, Inc. properties (Constant) %ViewInfo view description ViewInfo = { @xregdatagui.MonitorPlotView; '&Multiple Plots'; xregrespath('dataplots.bmp'); 'Multiple Plots'; 1}; end properties (AbortSet, SetObservable) %PLOTPROPERTIES Property is of type 'MATLAB array' PlotProperties = xregmonitorplotproperties; %SelectAllTests select all tests for display SelectAllTests = false; end properties (SetAccess=protected, AbortSet, SetObservable) %LISTCONTROL Property is of type 'MATLAB array' (read only) ListControl = []; %AXESHANDLES Property is of type 'MATLAB array' (read only) AxesHandles = []; %CardLayout card layout to contain instructions when there are no plots CardLayout end methods % constructor block function obj = MonitorPlotView(varargin) %MONITORPLOTVIEW constructor % OBJ = MONITORPLOTVIEW(VARARGIN) % Do the setup stuff from inside the abstractDataView constructor obj@xregdatagui.AbstractDataView(varargin{ : }); % converted super class constructor call obj.createActions; % Create the graphical objects and put in the Display field obj.pCreateDisplay; % Update the display obj.pUpdateDisplay end % monitorplotview end % constructor block methods function set.PlotProperties(obj,value) if ~isa(value, 'xregmonitorplotproperties') error(message('mbc:xregdatagui:monitorplotview:InvalidProperty')); end obj.PlotProperties = value; pPlotPropertiesChanged(obj); end end % set and get functions methods % public methods %---------------------------------------- function out = canPrint(obj) %#ok<MANU> %CANPRINT Check whether component can be printed % CANPRINT(OBJ) returns true for monitorplotview components. out = true; end % canPrint %---------------------------------------- function out = gettitle(obj) %#ok<MANU> %GETTITLE returns string describing this view % OUT = GETTITLE(OBJ) out = 'Multiple Data Plots'; end % gettitle %---------------------------------------- function pAddPlot(obj, ~,~) %PADDPLOT add a plot to the current list of plots % PADDPLOT(OBJ, EVENT) % Add a plot to the current properties object obj.PlotProperties = addPlot(obj.PlotProperties); % What variables are to be displayed obj.pModifyPlotVariables(); end % pAddPlot %---------------------------------------- function pCreateDisplay(obj) %pCreateDisplay Create all uicontrols and visual components for this view % pCreateDisplay(OBJ) hPromptPanel = mbcgui.container.layoutpanel('Parent', obj.Parent, ... 'BorderType', 'none', ... 'HitTest','off',... 'Tag','MonitorPromptPanel',... 'Units', 'pixels'); hTxt = uicontrol('Parent', hPromptPanel,... 'Style', 'text',... 'HitTest', 'off',... 'FontSize',10,... 'String', 'You have no plots setup yet. Right click to choose which variables to plot.'); hButtton = createButton(obj.Options.Actions(1),hPromptPanel); lyt = xreggridbaglayout(hPromptPanel,... 'dimension',[4 3],... 'gapy',10,... 'rowsizes',[-1 40 25 -1],... 'colsizes',[-1 100 -1],... 'elements',{[],[],[] hTxt,[],[] [],hButtton,[] [],[],[]},... 'MergeBlock',{[2 2],[ 1 3]}); hPromptPanel.LayoutComponent = lyt; % Create the list control to hold the axes obj.ListControl = xreglistctrl(obj.Parent,... 'border', [-1 -1 -1 -1],... 'UIContextMenu',obj.UIContextMenu,... 'fixnumcells', 2); % create a set of cards - one holds a prompt for the user, the other the % axes that we plot on obj.CardLayout = xregcardlayout(obj.Parent,... 'currentcard', 1,... 'numcards', 2); attach( obj.CardLayout, hPromptPanel, 1); attach( obj.CardLayout, obj.ListControl, 2); obj.ContentHandle = obj.CardLayout; end % pCreateDisplay function createActions(obj) %createActions create view actions (available from contextmenu) ms = obj.MessageService; obj.Options.Actions = [mbcgui.actions.StatefulAction(@obj.pAddPlot,... '&Add Plot...','Add plot') mbcgui.actions.StatefulAction(@obj.pModifyPlotProperties,... '&Plot Properties...','Plot properties') mbcgui.actions.StatefulAction(@obj.pModifyPlotVariables,... 'Plot &Variables','Plot variables') mbcgui.actions.StatefulAction(@obj.onRemoveAllPlots,... 'Remove &All Plots','Remove all plots') mbcgui.actions.StatefulAction(@obj.onRemovePlot,... '&Remove Plot','Remove plot') mbcgui.actions.ToggleAction(@obj.onSelectAllTests,... '&Select All Tests','Select all tests')]; obj.Actions = ms.Actions.OutlierTools; % add outlier tools to menu [obj.Options.Actions(2:end-1).Enabled] = deal(false); % select all tests obj.Options.Actions(6).Selected = obj.SelectAllTests; end %---------------------------------------- function pModifyPlotVariables(obj, ~,~) %PMODIFYPLOTVARIABLES change variables displayed on the plot % PMODIFYPLOTVARIABLES(OBJ, ~, ~) if length(obj.PlotProperties)==0 %#ok<ISMT> % add a new plot if there are not any obj.PlotProperties = addPlot(obj.PlotProperties); end % Is the current axes of the figure one of ours [OK, index] = ismember(get(ancestor(obj.Parent,'figure'), 'CurrentAxes'), obj.AxesHandles); % Only modify if the currentAxes is one or ours if OK obj.PlotProperties = editPropertiesDlg(obj.PlotProperties, index, obj.MessageService.Sweepset, obj.Parent); obj.pUpdateDisplay; end end % pModifyPlotVariables %---------------------------------------- function pObjectBeingDestroyed(obj, ~) %POBJECTBEINGDESTROYED called when the view is being destroyed % POBJECTBEINGDESTROYED(OBJ, EVENT) if isgraphics(obj.Parent) && ~mbcgui.util.isBeingDestroyed(obj.Parent) % Delete the viewer menu items used, by deleting their parent delete(get(obj.OptionMenuItems(1), 'Parent')); end end % pObjectBeingDestroyed %---------------------------------------- function pPlotPropertiesChanged(obj, ~,~) %PPLOTPROPERTIESCHANGED post-set obj.PlotProperties % PPLOTPROPERTIESCHANGED(OBJ, EVENT) if length(obj.PlotProperties)==0 %#ok<ISMT> set(obj.CardLayout,'CurrentCard',1); else set(obj.CardLayout,'CurrentCard',2); end % Have we got the correct number of axes to display the requested data if length(obj.PlotProperties) ~= length(obj.AxesHandles) % NO ... Do we need to add or remove axes? difference = length(obj.PlotProperties) - length(obj.AxesHandles); % Lets add or remove the correct number of axes if difference > 0 i_addAxes(obj, difference); else i_removeAxes(obj, -difference); end end % Are a maximal number of axis visible? topItem = get(obj.ListControl, 'top'); numItems = length(get(obj.ListControl, 'elements')); if numItems > 1 && topItem > numItems - 1 set(obj.ListControl, 'top', numItems - 1); end % OK - We have the correct number of axes for the plots now so it's as % though the data had changed obj.pssDataChangedUpdate(); % Enable of disable the options menu items if isempty(obj.PlotProperties) [obj.Options.Actions(2:end-1).Enabled] = deal(false); else [obj.Options.Actions(2:end-1).Enabled] = deal(true); end end % pPlotPropertiesChanged function onRemoveAllPlots(obj,~,~) %onRemoveAllPlots remove all axes callback obj.pRemovePlot(obj.AxesHandles); end function onRemovePlot(obj,~,~) %onRemovePlot remove current axes callback obj.pRemovePlot( get(ancestor(obj.Parent,'figure'), 'CurrentAxes')); end function onSelectAllTests(obj,~,~) %onSelectAllTests select all tests callback obj.SelectAllTests = ~obj.SelectAllTests; obj.pssDataChangedUpdate(); end %---------------------------------------- function pRemovePlot(obj, axesHandles) %PREMOVEPLOT remove the current plot from the view % PREMOVEPLOT(OBJ, EVENT) % Are the handles to axes to remove any of ours? [OK, index] = ismember(axesHandles, obj.AxesHandles); % Get their specific indices indexToRemove = index(OK); % Only remove if the currentAxes is one or ours if ~isempty(indexToRemove) % Remove from our list %obj.AxesHandles(indexToRemove) = []; % And delete the properties associated obj.PlotProperties = removePlot(obj.PlotProperties, indexToRemove); obj.pUpdateDisplay end end % pRemovePlot %---------------------------------------- function pUpdateDisplay(obj,~,~) %PUPDATEDISPLAY complete update of the view display % PUPDATEDISPLAY(OBJ) % Update the data via the plot properties obj.pPlotPropertiesChanged([]); end % pUpdateDisplay %---------------------------------------- function pdmsDataTypeChangedUpdate(obj, ~,~) %PDMSDATATYPECHANGEDUPDATE event handler for the dmsDataTypeChanged event % PDMSDATATYPECHANGEDUPDATE(OBJ, EVENT) % Find the sweepset listener ssListeners = obj.MessageService.findListeners(obj.Listeners, 'Sweepset'); % And sweepsetfilter listeners ssfListeners = obj.MessageService.findListeners(obj.Listeners, 'SweepsetFilter'); % If the dataObject isn't a sweepset then... if ~isobject(obj.MessageService.DataObject) % Turn off the sweepset listener [ssListeners.Enabled] = deal(false); [ssfListeners.Enabled] = deal(false); else % Turn on the sweepset listener [ssListeners.Enabled] = deal(true); [ssfListeners.Enabled] = deal(true); end end % pdmsDataTypeChangedUpdate %---------------------------------------- function sz = printSize(obj) %PRINTSIZE Returns the preferred printing size for the component % SZ = PRINTSIZE(OBJ) returns a two element vector containing the % preferred width and height for printing the component. % How many elements are being displayed by the ListControl numElements = length(get(obj.ListControl, 'element')); % Multiply the view y size by half the number of elements (2 rows are % shown at a time on screen) and add 50 for the title. sz = [700 numElements*175+50]; end % printSize %---------------------------------------- function layout = printCopy(obj, newFigure) %PRINTCOPY copy the layout to the newfigure % LAYOUT = PRINTCOPY(OBJ, FIGURE) % Create the correct number of new axes (-1 is an invalid handle) newAxes = gobjects(length(obj.PlotProperties), 1); elements = cell(size(newAxes)); for i = 1:length(obj.PlotProperties) elements{i} = mbcgui.widget.AxesContainer(... 'Parent', newFigure,... 'PositionSetting','outer',... 'Border', [5 5 5 5]); newAxes(i) = elements{i}.AxesHandle; end % Hold the old axes handle oldAxes = obj.AxesHandles; UIContextMenu = obj.UIContextMenu; try % Make the object think it's using the new axes to print obj.AxesHandles = newAxes; obj.UIContextMenu = []; % Plot the graphs obj.pssDataChangedUpdate([]); % Make sure the button down functions are unset set(newAxes, 'ButtonDownFcn', [],'Tag','dataPlot'); set(findobj(newAxes,'Type','Line'), 'ButtonDownFcn', []); catch E obj.AxesHandles = oldAxes; obj.UIContextMenu = UIContextMenu; rethrow(E); end % Put the old axes back obj.UIContextMenu = UIContextMenu; obj.AxesHandles = oldAxes; numRows = length(get(obj.ListControl, 'controls')); numCols = 2; % Need to ensure that elements has an even number of entries for the % transform below if mod(length(elements), 2) elements{end+1} = []; end % Need to ensure the elements are in column order for the grid layout elements = reshape(elements, [numCols numRows])'; % Place the axes in a layout to comply with the tenets of printcopy plotlayout = xreggridbaglayout(newFigure,... 'packstatus','on',... 'dimension', [numRows, numCols],... 'elements', elements); % Add a title titleH = axestext(newFigure, ... 'string',[obj.gettitle ' for "' obj.MessageService.ObjectName '"'],... 'fontsize', 11,... 'fontweight', 'bold',... 'HorizontalAlignment', 'center',... 'VerticalAlignment', 'middle',... 'interpreter', 'none'); layout = xreggridbaglayout(newFigure, ... 'packstatus','on',... 'Dimension', [2 1], ... 'Rowsizes', [25 -1], ... 'gapy', 20, ... 'border', [0 0 0 5], ... 'elements', {titleH, plotlayout}); end % printcopy %---------------------------------------- function pssDataChangedUpdate(obj, ~,~) %pDataChangedUpdate updates the view on receipt of a "DataChanged" event from the DMS % PSSDATACHANGEDUPDATE(OBJ,SRC,EVT) % % For now we will just re-plot the whole thing xlabels = get(obj.AxesHandles, {'XLabel'}); titles = get(obj.AxesHandles, {'Title'}); outlierLine = obj.MessageService.OutlierLine; for i = 1:length(obj.AxesHandles) % Remove lines from outlier control lines = findobj(obj.AxesHandles(i), 'Type', 'line'); outlierLine.remove(lines); % Delete remaining lines in the axes lines = findobj(obj.AxesHandles(i), 'Type', 'line'); delete(lines); set(xlabels{i}, 'String', ''); set(titles{i}, 'String', ''); end dispStr = '.'; ms = obj.MessageService; % get tests to plot if obj.SelectAllTests selectedIndices = 1:size(ms.Sweepset,3); else selectedIndices = ms.SelectedTests; end ssGoodData = ms.Sweepset(:,:,selectedIndices); ssBadData = []; for i = 1:length(obj.PlotProperties) plotProperties = obj.PlotProperties(i); xName = plotProperties.xName; yNames = plotProperties.yNames; props = plotProperties.properties; % Is there anything to plot? if isempty(yNames) continue end % Which data are we plotting here DISPLAY_BAD_DATA = strcmp(props.showBadData, 'on'); if DISPLAY_BAD_DATA selectedTests = ms.GoodToBadIndexMap(selectedIndices); if isempty(ssBadData) % retrieve sweepset with bad data only if needed ssBadData = ms.SweepsetWithBadData; end ss = ssBadData(:,:,selectedTests); else selectedTests = selectedIndices; ss = ssGoodData; end varYInd = find(ss, yNames); if ~isempty(varYInd) % Check that the yNames are valid Ysweepset = ss(:, varYInd); varXInd = find(ss, xName); if isempty(varXInd) % Is there a valid X Name if length(yNames) > 1 && length(selectedTests) > 1 % separate tests with magenta sweepline when more % than 1 y variable and more than 1 test [h, ~] = sweepplot(Ysweepset, dispStr,... 'parent', obj.AxesHandles(i),... 'plotproperties', props,... 'sweeplines', 'm'); else [h, ~] = sweepplot(Ysweepset, dispStr,... 'parent', obj.AxesHandles(i),... 'plotproperties', props); end else % Check that the yNames are valid [h, ~] = sweepplot(ss(:, varXInd), Ysweepset, dispStr,... 'parent', obj.AxesHandles(i),... 'plotproperties', props); end % Need to work out if we have multiple variables, multiple line types % etc. Note that the return of sweepsetplot is a nx1 array of handles % which references all variables in test 1 then test 2 etc. numVariables = length(yNames); numTests = numel(h)/numVariables; % Need to reshape the handles correctly reshapedH = reshape(h, numVariables, numTests); guids = getGuids(Ysweepset); for j = 1:numVariables outlierLine.add(reshapedH(j, :), guids); end end end % Ensure zoom is on after an update plot set(obj.AxesHandles, 'ButtonDownFcn', {@i_axesClick obj},... 'UIContextMenu',obj.UIContextMenu); end % pssDataChangedUpdate %---------------------------------------- function pssUnitsChangedUpdate(obj, ~,~) %pUnitsChangedUpdate updates the view on receipt of a "ssUnitsChanged" event from the DMS % pUnitsChangedUpdate(OBJ,SRC,EVT) % If units change without a data change we still need to update the plot if ~any(strcmp(obj.MessageService.EventQueue, 'ssDataChanged')) obj.pssDataChangedUpdate(); end end % pssUnitsChangedUpdate %---------------------------------------- function pssfBadDataChangedUpdate(obj, ~,~) %pssfBadDataChangedUpdate updates the view on receipt of a "ssfBadDataChanged" event from the DMS % PSSFBADDATACHANGEDUPDATE(OBJ) % Peek in the message queue to see if there is going to be an ssDataChanged % event. If not initiate one if ~ismember('ssDataChanged', obj.MessageService.EventQueue) obj.pssDataChangedUpdate([]); end end % pssfBadDataChangedUpdate function pssTestsChangedUpdate(obj,~,~) %pssTestsChangedUpdate test definition changed if obj.MessageService.isOneStage % hide test selector and controls obj.SelectAllTests = false; % disable select all tests set(obj.Options.Actions(6),'Enabled',true,'Selected',false,'Enabled',false); else % enable select all tests and separate test selection set(obj.Options.Actions(6),'Enabled',true); end if ~any(strcmp(obj.MessageService.EventQueue, 'ssDataChanged')) obj.pssDataChangedUpdate(); end end % pssTestsChangedUpdate %---------------------------------------- function [out] = serializeView(obj) %SERIALIZEVIEW save settings for multiple data plots % OUT = SERIALIZEVIEW(IN) out = {'PlotProperties' obj.PlotProperties,'SelectAllTests',obj.SelectAllTests}; end % serializeView function deserializeView(obj,varargin) %deserializeView appled saved settings for multiple data plots if nargin>1 deserializeView@xregdatagui.AbstractDataView(obj,varargin{:}); % set Action state for SelectAllTests obj.Options.Actions(6).Selected = obj.SelectAllTests; obj.pUpdateDisplay end end end % public methods methods (Hidden) % possibly private or hidden %---------------------------------------- function pModifyPlotProperties(obj, ~,~) %PMODIFYPLOTPROPERTIES plot properties dialog for current axes % PMODIFYPLOTPROPERTIES(OBJ, SRC, EVENT) % Is the current axes of the figure one of ours [OK, index] = ismember(get(ancestor(obj.Parent,'figure'), 'CurrentAxes'), obj.AxesHandles); % Only modify if the currentAxes is one or ours if OK obj.PlotProperties = plotPropertiesDlg(obj.PlotProperties, index, obj.MessageService.Sweepset, obj.Parent); obj.pUpdateDisplay; end end % pModifyPlotProperties %---------------------------------------- function pPostSetDataMessageService(obj, ~,~) %PPOSTSETDATAMESSAGESERVICE setup MessageService listeners % PPOSTSETDATAMESSAGESERVICE(OBJ, EVENT) % Call the super pPostSetDataMessageService pPostSetDataMessageService@xregdatagui.AbstractDataView(obj); dms = obj.MessageService; dmsListeners = [... event.listener(dms, 'dmsDataTypeChanged', @obj.pdmsDataTypeChangedUpdate);... event.listener(dms, 'ssfBadDataChanged', @obj.pssfBadDataChangedUpdate);... event.listener(dms, 'ssUnitsChanged', @obj.pssUnitsChangedUpdate);... event.listener(dms, 'ssDataChanged', @obj.pUpdateDisplay);... event.listener(dms, 'ssTestsChanged', @obj.pssTestsChangedUpdate);... event.listener(dms, 'SelectedTestsChanged',@obj.pssDataChangedUpdate);... ]; obj.Listeners = [obj.Listeners(:); dmsListeners(:)]; end % pPostSetDataMessageService end % possibly private or hidden end % classdef function i_addAxes(obj, numToAdd) %i_addAxes add axes to monitor plots % i_addAxes(obj, numToAdd) % Get the list controls elements elements = get(obj.ListControl, 'elements'); % How many axes in the last element of the control list if ~isempty(elements) && length(get(elements{end}, 'elements')) == 1 % Get the axes in the last control controlAx = get(elements{end}, 'axes'); newAxes = axes('Parent',get(controlAx,'Parent'),... 'Units','pixels',... 'XGrid','on',... 'YGrid','on',... 'Box','on',... 'Tag','dataPlot',... 'UIContextMenu',get(obj,'UIContextMenu'),... 'NextPlot','replacechildren',... 'DefaultLineHitTest','on'); set(newAxes, 'ButtonDownFcn', {@i_axesClick obj}); % Add the first axes set(elements{end}, 'axes', [controlAx newAxes]); obj.AxesHandles = [obj.AxesHandles ; newAxes]; numToAdd = numToAdd-1; end % Anything left to add? if numToAdd>0 % Create the new controls cell array newControls = cell(ceil(numToAdd/2), 1); % Now stuff them into the list control for i = 1:2:numToAdd % Which axes should be added (make sure we don't overflow the length) if i+1>numToAdd n = 1; else n = 2; end % Create a new axesinput object ind = ceil(i/2); newControls{ind} = xregaxesinput(obj.Parent, n, ... 'visible', 'on',... 'gapx', 60,... 'numcells', 2); newAxes = get(newControls{ind},'axes'); set(newAxes,'XGrid','on',... 'YGrid','on',... 'Box','on',... 'NextPlot','replacechildren',... 'UIContextMenu',get(obj,'UIContextMenu'),... 'DefaultLineHitTest','on') obj.AxesHandles = [obj.AxesHandles ; newAxes]; set(newAxes, 'ButtonDownFcn', {@i_axesClick obj}); end % And append to the display append(obj.ListControl, newControls); end end % i_addAxes %------------------------------------------------------------------------ function i_removeAxes(obj, numToRemove) %i_removeAxes remove (last) axes from monitor plots % i_removeAxes(obj, numToRemove) % How many axes are there after numAfter = length(obj.AxesHandles) - numToRemove; % Will we be left with an odd or even number of axes numLeftOver = rem(numAfter, 2); % Get the current list of controls controls = get(obj.ListControl, 'elements'); % How many controls are going to be left numControlsAfter = ceil(numAfter/2); % Remove the offending controls remove(obj.ListControl, numControlsAfter+1:length(controls)); % What about the odd axes left over? if numLeftOver % Get the axes from the last control ax = get(controls{numControlsAfter}, 'axes'); % Set the axes we want to keep back. set(controls{numControlsAfter}, 'axes', ax(1:numLeftOver)); end % Finally remove from the axes handles obj.AxesHandles = obj.AxesHandles(1:numAfter); end % i_removeAxes function i_axesClick(ax, ~, obj) %i_axesClick button down callback for axes % First dispatch to the container notify(obj, 'ButtonDown'); % Then zoom mv_zoom(ax); end % i_axesClick function onClick(~,~,obj) % First dispatch to the container notify(obj, 'ButtonDown'); end