サーチ…


ユーザーインターフェイスの周囲にデータを渡す

最も進んだユーザーインターフェイスでは、ユーザーがユーザーインターフェイスを構成するさまざまな機能間で情報を渡すことができる必要があります。 MATLABにはさまざまな方法があります。


guidata

MATLAB独自のGUI開発環境(GUIDE)は、コールバック間でデータを渡すためにhandlesという名前のstructを使用する方が好きです。このstructは、さまざまなUIコンポーネントのグラフィックスハンドルとユーザー指定のデータがすべて含まれています。 handlesを自動的に渡すGUIDE作成のコールバックを使用していない場合handlesguidataを使用して現在の値を取得できます

% 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アプローチと同様に、 setappdatagetappdataを使用して、グラフィックスハンドル内の値を格納および取得することができます。これらのメソッドを使用する利点は、格納されているすべてのデータを含む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 (素早くプロトタイプを作成し、 guidataset/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

この例を理解するためには、次のようにします。

  1. 上記のコードを新しいファイルに貼り付け、 interruptibleUI.mとして保存します。コードがファイルの最初の行から始まるようにします(これは、最初のメソッドが機能する上で重要です)。
  2. スクリプトを実行します。
  3. [ 計算 ]をクリックし、すぐ後に[ 一時停止#1]または[ 一時停止#2 ]のいずれかをクリックします。
  4. 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含まれていると結果が変わります。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow