A Interface



next up previous contents
Next: Fim Up: Aplicações Interactivas em Smalltalk/V Previous: O Exemplo

A Interface

 

Depois de desenvolvida a aplicação, é tempo de desenvolver a interface. Normalmente, uma aplicação será composta por uma classe relativa à aplicação (o Modelo, neste caso EstacaoServico) e uma classe que definirá a interface. A esta última vamos chamar ESView (ver apêndice H).

ESView é a classe da janela principal da nossa aplicação. Para ser janela principal de uma aplicação deverá ser subclasse de ViewManager. Uma janela típica em Smalltalk é composta por uma série de subpanes, cada uma das subpanes poderá ser de um tipo diferente, texto, uma lista, um botão, etcgif. Por convenção, as subclasses de ViewManager definem o método open ou openOn: para a inicialização das instâncias. Neste caso usamos open. O método começa por criar uma instancia de EstacaoServico e indicar que a label da janela deverá ser "E. S. Boa Viagem"gif. De seguida, são criadas as subpanes que constituem a janela.

Vamos definir seis subpanes, organizadas numa matriz de 23 (ver figura 1). As panes da primeira linha são utilizadas como títulos das panes da segunda linha, que contêm as matrículas dos carros que estão em cada uma das listas de espera.

  
Figure 1: Janela Principal

Começando pelo canto superior esquerdo, definimos então uma pane da classe StaticText (texto que não pode ser editado):

    subpane1 := StaticText new.
    subpane1 owner: self.
    subpane1 centered.
    subpane1 contents: 'Alinhamento'.
    subpane1 framingRatio: (0@0 extent: (1/3)@(1/10)).
    self addSubpane: subpane1.

Com

subpane1 owner: self
declaramos o dono da pane, neste caso a própria janelagif. Se nada for dito, o dono da pane é automaticamente self; por uma questão de convenção e de bom estilo de programação, é aconselhável declara-lo sempre explicitamente.

Com

subpane1 framingRatio: (0@0 extent: (1/3)@(1/10))
declaramos o tamanho da pane em coordenadas relativas, indicando o ponto superior esquerdo e o ponto inferior direito. Neste caso, a pane começa em 0@0 e vai até 1/3 da extensão horizontal e 1/10 da extensão vertical. Deste modo, se as dimensões da janela forem alteradas as dimensões da subpane ajustam-se automaticamente.

Finalmente, com

self addSubpane: subpane1
a pane é adicionada à janela.

De seguida vamos definir a lista relativa ao alinhamento:

    subpane2 :=  ListBox new
                   owner: self;
                   when: #getContents perform: #listAlinhamento:;
                   when: #select perform: #infoA:;
                   framingRatio: (0@(1/10) extent: (1/3)@(9/10)).
    self addSubpane: subpane2.
Neste caso trata-se de uma ListBox. Aqui, há que chamar a atenção para o método when:perform:: este método permite declarar que quando (when) um dado evento ocorre a pane deve executar (perform) um dado métodogif, esses métodos recebem como parâmetro a pane em que o evento ocorreu. O evento getContents é utilizado para obter o contéudo da lista, o evento select corresponde a selecção de um elemento da lista. Estamos, então, a declarar que o método que calcula a lista a apresentar na pane é listAlinhamento: e que quando é selccionado um elemento da lista deve ser utilizado o método infoA:, estes métodos são métodos de instância da classe ESView uma vez que é essa a classe do dono da pane (cf. método owner:). O método infoA: cria uma janela de diálogo com informação relativa ao carro cuja matrícula foi seleccionada (as janelas de diálogo serão apresentadas mais à frente).

De seguida são adicionadas as subpanes restantes. Apenas a título de exemplo, uma das panes é declarada com um dono diferente de self:

    self addSubpane:
        (   ListBox new
              owner: (estacao caixa);
              when: #getContents perform: #listCaixa:;
              when: #select perform: #info:;
              framingRatio: ((2/3)@(1/10) extent: (1/3)@(9/10))
        ).
Esta pane é declarada com dono (estacao caixa) (a caixa da estação de serviço), deste modo os métodos listCaixa: e info: deverão ser métodos da classe Caixagif.

Finalmente, é enviada a self a mensagem openWindow e adicionado um menu à janela. O menu é criado pela método menuES:

menuES
        "Menu da ES"
    | menu |
    menu := Menu labels: ('&Chega Carro\Regista &Lavagem\Regista 
&Alinhamento\Regista &Pagamento' withCrs
                         )
                 lines: #(1 3)
                 selectors: #(chegaCarro regLav regAl regCaixa).
    menu owner: self;
         title: '&Gestao'.
    ^menu!
O método labels:lines:selectors: cria um menu. Em labels indicam-se os nomes das opções, o & indica o caracter que servirá de atalho para a opção. Em lines indicam-se as posições de linhas separadoras no menu. Em selectors lista-se, para cada opção, o método que deverá ser utilizado quando a opção é seleccionada.

O método chegaCarro:

chegaCarro
        "chegar um carro"
    | car  |

    car := LeCarro new ler.
    ( car = nil ) ifFalse: [
                estacao chegaCarro: car.
                self changed:#listAlinhamento:.
                self changed:#listLavagem:]
começa por criar um automóvel e, de seguida, coloca-o na estação de serviço. Como a chegada de um carro à estação pode alterar as listas de espera de alinhamento e de lavagem, é enviada a self (a janela) a mensagem changed com listAlinhamento (para actualizar a lista de alinhamentos) e com listLavagem (para actualizar a lista de lavagens)gif.

Para criar um automóvel vamos utilizar uma janela de diálogo para ler as informações relativas ao mesmo (ver fig. 2).

  
Figure 2: Janela de Diálogo

Para tal criou-se a classe LeCarro (ver apêndice I) como subclasse de WindowDialog.

O método ler é enviado a uma instância desta classe e cria as subpanes necessárias para a leitura da informação relativa ao automóvel. São utilizadas panes de tipo EntryField para leitura de valores:

   self addSubpane:
        (EntryField new
            setName: #matricula;
            contents: '';
            framingBlock:
              [:box | (box leftTop rightAndDown: 40@lineHeight)
                                extentFromLeftTop: 90@lineHeight ]
        ).
A mensagem setName: indica um nome para a pane e permite depois "ir buscar" o seu valor (ver utilização do método query: em ok:). As coordenadas são, agora, dadas de modo diferente: neste caso são dadas de modo absoluto e não relativo, utilizando framingBlock:. A partir de uma caixa que é passada como parâmetro (box)gif é definida a caixa que a pane deverá ocupar:

Para a indicação do estado do automóvel são utilizadas check boxes:

    self addSubpane:
        (CheckBox new
            owner: self;
            contents: 'para Lavagem';
            when: #clicked perform: #setLavagem:;
            framingBlock:
              [:box | (box leftTop rightAndDown: 5@(8*lineHeight))
                                  extentFromLeftTop: 90@lineHeight ]
        ).
O método deverá ser de fácil interpretação.

Nota-se ainda a utilização de botões:

    self addSubpane:
        (Button new
            defaultPushButton;
            contents: 'OK';
            idOK;
            when: #clicked perform: #ok:;
            framingBlock:
              [:box | (box leftTop rightAndDown: (17@10)*charSize)
                                  extentFromLeftTop: (8@2)*charSize]
        ).
A mensagem defaultPushButton serve para indicar que este é o botão selccionado por defeito. A mensagem idOK serve para indicar que este botão deverá ser activado quando o utilizador prime Enter, existe também a mensagem idCancel que faz com que o botão seja activado quando o utilizador carrega em Escape e que é utilizada no botão Cancel.



next up previous contents
Next: Fim Up: Aplicações Interactivas em Smalltalk/V Previous: O Exemplo



Jose Franscisco Creissac Campos
Wed Jan 31 12:57:29 MET 1996