Recherche…


Passage de données autour de l'interface utilisateur

La plupart des interfaces utilisateur avancées exigent que l'utilisateur puisse transmettre des informations entre les différentes fonctions constituant une interface utilisateur. MATLAB dispose de plusieurs méthodes différentes pour le faire.


guidata

L' environnement de développement GUI (GUIDE) de MATLAB préfère utiliser une struct nommée handles pour transmettre des données entre les rappels. Cette struct contient tous les descripteurs graphiques des différents composants de l'interface utilisateur, ainsi que des données spécifiées par l'utilisateur. Si vous n'utilisez pas un rappel créé GUIDE-qui passe automatiquement handles , vous pouvez récupérer la valeur actuelle à l' aide guidata

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

Si vous souhaitez modifier une valeur stockée dans cette structure de données, vous pouvez la modifier, mais vous devez la stocker dans hObject pour que les modifications soient visibles par d'autres rappels. Vous pouvez le stocker en spécifiant un second argument d'entrée à guidata .

% Update the value
handles.myValue = 2;

% Save changes
guidata(hObject, handles)

La valeur de hObject n'a pas d'importance tant qu'il s'agit d'un composant d'interface utilisateur dans la même figure car les données sont finalement stockées dans la figure contenant hObject .

Meilleur pour:

  • Stockage de la structure des handles dans laquelle vous pouvez stocker toutes les poignées de vos composants d'interface graphique.
  • Stocker les "petites" autres variables auxquelles la plupart des callbacks doivent accéder.

Non recommandé pour :

  • Stockage de variables volumineuses auxquelles tous les callbacks et sous-fonctions ne doivent pas accéder (utilisez setappdata / getappdata pour ceux-ci).

setappdata / getappdata

À l' guidata approche guidata , vous pouvez utiliser setappdata et getappdata pour stocker et récupérer des valeurs dans un handle graphique. L'avantage de l'utilisation de ces méthodes est que vous ne pouvez récupérer que la valeur souhaitée plutôt qu'une struct entière contenant toutes les données stockées. Il est similaire à un magasin de clé / valeur.

Pour stocker des données dans un objet graphique

% Create some data you would like to store
myvalue = 2

% Store it using the key 'mykey'
setappdata(hObject, 'mykey', myvalue)

Et pour récupérer cette même valeur depuis un rappel différent

value = getappdata(hObject, 'mykey');

Note: Si aucune valeur n'a été stockée avant d'appeler getappdata , cela retournera un tableau vide ( [] ).

Semblable à guidata , les données sont stockées dans la figure contenant hObject .

Meilleur pour:

  • Stockage de variables volumineuses auxquelles tous les callbacks et sous-fonctions ne doivent pas accéder.

UserData

Chaque descripteur graphique a une propriété spéciale, UserData qui peut contenir toutes les données souhaitées. Il pourrait contenir un tableau de cellules, une struct ou même un scalaire. Vous pouvez tirer parti de cette propriété et stocker toutes les données que vous souhaitez associer à une poignée graphique donnée dans ce champ. Vous pouvez enregistrer et récupérer la valeur à l'aide des méthodes get / set standard pour les objets graphiques ou la notation par points si vous utilisez R2014b ou une version plus récente.

% 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;

Ensuite, à partir d'un autre rappel, vous pouvez récupérer ces données:

their_data = get(hObject, 'UserData');

% Or if you're using R2014b or newer:
% their_data = hObject.UserData;

Meilleur pour:

  • Stockage de variables avec une portée limitée (variables susceptibles d'être utilisées uniquement par l'objet dans lequel elles sont stockées, ou objets ayant une relation directe avec celui-ci).

Fonctions imbriquées

Dans MATLAB, une fonction imbriquée peut lire et modifier toute variable définie dans la fonction parente. De cette façon, si vous spécifiez un rappel comme une fonction imbriquée, il peut récupérer et modifier toutes les données stockées dans la fonction principale.

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

Meilleur pour:

  • Petites interfaces graphiques simples. (pour un prototypage rapide, ne pas avoir à implémenter les guidata et / ou set/getappdata ).

Non recommandé pour :

  • Interfaces graphiques moyennes, grandes ou complexes.

  • Interface graphique créée avec GUIDE .


Arguments de saisie explicites

Si vous devez envoyer des données à une fonction de rappel sans avoir à modifier les données dans le rappel, vous pouvez toujours envisager de transmettre les données au rappel en utilisant une définition de rappel soigneusement conçue.

Vous pouvez utiliser une fonction anonyme qui ajoute des entrées

% 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))

Vous pouvez également utiliser la syntaxe du tableau de cellules pour spécifier un rappel, en spécifiant à nouveau des entrées supplémentaires.

set(hObject, 'Callback', {@mycallback, data})

Meilleur pour:

  • Lorsque le rappel a besoin de data pour effectuer certaines opérations, la variable de data n'a pas besoin d'être modifiée et enregistrée dans un nouvel état.

Faire un bouton dans votre interface utilisateur qui interrompt l'exécution du rappel

Parfois, nous aimerions suspendre l'exécution du code pour inspecter l'état de l'application (voir Débogage ). Lorsque vous exécutez du code via l'éditeur MATLAB, vous pouvez utiliser le bouton "Pause" de l'interface utilisateur ou en appuyant sur Ctrl + c (sous Windows). Cependant, lorsqu'un calcul a été lancé à partir d'une interface graphique (via le rappel de certains uicontrol ), cette méthode ne fonctionne plus, et le rappel doit être interrompu via un autre rappel. Voici une démonstration de ce principe:

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

Pour vous assurer que vous comprenez cet exemple, procédez comme suit:

  1. Collez le code ci-dessus dans un nouveau fichier appelé et enregistrez-le en tant que interruptibleUI.m , de sorte que le code démarre sur la toute première ligne du fichier (cela est important pour que la 1ère méthode fonctionne).
  2. Exécutez le script.
  3. Cliquez sur Calculer et, peu après, cliquez sur Pause # 1 ou sur Pause # 2 .
  4. Assurez-vous de pouvoir trouver la valeur de superSecretVar .

Passer des données en utilisant la structure "handles"

Voici un exemple d'interface graphique de base avec deux boutons qui modifient une valeur stockée dans la structure des handles l'interface graphique.

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);

Pour tester l'exemple, enregistrez-le dans un fichier nommé gui_passing_data.m et lancez-le avec F5. Veuillez noter que dans un cas aussi simple, vous n'avez même pas besoin de stocker la valeur dans la structure des descripteurs car vous pouvez y accéder directement à partir de la propriété String de la zone d'édition.

Problèmes de performances lors du transfert de données autour de l'interface utilisateur

Deux techniques principales permettent de transmettre des données entre les fonctions d'interface graphique et les rappels: setappdata / getappdata et guidata (pour en savoir plus ). Le premier devrait être utilisé pour les plus grandes variables car il est plus efficace dans le temps. L'exemple suivant teste l'efficacité des deux méthodes.

Une interface graphique avec un simple bouton est créée et une grande variable (10000x10000 double) est stockée à la fois avec guidata et avec setappdata. Le bouton recharge et stocke la variable en utilisant les deux méthodes tout en chronométrant leur exécution. Le temps d'exécution et le pourcentage d'amélioration à l'aide de setappdata sont affichés dans la fenêtre de commande.

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);

Sur mon Xeon [email protected] GHz, j'obtiens une Difference: 0.00018957 ms / 73 % , en utilisant getappdata / setappdata, j'obtiens une amélioration des performances de 73%! Notez que le résultat ne change pas si une double variable 10x10 est utilisée. Cependant, le résultat changera si handles contient de nombreux champs avec de grandes données.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow