Buscar..


Transferencia de datos alrededor de la interfaz de usuario

La mayoría de las interfaces de usuario avanzadas requieren que el usuario pueda pasar información entre las diversas funciones que conforman una interfaz de usuario. MATLAB tiene varios métodos diferentes para hacerlo.


guidata

Propia de MATLAB entorno de desarrollo de interfaz gráfica de usuario (GUIDE) prefiere utilizar una struct con nombre handles para pasar datos entre las devoluciones de llamada. Esta struct contiene todos los controladores de gráficos para los diversos componentes de la interfaz de usuario, así como los datos especificados por el usuario. Si no está utilizando una devolución de llamada creada por GUIDE que pasa automáticamente a los handles , puede recuperar el valor actual utilizando guidata

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

Si desea modificar un valor almacenado en esta estructura de datos, puede modificarlo pero luego debe almacenarlo nuevamente dentro del hObject para que los cambios sean visibles por otras devoluciones de llamada. Puede almacenarlo especificando un segundo argumento de entrada a guidata .

% Update the value
handles.myValue = 2;

% Save changes
guidata(hObject, handles)

El valor de hObject no importa siempre que sea un componente UI dentro de la misma figure porque, en última instancia, los datos se almacenan dentro de la figura que contiene hObject .

Mejor para:

  • Almacenar la estructura de los handles , en la que puede almacenar todos los manejadores de sus componentes GUI.
  • Almacenar "pequeñas" otras variables a las que debe acceder la mayoría de las devoluciones de llamada.

No recomendado para :

  • Almacenar variables grandes a las que no deben acceder todas las devoluciones de llamada y subfunciones (use setappdata / getappdata para estas).

setappdata / getappdata

De manera similar al enfoque de guidata , puede usar setappdata y getappdata para almacenar y recuperar valores desde un controlador de gráficos. La ventaja de usar estos métodos es que puede recuperar solo el valor que desea en lugar de una struct completa que contenga todos los datos almacenados. Es similar a un almacén de clave / valor.

Para almacenar datos dentro de un objeto gráfico

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

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

Y para recuperar ese mismo valor desde una devolución de llamada diferente

value = getappdata(hObject, 'mykey');

Nota: Si no se almacenó ningún valor antes de llamar a getappdata , devolverá una matriz vacía ( [] ).

Al igual que con guidata , los datos se almacenan en la figura que contiene hObject .

Mejor para:

  • Almacenar variables grandes a las que no deben acceder todas las devoluciones de llamada y subfunciones.

UserData

Cada controlador de gráficos tiene una propiedad especial, UserData que puede contener cualquier información que desee. Podría contener una matriz de celdas, una struct o incluso un escalar. Puede aprovechar esta propiedad y almacenar cualquier dato que desee asociar con un controlador de gráficos dado en este campo. Puede guardar y recuperar el valor utilizando los métodos estándar de get / set para objetos gráficos o notación de puntos si está utilizando R2014b o más reciente.

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

Luego, desde dentro de otra devolución de llamada, puede recuperar estos datos:

their_data = get(hObject, 'UserData');

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

Mejor para:

  • Almacenamiento de variables con un alcance limitado (variables que probablemente solo serán utilizadas por el objeto en el que están almacenadas u objetos que tengan una relación directa con él).

Funciones anidadas

En MATLAB, una función anidada puede leer y modificar cualquier variable definida en la función principal. De esta manera, si especifica que una devolución de llamada sea una función anidada, puede recuperar y modificar cualquier dato almacenado en la función principal.

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

Mejor para:

  • GUIs pequeñas y simples. (para la creación rápida de prototipos, para no tener que implementar los guidata y / o set/getappdata ).

No recomendado para :

  • GUIs medianas, grandes o complejas.

  • GUI creado con GUIDE .


Argumentos de entrada explícitos

Si necesita enviar datos a una función de devolución de llamada y no necesita modificar los datos dentro de la devolución de llamada, siempre puede considerar pasar los datos a la devolución de llamada utilizando una definición de devolución de llamada cuidadosamente diseñada.

Podría usar una función anónima que agregue entradas

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

O puede usar la sintaxis de la matriz de celdas para especificar una devolución de llamada, especificando de nuevo entradas adicionales.

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

Mejor para:

  • Cuando la devolución de llamada necesita data para realizar algunas operaciones pero la variable de data no necesita modificarse y guardarse en un nuevo estado.

Hacer un botón en su interfaz de usuario que detiene la ejecución de devolución de llamada

A veces nos gustaría pausar la ejecución del código para inspeccionar el estado de la aplicación (ver Depuración ). Al ejecutar el código a través del editor MATLAB, esto se puede hacer usando el botón "Pausa" en la interfaz de usuario o presionando Ctrl + c (en Windows). Sin embargo, cuando se inició un cálculo desde una GUI (a través de la devolución de llamada de algún uicontrol ), este método ya no funciona, y la devolución de llamada debe interrumpirse a través de otra devolución de llamada. A continuación se muestra una demostración de este principio:

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

Para asegurarse de que comprende este ejemplo, haga lo siguiente:

  1. Pegue el código anterior en un nuevo archivo llamado y guárdelo como interruptibleUI.m , de modo que el código comience en la primera línea del archivo (esto es importante para que funcione el primer método).
  2. Ejecutar el script.
  3. Haga clic en Calcular y poco después haga clic en Pausa # 1 o en Pausa # 2 .
  4. Asegúrese de que puede encontrar el valor de superSecretVar .

Pasar datos utilizando la estructura de "manejadores"

Este es un ejemplo de una GUI básica con dos botones que cambian un valor almacenado en la estructura de los handles la 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);

Para probar el ejemplo, guárdelo en un archivo llamado gui_passing_data.m y gui_passing_data.m con F5. Tenga en cuenta que en un caso tan simple, ni siquiera necesitaría almacenar el valor en la estructura de los manejadores porque podría acceder directamente desde la propiedad String del cuadro de edición.

Problemas de rendimiento al pasar datos por la interfaz de usuario

Dos técnicas principales permiten pasar datos entre funciones GUI y devoluciones de llamada: setappdata / getappdata y guidata ( lea más sobre esto ). Lo primero debería usarse para variables más grandes, ya que es más eficiente en el tiempo. El siguiente ejemplo prueba la eficiencia de los dos métodos.

Se crea una GUI con un botón simple y se almacena una gran variable (10000x10000 doble) con guidata y con setappdata. El botón vuelve a cargar y almacena la variable utilizando los dos métodos mientras cronometra su ejecución. El tiempo de ejecución y la mejora del porcentaje con setappdata se muestran en la ventana de comandos.

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

En mi Xeon [email protected] GHz obtengo la Difference: 0.00018957 ms / 73 % , por lo tanto, al usar getappdata / setappdata obtengo una mejora en el rendimiento del 73%. Tenga en cuenta que el resultado no cambia si se usa una variable doble 10x10, sin embargo, el resultado cambiará si los handles contienen muchos campos con datos grandes.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow