MAC 441/5714 - Tópicos de Programação
Orientada a
Objetos
Aula 26 - 22/06/2004
Interfaces Gráficas em Smalltalk
- O conceito de interface gráfica e o conceito de sistemas
de janelas nasceram junto com o Smalltalk no laboratório Xerox
PARC (obviamente, influenciados por trabalhos mais simples que os
antecederam).
- A criação de interfaces gráficas pode ser
feita programaticamente (o que pode ser bem complicado,
- ou através do GUI Painter Tool que é uma ferramenta
excelente do Visual Works.
- Recomenda-se que aplicações gráficas
complexas se utilizem do MVC que vimos na aula passada devido às
suas inúmeras vantagens.
- No entanto, o uso do MVC não é obrigatório
em Smalltalk. Na verdade, o uso do MVC introduz uma boa dose de
complexidade extra que deve ser evitada se não for realmente
utilizada.
- Vamos primeiramente ver uma aplicação
bem simples que não usa o MVC e depois vamos sofistica-la um
pouquinho adicionando um View [e
um Controller].
antes de começar, uma nota sobre a classe Model.
Classe Model
- Como vimos na aula de dependências, todo objeto Smalltalk
pode participar do padrão Observer tanto como observador quando
como observado graças ao mecanismo de dependência.
- A classe Object implementa a lista de dependências
através de uma variável de classe que é
compartilhada por todos os objetos do sistema. Isso economiza
memória mas faz com que a busca pelos dependentes possa ser
muito demorada.
- Para resolver esse problema, criou-se a classe Model, subclasse de Object. A Model guarda as
dependências em uma variável de instância. Como
conseqüência, há um custo de espaço adicional
mas as buscas pelos dependentes ficam muito mais rápidos.
- Assim, recomenda-se que sempre que utilizemos o mecanismo de
dependência o façamos através de subclasses de Model. No entanto, se isso
não é possível (por exemplo, se queremos usar
classes já existentes como String ou SmallInteger então
podemos usar um ValueModel
que é um envólucro (wrapper)
que transforma um valor qualquer em um Model. O ValueModel mais simples
é um ValueHolder
que ilustramos a seguir.
- Exemplo:
- x := 5 asValue
- o x acima é um ValueHolder, para obter um SmallInteger
de volta a partir de x, basta fazermos
- x value
- podemos fazer isso com qualquer tipo de objeto
GUI Painter Tool
- O GUI Painter Tool permite criarmos interfaces gráfica
interativamente.
- A melhor forma de aprender é brincar um pouco com ele
(além de consultar a bibliografia listada no final desta aula).
- Inicialmente vamos fazer uma aplicação simples que,
informalmente falando, inclui a visão e o controlador dentro do
modelo. Depois vamos criar uma visão e um controlador
independentes.
- Por exemplo, vamos criar uma aplicação Super
Conversor como mostrado abaixo.
- Ao selecionarmos a opção Edit>>Install,
é gerada uma descrição
textual da nossa interface no método windowSpec da classe de
nossa
aplicação.
- Neste exemplo mais simples, a aplicação
ficará concentrada na classe ConversorGUI, subclasse de ApplicationModel.
- Ao selecionarmos a opção Edit>>Define, a
ferramenta irá criar, na nossa classe,
implementações para atributos e métodos
correspondentes ao modelo de dados de nossa aplicação (no
exemplo acima, criará atributos celsiusTemperature e fahrenheitTemperature e
respectivos métodos como visto abaixo).

- Como vimos acima, os Input
Field são convertidos em dados do modelo.
- Já os botões desencadeiam ações
(métodos no protocolo actions)
como visto abaixo:

- Neste instante, nossa aplicação já funciona:

Views
- Vamos complicar um pouquinho acrescentando agora um View independente para mostrar
graficamente a evolução das temperaturas (como se cada
nova temperatura convertida fosse na realidade a temperatura de um novo
dia).
- Para criar o View,
seguimos os seguintes passos:
- no GUI Painter Tool incluimos um ViewHolder na nossa
interface e o chamamos de graphView
(preenchendo o campo View)
- na classe ConversorGUI
criamos um atributo de instância chamado graphView criando também
um método de acesso.
- criamos também um atributo de instância enabled e respectivos
métodos de acesso que guardará um valor booleano
indicando se a classe está ou não pronta para ser
visualizada (na verdade, isso pode ser opcional dependendo do jeito que
seu código é organizado).
- criamos ainda um atributo de instância hist para guardar o
histórico das temperaturas convertidas.
- no método ConversorGUI>>initialize
escrevemos o seguinte:
- enabled := false.
hist := OrderedCollection new.
graphView := ConversorGraph new.
graphView model: self.
- Criamos agora a classe ConversorGraph
como sendo uma subclasse de UI.View
- Nesta classe devemos implementar 3 métodos:
- No protocolo controller
accessing:
defaultControllerClass
^NoController
- No protocolo updating:
update: anAspect with:
anObject
self
displayOn: (self graphicsContext)
- No protocolo displaying:
displayOn:
aGraphicsContext
"desenha as temperaturas"
| pt1 pt2 historico |
(self model enabled) ifFalse: [^self].
historico := self model hist.
pt1 := 0 @ (65 - (historico at: 1)).
2 to: (historico size) do:
[:cont |
pt2 := cont * 10 @ (65 - (historico at: cont)).
(LineSegment with: pt1 with: pt2) displayStrokedOn: aGraphicsContext.
pt1 := pt2 ]
- Agora só falta atualizar a classe ConversorGUI para
guardar o histórico e sinalizar a mudança do seu modelo
com self changed nos
pontos apropriados:
- ConversorGUI>>celsiusToFahrenheit
"convert the C temperature to F and add C to hist"
enabled := true.
fahrenheitTemperature value: celsiusTemperature
value *9/5 + 32.
hist add: celsiusTemperature value.
self changed.
- ConversorGUI>>fahrenheitToCelsius
"converts the F temperature to C"
| celsius |
celsius := (fahrenheitTemperature value - 32) * 5 /
9.
celsiusTemperature value: celsius.
hist add: celsius.
self changed
- Pronto, já temos uma aplicação com uma
visão associada (veja a figura abaixo). Agora podemos facilmente
acrescentar outras visões.

- As visões poderiam também estar dissociadas da
janela inicial da aplicação. Para tanto, as subclasses de
UI.View que
criássemos precisariam ser associadas a outras janelas (p.ex.
instâncias de ScheduledWindow).
Controllers
- Podemos sofisticar ainda mais nossa aplicação
associando um controlador ao último View de forma que, usando o
mouse, o usuário possa clicar no gráfico para entrar
novas temperaturas que seriam mostradas nos Input Fields tanto em C e em F.
- Mas isso é um pouco mais complicadinho e fica de
lição de casa :-)
Um exemplo completo: CustomView
- O pacote CustomView-Example
do VisualWorks vem com um exemplinho simples mas completo e
interessante de uma aplicação gráfica com 1
modelo, 1 visão e 1 controlador. Vale a pena dar uma bisoiada.
Referências
- Daniel D. Abdala e Aldo v. Wangenheim. Conhecendo o Smalltalk.
Capítulos 16 e 17.
- Parte 7, "Creating Graphical User Interfaces", do texto Introduction to Visual Works de
Ivan Tomek disponível na janela Welcome to VisualWorks do
VisualWorks.
- GUI Developer's Guide: Arquivo GUIDevGuide.pdf no
diretório doc da
distribuição do VisualWorks. (o da
distribuição é um pouco mais recente do que o do
link anterior).
- Tutorial
online sobre GUIs da Cincom
- Sobre o padrão MVC genérico: F. Buschmann, R.
Meunier, H. Rohnert, P.Sommerlad,
M. Stal Pattern-Oriented
Software Architecture - A System of Patterns. John Wiley and Sons
Ltd, Chichester, UK, 1996
Próxima Aula
Aula Anterior
Página de MAC 5714
Página do Fabio
Página do DCC