Поиск…


Передача данных вокруг пользовательского интерфейса

Большинство современных пользовательских интерфейсов требуют, чтобы пользователь мог передавать информацию между различными функциями, составляющими пользовательский интерфейс. Для этого MATLAB предлагает несколько различных методов.


guidata

Собственная среда разработки GUI MATLAB (GUIDE) предпочитает использовать struct named handles для передачи данных между обратными вызовами. Эта struct содержит все графические дескрипторы для различных компонентов пользовательского интерфейса, а также данные, заданные пользователем. Если вы не используете созданный GUIDE обратный вызов, который автоматически передает handles , вы можете получить текущее значение с помощью guidata

% hObject is a graphics handle to any UI component in your GUI
handles = guidata(hObject);

Если вы хотите изменить значение, хранящееся в этой структуре данных, вы можете изменить, но затем вы должны сохранить его обратно в hObject чтобы изменения отображались другими обратными вызовами. Вы можете сохранить его, указав второй входной аргумент для guidata .

% Update the value
handles.myValue = 2;

% Save changes
guidata(hObject, handles)

Значение hObject не имеет значения до тех пор, пока оно является компонентом пользовательского интерфейса на той же figure потому что в конечном итоге данные сохраняются в фигуре, содержащей hObject .

Лучше всего:

  • Хранение структуры handles , в которой вы можете хранить все ручки ваших компонентов графического интерфейса.
  • Хранение «малых» других переменных, к которым нужно обращаться большинством обратных вызовов.

Не рекомендуется для :

  • Хранение больших переменных, к которым не нужно обращаться всеми обратными вызовами и setappdata (используйте для них 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 не было сохранено getappdata , оно вернет пустой массив ( [] ).

Подобно guidata , данные хранятся на рисунке, который содержит hObject .

Лучше всего:

  • Хранение больших переменных, к которым не нужно обращаться всеми обратными вызовами и подфункциями.

UserData

Каждый графический дескриптор имеет специальное свойство UserData которое может содержать любые данные. Он может содержать массив ячеек, struct или даже скаляр. Вы можете воспользоваться этим свойством и сохранить любые данные, которые вы хотите связать с данным графическим дескриптором в этом поле. Вы можете сохранить и получить значение с помощью стандартных методов get / set для графических объектов или точечной нотации, если вы используете R2014b или новее.

% 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 вложенная функция может читать и изменять любую переменную, определенную в родительской функции. Таким образом, если вы укажете обратный вызов как вложенную функцию, он может извлекать и изменять любые данные, хранящиеся в основной функции.

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

Лучше всего:

  • Маленькие, простые графические интерфейсы. (для быстрого прототипирования, чтобы не было необходимости применять guidata и / или set/getappdata ).

Не рекомендуется для :

  • Средний, большой или сложный графический интерфейс.

  • GUI создан с помощью GUIDE .


Явные входные аргументы

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

Вы можете использовать анонимную функцию, которая добавляет входные данные

% 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 не нужно изменять и сохранять в новом состоянии.

Создание кнопки в пользовательском интерфейсе, которая приостанавливает выполнение обратного вызова

Иногда мы хотели бы приостановить выполнение кода, чтобы проверить состояние приложения (см. Раздел «Отладка» ). При запуске кода через редактор MATLAB это можно сделать, используя кнопку «Пауза» в пользовательском интерфейсе или нажав Ctrl + c (в Windows). Однако, когда вычисление было инициировано из графического интерфейса (через обратный вызов некоторого 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.

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 и запустите его с помощью F5. Обратите внимание, что в таком простом случае вам даже не нужно будет сохранять значение в структуре дескрипторов, потому что вы можете напрямую обращаться к нему из свойства String окна редактирования.

Проблемы с производительностью при передаче данных вокруг пользовательского интерфейса

Два основных метода позволяют передавать данные между функциями GUI и обратными вызовами: setappdata / getappdata и guidata ( подробнее об этом ). Первый должен использоваться для больших переменных, поскольку он более эффективен по времени. В следующем примере проверяется эффективность двух методов.

Создается графический интерфейс с простой кнопкой, и большая переменная (10000x10000 double) сохраняется как с guidata, так и с setappdata. Кнопка перезагружает и сохраняет обратно переменную, используя два метода, в то время как время их выполнения. Время выполнения и процентное улучшение с использованием 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] ГГц я получаю Difference: 0.00018957 ms / 73 % , поэтому используя getappdata / setappdata, я получаю повышение производительности на 73%! Обратите внимание, что результат не изменяется, если используется двойная переменная 10x10, однако результат будет изменяться, если в handles содержится много полей с большими данными.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow