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