MATLAB Language
MATLABユーザーインターフェイス
サーチ…
ユーザーインターフェイスの周囲にデータを渡す
最も進んだユーザーインターフェイスでは、ユーザーがユーザーインターフェイスを構成するさまざまな機能間で情報を渡すことができる必要があります。 MATLABにはさまざまな方法があります。
guidata
MATLAB独自のGUI開発環境(GUIDE)は、コールバック間でデータを渡すためにhandles
という名前のstruct
を使用する方が好きです。このstruct
は、さまざまなUIコンポーネントのグラフィックスハンドルとユーザー指定のデータがすべて含まれています。 handles
を自動的に渡すGUIDE作成のコールバックを使用していない場合handles
、 guidata
を使用して現在の値を取得できます
% hObject is a graphics handle to any UI component in your GUI
handles = guidata(hObject);
このデータ構造体に格納されている値を変更する場合は、変更することができますが、他のコールバックによって変更が表示されるように、 hObject
格納する必要があります。 guidata
への2番目の入力引数を指定することでそれを保存することができます。
% Update the value
handles.myValue = 2;
% Save changes
guidata(hObject, handles)
最終的にデータがhObject
を含むFigure内に格納されるため、 hObject
の値は同じfigure
内の UIコンポーネントであれば問題ありません。
ベスト:
- GUIコンポーネントのすべてのハンドルを格納できる
handles
構造を格納します。 - ほとんどのコールバックがアクセスする必要のある「小さな」変数を格納する。
推奨しない :
- すべてのコールバックとサブ関数がアクセスする必要のない大きな変数を格納します(これらのために
setappdata
/getappdata
を使用します)。
setappdata
/ getappdata
guidata
アプローチと同様に、 setappdata
とgetappdata
を使用して、グラフィックスハンドル内の値を格納および取得することができます。これらのメソッドを使用する利点は、格納されているすべてのデータを含むstruct
全体ではなく、 必要な値だけを取得できることです 。これは、キー/値ストアに似ています。
グラフィックオブジェクト内にデータを格納するには
% Create some data you would like to store
myvalue = 2
% Store it using the key 'mykey'
setappdata(hObject, 'mykey', myvalue)
また、別のコールバック内から同じ値を取得する
value = getappdata(hObject, 'mykey');
注意: getappdata
を呼び出す前に値が格納されていない場合は、空の配列( []
)が返されます。
guidata
と同様に、データはhObject
を含むFigureに格納されます。
ベスト:
- すべてのコールバックとサブ関数がアクセスする必要のない大きな変数を格納する。
UserData
すべてのグラフィックハンドルには特別なプロパティUserData
あり、これには任意のデータを入れることができます。それは、セル配列、 struct
、またはスカラーを含むことができます。このプロパティを利用して、特定のグラフィックスハンドルに関連付けるすべてのデータをこのフィールドに格納することができます。 R2014b以上を使用している場合は、グラフィックスオブジェクトまたはドット表記の標準のget
/ set
メソッドを使用して値を保存して取得できます。
% Create some data to store
mydata = {1, 2, 3};
% Store it within the UserData property
set(hObject, 'UserData', mydata)
% Of if you're using R2014b or newer:
% hObject.UserData = mydata;
次に、別のコールバックからこのデータを取得できます。
their_data = get(hObject, 'UserData');
% Or if you're using R2014b or newer:
% their_data = hObject.UserData;
ベスト:
- 制限されたスコープ(変数が格納されているオブジェクト、またはそれに直接関係するオブジェクトによってのみ使用される可能性のある変数)で変数を格納する。
入れ子関数
MATLABでは、ネストされた関数は、親関数で定義された変数を読み取り、変更できます。このようにして、ネストされた関数としてコールバックを指定すると、main関数に格納されているすべてのデータを取得および変更できます。
function mygui()
hButton = uicontrol('String', 'Click Me', 'Callback', @callback);
% Create a counter to keep track of the number of times the button is clicked
nClicks = 0;
% Callback function is nested and can therefore read and modify nClicks
function callback(source, event)
% Increment the number of clicks
nClicks = nClicks + 1;
% Print the number of clicks so far
fprintf('Number of clicks: %d\n', nClicks);
end
end
ベスト:
- 小さくてシンプルなGUI (素早くプロトタイプを作成し、
guidata
やset/getappdata
メソッドを実装する必要がないように)。
推奨しない :
中規模、大規模、または複雑なGUI
GUIDE
作成されたGUI
明示的な入力引数
データをコールバック関数に送信する必要があり、コールバック内のデータを変更する必要がない場合は、慎重に作成されたコールバック定義を使用してコールバックにデータを渡すことを常に検討できます。
入力を追加する無名関数を使うことができます
% Create some data to send to mycallback
data = [1, 2, 3];
% Pass data as a third input to mycallback
set(hObject, 'Callback', @(source, event)mycallback(source, event, data))
または、セル配列構文を使用してコールバックを指定し、追加の入力を指定することもできます。
set(hObject, 'Callback', {@mycallback, data})
ベスト:
- コールバックがいくつかの操作を実行するために
data
を必要とするが、data
変数を変更して新しい状態で保存する必要はない。
コールバックの実行を一時停止するボタンをUIに作成する
アプリケーションの状態を検査するためにコードの実行を一時停止することがあります(「 デバッグ 」を参照)。 MATLABエディタでコードを実行する場合は、UIの「一時停止」ボタンを使用するか、 Ctrl + c (Windowsの場合)を押して実行します。しかし、( uicontrol
のコールバックを介して)GUIから計算が開始されたuicontrol
、このメソッドはもう動作しません。コールバックは別のコールバックを介して中断されるべきです。以下はこの原則のデモンストレーションです:
function interruptibleUI
dbclear in interruptibleUI % reset breakpoints in this file
figure('Position',[400,500,329,160]);
uicontrol('Style', 'pushbutton',...
'String', 'Compute',...
'Position', [24 55 131 63],...
'Callback', @longComputation,...
'Interruptible','on'); % 'on' by default anyway
uicontrol('Style', 'pushbutton',...
'String', 'Pause #1',...
'Position', [180 87 131 63],...
'Callback', @interrupt1);
uicontrol('Style', 'pushbutton',...
'String', 'Pause #2',...
'Position', [180 12 131 63],...
'Callback', @interrupt2);
end
function longComputation(src,event)
superSecretVar = rand(1);
pause(15);
print('done!'); % we'll use this to determine if code kept running "in the background".
end
function interrupt1(src,event) % depending on where you want to stop
dbstop in interruptibleUI at 27 % will stop after print('done!');
dbstop in interruptibleUI at 32 % will stop after **this** line.
end
function interrupt2(src,event) % method 2
keyboard;
dbup; % this will need to be executed manually once the code stops on the previous line.
end
この例を理解するためには、次のようにします。
- 上記のコードを新しいファイルに貼り付け、
interruptibleUI.m
として保存します。コードがファイルの最初の行から始まるようにします(これは、最初のメソッドが機能する上で重要です)。 - スクリプトを実行します。
- [ 計算 ]をクリックし、すぐ後に[ 一時停止#1]または[ 一時停止#2 ]のいずれかをクリックします。
-
superSecretVar
の値を確認してください。
"handles"構造体を使用してデータを渡す
これは、GUIのhandles
構造に格納された値を変更する2つのボタンを備えた基本的なGUIの例です。
function gui_passing_data()
% A basic GUI with two buttons to show a simple use of the 'handles'
% structure in GUI building
% Create a new figure.
f = figure();
% Retrieve the handles structure
handles = guidata(f);
% Store the figure handle
handles.figure = f;
% Create an edit box and two buttons (plus and minus),
% and store their handles for future use
handles.hedit = uicontrol('Style','edit','Position',[10,200,60,20] , 'Enable', 'Inactive');
handles.hbutton_plus = uicontrol('Style','pushbutton','String','+',...
'Position',[80,200,60,20] , 'Callback' , @ButtonPress);
handles.hbutton_minus = uicontrol('Style','pushbutton','String','-',...
'Position',[150,200,60,20] , 'Callback' , @ButtonPress);
% Define an initial value, store it in the handles structure and show
% it in the Edit box
handles.value = 1;
set(handles.hedit , 'String' , num2str(handles.value))
% Store handles
guidata(f, handles);
function ButtonPress(hObject, eventdata)
% A button was pressed
% Retrieve the handles
handles = guidata(hObject);
% Determine which button was pressed; hObject is the calling object
switch(get(hObject , 'String'))
case '+'
% Add 1 to the value
handles.value = handles.value + 1;
set(handles.hedit , 'String', num2str(handles.value))
case '-'
% Substract 1 from the value
handles.value = handles.value - 1;
end
% Display the new value
set(handles.hedit , 'String', num2str(handles.value))
% Store handles
guidata(hObject, handles);
この例をテストするには、 gui_passing_data.m
というgui_passing_data.m
ファイルに保存し、F5 gui_passing_data.m
て起動します。このような単純なケースでは、エディットボックスのString
プロパティから直接アクセスできるので、ハンドル構造体に値を格納する必要はありません。
ユーザーインターフェイス周辺でデータを渡す際のパフォーマンスの問題
GUI関数とコールバックの間で、setappdata / getappdataとguidataの間でデータを渡すことができる主な技術は2つあります( 詳細はこちらを参照してください )。前者は時間の効率が良いので、より大きな変数に使用する必要があります。次の例は、2つのメソッドの効率をテストします。
単純なボタンを持つGUIが作成され、大きな変数(10000x10000 double)がguidataとsetappdataの両方に格納されます。ボタンは、2つのメソッドを使用して実行をタイミングを取って変数をリロードして保存します。 setappdataを使用した実行時間とパーセンテージの改善がコマンドウィンドウに表示されます。
function gui_passing_data_performance()
% A basic GUI with a button to show performance difference between
% guidata and setappdata
% Create a new figure.
f = figure('Units' , 'normalized');
% Retrieve the handles structure
handles = guidata(f);
% Store the figure handle
handles.figure = f;
handles.hbutton = uicontrol('Style','pushbutton','String','Calculate','units','normalized',...
'Position',[0.4 , 0.45 , 0.2 , 0.1] , 'Callback' , @ButtonPress);
% Create an uninteresting large array
data = zeros(10000);
% Store it in appdata
setappdata(handles.figure , 'data' , data);
% Store it in handles
handles.data = data;
% Save handles
guidata(f, handles);
function ButtonPress(hObject, eventdata)
% Calculate the time difference when using guidata and appdata
t_handles = timeit(@use_handles);
t_appdata = timeit(@use_appdata);
% Absolute and percentage difference
t_diff = t_handles - t_appdata;
t_perc = round(t_diff / t_handles * 100);
disp(['Difference: ' num2str(t_diff) ' ms / ' num2str(t_perc) ' %'])
function use_appdata()
% Retrieve the data from appdata
data = getappdata(gcf , 'data');
% Do something with data %
% Store the value again
setappdata(gcf , 'data' , data);
function use_handles()
% Retrieve the data from handles
handles = guidata(gcf);
data = handles.data;
% Do something with data %
% Store it back in the handles
handles.data = data;
guidata(gcf, handles);
私のXeon [email protected] GHzで私はDifference: 0.00018957 ms / 73 %
を得ますDifference: 0.00018957 ms / 73 %
、従ってgetappdata / setappdataを使用して私は73%の性能改善を得ます! 10x10倍精度変数を使用すると結果は変わらないことに注意してください。ただし、 handles
に大きなデータhandles
含む多くのフィールドhandles
含まれていると結果が変わります。