gusucode.com > appdesigner工具箱matlab源码程序 > appdesigner/+appdesigner/+internal/+service/AppManagementService.m
classdef AppManagementService < handle % APPMANAGEMENTSERVICE Singleton object to manage running apps % % % Copyright 2016 The MathWorks, Inc. % properties (Access = private) % A map to maintain the mapping relationship between full file % name of the app and the running app instance for App Designer % design time LiveAlert error handling RunningAppsFullFileName; end events (ListenAccess = {?appdesigner.internal.appalert.AppAlertController}) CallbackErrored end methods(Access=private) % Private constructor to prevent creating object externally function obj = AppManagementService() obj.RunningAppsFullFileName = containers.Map(); end end methods(Static) % Get singleton instance of the AppManagementService function obj = instance() persistent localUniqueInstance; if isempty(localUniqueInstance) obj = appdesigner.internal.service.AppManagementService(); localUniqueInstance = obj; else obj = localUniqueInstance; end end % Get figure handle in the running app function appFigure = getFigure(app) appFigure = []; runningAppFigures = findall(0, 'Type', 'figure', ... 'RunningAppInstance', app); if ~isempty(runningAppFigures) % If found, there should be only one match appFigure = runningAppFigures(1); end end end methods function runningApp = runApp(obj, appFullFileName) % Run the App as if by command line % This method is only used by App Designer to run an app [appPath, appName] = fileparts(appFullFileName); % Add to the MATLAB search path to ensure callback or user % authored functions executed correctly. If the path already % in the search path, it would move it to the top pathWithSep = [appPath pathsep]; if(~strncmp(pathWithSep, path, length(pathWithSep))) addpath(appPath); end % Clear the class clear(appName); % Initialize RunningAppsFullFileName obj.RunningAppsFullFileName(appFullFileName) = []; try runningApp = feval(appName); catch exception if isa(exception, 'appdesigner.internal.appalert.CallbackException') % Exception from app's constructor, but app object will % continue to be created, like referencing an undefined % variable runningApp = exception.App; obj.RunningAppsFullFileName(appFullFileName) = runningApp; else % Exception from app's constructor, and app object % will not be created, like syntax error obj.fireCallbackErroredEvent(exception, appFullFileName); end % Rethrow the exception to let App Designer handle it rethrow(exception); end % Run app successfully, and no exceptions being caught obj.RunningAppsFullFileName(appFullFileName) = runningApp; end function runningApp = getRunningApp(obj, fullFileName) % Get running app using its full file name runningApp = []; if obj.RunningAppsFullFileName.isKey(fullFileName) runningApp = obj.RunningAppsFullFileName(fullFileName); end end function value = isAppRunInAppDesigner(obj, fullFileName) % Returns true if app is run using App Designer value = obj.RunningAppsFullFileName.isKey(fullFileName); end function register(obj, app, uiFigure) % Set up listener and callback for running app validateattributes(app, ... {'matlab.apps.AppBase'}, ... {}); obj.manageApp(app, uiFigure); end function unregister(obj, app) % Do cleanup when unregistering the app validateattributes(app, ... {'matlab.apps.AppBase'}, ... {}); % Remove full file name and running app mapping appFullFileName = obj.getAppFullFileName(app); if (~isempty(appFullFileName)) % Only running the app from App Designer will register the % full file name and app mapping, and then needs to remove obj.RunningAppsFullFileName.remove(appFullFileName); end % Clear class definition to avoid name shadowing appClassName = class(app); clear(appClassName); end end methods (Access = private) function manageApp(obj, app, uiFigure) % 1) Make running figure have a dynamic property to point to the % running app instance appInstanceProp = addprop(uiFigure, 'RunningAppInstance'); appInstanceProp.Transient = true; uiFigure.RunningAppInstance = app; % 2) Set up listener to figure destroyed event addlistener(uiFigure, 'ObjectBeingDestroyed', @(src, e)delete(app)); % 3) Wire the error callback to each UIAxes uiAxesInApp = findall(uiFigure, 'Type', 'axes'); for ix = 1:numel(uiAxesInApp) uiAxesInApp(ix).ErrorCallback = ... @(source, event)obj.axeserrorhandler(event, app); end end function axeserrorhandler(obj, event, app) try appdesigner.internal.appdesignererrorcallback(event.Source, event); catch exception obj.fireCallbackErroredEvent(app, exception); rethrow(exception); end end function fullFileName = getAppFullFileName(obj, app) fullFileName = []; appFullFileNames = obj.RunningAppsFullFileName.keys(); ix = find(cellfun(@(x)~isempty(obj.RunningAppsFullFileName(x)) && ... eq(obj.RunningAppsFullFileName(x), app), appFullFileNames),... 1); if (ix > 0) % Can get the running app's full file name only when % running from App Designer fullFileName = appFullFileNames{ix}; else [fileNameFromWhich, ~] = which(class(app)); if obj.RunningAppsFullFileName.isKey(fileNameFromWhich) % The app is run from App Designer, and so use the full % file name from which() command as app full file name. % This case happens because the exception is thrown % from component's callback when calling % createComponents() in constructor fullFileName = fileNameFromWhich; end end end function fireCallbackErroredEvent(obj, exception, appFullFileName) notify(obj, 'CallbackErrored', ... appdesigner.internal.appalert.CallbackErroredData(exception, appFullFileName)); end end methods (Access = {?matlab.apps.AppBase}) function tryCallback(obj, app, callback, requiresEventData, event) try if requiresEventData callback(app, event); else % For callbacks that do not need events callback(app); end catch exception appFullFileName = obj.getAppFullFileName(app); if (~isempty(appFullFileName)) % Only fire the event when app is run from App % Designer, and under this case there would be a full file % name and running app instance mapping relationship obj.fireCallbackErroredEvent(exception, appFullFileName); end % When startup method called in app's constructor has a % non-syntax parsing error, for example, reference a % undefined variable: disp(undefinedVariable), the app will % still be created, but not populated, so put the app into % exception to let the caller get it callbackException = appdesigner.internal.appalert.CallbackException(exception, app); throw(callbackException); end end end end