MATLAB Language
Interfacce utente MATLAB
Ricerca…
Passaggio dei dati intorno all'interfaccia utente
Le interfacce utente più avanzate richiedono all'utente di essere in grado di passare informazioni tra le varie funzioni che costituiscono un'interfaccia utente. MATLAB ha un numero di metodi diversi per farlo.
guidata
L' ambiente di sviluppo GUI della MATLAB preferisce utilizzare una struct
denominata handles
per passare i dati tra i callback. Questa struct
contiene tutti gli handle grafici per i vari componenti dell'interfaccia utente, nonché i dati specificati dall'utente. Se non si utilizza un callback creato da GUIDE che passa automaticamente gli handles
, è possibile recuperare il valore corrente utilizzando guidata
% hObject is a graphics handle to any UI component in your GUI
handles = guidata(hObject);
Se si desidera modificare un valore archiviato in questa struttura dati, è possibile modificarlo, ma è necessario memorizzarlo nuovamente all'interno di hObject
affinché le modifiche siano visibili da altri callback. È possibile memorizzarlo specificando un secondo argomento di input per guidata
.
% Update the value
handles.myValue = 2;
% Save changes
guidata(hObject, handles)
Il valore di hObject
non ha importanza fintanto che si tratta di un componente dell'interfaccia utente all'interno della stessa figure
poiché, in definitiva, i dati vengono archiviati nella figura che contiene hObject
.
Meglio per:
- Memorizzazione della struttura delle
handles
, in cui è possibile memorizzare tutti gli handle dei componenti della GUI. - Memorizzazione di "piccole" altre variabili a cui è necessario accedere tramite la maggior parte delle richiamate.
Non consigliato per :
- Memorizzazione di variabili di grandi dimensioni a cui non è necessario accedere con tutte le richiamate e le
setappdata
(utilizzaresetappdata
/getappdata
per queste).
setappdata
/ getappdata
Simile al guidata
approccio, è possibile utilizzare setappdata
e getappdata
per memorizzare e recuperare i valori all'interno di gestire una grafica. Il vantaggio dell'utilizzo di questi metodi è che è possibile recuperare solo il valore desiderato anziché un'intera struct
contenente tutti i dati memorizzati. È simile a un archivio di chiavi / valori.
Per memorizzare i dati all'interno di un oggetto grafico
% Create some data you would like to store
myvalue = 2
% Store it using the key 'mykey'
setappdata(hObject, 'mykey', myvalue)
E per recuperare lo stesso valore da un callback differente
value = getappdata(hObject, 'mykey');
Nota: se non è stato memorizzato alcun valore prima di chiamare getappdata
, verrà restituito un array vuoto ( []
).
Simile a guidata
, i dati sono memorizzati nella figura che contiene hObject
.
Meglio per:
- Memorizzazione di variabili di grandi dimensioni a cui non è necessario accedere con tutte le richiamate e le sottofunzioni.
UserData
Ogni handle grafico ha una proprietà speciale, UserData
che può contenere tutti i dati che desideri. Potrebbe contenere un array di celle, una struct
o anche uno scalare. È possibile usufruire di questa proprietà e memorizzare in questo campo tutti i dati che si desidera associare a un determinato elemento grafico. È possibile salvare e recuperare il valore utilizzando i metodi get
/ set
standard per gli oggetti grafici o la notazione dei punti se si utilizza R2014b o più recente.
% 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;
Quindi dall'interno di un altro callback, è possibile recuperare questi dati:
their_data = get(hObject, 'UserData');
% Or if you're using R2014b or newer:
% their_data = hObject.UserData;
Meglio per:
- Memorizzazione di variabili con un ambito limitato (variabili che possono essere utilizzate solo dall'oggetto in cui sono memorizzate o oggetti che hanno una relazione diretta con esso).
Funzioni annidate
In MATLAB, una funzione nidificata può leggere e modificare qualsiasi variabile definita nella funzione genitore. In questo modo, se si specifica una richiamata come funzione nidificata, è possibile recuperare e modificare qualsiasi dato memorizzato nella funzione 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
Meglio per:
- GUI piccole e semplici. (per la prototipazione rapida, per non dover implementare i metodi
guidata
e / oset/getappdata
).
Non consigliato per :
GUI di media, grande o complessa.
GUI creata con
GUIDE
.
Argomenti di input espliciti
Se è necessario inviare dati a una funzione di callback e non è necessario modificare i dati all'interno del callback, è sempre possibile considerare di trasferire i dati al callback utilizzando una definizione callback accuratamente predisposta.
Potresti usare una funzione anonima che aggiunge input
% 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))
In alternativa, è possibile utilizzare la sintassi dell'array di celle per specificare un callback, specificando nuovamente input aggiuntivi.
set(hObject, 'Callback', {@mycallback, data})
Meglio per:
- Quando il callback necessita di
data
per eseguire alcune operazioni, la variabile didata
non deve essere modificata e salvata in un nuovo stato.
Creazione di un pulsante nell'interfaccia utente che sospende l'esecuzione di callback
A volte vorremmo mettere in pausa l'esecuzione del codice per ispezionare lo stato dell'applicazione (vedere Debug ). Quando si esegue il codice tramite l'editor MATLAB, è possibile farlo utilizzando il pulsante "Pausa" nell'interfaccia utente o premendo Ctrl + c (su Windows). Tuttavia, quando un calcolo è stato avviato da una GUI (tramite il callback di alcuni uicontrol
), questo metodo non funziona più e il callback deve essere interrotto tramite un altro callback. Di seguito è una dimostrazione di questo 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
Per assicurarti di aver compreso questo esempio, procedi nel seguente modo:
- Incolla il codice sopra in un nuovo file chiamato e salvalo come
interruptibleUI.m
, in modo tale che il codice inizi sulla prima riga del file (questo è importante perché il 1 ° metodo funzioni). - Esegui lo script.
- Fai clic su Calcola e, poco dopo, fai clic su Pausa n. 1 o Pausa n . 2 .
- Assicurati di poter trovare il valore di
superSecretVar
.
Trasmettere dati in giro usando la struttura "maniglie"
Questo è un esempio di una GUI di base con due pulsanti che modificano un valore memorizzato nella struttura degli handles
della 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);
Per testare l'esempio, salvarlo in un file chiamato gui_passing_data.m
e lanciarlo con F5. Si noti che in un caso così semplice, non è nemmeno necessario memorizzare il valore nella struttura degli handle perché è possibile accedervi direttamente dalla proprietà String
della casella di modifica.
Problemi di prestazioni quando si passano dati intorno all'interfaccia utente
Due tecniche principali consentono il passaggio dei dati tra le funzioni della GUI e le callback: setappdata / getappdata e guidata ( leggi di più a riguardo ). Il primo dovrebbe essere usato per variabili più grandi in quanto è più efficiente nel tempo. L'esempio seguente verifica l'efficienza dei due metodi.
Viene creata una GUI con un semplice pulsante e una grande variabile (10000x10000 double) viene memorizzata sia con guidata che con setappdata. Il pulsante ricarica e memorizza la variabile utilizzando i due metodi durante il tempo della loro esecuzione. Il tempo di esecuzione e il miglioramento percentuale usando setappdata vengono visualizzati nella finestra di comando.
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);
Sulla mia Xeon W3530@2,80 GHz ho Difference: 0.00018957 ms / 73 %
, quindi usando getappdata / setappdata ottengo un miglioramento delle prestazioni del 73%! Si noti che il risultato non cambia se viene utilizzata una variabile doppia 10x10, tuttavia, il risultato cambierà se gli handles
contengono molti campi con dati di grandi dimensioni.