イベントとリスナーを使用するための手法
例の概要
この例は、2 つのクラスを定義します。
fcneval— 評価機能をもつクラスは、MATLAB® 式を含み、指定した範囲でこの式を評価します。fcnview— 表示機能をもつクラスは、fcnevalオブジェクトを含み、fcnevalに含まれるデータを使用して評価した式の表面グラフを表示します。
このクラスは、2 つのイベントを定義します。
新しい値が MATLAB 関数に対して指定されるときに起こる、クラス定義されたイベント
範囲を含むプロパティが変更されるときに発生するプロパティ イベント
次の図は、2 つのオブジェクトの関係を示します。fcnview オブジェクトは、fcneval オブジェクトを含み、このオブジェクトのデータからグラフを作成します。fcnview は、fcneval オブジェクトのデータに変化があると、グラフを変更するために、リスナーを作成します。

この例で示した手法
クラス定義内でのイベントの名前付け
notifyを呼び出すことによるイベントのトリガーSetObservable属性によるプロパティ イベントの有効化クラス定義イベントとプロパティ
PostSetイベントに対するリスナーの作成追加の引数を受け取るリスナー コールバック関数の定義付け
リスナーの有効化と無効化
fcneval クラスのまとめ
fcneval クラスは、2 変数の指定範囲で MATLAB 式を評価します。fcneval は fcnview クラスのオブジェクトが表面としてグラフ化するデータのソースです。fcneval はこの例で使用されているイベントのソースです。クラス定義のリストは、@fcneval/fcneval.m クラスのコードを参照してください。
| プロパティ | 値 | 目的 |
|---|---|---|
FofXY | 関数ハンドル | MATLAB 式 (2 変数の関数) |
Lm | 2 要素ベクトル | 関数が評価される両変数の範囲。SetObservable 属性を true に設定すると、プロパティ イベント リスナーが有効になります。 |
Data | x、y、および z 行列をもつ構造体 | 関数を評価した結果のデータ。表面グラフに使用されます。Dependent 属性が true に設定されると、クエリされたときに保存されたデータがない場合に、get.Data メソッドが呼び出されてプロパティ値を決定します。 |
| イベント | トリガーされるとき |
|---|---|
UpdateGraph | FofXY プロパティ関数 set (set.FofXY) は、このクラスのオブジェクトに上の MATLAB 式に対して新しい値が指定されると、notify メソッドを呼び出します。 |
| メソッド | 目的 |
|---|---|
fcneval | クラス コンストラクター。入力は、関数ハンドルと、関数を評価する範囲を指定する 2 要素ベクトルです。 |
set.FofXY | FofXY プロパティの関数 set。オブジェクトの作成中など、プロパティ値の設定時に常に呼び出されます。 |
set.Lm | Lm プロパティの関数 set。有効な範囲の判定に使用されます。 |
get.Data | Data プロパティの関数 get。このメソッドは、クラス メンバーあるいは外部からデータがクエリされると常に Data プロパティの値を計算します。 |
grid | データの計算に使用される静的メソッド (Static 属性が true)。 |
fcnview クラスのまとめ
fcnview クラスのオブジェクトは、関数表示で作成される 4 つの表面グラフのデータ ソースとして、fcneval オブジェクトを含んでいます。fcnview は、fcneval オブジェクトに含まれるデータの変更に応答するリスナーとコールバック関数を作成します。クラス定義のリストは、@fcnview/fcnview.m クラスのコードを参照してください。
| プロパティ | 値 | 目的 |
|---|---|---|
FcnObject | fcneval オブジェクト | このオブジェクトは、関数グラフの作成に使用するデータを含みます。 |
HAxes | axes ハンドル | fcnview オブジェクトの各インスタンスは、サブプロットを含む axes ハンドルを保存します。 |
HLUpdateGraph | UpdateGraph イベントに対する event.listener オブジェクト | event.listener オブジェクトの Enabled プロパティを true に設定すると、リスナーが有効になり、false にするとリスナーが無効になります。 |
HLLm | Lm プロパティ イベントに対する event.listener オブジェクト | event.listener オブジェクトの Enabled プロパティを true に設定すると、リスナーが有効になり、false にするとリスナーが無効になります。 |
HEnableCm | uimenu ハンドル | コンテキスト メニュー上にあるリスナーを有効にするための項目 (確認した動作の処理に使用します)。 |
HDisableCm | uimenu ハンドル | コンテキスト メニュー上にあるリスナーを無効にするための項目 (確認した動作の管理に使用します)。 |
HSurface | surface ハンドル | 表面データを更新するイベント コールバックが使用します。 |
| メソッド | 目的 |
|---|---|
fcnview | クラス コンストラクター。入力は fcneval オブジェクトです。 |
createLisn | addlistener を呼び出し、UpdateGraph および Lm プロパティの PostSet リスナーを作成します。 |
lims | Axes の範囲を fcneval オブジェクトの Lm プロパティの現在値に設定します。イベント ハンドラーによって使用されます。 |
updateSurfaceData | 新しいオブジェクトを作成せずに表面データを更新します。イベント ハンドラーによって使用されます。 |
listenUpdateGraph | UpdateGraph イベントに対するコールバック。 |
listenLm | Lm プロパティの PostSet イベントに対するコールバック。 |
delete | fcnview クラスの delete メソッド。 |
createViews | 各サブプロットに対して fcnview クラスのインスタンスを作成する静的メソッド。リスナーを有効化/無効化するコンテキスト メニューを定義し、サブプロットを作成します。 |
ハンドル クラスから継承されるメソッド
fcneval と fcnview クラスは、handle クラスからメソッドを継承します。次の表は、この例で使用された継承されたメソッドの一覧です。
ハンドル クラスのメソッドは、handle クラスをサブクラス化するときに継承される、すべてのメソッドを一覧表示します。
| ハンドル クラスから継承されるメソッド | 目的 |
|---|---|
addlistener | 特定のイベントに対してリスナーを登録し、イベントが定義するオブジェクトにリスナーを追加します。 |
notify | イベントをトリガーし、すべての登録されたリスナーに通知します。 |
fcneval と fcnview クラスの使用
この節では、クラスの使用方法について説明します。
2 変数関数の MATLAB 式とその関数を評価する範囲を含む、
fcnevalクラスのインスタンスを作成します。fcnviewクラスの静的関数createViewsを使用して、関数を表示します。MATLAB 式または
fcnevalオブジェクトが含む範囲、および生成されたイベントに応答するすべてのfcnviewオブジェクトを変更します。
2 つの引数 (無名関数と 2 要素の単調増加するベクトル) と共にそのコンストラクターを呼び出すことによって、fcneval オブジェクトを作成します。以下に例を示します。
feobject = fcneval(@(x,y) x.*exp(-x.^2-y.^2),[-2 2]);
createViews 静的メソッドを使用して、関数のグラフを作成します。クラスの名前を使用して静的関数を呼び出します。
fcnview.createViews(feobject);
createView メソッドは、fcneval オブジェクトに含まれる関数の 4 つの表示を作成します。

各サブプロットは、そのグラフに関連するリスナーを有効にしたり無効にできる、コンテキスト メニューを定義します。たとえば、サブプロット 221 (左上) 上のリスナーを無効にし、fcneval オブジェクトに含まれている MATLAB 式を変更する場合、UpdateGraph イベントがトリガーされると、残りの 3 つのサブプロットだけが更新されます。
feobject.FofXY = @(x,y) x.*exp(-x.^.5-y.^.5);

同様に、feobject.Lm プロパティに値を割り当てて範囲を変更する場合、feobject が PostSet プロパティ イベントをトリガーすると、リスナー コールバックがグラフを更新します。
feobject.Lm = [-8 3];

この図では、サブプロット 221 のコンテキスト メニューが、リスナーを再び有効にしています。PostSet プロパティのイベントのリスナー コールバックは、表面データの更新も行うので、すべての表示が同期します。
UpdateGraph イベントおよびリスナーの実装
UpdateGraph イベントは、fcneval オブジェクトに含まれる数学関数の MATLAB 式が変更されるときに発生します。表面グラフを含む fcnview オブジェクトは、このイベントをリッスンしています。したがって、これらはグラフを更新して新しい関数を表示することができます。
UpdateGraph イベントの定義とトリガー
UpdateGraph イベントは、クラス定義のイベントです。fcneval クラスは、イベントに名前を付け、イベントが発生すると notify を呼び出します。

fcnview クラスは、このイベントに対してリスナーを定義します。fcneval がイベントをトリガーすると、fcnview のリスナーは、以下のアクションを実行するコールバック関数を実行します。
fcnviewオブジェクトによって保存された surface オブジェクトのハンドルがまだ有効であるかどうかを判定します。すなわち、オブジェクトはまだ存在します。fcnevalオブジェクトのDataプロパティをクエリすることによって、表面のXData、YData、ZDataを更新します。
fcneval クラスは、event ブロックでイベントの名前を定義します。
events
UpdateGraph
endイベントのトリガー タイミングの決定
fcneval クラスは FofXY プロパティの set メソッドを定義します。FofXY は、数学関数で使用する MATLAB 式を保存するプロパティです。この式は、2 変数関数の有効な 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 は、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
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 = [];
endfcneval.isSuitable メソッドでは、FofXY プロパティに割り当てられた式が、クラスの設計で必要とされている条件を満たしているかどうかをテストすることもできます。
その他の方法
FofXY プロパティの set イベントをクラスに実装することもできます。この場合は、notify を呼び出す必要がなくなります (プロパティ値変更のリスニングを参照)。この場合は、クラスのイベントを定義すると、イベントのトリガーを細かく制御できるようになるので、柔軟性が向上します。
たとえば、新しいデータが前のデータと異なるときだけ、グラフが更新されるようにするとします。新しい式で許容誤差内のデータが生成された場合は、set.FofXY メソッドはイベントをトリガーできないので、グラフは更新されません。ただし、プロパティは新しい値に設定されます。
UpdateGraph イベントのリスナーおよびコールバック
fcnview クラスは、addlistener メソッドを使用して、UpdateGraph イベントのリスナーを作成します。
obj.HLUpdateGraph = addlistener(obj.FcnObject,'UpdateGraph',...
@(src,evnt)listenUpdateGraph(obj,src,evnt)); % Add obj to argument listfcnview オブジェクトは、その HLUpdateGraph プロパティの event.listener オブジェクトにハンドルを保存します。これは、コンテキスト メニューによってリスナーを有効あるいは無効にするために使用されます。リスナーの有効化と無効化を参照してください。
fcnview オブジェクト (obj) は、リスナー コールバックに渡される、2 つの既定の引数 (src、evnt) に追加されます。イベントのソース (src) は fcneval オブジェクトですが、fcnview オブジェクトは、コールバックが更新する surface オブジェクトのハンドルを含むことを覚えておいてください。
関数 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);
endPostSet イベントのリスナー
すべてのプロパティは、あらかじめ定義された PostSet イベントをサポートします。プロパティ イベントの詳細については、プロパティの設定とクエリのイベントを参照してください。この例は、fcneval Lm プロパティの PostSet イベントを使用します。このプロパティは、数学関数が評価される範囲を指定する 2 要素ベクトルを含みます。このプロパティが obj.Lm = [-3 5]; のようなステートメントによって変更された直後、このイベントをリッスンしている fcnview オブジェクトは、グラフを更新して新しいデータを反映します。

Lm プロパティの割り当て過程の順序
fcneval クラスは、Lm プロパティを設定する関数を定義します。オブジェクトの作成中、またはプロパティを再度割り当てるときに、値がこのプロパティに割り当てられると、以下の過程が起こります。
Lmプロパティに引数値を割り当てる試みが行われます。set.Lmメソッドを実行して、値が適切な範囲にあるかどうかを確認します。範囲内にある場合、割り当てます。範囲外にある場合は、エラーが発生しします。Lmの値が適切に設定されると、MATLAB はPostSetイベントをトリガーします。すべてのリスナーはそれらのコールバックを実行しますが、その順序は決まっていません。
プロパティが実際に割り当てられるまでは、PostSet イベントは発生しません。PostSet イベントが発生する前に、プロパティの関数 set は、起こり得る割り当てエラーを処理することができます。
PostSet プロパティ イベントの有効化
PostSet イベントのリスナーを作成するには、プロパティの SetObservable 属性を true に設定する必要があります。
properties (SetObservable = true) Lm = [-2*pi 2*pi]; % specifies default value end end
MATLAB は、イベントを自動的にトリガーするので、notify を呼び出す必要はありません。
プロパティの属性では、すべてのプロパティ属性の一覧を示しています。
PostSet イベントのリスナーおよびコールバック
fcnview クラスは、addlistener メソッドを使用して、PostSet イベントのリスナーを作成します。
obj.HLLm = addlistener(obj.FcnObject,'Lm','PostSet',...
@(src,evnt)listenLm(obj,src,evnt)); % Add obj to argument listfcnview オブジェクトは、その HLLm プロパティの event.listener オブジェクトにハンドルを保存します。これは、コンテキスト メニューによってリスナーを有効または無効にするために使用されます (リスナーの有効化と無効化を参照)。
fcnview オブジェクト (obj) は、リスナー コールバックに渡される、2 つの既定の引数 (src、evnt) に追加されます。イベントのソース (src) は fcneval オブジェクトですが、fcnview オブジェクトは、コールバックが更新する surface オブジェクトのハンドルを含むことを覚えておいてください。
コールバックは、範囲を変更すると、数学関数が異なる範囲で評価されるので、Axes の範囲を設定し、表面データを更新します。
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 オブジェクトはそれぞれ、作成するリスナー オブジェクトのハンドルを保存します。したがって、リスナーは、グラフの作成後、コンテキスト メニューにより有効化または無効化できます。すべてのリスナーは、Enabled というプロパティを定義する、event.listener クラスのインスタンスです。既定では、このプロパティは、リスナーを有効化する true の値をもちます。このプロパティを false に設定すると、リスナーは存在しますが無効化されます。この例は、各グラフの座標上にアクティブなコンテキスト メニューを作成します。これにより、Enabled プロパティの値を変更することが可能になります。
コンテキスト メニュー コールバック
メニュー上の 2 つの項目に対応するコンテキスト メニューが使用する 2 つのコールバックがあります。
Listen —
UpdateGraphとPostSetリスナーのEnabledプロパティをtrueに設定し、Listen メニュー項目の隣にチェック マークを追加します。Don't Listen —
UpdateGraphとPostSetリスナーのEnabledプロパティを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') % Uncheck 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) if nargin > 0 obj.FofXY = fcn_handle; 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