Sign in Join
Wiki copytemporary

Howdy World (Hello World on steroids)/pt: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11Este capítulo é um tutorial para Lazarus. Ele explica os primeiros passos para obter um software funcional e também algumas das melhores práticas. O resultado final (esperado) é duplo: o leitor entende os conceitos básicos para construir um software com Lazarus e obtém um verdadeiro programa funcional. Uma calculadora é algo razoavelmente fácil de implementar e todo mundo entende seus conceitos, sem necessidade de explicações de antemão. Esta calculadora é limitada a cálculos com números inteiros, mas pode facilmente ser estendida.
22
33Este tutorial não descreve o processo de instalação do Lazarus. Ele assume que o Lazarus está instalado e pronto para uso (preferivelmente a última versão estável, que no momento é a 0.9.30 com FreePascal 2.4.2). Este tutorial é mais ou menos independente de plataforma. Todos os screenshots foram feitos em Windows XP, portanto com o esquema de cores azul/vermelho/cinza.
44
55Leitura recomendada antes de começar: uma introdução abrangente da [Lazarus IDE](http://wiki.lazarus.freepascal.org/Lazarus_Tutorial).
66
77## Vamos começar
88
99É melhor criar um diretório separado para cada projeto. Portanto, vamos criar um diretório para salvar o projeto da nossa Calculadora. Este diretório será a raiz de todos os arquivos criados para o projeto.
1010
1111- Criar um novo diretório para o projeto de demonstração (digamos, \$HOME/CalculatorDemo).
1212
1313(*Texto que descreve ações que precisam ser executadas é marcado, como a linha anterior*)
1414
1515Com isto resolvido, é hora de iniciar um novo projeto Lazarus.
1616
1717- Inicie o Lazarus.
1818- Escolha Projeto/Novo Projeto… do menu.
1919- Na caixa de diálogo que é apresentada selecione Aplicação e pressione OK (if the IDE reclama sobre modificações não salvas, pressione Não).
2020
2121Para ter certeza de que todos os arquivos do nosso novo projeto estarão no diretório certo, o projeto precisa ser salvo primeiro.
2222
2323- Do menu, escolha Arquivo/Salvar Tudo.
2424- Selecione o diretório que foi criado previamente (\$HOME/CalculatorDemo).
2525- Insira um novo nome de projeto: CalculatorDemo.
2626- Pressione Salvar (ou o que corresponder a isto em seu idioma).
2727- A IDE quer salvar o formulário principal também: insira ’’ufrmMain’’ como o nome da unidade do formulário.
2828- A IDE pergunta se o nome do arquivo deve ser modificado para minúsculas. Confirme isto pressionando o botão 'Renomear para minúsculas'.
2929
3030Em teoria, o programa que é assim criado é um software válido que pode ser executado. Antes de compilá-lo pela primeira vez, duas modificações são recomendadas: atribuição de novos locais (diretórios) para as unidades de destino e nome do arquivo compilado.
3131
3232- Do menu, escolha Projeto/Opções de Projeto.
3333- Selecione Opções do Compilador/Caminhos (clique no nó da TreeView).
3434- Insira o texto ‘‘bin\\’‘ antes do nome de arquivo alvo (ou ‘‘bin/’‘ ´para ambientes *nix).
3535- Note também o prefixo ‘‘lib’‘ para ‘diretório de saída das unidades’ (não o modifique).
3636- Pressione OK.
3737
3838A imagem abaixo mostra como isto ficaria em uma máquina Windows. ![](/wiki-images/e/eb/Tutcal_project_options.png)
3939
4040Fazendo isto, a pasta do projeto não será desordenada com a saída gerada pelo compilador. Os arquivos da pasta do projeto são necessárias para construir o programa. Tudo nos diretórios ’’lib’’ e ’’bin’’ podem ser eliminados quando do arquivamento do projeto.
4141
4242Agora, como um primeiro teste, o projeto pode ser compilado e executado.
4343
4444- Pressione F9 (o atalho para compilar, construir e executar o programa). Se tudo sair bem, uma tela em branco será mostrada. Agora sabemos que foi criada uma base sólida sobre a qual podemos começar a construir o programa.
4545- Finalize a execução clicando no ícone Fechar (isto depende do OS).
4646
4747## O primeiro componente
4848
4949O Lazarus tem uma assim chamada 'paleta de componentes':
5050
5151![](/wiki-images/d/d4/Tutcal_component_palette.png)
5252
5353Todos os componentes que estão disponíveis para construir uma interface do usuário estão logicamente agrupados nas abas de componentes. O número real de abas depende dos pacotes instalados, mas a paleta básica se parece com a imagem acima. A primeira aba é a Standard, seguida pela Aditional, Common controls, etc.
5454
5555Para retornar o nome do tipo de um componente, passe o mouse sobre o ícone do componente e um aviso será mostrado (mais precisamente, o nome de classe do componente). Para uma explanação sobre todos os controles, veja esta [Lazarus introduction](http://wiki.lazarus.freepascal.org/Lazarus_Tutorial)
5656
5757A primeira coisa que é preciso ser feita para o programa de Calculadora é criar uma área de display, onde estão os números. Para isto, um controle TEdit é usado.
5858
59-`# Nota: os componentes visuais são referidos como Controles. `\
60-`  Para o nosso propósito, a diferença entre um "componente" e um "controle" não é relevante.`
59+```text
60+# Nota: os componentes visuais são referidos como Controles.
61+ Para o nosso propósito, a diferença entre um "componente" e um "controle" não é relevante.
62+```
6163
6264Para colocar um controle no formulário, este precisa ter o foco. Pressionar F12 no teclado muda o foco do editor de formulários para o editor de código e vice-versa.
6365
6466- Pressione F12 uma vez ou duas para posicionar a janela do formulário no topo (veja imagem abaixo).
6567
6668![](/wiki-images/e/ed/Tutcal_form_on_top.png)
6769
6870- Selecione a aba Standard na palete de componentes. Esta é a aba que é selecionada por padrão.
6971- Clique sobre o componente TEdit (passe o mouse sobre os ícones de componentes para obter os nomes de classe).
7072- Clique em algum lugar no formulário. Isto colocará um controle TEdit no formulário com um nome que é o mesmo nome da classe do componente sem a letra "T" e com um número no final (por exemplo, Edit1). Quando um segunto TEdit for colocado no formulário, será nomeado como Edit2, e assim por diante. Isto se aplica a todos os componentes.
7173
7274Agora que o controle está colocado no formulário, ele pode ser personalizado como você precisar. Esta personalização tem lugar no Inspetor de Objetos. Esta é a janela à esquerda da tela, com a lista de propriedades para o controle TEdit que são personalizáveis. ![](/wiki-images/5/59/Tutcal_object_inspector.png)
7375
7476As propriedades determinam a aparência e o comportamento do controle. É fácil modificá-las: apenas clique em uma delas, insira um novo valor e pressione Enter. O efeito no controle é imediatamente visível. As propriedades no Inspetor de Objetos são todas propriedades para um único controle, particularmente o controle que tem o foco ou está selecionado. Portanto, a maneira de modificar propriedades de um controle é primeiramente selecioná-lo e então fazer as modificações no Inspetor de Objetos. Se nenhum controle for selecionado, as propriedades do formulário serão mostradas.
7577
7678Faça as seguintes modificações nas propriedades de Edit1:
7779
7880- Mude o nome para *edDisplay*.
7981- Mude Align para *alTop* (não confundir com Alignment).
8082- Mude Alignment para *alRightJustify* (não confundir com Align).
8183- Mude BorderSpacing.Around para *6*.
8284- Mude Text para *0* (o número zero, não a letra "O").
8385
8486Estas propriedades são bastante autoexplicativas, especialmente quando as modificações acima são feitas e o efeito é monitorado no formulário.
8587
8688Como um toque final, a fonte que é usada para mostrar textos em todos os controles será mudada. A fonte pode ser mudada de duas maneiras: na propriedade Font de edDisplay ou na mesma propriedade do formulário. Mudando a fonte do formulário tem o benefício de que todos os componentes anteriormente criados 'herdarão' a nova fonte, e é aí que nós faremos a nossa modificação.
8789
8890- Clique em algum lugar do formulário. Isto desselecionará o controle edDisplay e selecionará o próprio formulário. As propriedades do formulário são agora mostradas no Inspetor de Objetos.
8991- No Inspetor de Objetos, clique na propriedade Font. A linha da propriedade Font será realçada e um botão com três pontos será mostrado.
9092- Clique no botão com três pontos. Isto abre a caixa de diálogo Fonts.
9193- Selecione Verdana, Bold, Tamanho 10.
9294- Pressione OK.
9395
9496O título do formulário não é muito significativo. O padrão é 'Form1'.
9597
9698- No Inspetor de Objetos, clique na propriedade Caption.
9799- Mude o texto para *Calculator Demo*.
98100
99101O resultado de todas estas ações será um formulário que se parece com este: ![](/wiki-images/e/e0/Tutcal_form_with_tedit.png)
100102
101103Agora é uma boa hora para salvar o formulário.
102104
103105- Selecione do menu Arquivo/Salvar ou pressione a combinação de teclas Ctrl+S.
104106
105107Lembre-se de salvá-lo e o salve com frequência!
106108
107109## Botões
108110
109111Para que serve uma calculadora se não puderem ser inseridos números? Portanto o próximo passo será adicionar botões para os dígitos 0..9. Antes de posicionar os botões para os dígitos, um assim chamado "contêiner" é colocado no formulário. Um TPanel é outro contêiner que podem ser colocados no formulário. Contêineres podem ser colocados dentro de outros, mas isto é para outro tutorial.
110112
111113- Na paleta de componentes, selecione um controle TPanel.
112114- Clique no formulário para criar o painel. Um novo painel está agora visível no formulário com largura, altura e rótulo padronizados. Note que a fonte do painel é herdade do formulário. Este painel será usado para agrupar todos os botões, então algumas propriedades devem ser modificadas.
113115- Remova o texto *Panel1* da propriedade Caption.
114116- Modifique Align par a *alClient*.
115117- Modifique Borderspacing.Around para *6*.
116118- Aumente a largura (width) do formulário para *300* (primeiro clique no formulário na TreeView do Inspetor de Objetos).
117119- Aumente a altura (height) do formulário para *350*.
118120
119121(Aumentando o tamanho do formulário nos dará algum espaço para mover os controles.)
120122
121123Agora os botões dos dígitos serão adicionados.
122124
123125- Na paleta de componentes, selecione um controle TButton.
124126- Clique no formulário para posicioná-lo. O botão é colocado no painel. Isto é visível no Inspetor de Objetos: Button1 é chamado *filho* (child) de Panel1. Isto efetivamente significa que, se o painel for movido, todos os controles filhos serão movidos, e quando Panel1 for eliminado, todos os controles-filhos o serão também.
125127- No Inspetor de Objetos, modifique o valor de Caption para *0* (o número zero).
126128- Modifique a largura (width) para *32*.
127129- Modifique a altura (height) para *30*.
128130- Modifique o nome (name) para *btnDigit0*.
129131
130132Isto precisa ser repetido para os dígitos 1..9. A forma mais rápida de fazê-lo é copiando e colando o botão já criado.
131133
132134- Clique com o botão direito do mouse no botão do dígito *0* e selecione Copiar do menu popup.
133135- Clique com o botão direito do mouse em algum lugar de Panel1 e selelcione Colar. Um segundo botão é adicionado ao painel. A única diferença do primeiro botão é o nome *btnDigit1*.
134136- Modifique a propriedade Caption (rótulo) no Inspetor de Objetos para *1*.
135137- Repita a ação de Colar mais oito vezes para os botões seguintes.
136138- Mova os botões no formulário para obter um layout que se pareça com uma calculadora:
137139
138140![](/wiki-images/c/cd/Tutcal_calculator_digits.png)
139141
140142Os próximos são os quatro botões para operações matemáticas (soma, subtração, etc).
141143
142144- Coloque um novo TButton no formulário.
143145- Modifique a largura para *32*.
144146- Modifique a altura para *30*.
145147- Modifique a propriedade Caption para *+*.
146148- Modifique o nome para *btnFunction*.
147149- Copie e Cole este botão mais três vezes, modificando o rótulo (caption) para *-*, *** e */*.
148150- Alinhe os botões verticalmente, ao lado dos botões dos dígitos.
149151
150152E os botões especiais:
151153
152154- Copie e Cole o botão *+*.
153155- Modifique o rótulo para *+/-*
154156- Modifique o nome para *btnPlusMinus*.
155157- Posicione o botão bem embaixo do botão *3*.
156158- Copie e Cole o botão *+*.
157159- Modifique o rótulo para *C*.
158160- Modifique o nome para *btnClear*.
159161- Posicione o botão à direita do botão *+*.
160162- Copie e Cole o botão *+*.
161163- Modifique o rótulo para *=*.
162164- Modifique o nome para *btnCalculate*.
163165- Posicione o botão à direita do botão */*.
164166
165167Redimensione o formulário para que se ajuste aos botões.
166168
167169Isto deve resultar em algo assim:
168170
169171![](/wiki-images/c/c2/Tutcal_calculator_gui_ready.png)
170172
171173Lembre-se de salvar e salve com frequência!
172174
173175## Respondendo a eventos
174176
175177Uma aplicação GUI muitas vezes é uma aplicação baseada em eventos. Isto significa que a aplicação não faz nada até que digamos a ela o que fazer. Por exemplo, inserindo dados através do teclado, pressionando botões, etc. Uma das coisas que a calculadora precisa fazer é responder a cliques do mouse nos botões 0..9. É nisto que a IDE nos ajuda. Acrescentar um *manipulador de eventos* para um botão é fácil.
176178
177179- Certifique-se de que o formulário com todos os botões está visível (se necessário, pressione F12 uma ou duas vezes).
178180- Dê um duplo-clique no botão com o rótulo *0*: a IDE automaticamente cria um procedimento que manipulará eventos de cliques do mouse: procedure TForm1.btnDigit0Click(Sender :TObject);
179181- Digite o seguinte código entre as palavras-chave *begin* e *end*:
180182
181183``` pascal
182184edDisplay.Text := edDisplay.Text + '0'
183185```
184186
185187edtDisplay é uma caixa de edição. A forma de modificar o conteúdo de uma caixa de edição é modificando a propriedade *Text*. A declaração acima acrescenta o número '0' ao texto e isto é visível na tela imediatamente.
186188
187189- Dê um duplo-clique no botão com o rótulo *1*.
188190- Digite o seguinte código entre as palavras-chave *begin* e *end*:
189191
190192``` pascal
191193edDisplay.Text := edDisplay.Text + '1'
192194```
193195
194196É uma boa ideia compilar o código a intervalos para verificar erros de sintaxe.
195197
196198- Selecione do menu: Executar/Executar (ou pressione F9). O projeto é compilado e a aplicação se inicia.
197199- Clique nos dígitos 0 e 1 algumas vezes e veja o que acontece na tela. Os outros botões ainda não fazem nada obviamente porque os manipuladores de eventos ainda não foram acrescentados.
198200- Pare o programa de Calculadora.
199201
200202É claro que agora é fácil acrescentar manipuladores de eventos para os dígitos 2..9, mas isto vai resultar em muito código redundante. Cada manipulador de eventos faz exatamente a mesma coisa, apenas os dígitos são diferentes. A forma de eliminar esta redundãncia é criar um procedimento que faça o processamento e acrescentar uma variável (os dígitos 0..9) como parâmetro.
201203
202204- No editor, localize as linhas em que a classe TForm1 é declarada. Ele vai se parecer com isto:
203205
204206``` pascal
205207 { TForm1 }
206208
207209 TForm1 = class(TForm)
208210 btnDigit0: TButton;
209211 btnDigit1: TButton;
210212 btnDigit2: TButton;
211213 btnDigit3: TButton;
212214 btnDigit4: TButton;
213215 btnDigit5: TButton;
214216 btnDigit6: TButton;
215217 btnDigit7: TButton;
216218 btnDigit8: TButton;
217219 btnDigit9: TButton;
218220 btnFunction: TButton;
219221 btnFunction1: TButton;
220222 btnFunction2: TButton;
221223 btnFunction3: TButton;
222224 btnCalculate: TButton;
223225 btnPlusMinus: TButton;
224226 btnClear: TButton;
225227 edDisplay: TEdit;
226228 Panel1: TPanel;
227229 procedure btnDigit0Click(Sender: TObject);
228230 procedure btnDigit1Click(Sender: TObject);
229231 private
230232 { private declarations }
231233 public
232234 { public declarations }
233235 end;
234236```
235237
236238Note que, na definição de classe acima, todos os controles e procedimentos declarados foram adicionados ao formulário automaticamente. ***Não faça qualquer modificação manual aí!!*** A IDE vai ficar nervosa que você o fizer. O lugar para acrescentar variáveis, procedientos e funções personalizadas são as seções *private* e *public*. Como regra geral, variáveis personalizadas são adicionadas à seção *private*. Procedimentos e funções são acrescentadas à seção *private* também, exceto quando outros formulários precisam chamar estes procedimentos ou funções (o que deve acontecer com frequência).
237239
238240De volta para o nosso problema de dígitos: precisamos de um procedimento que acrescente dígitos ao display em que os dígitos possam variar.
239241
240242- Acrescente o procedimento AddDigit à seção *private* do formulário.
241243
242244``` pascal
243245 private
244246 { private declarations }
245247 procedure AddDigit(const pDigit: byte);
246248 public
247249 { public declarations }
248250 end;
249251```
250252
251253Em seguida, o código efetivo para o procedimento AddDigit precisa ser inserido. Novamente a IDE vem em nosso auxílio:
252254
253255- Posicione o caret na linha AddDigit.
254256- Pressione a combinação de teclas Ctrl+Shift+C.
255257
256258A IDE gerará a definição deste novo método e posicionará o cursor de texto dentro do procedimento vazio. Podemos imediatamente começar a digitar.
257259
258260- Acrescente o seguinte código ao procedimento AddDigit:
259261
260262``` pascal
261263edDisplay.Text := edDisplay.Text + IntToStr(pDigit)
262264```
263265
264266Note que a função *IntToStr()* traduz o valor numérico, o digito, para um valor de sequência de caracteres de modo que possa ser mostrado no display.
265267
266268E para usar este procedimento para todos os dígitos:
267269
268270- Abra o formulário (se necessário, pressione F12 uma ou duas vezes).
269271- Dê um duplo-clique no botão do dígito '0': isto abrirá o editor no manipulador de eventos para o botão 0.
270272- Substituia o código no manipulador de eventos por: AddDigit(0). O procedimento se parecerá com isto:
271273
272274``` pascal
273275procedure TForm1.btnDigit0Click(Sender: TObject);
274276begin
275277 AddDigit(0)
276278end;
277279```
278280
279281- Faça o mesmo para o botão do dígito 1:
280282
281283``` pascal
282284procedure TForm1.btnDigit1Click(Sender: TObject);
283285begin
284286 AddDigit(1)
285287end;
286288```
287289
288290Antes de completar esta sequência para todos os outros botões de dígitos, certifique-se de que ele realmente faz o que queremos.
289291
290292- Execute o programa (pressione F9).
291293- Clique nos botões 0 e 1 algumas vezes e veja se ele realmente funciona.
292294- Finalize o programa.
293295
294296*Um bom programador é um programador preguiçoso*
295297
296298Agora podemos acrescentar manipuladores de eventos para os botões 2..9. Mas agora novamente temos o mesmo problema de criar código redundante: AddDigit(2), AddDigit(3), etc. E se houvesse uma maneira de contar os botões separadamente? Felizmente existe.
297299
298300Todos os componentes têm uma propriedade de valor inteiro chamada *Tag* (lembre-se: um controle é apenas um tipo especial de componente que herda esta propriedade *Tag*. É uma propriedade que não tem uma função verdadeira. Está lá para usarmos como acharmos melhor. E se cada botão pudesse ter seu valor de dígito armazenado na propriedade *Tag*...
299301
300302- Abra o formulário (se necessário, pressione F12 uma ou duas vezes).
301303- Selecione o botão do dígito 1 (clique nele uma vez).
302304- No Inspetor de Objetos, localize a propriedade *Tag* e modifique seu valor par *1*.
303305- Selecione o botão do dígito 2 (clique nele uma vez).
304306- No Inspetor de Objetos, localize a propriedade *Tag* e modifique seu valor para *2*.
305307- Repita isto para os botões dos dígitos 3..9.
306308
307309Agora todos os botões de dígitos têm um valor de Tag único. Não modificamos o botão do dígito 0 porque o valor padrão de Tag já é 0. Agora, para fazer uso deste valor de Tag:
308310
309311- Abra o formulário.
310312- Dê um duplo-clique no botão do dígito 0 (isto abrirá o editor no manipulador de eventos para o botão do dígito 0).
311313- Note que o procedimento *btnDigit0Click* tem um parâmetro *Sender* de tipo *TObject*.
312314
313315``` pascal
314316procedure TForm1.btnDigit0Click(Sender: TObject);
315317```
316318
317319O parâmetro *Sender* é na verdade uma referência ao botão que foi pressionado. Através de conversão de tipo, podemos usar o parâmetro como se fosse um TButton.
318320
319321- Modifique o código assim:
320322
321323``` pascal
322324procedure TForm1.btnDigit0Click(Sender: TObject);
323325begin
324326 AddDigit(TButton(Sender).Tag)
325327end;
326328```
327329
328330Não parece que tenhamos feito muito, substituindo uma linha de código por outra. No entanto, Se tivéssemos de adicionar código para os outros dígitos (1..9), ele ficaria exatamente igual. Assim, todos os outros dígitos podem reusar este método!
329331
330332Vamos olhar melhor para o Inspetor de Objetos.
331333
332334- Abra o formulário.
333335- Selecione o botão do dígito 0 (btnDigit0).
334336- No Inspetor de Objetos, selecione a aba Events (veja abaixo).
335337
336338![](/wiki-images/1/12/Tutcal_oi_events.png)
337339
338340No Inspetor de Objetos, podemos não somente mudar as propriedades do controle, mas também os manipuladores de eventos. No Inspetor de Objetos podemos ver que, tão logo o botão do dígito 0 for clicado, o método *btnDigit0Click* seá chamado (este é o manipulador de eventos *OnClick*).
339341
340342- No formulário: selecione o botão do dígito 2. No Inspetor de Objetos podemos agora ver que não há qualquer manipulador de eventos OnClick para o botão 2.
341343- Abra a lista dropdown para o evento OnClick e selecione *btnDigit0Click*.
342344- Faça o mesmo para todos os outros botões de dígitos 3..9.
343345
344346Now all buttons share one common procedure that does exactly what we want except for digit button 1. Remember that for digit button 1 we created an event handler *btnDigit1Click* that we don't really need anymore.
345347
346348- On the form: select digit button 1. In the Object Inspector we can now see that the event handler indeed is *btnDigit1Click*.
347349- Open the drop down list for the OnClick event and select btnDigit0Click.
348350
349351Now all buttons share the same event handler to add digits to the display box.
350352
351353- Run the program (press F9).
352354- Click all buttons 0..9 a couple of times and see that it actually works.
353355- End the program.
354356
355357For the digit buttons one final thing needs to be done: event handler *btnDigit1Click* still exists but is not used anymore and should be deleted. The safest way to do this (for now) is to let the IDE handle it. For this to work the option *Auto remove empty methods* must be enabled.
356358
357359- In the menu select Environment/Options.
358360- Click on Editor/Completion and Hints.
359361- Enable the option 'Auto remove empty methods' (see image below).
360362- Press OK.
361363
362364![](/wiki-images/4/4c/Tutcal_auto_remove.png)
363365
364366From now on all empty methods are automatically deleted as soon as the source file is saved.
365367
366368- Locate method *procedure TForm1.btnDigit1Click(Sender: TObject)*
367369- Delete the line 'AddDigit(1)'
368370- Save the file (press Ctrl-S or select from the menu File/Save). The now empty procedure is automatically deleted.
369371
370372## Tuning
371373
372374So far so good: the calculator program compiles, runs and responds to clicks on digit buttons 0..9. But it doesn't behave exactly as we want: there's always this annoying zero at the beginning of the number and the number of allowed digits is too large.
373375
374376### Removing leading zeroes
375377
376378Normally an integer number that's bigger (or smaller) than zero does not start with a leading zero. So our calculator should compensate for this. Luckily this is easy to implement. Remember that we have one and only one method that handles the addition of digits: AddDigit. This is the place to add logic to suppress the leading zero.
377379
378380- Locate method *procedure TForm1.AddDigit(const pDigit: byte)*
379381- Change the code to:
380382
381383``` pascal
382384procedure TForm1.AddDigit(const pDigit: byte);
383385begin
384386 // Suppress leading zeroes when adding digits
385387 if edDisplay.Text = '0' then
386388 edDisplay.Text := IntToStr(pDigit)
387389 else
388390 edDisplay.Text := edDisplay.Text + IntToStr(pDigit)
389391end;
390392```
391393
392394### Limit the number of digits
393395
394396This demo calculator cannot handle numbers that are too large. So we need to limit the number of digits that can be entered. Again the logical place to do this is the AddDigit method:
395397
396398- Locate method *procedure TForm1.AddDigit(const pDigit: byte)*
397399- Change the code to:
398400
399401``` pascal
400402procedure TForm1.AddDigit(const pDigit: byte);
401403begin
402404 // Limit the number of digits
403405 if length(edDisplay.Text) < 8 then
404406 begin
405407 // Suppress leading zeroes when adding digits
406408 if edDisplay.Text = '0' then
407409 edDisplay.Text := IntToStr(pDigit)
408410 else
409411 edDisplay.Text := edDisplay.Text + IntToStr(pDigit)
410412 end;
411413end;
412414```
413415
414416## Operations
415417
416418Now it's time to look at the actual operations we want to implement: adding, subtracting, multiplying and dividing. The way the user is going to use the calculator goes something like this:
417419
4184201. The user enters a number
4194212. The user presses a function button (e.g. '+')
4204223. The user enters a second number
4214234. The user presses '='
4224245. The program responds with the addition of the first and second number
423425
424426As soon as the user presses the '=' button the program has to know three things:
425427
4264281. What was the operation?
4274292. What was the first number?
4284303. What was the second number?
429431
430432### What was the operation?
431433
432434Somehow we need to register that a certain operation type was selected. And for that we need to know what operation types are available. One way to implement this is to create an enumeration type (or user defined scalar type) that contains all valid operations.
433435
434436- In the Source Editor, find the location where the form is declared.
435437- Just above the form add the type declaration *TOperationType* for all supported operations:
436438
437439``` pascal
438440 type
439441
440442 { All supported operations for our calculator }
441443 TOperationType = (otNone, otPlus, otMinus, otMultiply, otDivide);
442444
443445 { TForm1 }
444446
445447 TForm1 = class(TForm)
446448```
447449
448450- To temporarily store the operation, a variable is added to the private section of the form's class declaration.
449451
450452``` pascal
451453 private
452454 { private declarations }
453455 SelectedOperation: TOperationType;
454456 procedure AddDigit(const pDigit: byte);
455457```
456458
457459### What was the first number?
458460
459461To temporarily store the first number that was entered we need a variable.
460462
461463- Add a variable to the form's private section.
462464
463465``` pascal
464466 private
465467 { private declarations }
466468 SelectedOperation: TOperationType;
467469 FirstNumber: longint;
468470 procedure AddDigit(const pDigit: byte);
469471```
470472
471473### What was the second number?
472474
473475- Add variable SecondNumber to the form's private section (the same as FirstNumber) and make it a longint as well.
474476
475477## What's on your mind
476478
477479Now it's time to implement the actions for the function buttons: add, subtract, multiply and divide. Let's start with the '+' button. As mentioned in one of the previous sections: when the '+' button is pressed, we need to store the first number and the operation type. So that's what we are going to do.
478480
479481- Open the form.
480482- Double click on button '+'. The IDE creates an event handler and the text cursor is placed inside the empty begin/end block.
481483
482484Hold on! Before adding all the code here and doing the same for the three other operation buttons, remember what was said when the digit buttons 0..9 were added: do not add redundant code! It's reasonable to assume that the code for the other three operation buttons will be the same as for the '+' button, except for the operation itself. So let's save ourselves some work.
483485
484486- Locate the form's private section and add a new procedure: StoreOperation.
485487
486488``` pascal
487489 private
488490 { private declarations }
489491 SelectedOperation: TOperationType;
490492 FirstNumber: longint;
491493 SecondNumber: longint;
492494 procedure AddDigit(const pDigit: byte);
493495 procedure StoreOperation(const pOperation: TOperationType);
494496```
495497
496498- Place the blinking text cursor on the newly inserted line.
497499- Press Ctrl-Shift-C (you know the drill by now…).
498500
499501The StoreOperation procedure must do two things: store the operation and store the number that was entered in the display, so it can be used afterwards.
500502
501503- Add the following code to the newly created procedure:
502504
503505``` pascal
504506procedure TForm1.StoreOperation(const pOperation: TOperationType);
505507begin
506508 // Store the operation so it can be used later
507509 SelectedOperation := pOperation;
508510
509511 // Store the number that was entered by the user
510512 FirstNumber := StrToInt(edDisplay.Text);
511513end;
512514```
513515
514516Ok, now back to the '+' button:
515517
516518- Open the form.
517519- Double click on button '+'.
518520- Add the following code: StoreOperation(otPlus);
519521- Open the form.
520522- Double click on button '-'.
521523- Add the following code: StoreOperation(otMinus);
522524- Open the form.
523525- Double click on button '*'.
524526- Add the following code: StoreOperation(otMultiply);
525527- Open the form.
526528- Double click on button '/'.
527529- Add the following code: StoreOperation(otDivide);
528530
529531Now test the program and see what happens:
530532
531533- Run the program (press F9)
532534- Enter a number (e.g. 21267)
533535- Press the '+' button
534536- Enter the second number (e.g. 31170)
535537
536538What happens is that after pressing the '+' button, or any other operation button for that matter, the display is not cleared. We must find a way to make sure that the display is cleared, as soon as an operation button is pressed. Because we have used procedures to group similar actions, this again is an easy change.
537539
538540- Locate the form's private section and add a new variable: doClearDisplay. This variable will be the trigger for erasing the display.
539541
540542``` pascal
541543 private
542544 { private declarations }
543545 SelectedOperation: TOperationType;
544546 FirstNumber: longint;
545547 SecondNumber: longint;
546548 doClearDisplay: boolean;
547549 procedure AddDigit(const pDigit: byte);
548550 procedure StoreOperation(const pOperation: TOperationType);
549551```
550552
551553- Position the blinking text cursor on the line StoreOperation.
552554- Press Ctrl-Shift-CursorDown: this moves the cursor to the body of the StoreOperation procedure.
553555- In the StoreOperation procedure we must update the doClearDisplay variable, so we know that the display must be cleared as soon as a new number is entered. Change the code to this:
554556
555557``` pascal
556558procedure TForm1.StoreOperation(const pOperation: TOperationType);
557559begin
558560 // Store the operation so it can be used later
559561 SelectedOperation := pOperation;
560562
561563 // Store the number that was entered by the user
562564 FirstNumber := StrToInt(edDisplay.Text);
563565
564566 // The display must start with a new number
565567 doClearDisplay := true;
566568end;
567569```
568570
569571- And where better to clear the display than in the AddDigit procedure? Change the code to:
570572
571573``` pascal
572574procedure TForm1.AddDigit(const pDigit: byte);
573575begin
574576 // Must the display be cleared first?
575577 if doClearDisplay then
576578 begin
577579 edDisplay.Text := '0'; // Reset the display to zero
578580 doClearDisplay := false; // Once is enough...
579581 end;
580582
581583 // Limit the number of digits
582584 if length(edDisplay.Text) < 8 then
583585 begin
584586 // Suppress leading zeroes when adding digits
585587 if edDisplay.Text = '0' then
586588 edDisplay.Text := IntToStr(pDigit)
587589 else
588590 edDisplay.Text := edDisplay.Text + IntToStr(pDigit)
589591 end;
590592end;
591593```
592594
593595Now let's see what happens:
594596
595597- Run the program (press F9).
596598- Enter a number.
597599- Press an operation button.
598600- Enter another number.
599601- End the program.
600602
601603We're almost there: we can enter numbers and select an operation. Now it's time to do the actual calculations.
602604
603605## Doing the math
604606
605607Now that we have stored a number and an operation, it's time to do the actual calculations. Let's start easy with additions.
606608
607609- Open the form.
608610- Double click on button '=' (the event handler is created).
609611- Change the procedure:
610612
611613``` pascal
612614procedure TForm1.btnCalculateClick(Sender: TObject);
613615var result: longint;
614616begin
615617 case SelectedOperation of
616618 otPlus: begin
617619 // Retrieve the second number
618620 SecondNumber := StrToInt(edDisplay.Text);
619621 // Do the math
620622 result := FirstNumber + SecondNumber;
621623 // Display the resulting value
622624 edDisplay.Text := IntToStr(result);
623625 end;
624626 end;
625627end;
626628```
627629
628630The above code is pretty straightforward: if the user presses the '+' button, the first and second number are added up and the result is stored in the edit control on screen.
629631
630632We now have a working calculator (that can only add two numbers):
631633
632634- Run the program (press F9).
633635- Enter a number.
634636- Press '+'
635637- Enter another number.
636638- Press '=' : the sum of the two numbers is displayed.
637639- End the program.
638640
639641We now could add the 3 missing operations ('-', '*' and '/'). But hold on, there is room for improvement. Let's have a look at the following statement:
640642
641643``` pascal
642644SecondNumber := StrToInt(edDisplay.Text);
643645```
644646
645647We have seen such a statement before: the StoreOperation procedure contains something similar:
646648
647649``` pascal
648650FirstNumber := StrToInt(edDisplay.Text);
649651```
650652
651653Calculating the number the user entered now happens in two different locations. We do not want to repeat ourselves so it's a good idea to create a new function that does the calculation.
652654
653655- Locate the form's private section and add a new function: GetDisplayValue.
654656
655657``` pascal
656658 private
657659 { private declarations }
658660 SelectedOperation: TOperationType;
659661 FirstNumber: longint;
660662 SecondNumber: longint;
661663 doClearDisplay: boolean;
662664 procedure AddDigit(const pDigit: byte);
663665 procedure StoreOperation(const pOperation: TOperationType);
664666 function GetDisplayValue: longint;
665667```
666668
667669- Position the blinking text cursor on the line GetDisplayValue.
668670- Press Ctrl-Shift-C.
669671- Add this code to extract the value that is in the display field:
670672
671673``` pascal
672674function TForm1.GetDisplayValue: longint;
673675begin
674676 result := StrToInt(edDisplay.Text);
675677end;
676678```
677679
678680- Locate the statement: FirstNumber := StrToInt(edDisplay.Text);
679681- Change it to: FirstNumber := GetDisplayValue;
680682
681683So much for refactoring, back to the calculations.
682684
683685- Locate the statement: SecondNumber := StrToInt(edDisplay.Text);
684686- Change it to: SecondNumber := GetDisplayValue;
685687
686688Now look at the following statements:
687689
688690``` pascal
689691 // Retrieve the second number
690692 SecondNumber := GetDisplayValue;
691693 // Do the math
692694 result := FirstNumber + SecondNumber;
693695```
694696
695697Variable *SecondNumber* is used to store the value in the display box. However it is not really necessary. The above code can be simplified:
696698
697699``` pascal
698700 // Do the math
699701 result := FirstNumber + GetDisplayValue;
700702```
701703
702704Now look at the following statements:
703705
704706``` pascal
705707 // Do the math
706708 result := FirstNumber + GetDisplayValue;
707709 // Display the resulting value
708710 edDisplay.Text := IntToStr(result);
709711```
710712
711713The same now applies to the *result* variable. The above code can be simplified to:
712714
713715``` pascal
714716 // Display the resulting value
715717 edDisplay.Text := IntToStr(FirstNumber + GetDisplayValue);
716718```
717719
718720Now we can add the 3 remaining operations.
719721
720722- Open the form.
721723- Double click on button '=' (the event handler is created).
722724- Change the code:
723725
724726``` pascal
725727procedure TForm1.btnCalculateClick(Sender: TObject);
726728begin
727729 case SelectedOperation of
728730 otPlus : edDisplay.Text := IntToStr(FirstNumber + GetDisplayValue);
729731 otMinus : edDisplay.Text := IntToStr(FirstNumber - GetDisplayValue);
730732 otMultiply : edDisplay.Text := IntToStr(FirstNumber * GetDisplayValue);
731733 otDivide : edDisplay.Text := IntToStr(FirstNumber div GetDisplayValue);
732734 end;
733735end;
734736```
735737
736738We now have a working calculator for all 4 operations:
737739
738740- Run the program (press F9).
739741- Enter a number.
740742- Press an operation button.
741743- Enter another number.
742744- Press '=' : the result is displayed.
743745- End the program.
744746
745747## Intermezzo
746748
747749After all this work we now have a useable calculator. But some things are not quite right. The obvious thing to try is dividing a number by zero. And the buttons 'C' and '+/-' don't work yet. These things will be addressed in the next section.
748750
749751First let's add another procedure, just for the fun of it (well, there's more to it of course:-)):
750752
751753- Locate the form's private section and add a new procedure: SetDisplayValue.
752754
753755``` pascal
754756 private
755757 { private declarations }
756758 SelectedOperation: TOperationType;
757759 FirstNumber: longint;
758760 SecondNumber: longint;
759761 doClearDisplay: boolean;
760762 procedure AddDigit(const pDigit: byte);
761763 procedure StoreOperation(const pOperation: TOperationType);
762764 function GetDisplayValue: longint;
763765 procedure SetDisplayValue(const pValue: longint);
764766```
765767
766768- Position the blinking text cursor on the line SetDisplayValue.
767769- Press Ctrl-Shift-C.
768770- Add the code below to give the display a new value, as the procedure name suggests:
769771
770772``` pascal
771773procedure TForm1.SetDisplayValue(const pValue: longint);
772774begin
773775 edDisplay.Text := IntToStr(pValue);
774776end;
775777```
776778
777779The above code should look pretty familiar. Have a look at the procedure that does all calculations:
778780
779781``` pascal
780782procedure TForm1.btnCalculateClick(Sender: TObject);
781783begin
782784 case SelectedOperation of
783785 otPlus : edDisplay.Text := IntToStr(FirstNumber + GetDisplayValue);
784786 otMinus : edDisplay.Text := IntToStr(FirstNumber - GetDisplayValue);
785787 otMultiply : edDisplay.Text := IntToStr(FirstNumber * GetDisplayValue);
786788 otDivide : edDisplay.Text := IntToStr(FirstNumber div GetDisplayValue);
787789 end;
788790end;
789791```
790792
791793This procedure can now be simplified.
792794
793795- Change the code to:
794796
795797``` pascal
796798procedure TForm1.btnCalculateClick(Sender: TObject);
797799begin
798800 case SelectedOperation of
799801 otPlus : SetDisplayValue(FirstNumber + GetDisplayValue);
800802 otMinus : SetDisplayValue(FirstNumber - GetDisplayValue);
801803 otMultiply : SetDisplayValue(FirstNumber * GetDisplayValue);
802804 otDivide : SetDisplayValue(FirstNumber div GetDisplayValue);
803805 end;
804806end;
805807```
806808
807809## Loose ends
808810
809811There's a couple of things that need to be done: implement the two loose buttons and make the calculator a bit more robust.
810812
811813### The +/- button
812814
813815- Open the form.
814816- Double click on button '+/-' (the event handler is created).
815817- Add the following code:
816818
817819``` pascal
818820procedure TForm1.btnPlusMinusClick(Sender: TObject);
819821begin
820822 SetDisplayValue(0 - GetDisplayValue);
821823end;
822824```
823825
824826See here the fruits of our labor. We have reused the procedure SetDisplayValue and GetDisplayValue to implement the negate function.
825827
826828Try it by running the program and press the '+/-' button.
827829
828830### The Clear button
829831
830832- Open the form.
831833- Double click on button 'C' (the event handler is created).
832834- Add the following code:
833835
834836``` pascal
835837procedure TForm1.btnClearClick(Sender: TObject);
836838begin
837839 SetDisplayValue(0); // Start from scratch
838840 SelectedOperation := otNone; // No operation selected yet
839841 doClearDisplay := false; // The display is already cleared
840842end;
841843```
842844
843845The above code is pretty self-explanatory: the display is cleared (the first line), we 'forget' about any pending operation by clearing the SelectedOperation variable and there is no need to clear the display when adding new digits, because it was already cleared by the first statement.
844846
845847### Divide by zero
846848
847849Let's go crazy: run the application and try to divide 10 by 0. This will make the application crash horribly. We have to make sure that this will not happen. The place to check this of course is the place where all calculations are performed (TForm1.btnCalculateClick).
848850
849851- Open the form.
850852- Double click on button '='.
851853- Update the otDivide section:
852854
853855``` pascal
854856procedure TForm1.btnCalculateClick(Sender: TObject);
855857begin
856858 case SelectedOperation of
857859 otPlus : SetDisplayValue(FirstNumber + GetDisplayValue);
858860 otMinus : SetDisplayValue(FirstNumber - GetDisplayValue);
859861 otMultiply : SetDisplayValue(FirstNumber * GetDisplayValue);
860862 otDivide : if GetDisplayValue <> 0 then
861863 SetDisplayValue(FirstNumber div GetDisplayValue)
862864 else
863865 begin
864866 // Display an error message to the user
865867 MessageDlg('It is not possible to divide a number by 0!', mtWarning, [mbCancel],0);
866868 // Reset the calculator to start with a clean slate
867869 btnClearClick(nil);
868870 end
869871 end;
870872end;
871873```
872874
873875Things noteworthy:
874876
8758771. *MessageDlg* is a function for displaying a short warning/error/informative message to the user. *ShowMessage* can also be used. It is a bit easier to use but cannot display warning/error icons.
8768782. *btnClearClick(nil)*: as soon as an error condition occurs we must reset the calculator. That's basically the same as pressing the 'C' button. We simulate pressing the 'C' button by directly calling the event handler.
877879
878880### Overflows
879881
880882- Start the calculator
881883- Enter 99999999 as the first number (8 digits)
882884- Press button '*'
883885- Enter 99999999 as the second number
884886- Press button '='
885887
886888Now two things could have happened:
887889
8888901. The application gives an incorrect result (something like 1674919425).
8898912. The application raises an error.
890892
891893If it's the first option then overflow errors have been disabled for the project. Enable them by ticking the Overflow option box:
892894
893895- Select from the menu Project/Project Options…
894896- Select in the treeview Compiler Options/Code Generation.
895897- Tick the box *Overflow (-Co)* (see image below).
896898- Click OK.
897899
898900![](/wiki-images/3/34/Tutcal_compiler_overflow.png)
899901
900902Now run the program again and see what happens with overflows.
901903
902904If it was the second (and expected) option then this means we have to add exception handling to our little calculator. Exception handling is used to prevent an application from crashing when errors occur. In our case we want to handle the arithmetic overflow error that might occur. Because all calculations are done in one place, it's easy to catch any overflow errors.
903905
904906- Open the form.
905907- Double click on button '='.
906908- Change the code in the procedure to this:
907909
908910``` pascal
909911procedure TForm1.btnCalculateClick(Sender: TObject);
910912begin
911913 // Catch any overflow errors
912914 try
913915 case SelectedOperation of
914916 otPlus : SetDisplayValue(FirstNumber + GetDisplayValue);
915917 otMinus : SetDisplayValue(FirstNumber - GetDisplayValue);
916918 otMultiply : SetDisplayValue(FirstNumber * GetDisplayValue);
917919 otDivide : if GetDisplayValue <> 0 then
918920 SetDisplayValue(FirstNumber div GetDisplayValue)
919921 else
920922 begin
921923 // Display an error message to the user
922924 MessageDlg('It is not possible to divide a number by 0!', mtWarning, [mbCancel],0);
923925 // Reset the calculator to start with a clean slate
924926 btnClearClick(nil);
925927 end
926928 end;
927929 except
928930 on E: EIntOverflow do
929931 begin
930932 // Display an error message
931933 MessageDlg('The result of the calculation was too big for the calculator to process!', mtWarning, [mbCancel], 0);
932934 // Reset the calculator
933935 btnClearClick(nil);
934936 end
935937 end
936938end;
937939```
938940
939941Note that even with the exception handling in place, the application will still halt when run from inside the IDE. However the application is only paused and still active. Press F9 to continue. This will display another pop up window, a debugger notification window. Press Continue. Now our own exception handler will kick in with the expected message. If you were to run the application stand alone (i.e. outside the IDE) then you would only see the message box that we added to the exception handler.
940942
941943## Final tweaks
942944
943945Remember that at a certain point a variable called *SecondNumber* was introduced. If you choose Run/Build All from the menu, then after building the application the compiler will display a message in the Messages window that this particular variable is never used. If you click on the message text the IDE opens the exact location in the editor where the variable is declared.
944946
945947- Click on message *Note: Private field "TForm1.SecondNumber" is never used*.
946948- Delete longint variable *SecondNumber*.
947949
948950As soon as our demo Calculator is started we see that the number 0 in the display has the focus (see image below).
949951
950952![](/wiki-images/4/46/Tutcal_calculator_focus.png)
951953
952954This is counter intuitive because a user should not be able to enter anything in that box. Even worse as soon as she would type something in there that is not a number, our calculator would crash! So it's better not to give access to the that control at all. We are going to fix that.
953955
954956- Open the form.
955957- Select the display control (click on edDisplay).
956958- In the Object Inspector change ReadOnly to *True* (if the Events tab is still visible, select the Properties tab first to gain access to all properties). From now on it's impossible to enter any data manually.
957959- Change TabStop to *False*. Now it's impossible to get to the display field via the keyboard and the display field will not be highlighted anymore.
958960
959961Run the program to see the result of these changes.
960962
961963(Under construction: OO-ify the program)
962964
963965---
964966*From the Free Pascal wiki, page "Howdy World (Hello World on steroids)/pt", as it stood on 2020-02-17 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history