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 / lub set/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 zmienna data 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:

  1. 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).
  2. Uruchom skrypt.
  3. Kliknij Oblicz i wkrótce potem kliknij Pauza nr 1 lub Pauza nr 2 .
  4. 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.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow