MATLAB Language
Interfejsy użytkownika MATLAB
Szukaj…
Przekazywanie danych wokół interfejsu użytkownika
Najbardziej zaawansowane interfejsy użytkownika wymagają od użytkownika przekazywania informacji między różnymi funkcjami tworzącymi interfejs użytkownika. MATLAB ma do tego wiele różnych metod.
guidata
Własne środowisko programistyczne GUI MATLAB-a (GUIDE) woli używać struct
nazwie handles
do przesyłania danych między wywołaniami zwrotnymi. Ta struct
zawiera wszystkie uchwyty graficzne do różnych składników interfejsu użytkownika, a także dane określone przez użytkownika. Jeśli nie używasz wywołania zwrotnego utworzonego przez GUIDE, który automatycznie przekazuje handles
, możesz pobrać bieżącą wartość za pomocą guidata
% hObject is a graphics handle to any UI component in your GUI
handles = guidata(hObject);
Jeśli chcesz zmodyfikować wartość przechowywaną w tej strukturze danych, możesz ją zmodyfikować, ale musisz ją zapisać z powrotem w obiekcie hObject
aby zmiany były widoczne dla innych wywołań zwrotnych. Możesz go zapisać, podając drugi argument wejściowy do guidata
.
% Update the value
handles.myValue = 2;
% Save changes
guidata(hObject, handles)
Wartość hObject
nie ma znaczenia, dopóki jest składnikiem interfejsu użytkownika na tej samej figure
ponieważ ostatecznie dane są przechowywane na figurze zawierającej hObject
.
Najlepszy dla:
- Przechowywanie struktury
handles
, w której można przechowywać wszystkie uchwyty komponentów GUI. - Przechowywanie „małych” innych zmiennych, do których dostęp ma większość wywołań zwrotnych.
Niezalecane dla :
- Przechowywanie dużych zmiennych, do których nie muszą mieć dostępu wszystkie wywołania zwrotne i podfunkcje (dla nich użyj
setappdata
/getappdata
).
setappdata
/ getappdata
Podobnie do podejścia z guidata
, możesz używać setappdata
i getappdata
do przechowywania i pobierania wartości z uchwytu graficznego. Zaletą korzystania z tych metod jest to, że można pobrać tylko żądaną wartość, a nie całą struct
zawierającą wszystkie przechowywane dane. Jest podobny do magazynu kluczy / wartości.
Do przechowywania danych w obiekcie graficznym
% Create some data you would like to store
myvalue = 2
% Store it using the key 'mykey'
setappdata(hObject, 'mykey', myvalue)
I aby pobrać tę samą wartość z innego wywołania zwrotnego
value = getappdata(hObject, 'mykey');
Uwaga: Jeśli przed wywołaniem getappdata
nie została zapisana żadna wartość, zwróci pustą tablicę ( []
).
Podobnie jak guidata
, dane są przechowywane na rysunku zawierającym obiekt hObject
.
Najlepszy dla:
- Przechowywanie dużych zmiennych, do których nie muszą mieć dostępu wszystkie wywołania zwrotne i podfunkcje.
UserData
Każdy uchwyt graficzny ma specjalną właściwość UserData
która może zawierać dowolne dane. Może zawierać tablicę komórek, struct
, a nawet skalar. Możesz skorzystać z tej właściwości i przechowywać w tym polu dowolne dane, które chcesz powiązać z danym uchwytem graficznym. Możesz zapisać i pobrać wartość przy użyciu standardowych metod get
/ set
dla obiektów graficznych lub notacji kropkowej, jeśli używasz R2014b lub nowszej.
% 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;
Następnie z innego wywołania zwrotnego możesz pobrać te dane:
their_data = get(hObject, 'UserData');
% Or if you're using R2014b or newer:
% their_data = hObject.UserData;
Najlepszy dla:
- Przechowywanie zmiennych o ograniczonym zakresie (zmienne, które mogą być używane tylko przez obiekt, w którym są przechowywane, lub obiekty mające bezpośredni związek z nim).
Funkcje zagnieżdżone
W MATLAB funkcja zagnieżdżona może odczytywać i modyfikować dowolne zmienne zdefiniowane w funkcji nadrzędnej. W ten sposób, jeśli określisz wywołanie zwrotne jako funkcję zagnieżdżoną, może on pobierać i modyfikować dowolne dane przechowywane w funkcji głównej.
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
Najlepszy dla:
- Małe, proste GUI. (w celu szybkiego prototypowania, aby nie musieć implementować
guidata
i / lubset/getappdata
).
Niezalecane dla :
Średnie, duże lub złożone GUI.
GUI utworzone za pomocą
GUIDE
.
Jawne argumenty wejściowe
Jeśli musisz wysłać dane do funkcji wywołania zwrotnego i nie musisz modyfikować danych w ramach wywołania zwrotnego, zawsze możesz rozważyć przesłanie danych do wywołania zwrotnego za pomocą starannie spreparowanej definicji wywołania zwrotnego.
Możesz użyć anonimowej funkcji, która dodaje dane wejściowe
% 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))
Lub możesz użyć składni macierzy komórkowej, aby określić wywołanie zwrotne, ponownie określając dodatkowe dane wejściowe.
set(hObject, 'Callback', {@mycallback, data})
Najlepszy dla:
- Gdy wywołanie zwrotne potrzebuje
data
do wykonania niektórych operacji, ale zmiennadata
nie musi być modyfikowana i zapisywana w nowym stanie.
Utworzenie przycisku w interfejsie użytkownika, który wstrzymuje wykonanie wywołania zwrotnego
Czasami chcielibyśmy wstrzymać wykonywanie kodu, aby sprawdzić stan aplikacji (patrz Debugowanie ). Podczas uruchamiania kodu za pomocą edytora MATLAB można to zrobić za pomocą przycisku „Pauza” w interfejsie użytkownika lub naciskając Ctrl + c (w systemie Windows). Jednak gdy obliczenia zostały zainicjowane z GUI (przez wywołanie zwrotne niektórych uicontrol
), ta metoda już nie działa, a wywołanie zwrotne powinno zostać przerwane przez inne wywołanie zwrotne. Poniżej znajduje się wykaz tej zasady:
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
Aby upewnić się, że rozumiesz ten przykład, wykonaj następujące czynności:
- Wklej powyższy kod do nowego pliku o nazwie i zapisz go jako
interruptibleUI.m
, tak aby kod zaczynał się od pierwszego wiersza pliku (jest to ważne, aby pierwsza metoda zadziałała). - Uruchom skrypt.
- Kliknij Oblicz i wkrótce potem kliknij Pauza nr 1 lub Pauza nr 2 .
- Upewnij się, że możesz znaleźć wartość
superSecretVar
.
Przekazywanie danych za pomocą struktury „uchwytów”
To jest przykład podstawowego GUI z dwoma przyciskami, które zmieniają wartość przechowywaną w strukturze 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);
Aby przetestować przykład, zapisz go w pliku o nazwie gui_passing_data.m
i uruchom za pomocą F5. Należy pamiętać, że w tak prostym przypadku nie trzeba nawet przechowywać wartości w strukturze uchwytów, ponieważ można uzyskać bezpośredni dostęp do niej z właściwości String
pola edycji.
Problemy z wydajnością podczas przesyłania danych wokół interfejsu użytkownika
Dwie główne techniki pozwalają na przesyłanie danych między funkcjami GUI a wywołaniami zwrotnymi: setappdata / getappdata i guidata ( czytaj więcej na ten temat ). Pierwszego należy użyć w przypadku większych zmiennych, ponieważ jest on bardziej wydajny. Poniższy przykład testuje skuteczność dwóch metod.
Tworzony jest interfejs GUI z prostym przyciskiem, a duża zmienna (podwójna 10000 x 10000) jest przechowywana zarówno z guidata, jak iz setappdata. Przycisk ponownie ładuje i zapisuje zmienną przy użyciu dwóch metod podczas pomiaru ich wykonania. Czas działania i poprawa procentowa za pomocą setappdata są wyświetlane w oknie poleceń.
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);
Na moim Xeon [email protected] GHz dostaję Difference: 0.00018957 ms / 73 %
, dzięki czemu używając getappdata / setappdata uzyskuję poprawę wydajności o 73%! Zauważ, że wynik nie zmienia się, jeśli używana jest podwójna zmienna 10x10, jednak wynik zmieni się, jeśli handles
zawierają wiele pól z dużymi danymi.