MATLAB Language
Пользовательские интерфейсы MATLAB
Поиск…
Передача данных вокруг пользовательского интерфейса
Большинство современных пользовательских интерфейсов требуют, чтобы пользователь мог передавать информацию между различными функциями, составляющими пользовательский интерфейс. Для этого 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
Чтобы убедиться, что вы понимаете этот пример, выполните следующие действия:
- Вставьте вышеуказанный код в новый файл и сохраните его как
interruptibleUI.m
, чтобы код начинался в самой первой строке файла (это важно для работы первого метода). - Запустите скрипт.
- Нажмите « Вычислить» и вскоре после этого нажмите « Пауза №1» или « Пауза № 2» .
- Убедитесь, что вы можете найти значение
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
содержится много полей с большими данными.