Basicamente, a interface visual se baseia em um arquivo do tipo figura (*.fig) que irá operar em conjunto com o código desenvolvido em um arquivo *.m. Usualmente, o desenvolvedor somente necessita implementar as funções de resposta aos eventos desencadeados pelos objetos da figura (como botões e caixas de texto). A maioria do código que cria o objeto figura e dispõe os objetos gráficos é automaticamente criada pelo MATLAB em conjunto com o GUIDE. Isso faz com que se tenha grande nível de abstração, focando apenas nas atividades desencadeadas pelas ações do usuário na interface.
Além da estrutura básica do código, o GUIDE fornece templates prontos para as funções de resposta, o que facilita e agiliza muito o desenvolvimento da aplicação.
O GUIDE também fornece uma ferramenta que permite o desenvolvimento de menus para a aplicação (como as abas: Arquivo, Editar, Ajuda em programas visuais), o que com certeza torna a aplicação versátil e poderosa.
Para iniciar a ferramenta GUIDE, basta escrever guide na janela de comandos. A janela abaixo será apresentada.
Nesta janela é possível utilizar templates prontos de GUI (Graphical User Interface - Interface Gráfica de Usuário). Ou inclusive abrir uma interface já existente. Para nosso primeiro exemplo, selecione Blank GUI. E pressione OK.
Nesta janela, a interface gráfica será desenvolvida (projetada). Na barra da esquerda, estão dispostos os vários objetos gráficos que podem ser utilizados para criar as mais diversas aplicações. Esses objetos são apresentados na imagem abaixo, com exceção dos controles ActiveX.
Nessa janela é possível modificar algumas propriedades da interface, como permitir ou não redimensionamento a janela. No momento, aconselha-se que outras propriedades não sejam modificadas, mantendo as opções como na figura acima.
O GUIDE possui uma barra de ferramentas muito útil para desenvolvimento de menus e para adicionar uma barra de ferramentas para a própria aplicação, como será visto adiante.
Da esquerda para a direita:
Nova Figura; Abrir; Salvar; Recortar; Copiar; Colar; Desfazer; Refazer; Alinhar Objetos; Editor de Menus; Editor da Ordem do botão Tab; Editor da Barra de Ferramentas; Editor do M-File; Inspetor de Propriedades; Navegador de Objetos; Rodar Figura.
Algumas ferramentas são auto-explicativas. Uma menção especial ao Editor de Menus e ao Editor de Ferramentas, os quais permitem inserir menus e uma barra de ferramentas às aplicações.
Para entender como desenvolver uma aplicação gráfica e como o MATLAB fornece meios para manipular os objetos no código, desenvolveremos uma aplicação extremamente simples, que imprime no terminal uma frase quando um botão é pressionado.
Para iniciar, crie uma pasta para salvar os arquivos da aplicação, e selecione-a como a pasta corrente. Após, abra o GUIDE e crie uma nova figura como abaixo, adicionando apenas um botão na janela. É possível modificar o nome do botão, manipulando as propriedades através do inspetor de propriedades .
Perceba agora uma diferença fundamental: O objeto botão adicionado possui nome (Tag) pushbutton1, enquanto o texto apresentado ao usuário na aplicação é dado pela propriedade String do objeto. Modifique algumas propriedades utilizando o inspetor de propriedades.
Após terminar a figura, salve-a com o nome teste.fig.
Perceba que ao salvar a figura, um código foi automaticamente gerado em um arquivo.m com o nome da figura (no caso, teste.m é gerado na pasta corrente). Esse código (é uma função) será responsável por criar o objeto figura além de executar as funções de resposta aos eventos disparados pelos objetos da figura, que nesse caso é apenas um botão. Verifique isso clicando na ferramenta Object Browser .
No código gerado, atente para:
% --- Executes on button press in pushbutton1.
function pushbutton1_Callback(hObject, eventdata, handles) % hObject handle to pushbutton1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA)
Trata-se da função de callback do evento associado ao ato de pressionar o botão na interface criada. O nome da função é gerado automaticamente, e sempre terá o nome do objeto seguido pela palavra Callback separadas pelo caractere underline. Existem outras callbacks associadas aos objetos que possuem outros nomes, como veremos adiante.
Os argumentos da função é que possibilitam a manipulação de toda a interface gráfica. • hObject é uma referência objeto fonte da interrupção. No caso da função
apresentada acima, hObject aponta para o botão pushbutton1. • eventdata trás informações sobre o evento acionado.
• handles é uma estrutura que contém referências a todos os objetos utilizados na figura. Isso obviamente permite alteração das propriedades de qualquer objeto em tempo de execução. O usuário pode adicionar outras variáveis à estrutura handles, o que permite que callbacks diferentes tenham acesso à
Rode, agora, a aplicação, chamando a função na janela de comando ou pressionando no GUIDE. Pressione o botão na janela que abre e verifique o que acontece.
MANIPULANDO GRÁFICOS NO GUIDE
Nosso segundo exemplo será um pouco mais complexo. Crie a interface abaixo:Neste exemplo, utilizando botões, um ponto no gráfico será movimentado uma unidade.
Note que é empregado um objeto axes. Este objeto é utilizado para traçar curvas ou apresentar figuras e texto. É um objeto complexo, porém muito poderoso. Os quatro botões serão utilizados para alterar, em tempo real, a posição de um ponto no gráfico.
Lembre-se de dar nomes sugestivos aos tags dos botões para facilmente referenciá-los no código.
Salve o arquivo. Após salvo o *.fig, o arquivo fonte (*.m) automaticamente será aberto.
Note que o arquivo criado contém muitas funções, porém, apenas a função de abertura OpeningFcn e as funções associadas aos botões serão manipuladas. A função de abertura é
preparar a interface para que esta fique pronta para interação com o usuário. Neste exemplo, o gráfico e coordenadas iniciais de um ponto serão atribuídas nessa função. Para fazer isso, um código semelhante ao apresentado na seqüência pode ser utilizado.
function move_point_OpeningFcn(hObject, eventdata, handles, varargin) handles.x = 0;
handles.y = 0; % Coordenadas iniciais
handles.curva = plot(handles.axes1, handles.x, handles.y,'s'); set(handles.curva,'LineWidth',3,'MarkerSize',12);
set(handles.axes1, 'XLim',[-10 10],'YLim',[-10 10]) handles.output = hObject; % Já fazia parte da função guidata(hObject, handles); % Já fazia parte da função
Salienta-se que, no ambiente GUIDE, utiliza-se uma estrutura (handles) para que informações sejam passadas entre as funções de callback. Esse foi o motivo de criar as coordenadas associadas a esta estrutura. Essa estrutura contém também ponteiros para todos os objetos inseridos no contexto da interface, como botões, gráficos, sliders e outros.
A função guidata é utilizada para atualizar a estrutura de dados ou para obter esta estrutura em funções que não estejam associadas à interface.
Para implementar a funcionalidade dos botões, basta atualizar as coordenadas no gráfico manipulando as propriedades da curva ou mesmo plotando um novo gráfico (esta última técnica não é indicada uma vez que é muito lenta).
Abaixo a implementação de um dos quatro botões:
function pbcima_Callback(hObject, eventdata, handles) handles.y = handles.y+1; % Atualiza coordenada
set(handles.curva,'YData',handles.y); % Atualiza coord no gráfico guidata(hObject, handles); % Atualiza handles no escopo da interface
Um código pratcamente1 igual a esse pode ser utilizado para cada botão. Faça você
mesmo, meu querido padawan.
USANDO TIMERS
Muito embora o programa funcione, ele está longe de ter uso prático em aplicações mais avançadas. Quando se deseja atualizar uma curva em tempo real, por exemplo, muitos dados são processados, e as curvas devem ser atualizadas continuamente. O uso de laços dentro de callbacks de botões, por exemplo, é desaconselhado, pois enquanto o programa1 Referência ao saudoso José Canjica Martins.
Perceba que a função funcaozinha é executada de segundo em segundo. Entre as chamadas da função, qualquer atividade pode ser realizada (o programa fica livre para atender a outras callbacks, por exemplo). Dessa maneira, é fundamental o uso de timers para tornar as aplicações interativas.
Continuaremos com o exemplo dos botões, porém dessa vez fazendo com que o ponto seja aleatoriamente deslocado a cada meio segundo. Para tanto, adicione mais dois botões à interface:
Na função de abertura, o timer deve ser programado (mas não inicializado):
function move_point_OpeningFcn(hObject, eventdata, handles, varargin) handles.x = 0;
handles.y = 0; % Coordenadas iniciais
handles.curva = plot(handles.axes1, handles.x, handles.y,'s'); set(handles.curva,'LineWidth',3,'MarkerSize',12);
set(handles.axes1, 'XLim',[-10 10],'YLim',[-10 10]); handles.t1 = timer('ExecutionMode','FixedRate',...
'Period',1,... 'TimerFcn',@callback_timert1,... 'UserData',hObject); handles.output = hObject; guidata(hObject, handles);
Perceba a linha onde o timer é criado:
handles.t1 = timer('ExecutionMode','FixedRate',...
'Period',0.5,...
'TimerFcn',@callback_timert1,...
'UserData',hObject);
Mais informações sobre as propriedades do objeto timer podem ser encontradas na documentação, como os possíveis modos de execução. No que tange à essa oficina e ao uso do GUIDE, porém, é importante notar que há uma propriedade 'UserData', que neste caso, é setada com um ponteiro para a figura (hObject)3. Isso é fundamental, uma vez que a callback
do timer não está associada à interface (ela será implementada no mesmo arquivo, todavia). No final do arquivo *.m, crie uma a callback do timer:
function callback_timert1(hObject, eventdata)
hUi = get(hObject,'UserData'); % Obtém ponteiro da interface handles = guidata(hUi); % Obtém estrutura handles da interface handles.x = handles.x + 3*round(rand()) - 1;
handles.y = handles.y + 3*round(rand()) - 1;
set(handles.curva,'XData',handles.x,'YData',handles.y); % Atualiza coord no gráfico
guidata(hUi, handles); % Atualiza handles no escopo da interface
Para iniciar ou parar o avanço aleatório do ponto, basta iniciar ou parar o timer nas callbacks dos botões iniciar e parar:
function pbligar_Callback(hObject, eventdata, handles) start(handles.t1);
% --- Executes on button press in pbdesligar.
function pbdesligar_Callback(hObject, eventdata, handles) stop(handles.t1);
MANIPULANDO GRÁFICOS COMPLEXOS EM TEMPO REAL
Com uma boa noção sobre o uso do GUIDE e da implementação de rotinas em callbacks utilizando timers, é possível agora aplicar esses conhecimentos para coleta de dados e apresentação de informação em tempo real. Como um primeiro exemplo, um osciloscópio de áudio será desenvolvido.Crie a interface da figura abaixo:
3 hObject é uma referência para objetos diferentes em funções diferentes. Nas callbaks de botões, ela aponta para
o botão que gerou o evento. Porém na função de abertura ela aponta para a própria figura.
function oscilo_supimpa_OpeningFcn(hObject, eventdata, handles, varargin) handles.control = 0; handles.fs = 22050; handles.spf = 1024; handles.tempo = (0:1023)/handles.fs; handles.data = zeros(size(handles.tempo)); handles.curvas = plot(handles.axes1,handles.tempo,handles.data); set(handles.axes1,'XLim',[0 1023/handles.fs],'YLim',[-1 1]); handles.ARec = dsp.AudioRecorder(... 'DeviceName','Default',... 'SampleRate',handles.fs, ... 'NumChannels',1, ... 'BufferSizeSource','Property',... 'BufferSize',handles.spf, ... 'QueueDuration',0.0, ... 'SamplesPerFrame', handles.spf); handles.timer01 = timer(... 'ExecutionMode','fixedRate',... 'Period',0.01+handles.spf/handles.fs,... 'TimerFcn',@callback_timer,... 'UserData',hObject); handles.output = hObject; guidata(hObject, handles);
A callback do timer deve ser desenvolvida como segue. Lembre-se que o GUIDE não cria automaticamente esta função. Ela deve ser inserida “manualemnte” pelo desenvolvedor no arquivo *.m.
function callback_timer(hObject,eventdata) hUi = get(hObject,'UserData');
handles = guidata(hUi); aux = step(handles.ARec);
set(handles.curvas,'YData',aux);
Nesta interface, apenas um botão será utilizado, e o estado deste será alterado ao ser pressionado. A callback associada ao botão é apresentada abaixo.
function botao_Callback(hObject, eventdata, handles)
if ~handles.control handles.control = 1; start(handles.timer01); set(hObject,'String','OFF');
else
handles.control = 0;
set(hObject,'String','ON'); stop(handles.timer01);
end
guidata(hObject,handles);