Методы для Использования Событий и прослушивателей

Обзор в качестве примера

Этот пример задает два класса:

  • fcneval — Функциональный класс средства анализа содержит выражение MATLAB® и выполняет это выражение в заданной области значений

  • fcnview — Функциональный класс средства просмотра содержит объект fcneval и отображает поверхностные графики выполненного выражения с помощью данных, содержавшихся в fcneval.

Этот класс задает два события:

  • Заданное классом событие, которое происходит, когда новое значение задано для функции MATLAB

  • Событие свойства, которое происходит, когда свойство, содержащее пределы, изменяется

Следующая схема показывает отношение между двумя объектами. Объект fcnview содержит объект fcneval и создает графики из данных, которые он содержит. fcnview создает прослушиватели, чтобы изменить графики, если какие-либо из данных в объекте fcneval изменяются.

Методы, продемонстрированные в этом примере

  • Именование события в определении класса

  • Инициирование события путем вызова notify

  • Включение события свойства через атрибут SetObservable

  • Создание прослушивателей для заданных классом событий и событий свойства PostSet

  • Определение функций обратного вызова прослушивателя, которые принимают дополнительные аргументы

  • Включение и отключение прослушивателей

Сводные данные fcneval Класса

Класс fcneval выполняет выражение MATLAB в заданной области значений двух переменных. fcneval является источником данных что объекты графика класса fcnview как поверхность. fcneval является источником событий, используемых в этом примере. Для списка определения класса см. @fcneval/fcneval.m Код Класса

СвойствоЗначениеЦель
FofXYуказатель на функциюВыражение MATLAB (функция двух переменных).
Lmдвухэлементный векторПределы, по которым функция выполнена в обеих переменных. набор атрибута SetObservable к true, чтобы включить прослушиватели события свойства.
Dataструктура с x, y, и z матрицыДанные, следующие из выполнения функции. Используемый для поверхностного графика. набор атрибута Dependent к true, что означает метод get.Data, называется, чтобы определить значение свойства, когда запрошено, и никакие данные не хранятся.

СобытиеКогда инициировано
UpdateGraphФункция набора свойств FofXY (set.FofXY) вызывает метод notify, когда новое значение задано для выражения MATLAB на объекте этого класса.

МетодЦель
fcnevalКонструктор класса. Входные параметры являются указателем на функцию и двухэлементным вектором, задающим пределы, по которым можно выполнить функцию.
set.FofXYФункция набора свойств FofXY. Названный каждый раз, когда значение свойства установлено, включая во время объектной конструкции.
set.LmФункция набора свойств Lm. Используемый, чтобы протестировать на допустимые пределы.
get.DataСвойство Data получает функцию. Этот метод вычисляет значения для свойства Data каждый раз, когда те данные запрошены (членами класса или внешне).
gridСтатический метод (набор атрибута Static к true) используемый в вычислении данных.

Сводные данные fcnview Класса

Объекты класса fcnview содержат объекты fcneval как источник данных для четырех поверхностных графиков, созданных в функциональном представлении. fcnview создает прослушиватели и функции обратного вызова, которые отвечают на изменения в данных, содержавшихся в объектах fcneval. Для списка определения класса см. @fcnview/fcnview.m Код Класса

СвойствоЗначениеЦель
FcnObjectОбъект fcnevalЭтот объект содержит данные, которые используются, чтобы создать функциональные графики.
HAxesуказатель осейКаждый экземпляр fcnview объектно-ориентированная память указатель осей, содержащих его подграфик.
HLUpdateGraphОбъект event.listener для события UpdateGraphУстановка свойства Enabled объекта event.listener к true включает прослушиватель; false отключает прослушиватель.
HLLmОбъект event.listener для события свойства LmУстановка свойства Enabled объекта event.listener к true включает прослушиватель, false отключает прослушиватель.
HEnableCmуказатель uimenuЭлемент в контекстном меню раньше включал прослушиватели (раньше обрабатывал проверяемое поведение),
HDisableCmуказатель uimenuЭлемент в контекстном меню раньше отключал прослушиватели (раньше управлял проверяемым поведением),
HSurfaceповерхностный указательИспользуемый коллбэками события, чтобы обновить поверхностные данные.

МетодЦель
fcnviewКонструктор класса. Вход является объектом fcneval.
createLisnВызовы addlistener, чтобы создать прослушиватели для свойства UpdateGraph и Lm прослушиватели PostSet.
limsУстанавливает пределы осей к текущему значению свойства Lm объекта fcneval. Используемый обработчиками событий.
updateSurfaceDataОбновляет поверхностные данные, не создавая новый объект. Используемый обработчиками событий.
listenUpdateGraphКоллбэк для события UpdateGraph.
listenLmКоллбэк для события PostSet свойства Lm
deleteУдалите метод для класса fcnview.
createViewsСтатический метод, который создает экземпляр класса fcnview для каждого подграфика, задает контекстные меню, которые позволяют/запрещают прослушиватели, и создает подграфики

Методы, наследованные от класса Handle

И fcneval и классы fcnview наследовали методы от класса handle. В следующей таблице перечислены только те унаследованные методы, используемые в этом примере.

Методы Класса Handle предоставляют полный список методов, которые наследованы, когда вы разделяете на подклассы класс handle.

Методы, наследованные от класса HandleЦель
addlistenerУкажите прослушиватель для определенного события и присоедините прослушиватель объекта event-defining.
notifyИнициируйте событие и уведомите все зарегистрированные прослушиватели.

Используя fcneval и fcnview Классы

Этот раздел объясняет, как использовать классы.

  • Создайте экземпляр класса fcneval, чтобы содержать выражение MATLAB функции двух переменных и области значений, в которой вы хотите выполнить эту функцию

  • Используйте класс fcnview статический функциональный createViews, чтобы визуализировать функцию

  • Измените выражение MATLAB или пределы, содержавшие в объекте fcneval, и все объекты fcnview отвечают на сгенерированные события.

Вы создаете объект fcneval путем вызова его конструктора с двумя аргументами — анонимная функция и двухэлементное, монотонно увеличения вектора. Например:

feobject = fcneval(@(x,y) x.*exp(-x.^2-y.^2),[-2 2]);

Используйте статический метод createViews создать графики функции. Используйте имя класса, чтобы вызвать статическую функцию:

fcnview.createViews(feobject);

Метод createView генерирует четыре представления функции, содержавшейся в объекте fcneval.

Каждый подграфик задает контекстное меню, которое может включить и отключить прослушиватели, сопоставленные с тем графиком. Например, если вы отключаете прослушиватели на подграфике 221 (верхний левый угол) и изменяете выражение MATLAB, содержавшее в объекте fcneval, только остающиеся три обновления подграфиков, когда событие UpdateGraph инициировано:

feobject.FofXY = @(x,y) x.*exp(-x.^.5-y.^.5);

Точно так же, если вы изменяете пределы путем присвоения значения свойству feobject.Lm , feobject инициировал событие свойства PostSet, и коллбэки прослушивателя обновляют график.

feobject.Lm = [-8 3];

В этой фигуре прослушиватели повторно включены через контекстное меню для подграфика 221. Поскольку коллбэк прослушивателя для события свойства PostSet также обновляет поверхностные данные, все представления теперь синхронизируются

Реализация события UpdateGraph и прослушивателя

Событие UpdateGraph имеет место, когда представление MATLAB математической функции, содержавшейся в объекте fcneval, изменяется. Объекты fcnview, которые содержат поверхностные графики, прислушиваются к этому событию, таким образом, они могут обновить графики, чтобы представлять новую функцию.

Определение и инициирование события UpdateGraph

Событие UpdateGraph является заданным классом событием. Имена классов fcneval событие и вызовы notify, когда событие имеет место.

Класс fcnview задает прослушиватель для этого события. Когда fcneval инициировал событие, прослушиватель fcnview выполняет функцию обратного вызова, которая выполняет следовать действия:

  • Определяет, допустим ли указатель объекта подложки, хранившего объектом fcnview все еще, (то есть, делает объект, все еще существуют),

  • Обновляет поверхностный XData, YData и ZData путем запроса свойства Data объекта fcneval.

Класс fcneval задает имя события в блоке event:

events
   UpdateGraph 
end

Определите, когда инициировать событие

Класс fcneval задает метод набора свойств для свойства FofXY. FofXY является свойством, которое хранит выражение MATLAB для математической функции. Это выражение должно быть допустимым выражением MATLAB для функции двух переменных.

Метод set.FofXY:

  • Определяет пригодность выражения

  • Если выражение подходит:

    • Присваивает выражение свойству FofXY

    • Инициировал событие UpdateGraph

Если fcneval.isSuitable не возвращает объект MException, метод set.FofXY присваивает значение свойству и инициировал событие UpdateGraph.

function set.FofXY(obj,func)
% Determine if function is suitable to create a surface
   me = fcneval.isSuitable(func);
   if ~isempty(me)
      throw(me)
   end
% Assign property value
   obj.FofXY = func; 
% Trigger UpdateGraph event
   notify(obj,'UpdateGraph');
end

Определение пригодности выражения

Вызовы метода set.FofXY статический метод (fcneval.isSuitable), чтобы определить пригодность заданного выражения. fcneval.isSuitable возвращает объект MException, если он решает, что выражение является неподходящим. fcneval.isSuitable вызывает конструктора MException непосредственно, чтобы создать более полезные сообщения об ошибке для пользователя.

set.FofXY выпускает исключение с помощью метода MException.throw. Издание исключения отключает выполнение set.FofXY и препятствует тому, чтобы метод делал присвоение на свойство или инициировал событие UpdateGraph.

Вот метод fcneval.isSuitable:

function isOk = isSuitable(funcH)
   v = [1 1;1 1];
   % Can the expression except 2 numeric inputs
   try
      funcH(v,v);
   catch %#ok<CTCH>
      me = MException('DocExample:fcneval',...
         ['The function ',func2str(funcH),' Is not a suitable F(x,y)']);
      isOk = me;
      return
   end
   % Does the expression return non-scalar data
   if isscalar(funcH(v,v));
      me = MException('DocExample:fcneval',...
         ['The function ',func2str(funcH),'' Returns a scalar when evaluated']);
      isOk = me;
      return
   end
   isOk = [];
end

Метод fcneval.isSuitable мог обеспечить дополнительный тест, чтобы гарантировать, что выражение, присвоенное свойству FofXY, соответствует критериям, требуемым проектом класса.

Другие подходы

Класс, возможно, реализовал событие набора свойств для свойства FofXY и не должен будет, поэтому, вызывать notify (см., Прислушиваются к Изменениям в Значениях свойств). Определение события класса обеспечивает больше гибкости в этом случае, потому что можно лучше управлять инициированием события.

Например, предположите, что вы хотели обновить график, только если новые данные отличаются. Если бы новое выражение произвело те же данные в некотором допуске, метод set.FofXY не мог бы инициировать событие и постараться не обновлять график. Однако метод мог все еще установить свойство на новое значение.

Прослушиватель и обратный вызов для события UpdateGraph

Класс fcnview создает прослушиватель для события UpdateGraph с помощью метода addlistener:

obj.HLUpdateGraph = addlistener(obj.FcnObject,'UpdateGraph',...
            @(src,evnt)listenUpdateGraph(obj,src,evnt)); % Add obj to argument list

Объектно-ориентированная память fcnview указатель на объект event.listener в его свойстве HLUpdateGraph, которое используется, чтобы позволить/запретить прослушиватель контекстным меню (см., Включает и Отключает Прослушиватели).

Объект fcnview (obj) добавляется к этим двум параметрам по умолчанию (src, evnt) передал коллбэку прослушивателя. Следует иметь в виду, источник события (src) является объектом fcneval, но объект fcnview содержит указатель объекта подложки, который обновляет коллбэк.

Функция listenUpdateGraph задана можно следующим образом:

function listenUpdateGraph(obj,src,evnt)
   if ishandle(obj.HSurface) % If surface exists
      obj.updateSurfaceData % Update surface data
   end
end

Функция updateSurfaceData является методом класса, который обновляет поверхностные данные, когда различная математическая функция присвоена объекту fcneval. Обновление данных о графическом объекте более эффективно, чем создание нового объекта с помощью новых данных:

function updateSurfaceData(obj)
% Get data from fcneval object and set surface data
   set(obj.HSurface,...
      'XData',obj.FcnObject.Data.X,...
      'YData',obj.FcnObject.Data.Y,...
      'ZData',obj.FcnObject.Data.Matrix); 
end

Прослушиватель события PostSet

Все свойства поддерживают предопределенное событие PostSet (См. События Набора свойств и Запроса для получения дополнительной информации о событиях свойства). Этот пример использует событие PostSet для свойства fcneval Lm. Это свойство содержит двухэлементный вектор, задающий область значений, в которой выполнена математическая функция. Сразу после того, как это свойство изменяется (оператором как obj.Lm = [-3 5];), объекты fcnview, прислушивающиеся к этому событию, обновляют график, чтобы отразить новые данные.

Последовательность во время присвоения свойства Lm

Класс fcneval задает функцию множества для свойства Lm. Когда значение присвоено этому свойству во время объектной конструкции или переназначения свойства, следующая последовательность происходит:

  1. Попытка предпринята, чтобы присвоить значение аргумента свойству Lm.

  2. Метод set.Lm выполняется, чтобы проверять, является ли значение в соответствующей области значений — если да, это делает присвоение, если не, это генерирует ошибку.

  3. Если значение Lm установлено успешно, MATLAB инициировал событие PostSet.

  4. Все прослушиватели выполняют свои коллбэки, но порядок недетерминирован.

Событие PostSet не имеет место, пока фактическое присвоение свойства не происходит. Функция набора свойств обеспечивает возможность иметь дело с потенциальными ошибками присвоения, прежде чем событие PostSet будет иметь место.

Включение события свойства PostSet

Чтобы создать прослушиватель для события PostSet, необходимо установить атрибут SetObservable свойства на true:

properties (SetObservable = true)
   Lm = [-2*pi 2*pi];  % specifies default value
end 

MATLAB автоматически инициировал событие, таким образом, не необходимо вызвать notify.

Укажите, что Атрибуты свойств предоставляют список всех атрибутов свойства.

Прослушиватель и обратный вызов для события PostSet

Класс fcnview создает прослушиватель для события PostSet с помощью метода addlistener:

obj.HLLm = addlistener(obj.FcnObject,'Lm','PostSet',...
            @(src,evnt)listenLm(obj,src,evnt)); % Add obj to argument list

Объектно-ориентированная память fcnview указатель на объект event.listener в его свойстве HLLm, которое используется, чтобы позволить/запретить прослушиватель контекстным меню (см., Включает и Отключает Прослушиватели).

Объект fcnview (obj) добавляется к этим двум параметрам по умолчанию (src, evnt) передал коллбэку прослушивателя. Следует иметь в виду, источник события (src) является объектом fcneval, но объект fcnview содержит указатель объекта подложки, который обновляет коллбэк.

Коллбэк устанавливает пределы осей и обновляет поверхностные данные, потому что изменение пределов заставляет математическую функцию быть оцененной в различной области значений:

function listenLm(obj,src,evnt)
   if ishandle(obj.HAxes) % If there is an axes
      lims(obj); % Update its limits
      if ishandle(obj.HSurface) % If there is a surface
         obj.updateSurfaceData % Update its data
      end
   end
end 

Включение и отключение прослушивателей

Каждый fcnview объектно-ориентированная память указатель объектов прослушивателя, которые это создает так, чтобы прослушиватели могли быть включены или отключены через контекстное меню после графиков, создается. Все прослушиватели являются экземплярами класса event.listener, который задает свойство под названием Enabled. По умолчанию это свойство имеет значение true, который включает прослушиватель. Если вы устанавливаете это свойство на false, прослушиватель все еще существует, но отключен. Этот пример создает контекстное меню, активное на осях каждого графика, который обеспечивает способ изменить значение свойства Enabled.

Обратный вызов контекстного меню

Существует два коллбэка, используемые контекстным меню, соответствующим этим двум элементам в меню:

  • Listen — Устанавливает свойство Enabled и для UpdateGraph и для прослушивателей PostSet true и добавляет галочку рядом с пунктом меню Listen.

  • Don't Listen — Устанавливает свойство Enabled и для UpdateGraph и для прослушивателей PostSet false и добавляет галочку рядом с пунктом меню Don't Listen.

Оба коллбэка включают объект fcnview как аргумент (в дополнение к необходимому источнику и аргументам данных о событиях), чтобы обеспечить доступ к указателю объектов прослушивателя.

Функция enableLisn вызвана, когда пользователь выбирает Listen из контекстного меню.

function enableLisn(obj,src,evnt)
   obj.HLUpdateGraph.Enabled = true; % Enable listener
   obj.HLLm.Enabled = true; % Enable listener
   set(obj.HEnableCm,'Checked','on') % Check Listen
   set(obj.HDisableCm,'Checked','off') % Uncheck Don't Listen
end

Функция disableLisn вызвана, когда пользователь выбирает Don't Listen из контекстного меню.

function disableLisn(obj,src,evnt)
   obj.HLUpdateGraph.Enabled = false; % Disable listener
   obj.HLLm.Enabled = false; % Disable listener
   set(obj.HEnableCm,'Checked','off') % Unheck Listen
   set(obj.HDisableCm,'Checked','on') % Check Don't Listen
end

Код Класса @fcneval/fcneval.m

classdef fcneval < handle
   properties
      FofXY
   end 
   
   properties (SetObservable = true)
      Lm = [-2*pi 2*pi]
   end % properties SetObservable = true
   
   properties (Dependent = true)
      Data
   end 
   
   events
      UpdateGraph 
   end
   
   methods
      function obj = fcneval(fcn_handle,limits)  % Constructor returns object
         if nargin > 0
            obj.FofXY = fcn_handle;                 % Assign property values
            obj.Lm = limits;
            
         end
      end 
      
      function set.FofXY(obj,func)
         me = fcneval.isSuitable(func);
         if ~isempty(me)
            throw(me)
         end
         obj.FofXY = func;
         notify(obj,'UpdateGraph');  
      end 
      
      function set.Lm(obj,lim)  
         if  ~(lim(1) < lim(2))
            error('Limits must be monotonically increasing')
         else
            obj.Lm = lim;
         end
      end 
      
      function data = get.Data(obj)   
         [x,y] = fcneval.grid(obj.Lm);  
         matrix = obj.FofXY(x,y);
         data.X = x;
         data.Y = y;
         data.Matrix = real(matrix);  
         
      end 
      
   end % methods
   
   methods (Static = true)      
      function [x,y] = grid(lim)
         inc = (lim(2)-lim(1))/20;
         [x,y] = meshgrid(lim(1):inc:lim(2));
      end % grid
      
      function isOk = isSuitable(funcH)
         v = [1 1;1 1];
         try
            funcH(v,v);
         catch  %#ok<CTCH>
            me = MException('DocExample:fcneval',...
               ['The function ',func2str(funcH),' Is not a suitable F(x,y)']);
            isOk = me;
            return
         end
         if isscalar(funcH(v,v));
            me = MException('DocExample:fcneval',...
               ['The function ',func2str(funcH),' Returns a scalar when evaluated']);
            isOk = me;
            return
         end
         isOk = [];
      end
      
   end 
   
end 

Код Класса @fcnview/fcnview.m

classdef fcnview < handle
   properties
      FcnObject     % fcneval object
      HAxes         % subplot axes handle
      HLUpdateGraph % UpdateGraph listener handle
      HLLm          % Lm property PostSet listener handle
      HEnableCm     % "Listen" context menu handle
      HDisableCm    % "Don't Listen" context menu handle
      HSurface      % Surface object handle
   end
   
   methods
      function obj = fcnview(fcnobj)
         if nargin > 0
            obj.FcnObject = fcnobj;
            obj.createLisn;
         end
      end
      
      function createLisn(obj)
         obj.HLUpdateGraph = addlistener(obj.FcnObject,'UpdateGraph',...
            @(src,evnt)listenUpdateGraph(obj,src,evnt));
         obj.HLLm = addlistener(obj.FcnObject,'Lm','PostSet',...
            @(src,evnt)listenLm(obj,src,evnt));
      end
      
      function lims(obj)
         lmts = obj.FcnObject.Lm;
         set(obj.HAxes,'XLim',lmts);
         set(obj.HAxes,'Ylim',lmts);
      end
      
      function updateSurfaceData(obj)
         data = obj.FcnObject.Data;
         set(obj.HSurface,...
            'XData',data.X,...
            'YData',data.Y,...
            'ZData',data.Matrix);
      end
      
      function listenUpdateGraph(obj,~,~)
         if ishandle(obj.HSurface)
            obj.updateSurfaceData
         end
      end
      
      function listenLm(obj,~,~)
         if ishandle(obj.HAxes)
            lims(obj);
            if ishandle(obj.HSurface)
               obj.updateSurfaceData
            end
         end
      end
      
      function delete(obj)
         if ishandle(obj.HAxes)
            delete(obj.HAxes);
         else
            return
         end
      end
      
   end
   methods (Static)
      createViews(a)
   end
end

@fcnview/createViews

function createViews(fcnevalobj)
   p = pi; deg = 180/p;
   hfig = figure('Visible','off',...
      'Toolbar','none');
   
   for k=4:-1:1
      fcnviewobj(k) = fcnview(fcnevalobj);
      axh = subplot(2,2,k);
      fcnviewobj(k).HAxes = axh;
      hcm(k) = uicontextmenu;
      set(axh,'Parent',hfig,...
         'FontSize',8,...
         'UIContextMenu',hcm(k))
      fcnviewobj(k).HEnableCm = uimenu(hcm(k),...
         'Label','Listen',...
         'Checked','on',...
         'Callback',@(src,evnt)enableLisn(fcnviewobj(k),src,evnt));
      fcnviewobj(k).HDisableCm = uimenu(hcm(k),...
         'Label','Don''t Listen',...
         'Checked','off',...
         'Callback',@(src,evnt)disableLisn(fcnviewobj(k),src,evnt));
      az = p/k*deg;
      view(axh,az,30)
      title(axh,['View: ',num2str(az),' 30'])
      fcnviewobj(k).lims;
      surfLight(fcnviewobj(k),axh)
   end
   set(hfig,'Visible','on')
end
function surfLight(obj,axh)
   obj.HSurface = surface(obj.FcnObject.Data.X,...
      obj.FcnObject.Data.Y,...
      obj.FcnObject.Data.Matrix,...
      'FaceColor',[.8 .8 0],'EdgeColor',[.3 .3 .2],...
      'FaceLighting','phong',...
      'FaceAlpha',.3,...
      'HitTest','off',...
      'Parent',axh);
   lims(obj)
   camlight left; material shiny; grid off
   colormap copper
end

function enableLisn(obj,~,~)
   obj.HLUpdateGraph.Enabled = true;
   obj.HLLm.Enabled = true;
   set(obj.HEnableCm,'Checked','on')
   set(obj.HDisableCm,'Checked','off')
end

function disableLisn(obj,~,~)
   obj.HLUpdateGraph.Enabled = false;
   obj.HLLm.Enabled = false;
   set(obj.HEnableCm,'Checked','off')
   set(obj.HDisableCm,'Checked','on')
end