Difference between revisions of "Howdy World (Hello World on steroids)/pt"

From Lazarus wiki
Jump to navigationJump to search
m (Fixed syntax highlighting)
 
(9 intermediate revisions by 4 users not shown)
Line 1: Line 1:
 +
{{Howdy_World_(Hello_World_on_steroids)}}
 +
 
Este 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.
 
Este 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.
  
Line 155: Line 157:
 
* 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);
 
* 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);
 
* Digite o seguinte código entre as palavras-chave ''begin'' e ''end'':
 
* Digite o seguinte código entre as palavras-chave ''begin'' e ''end'':
<Delphi>edDisplay.Text := edDisplay.Text + '0'</Delphi>
+
<syntaxhighlight lang=pascal>edDisplay.Text := edDisplay.Text + '0'</syntaxhighlight>
 
edtDisplay é 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.
 
edtDisplay é 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.
 
* Dê um duplo-clique no botão com o rótulo ''1''.
 
* Dê um duplo-clique no botão com o rótulo ''1''.
 
* Digite o seguinte código entre as palavras-chave ''begin'' e ''end'':
 
* Digite o seguinte código entre as palavras-chave ''begin'' e ''end'':
<Delphi>edDisplay.Text := edDisplay.Text + '1'</Delphi>
+
<syntaxhighlight lang=pascal>edDisplay.Text := edDisplay.Text + '1'</syntaxhighlight>
  
 
É uma boa ideia compilar o código a intervalos para verificar erros de sintaxe.
 
É uma boa ideia compilar o código a intervalos para verificar erros de sintaxe.
Line 168: Line 170:
 
É 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.
 
É 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.
  
* In the editor locate the lines where class TForm1 is declared. It will look something like this:
+
* No editor, localize as linhas em que a classe TForm1 é declarada. Ele vai se parecer com isto:
<Delphi>  { TForm1 }
+
 
 +
<syntaxhighlight lang=pascal>  { TForm1 }
  
 
   TForm1 = class(TForm)
 
   TForm1 = class(TForm)
Line 197: Line 200:
 
   public
 
   public
 
     { public declarations }
 
     { public declarations }
   end; </Delphi>
+
   end; </syntaxhighlight>
  
Note that in the above class definition all controls and procedures are declared that we added to the form via 'pointing-and-clicking'. '''''Do not make any manual changes in there!!''''' The IDE will get nervous when you do. The place to add custom variables, procedures and functions is in the private or public sections. As a general rule all custom variables are added to the the private section. Procedures and functions are added to the private section as well, except when other forms need to call these procedures or functions (which shouldn't happen too often).
+
Note 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 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).
  
Back to our digits problem: we need a procedure that adds digits to the display, where the digits themselves can vary.
+
De 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.
* Add procedure AddDigit to the private section of the form.
+
 
<Delphi>  private
+
* Acrescente o procedimento AddDigit à seção ''private'' do formulário.
 +
<syntaxhighlight lang=pascal>  private
 
     { private declarations }
 
     { private declarations }
 
     procedure AddDigit(const pDigit: byte);
 
     procedure AddDigit(const pDigit: byte);
 
   public
 
   public
 
     { public declarations }
 
     { public declarations }
   end; </Delphi>
+
   end; </syntaxhighlight>
 +
 
 +
Em seguida, o código efetivo para o procedimento AddDigit precisa ser inserido. Novamente a IDE vem em nosso auxílio:
 +
* Posicione o caret na linha AddDigit.
 +
* Pressione a combinação de teclas Ctrl+Shift+C.
 +
A 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.
 +
 
 +
* Acrescente o seguinte código ao procedimento AddDigit:
 +
<syntaxhighlight lang=pascal>edDisplay.Text := edDisplay.Text + IntToStr(pDigit)</syntaxhighlight>
  
Next the actual code of the AddDigit procedure must be entered. Again the IDE comes to the rescue:
+
Note 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.
* Place the blinking text cursor on the AddDigit line.
 
* Press key combination Ctrl-Shift-C.
 
The IDE generates the definition of this new method and places the text cursor inside the empty body. We can immediately start typing.
 
* Add the following code to the AddDigit procedure body:
 
<Delphi>edDisplay.Text := edDisplay.Text + IntToStr(pDigit)</Delphi>
 
Note that function ''IntToStr()'' translates a numerical value, the digit, to a string value so it can be added to the display text.
 
  
Now to use this procedure for all digits 0..9:
+
E para usar este procedimento para todos os dígitos:
* Open the form (if necessary press F12 once or twice).
+
* Abra o formulário (se necessário, pressione F12 uma ou duas vezes).
* Double click on digit button 0: this will open the editor in the event handler for button 0.
+
* Dê um duplo-clique no botão do dígito '0': isto abrirá o editor no manipulador de eventos para o botão 0.
* Replace the code in the event handler with: AddDigit(0). The procedure will look like this:
+
* Substituia o código no manipulador de eventos por: AddDigit(0). O procedimento se parecerá com isto:
<Delphi>procedure TForm1.btnDigit0Click(Sender: TObject);
+
<syntaxhighlight lang=pascal>procedure TForm1.btnDigit0Click(Sender: TObject);
 
begin
 
begin
 
   AddDigit(0)
 
   AddDigit(0)
end;</Delphi>
+
end;</syntaxhighlight>
  
* Do the same for digit button 1:
+
* Faça o mesmo para o botão do dígito 1:
<Delphi>procedure TForm1.btnDigit1Click(Sender: TObject);
+
<syntaxhighlight lang=pascal>procedure TForm1.btnDigit1Click(Sender: TObject);
 
begin
 
begin
 
   AddDigit(1)
 
   AddDigit(1)
end;</Delphi>
+
end;</syntaxhighlight>
  
Before completing this sequence for all other digit buttons, make sure that this actually does what we want.
+
Antes de completar esta sequência para todos os outros botões de dígitos, certifique-se de que ele realmente faz o que queremos.
* Run the program (press F9).
+
* Execute o programa (pressione F9).
* Click buttons 0 and 1 a couple of times and see that it actually works.
+
* Clique nos botões 0 e 1 algumas vezes e veja se ele realmente funciona.
* End the program.
+
* Finalize o programa.
  
''A good programmer is a lazy programmer''
+
''Um bom programador é um programador preguiçoso''
  
Now we can add event handlers to digit buttons 2..9. But then again we have the same problem of creating a lot of redundant code: AddDigit(2), AddDigit(3) etc.
+
Agora 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.
What if there were a way to tell the buttons apart? Luckily there is.
+
E se houvesse uma maneira de contar os botões separadamente? Felizmente existe.
  
All components have an integer property called ''Tag'' (remember: a control is just a special kind of component that inherits this Tag property). It is a property that has no actual function. It is there for us to use as we see fit. Now what if each button would have it's digit value stored in the Tag property…
+
Todos 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''...
* Open the form (if necessary press F12 once or twice).
+
* Abra o formulário (se necessário, pressione F12 uma ou duas vezes).
* Select digit button 1 (click on it once).
+
* Selecione o botão do dígito 1 (clique nele uma vez).
* In the Object Inspector locate the Tag property and change it's value to ''1''.
+
* No Inspetor de Objetos, localize a propriedade ''Tag'' e modifique seu valor par ''1''.
* Select digit button 2 (click on it once).
+
* Selecione o botão do dígito 2 (clique nele uma vez).
* In the Object Inspector locate the Tag property and change it's value to ''2''.
+
* No Inspetor de Objetos, localize a propriedade ''Tag'' e modifique seu valor para ''2''.
* Repeat this for digit buttons 3..9.
+
* Repita isto para os botões dos dígitos 3..9.
  
Now all digit buttons have a unique Tag value. We didn't change digit button 0 because the default Tag value already is 0.
+
Agora 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.
Now to make use if this Tag value:
+
Agora, para fazer uso deste valor de Tag:
* Open the form.
+
* Abra o formulário.
* Double click on digit button 0 (this will open the editor in the event handler of digit button 0).  
+
* 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).
* Note that procedure ''btnDigit0Click'' has a parameter ''Sender '' of type ''TObject''.
+
* Note que o procedimento ''btnDigit0Click'' tem um parâmetro ''Sender'' de tipo ''TObject''.
<Delphi>procedure TForm1.btnDigit0Click(Sender: TObject);</Delphi>
+
<syntaxhighlight lang=pascal>procedure TForm1.btnDigit0Click(Sender: TObject);</syntaxhighlight>
The ''Sender '' parameter is actually a reference to the button that was pressed. Via typecasting we can use the parameter as if it was a TButton.  
+
O 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.
* Change the code like this:
+
* Modifique o código assim:
<Delphi>procedure TForm1.btnDigit0Click(Sender: TObject);
+
<syntaxhighlight lang=pascal>procedure TForm1.btnDigit0Click(Sender: TObject);
 
begin
 
begin
 
   AddDigit(TButton(Sender).Tag)
 
   AddDigit(TButton(Sender).Tag)
end;</Delphi>
+
end;</syntaxhighlight>
  
It doesn't look like we have accomplished much, replacing one line of code with another. However if we were to add the code for the other digits (1..9) it would look ''exactly'' the same. So all other digits can reuse this particular method!
+
Nã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!
  
Let's have a closer look at the Object Inspector.  
+
Vamos olhar melhor para o Inspetor de Objetos.  
* Open the form.
+
* Abra o formulário.
* Select digit button 0 (btnDigit0).
+
* Selecione o botão do dígito 0 (btnDigit0).
* In the Object Inspector select tab Events (see below).
+
* No Inspetor de Objetos, selecione a aba Events (veja abaixo).
  
  
Line 275: Line 281:
  
  
In the object inspector we can not only change the properties of a control but also the event handlers. In the Object Inspector we can see that as soon as digit button 0 is clicked, method ''btnDigit0Click '' is called (this is the ''OnClick'' event handler).
+
No 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'').
  
* On the form: select digit button 2. In the Object Inspector we can now see that there is no OnClick event handler for button 2.
+
* 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.
* Open the drop down list for the OnClick event and select ''btnDigit0Click''.
+
* Abra a lista dropdown para o evento OnClick e selecione ''btnDigit0Click''.
* Do this for all other digit buttons 3..9.
+
* Faça o mesmo para todos os outros botões de dígitos 3..9.
  
 
Now 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.
 
Now 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.
Line 314: Line 320:
 
* Locate method ''procedure TForm1.AddDigit(const pDigit: byte)''
 
* Locate method ''procedure TForm1.AddDigit(const pDigit: byte)''
 
* Change the code to:
 
* Change the code to:
<Delphi>procedure TForm1.AddDigit(const pDigit: byte);
+
<syntaxhighlight lang=pascal>procedure TForm1.AddDigit(const pDigit: byte);
 
begin
 
begin
 
   // Suppress leading zeroes when adding digits
 
   // Suppress leading zeroes when adding digits
Line 321: Line 327:
 
   else
 
   else
 
     edDisplay.Text := edDisplay.Text + IntToStr(pDigit)
 
     edDisplay.Text := edDisplay.Text + IntToStr(pDigit)
end;</Delphi>
+
end;</syntaxhighlight>
  
 
===Limit the number of digits===
 
===Limit the number of digits===
Line 327: Line 333:
 
* Locate method ''procedure TForm1.AddDigit(const pDigit: byte)''
 
* Locate method ''procedure TForm1.AddDigit(const pDigit: byte)''
 
* Change the code to:
 
* Change the code to:
<Delphi>procedure TForm1.AddDigit(const pDigit: byte);
+
<syntaxhighlight lang=pascal>procedure TForm1.AddDigit(const pDigit: byte);
 
begin
 
begin
 
   // Limit the number of digits
 
   // Limit the number of digits
Line 338: Line 344:
 
       edDisplay.Text := edDisplay.Text + IntToStr(pDigit)
 
       edDisplay.Text := edDisplay.Text + IntToStr(pDigit)
 
   end;
 
   end;
end; </Delphi>
+
end; </syntaxhighlight>
  
 
==Operations==
 
==Operations==
Line 357: Line 363:
 
* In the Source Editor, find the location where the form is declared.
 
* In the Source Editor, find the location where the form is declared.
 
* Just above the form add the type declaration ''TOperationType'' for all supported operations:
 
* Just above the form add the type declaration ''TOperationType'' for all supported operations:
<Delphi> type
+
<syntaxhighlight lang=pascal> type
  
 
   { All supported operations for our calculator }
 
   { All supported operations for our calculator }
Line 364: Line 370:
 
   { TForm1 }
 
   { TForm1 }
  
   TForm1 = class(TForm)</Delphi>
+
   TForm1 = class(TForm)</syntaxhighlight>
  
 
* To temporarily store the operation, a variable is added to the private section of the form's class declaration.
 
* To temporarily store the operation, a variable is added to the private section of the form's class declaration.
<Delphi>  private
+
<syntaxhighlight lang=pascal>  private
 
     { private declarations }
 
     { private declarations }
 
     SelectedOperation: TOperationType;
 
     SelectedOperation: TOperationType;
     procedure AddDigit(const pDigit: byte);</Delphi>
+
     procedure AddDigit(const pDigit: byte);</syntaxhighlight>
  
 
===What was the first number?===
 
===What was the first number?===
 
To temporarily store the first number that was entered we need a variable.
 
To temporarily store the first number that was entered we need a variable.
 
* Add a variable to the form's private section.
 
* Add a variable to the form's private section.
<Delphi>  private
+
<syntaxhighlight lang=pascal>  private
 
     { private declarations }
 
     { private declarations }
 
     SelectedOperation: TOperationType;
 
     SelectedOperation: TOperationType;
 
     FirstNumber: longint;
 
     FirstNumber: longint;
     procedure AddDigit(const pDigit: byte);</Delphi>
+
     procedure AddDigit(const pDigit: byte);</syntaxhighlight>
  
 
===What was the second number?===
 
===What was the second number?===
Line 391: Line 397:
 
Hold 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.
 
Hold 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.
 
* Locate the form's private section and add a new procedure: StoreOperation.
 
* Locate the form's private section and add a new procedure: StoreOperation.
<Delphi>  private
+
<syntaxhighlight lang=pascal>  private
 
     { private declarations }
 
     { private declarations }
 
     SelectedOperation: TOperationType;
 
     SelectedOperation: TOperationType;
Line 397: Line 403:
 
     SecondNumber: longint;
 
     SecondNumber: longint;
 
     procedure AddDigit(const pDigit: byte);
 
     procedure AddDigit(const pDigit: byte);
     procedure StoreOperation(const pOperation: TOperationType);</Delphi>
+
     procedure StoreOperation(const pOperation: TOperationType);</syntaxhighlight>
 
* Place the blinking text cursor on the newly inserted line.
 
* Place the blinking text cursor on the newly inserted line.
 
* Press Ctrl-Shift-C (you know the drill by now…).
 
* Press Ctrl-Shift-C (you know the drill by now…).
Line 403: Line 409:
 
The 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.
 
The 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.
 
* Add the following code to the newly created procedure:
 
* Add the following code to the newly created procedure:
<Delphi>procedure TForm1.StoreOperation(const pOperation: TOperationType);
+
<syntaxhighlight lang=pascal>procedure TForm1.StoreOperation(const pOperation: TOperationType);
 
begin
 
begin
 
   // Store the operation so it can be used later
 
   // Store the operation so it can be used later
Line 410: Line 416:
 
   // Store the number that was entered by the user
 
   // Store the number that was entered by the user
 
   FirstNumber := StrToInt(edDisplay.Text);
 
   FirstNumber := StrToInt(edDisplay.Text);
end;</Delphi>
+
end;</syntaxhighlight>
  
 
Ok, now back to the '+' button:
 
Ok, now back to the '+' button:
Line 434: Line 440:
 
What 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.
 
What 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.
 
* Locate the form's private section and add a new variable: doClearDisplay. This variable will be the trigger for erasing the display.
 
* Locate the form's private section and add a new variable: doClearDisplay. This variable will be the trigger for erasing the display.
<Delphi>  private
+
<syntaxhighlight lang=pascal>  private
 
     { private declarations }
 
     { private declarations }
 
     SelectedOperation: TOperationType;
 
     SelectedOperation: TOperationType;
Line 441: Line 447:
 
     doClearDisplay: boolean;
 
     doClearDisplay: boolean;
 
     procedure AddDigit(const pDigit: byte);
 
     procedure AddDigit(const pDigit: byte);
     procedure StoreOperation(const pOperation: TOperationType);</Delphi>
+
     procedure StoreOperation(const pOperation: TOperationType);</syntaxhighlight>
 
* Position the blinking text cursor on the line StoreOperation.
 
* Position the blinking text cursor on the line StoreOperation.
 
* Press Ctrl-Shift-CursorDown: this moves the cursor to the body of the StoreOperation procedure.
 
* Press Ctrl-Shift-CursorDown: this moves the cursor to the body of the StoreOperation procedure.
 
* 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:
 
* 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:
<Delphi>procedure TForm1.StoreOperation(const pOperation: TOperationType);
+
<syntaxhighlight lang=pascal>procedure TForm1.StoreOperation(const pOperation: TOperationType);
 
begin
 
begin
 
   // Store the operation so it can be used later
 
   // Store the operation so it can be used later
Line 455: Line 461:
 
   // The display must start with a new number
 
   // The display must start with a new number
 
   doClearDisplay := true;
 
   doClearDisplay := true;
end;</Delphi>
+
end;</syntaxhighlight>
 
* And where better to clear the display than in the AddDigit procedure? Change the code to:
 
* And where better to clear the display than in the AddDigit procedure? Change the code to:
<Delphi>procedure TForm1.AddDigit(const pDigit: byte);
+
<syntaxhighlight lang=pascal>procedure TForm1.AddDigit(const pDigit: byte);
 
begin
 
begin
 
   // Must the display be cleared first?
 
   // Must the display be cleared first?
Line 475: Line 481:
 
       edDisplay.Text := edDisplay.Text + IntToStr(pDigit)
 
       edDisplay.Text := edDisplay.Text + IntToStr(pDigit)
 
   end;
 
   end;
end;</Delphi>
+
end;</syntaxhighlight>
  
 
Now let's see what happens:
 
Now let's see what happens:
Line 492: Line 498:
 
* Double click on button '=' (the event handler is created).
 
* Double click on button '=' (the event handler is created).
 
* Change the procedure:
 
* Change the procedure:
<Delphi>procedure TForm1.btnCalculateClick(Sender: TObject);
+
<syntaxhighlight lang=pascal>procedure TForm1.btnCalculateClick(Sender: TObject);
 
var result: longint;
 
var result: longint;
 
begin
 
begin
Line 505: Line 511:
 
             end;
 
             end;
 
   end;
 
   end;
end;</Delphi>
+
end;</syntaxhighlight>
 
The 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.
 
The 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.
  
Line 517: Line 523:
  
 
We 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:
 
We 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:
<Delphi>SecondNumber := StrToInt(edDisplay.Text);</Delphi>
+
<syntaxhighlight lang=pascal>SecondNumber := StrToInt(edDisplay.Text);</syntaxhighlight>
 
We have seen such a statement before: the StoreOperation procedure contains something similar:
 
We have seen such a statement before: the StoreOperation procedure contains something similar:
<Delphi>FirstNumber := StrToInt(edDisplay.Text);</Delphi>
+
<syntaxhighlight lang=pascal>FirstNumber := StrToInt(edDisplay.Text);</syntaxhighlight>
 
Calculating 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.
 
Calculating 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.
  
 
* Locate the form's private section and add a new function: GetDisplayValue.
 
* Locate the form's private section and add a new function: GetDisplayValue.
<Delphi>  private
+
<syntaxhighlight lang=pascal>  private
 
     { private declarations }
 
     { private declarations }
 
     SelectedOperation: TOperationType;
 
     SelectedOperation: TOperationType;
Line 531: Line 537:
 
     procedure AddDigit(const pDigit: byte);
 
     procedure AddDigit(const pDigit: byte);
 
     procedure StoreOperation(const pOperation: TOperationType);
 
     procedure StoreOperation(const pOperation: TOperationType);
     function GetDisplayValue: longint;</Delphi>
+
     function GetDisplayValue: longint;</syntaxhighlight>
 
* Position the blinking text cursor on the line GetDisplayValue.
 
* Position the blinking text cursor on the line GetDisplayValue.
 
* Press Ctrl-Shift-C.
 
* Press Ctrl-Shift-C.
 
* Add this code to extract the value that is in the display field:
 
* Add this code to extract the value that is in the display field:
<Delphi>function TForm1.GetDisplayValue: longint;
+
<syntaxhighlight lang=pascal>function TForm1.GetDisplayValue: longint;
 
begin
 
begin
 
   result := StrToInt(edDisplay.Text);
 
   result := StrToInt(edDisplay.Text);
end;</Delphi>
+
end;</syntaxhighlight>
 
* Locate the statement: FirstNumber := StrToInt(edDisplay.Text);
 
* Locate the statement: FirstNumber := StrToInt(edDisplay.Text);
 
* Change it to: FirstNumber := GetDisplayValue;
 
* Change it to: FirstNumber := GetDisplayValue;
Line 546: Line 552:
 
* Change it to: SecondNumber := GetDisplayValue;
 
* Change it to: SecondNumber := GetDisplayValue;
 
Now look at the following statements:
 
Now look at the following statements:
<Delphi>              // Retrieve the second number
+
<syntaxhighlight lang=pascal>              // Retrieve the second number
 
               SecondNumber := GetDisplayValue;
 
               SecondNumber := GetDisplayValue;
 
               // Do the math
 
               // Do the math
               result := FirstNumber + SecondNumber;</Delphi>
+
               result := FirstNumber + SecondNumber;</syntaxhighlight>
 
Variable ''SecondNumber'' is used to store the value in the display box. However it is not really necessary. The above code can be simplified:
 
Variable ''SecondNumber'' is used to store the value in the display box. However it is not really necessary. The above code can be simplified:
<Delphi>              // Do the math
+
<syntaxhighlight lang=pascal>              // Do the math
               result := FirstNumber + GetDisplayValue;</Delphi>
+
               result := FirstNumber + GetDisplayValue;</syntaxhighlight>
  
 
Now look at the following statements:
 
Now look at the following statements:
<Delphi>              // Do the math
+
<syntaxhighlight lang=pascal>              // Do the math
 
               result := FirstNumber + GetDisplayValue;
 
               result := FirstNumber + GetDisplayValue;
 
               // Display the resulting value
 
               // Display the resulting value
               edDisplay.Text := IntToStr(result);</Delphi>
+
               edDisplay.Text := IntToStr(result);</syntaxhighlight>
 
The same now applies to the ''result'' variable. The above code can be simplified to:
 
The same now applies to the ''result'' variable. The above code can be simplified to:
<Delphi>              // Display the resulting value
+
<syntaxhighlight lang=pascal>              // Display the resulting value
               edDisplay.Text := IntToStr(FirstNumber + GetDisplayValue);</Delphi>
+
               edDisplay.Text := IntToStr(FirstNumber + GetDisplayValue);</syntaxhighlight>
  
 
Now we  can add the 3 remaining operations.  
 
Now we  can add the 3 remaining operations.  
Line 567: Line 573:
 
* Double click on button '=' (the event handler is created).
 
* Double click on button '=' (the event handler is created).
 
* Change the code:
 
* Change the code:
<Delphi>procedure TForm1.btnCalculateClick(Sender: TObject);
+
<syntaxhighlight lang=pascal>procedure TForm1.btnCalculateClick(Sender: TObject);
 
begin
 
begin
 
   case SelectedOperation of
 
   case SelectedOperation of
Line 575: Line 581:
 
     otDivide  : edDisplay.Text := IntToStr(FirstNumber div GetDisplayValue);
 
     otDivide  : edDisplay.Text := IntToStr(FirstNumber div GetDisplayValue);
 
   end;
 
   end;
end;</Delphi>
+
end;</syntaxhighlight>
  
 
We now have a working calculator for all 4 operations:
 
We now have a working calculator for all 4 operations:
Line 590: Line 596:
 
First let's add another procedure, just for the fun of it (well, there's more to it of course:-)):
 
First let's add another procedure, just for the fun of it (well, there's more to it of course:-)):
 
* Locate the form's private section and add a new procedure: SetDisplayValue.
 
* Locate the form's private section and add a new procedure: SetDisplayValue.
<Delphi>  private
+
<syntaxhighlight lang=pascal>  private
 
     { private declarations }
 
     { private declarations }
 
     SelectedOperation: TOperationType;
 
     SelectedOperation: TOperationType;
Line 599: Line 605:
 
     procedure StoreOperation(const pOperation: TOperationType);
 
     procedure StoreOperation(const pOperation: TOperationType);
 
     function GetDisplayValue: longint;
 
     function GetDisplayValue: longint;
     procedure SetDisplayValue(const pValue: longint);</Delphi>
+
     procedure SetDisplayValue(const pValue: longint);</syntaxhighlight>
 
* Position the blinking text cursor on the line SetDisplayValue.
 
* Position the blinking text cursor on the line SetDisplayValue.
 
* Press Ctrl-Shift-C.
 
* Press Ctrl-Shift-C.
 
* Add the code below to give the display a new value, as the procedure name suggests:
 
* Add the code below to give the display a new value, as the procedure name suggests:
<Delphi>procedure TForm1.SetDisplayValue(const pValue: longint);
+
<syntaxhighlight lang=pascal>procedure TForm1.SetDisplayValue(const pValue: longint);
 
begin
 
begin
 
   edDisplay.Text := IntToStr(pValue);
 
   edDisplay.Text := IntToStr(pValue);
end;</Delphi>
+
end;</syntaxhighlight>
  
 
The above code should look pretty familiar. Have a look at the procedure that does all calculations:
 
The above code should look pretty familiar. Have a look at the procedure that does all calculations:
<Delphi>procedure TForm1.btnCalculateClick(Sender: TObject);
+
<syntaxhighlight lang=pascal>procedure TForm1.btnCalculateClick(Sender: TObject);
 
begin
 
begin
 
   case SelectedOperation of
 
   case SelectedOperation of
Line 617: Line 623:
 
     otDivide  : edDisplay.Text := IntToStr(FirstNumber div GetDisplayValue);
 
     otDivide  : edDisplay.Text := IntToStr(FirstNumber div GetDisplayValue);
 
   end;
 
   end;
end;</Delphi>
+
end;</syntaxhighlight>
 
This procedure can now be simplified.
 
This procedure can now be simplified.
 
* Change the code to:
 
* Change the code to:
<Delphi>procedure TForm1.btnCalculateClick(Sender: TObject);
+
<syntaxhighlight lang=pascal>procedure TForm1.btnCalculateClick(Sender: TObject);
 
begin
 
begin
 
   case SelectedOperation of
 
   case SelectedOperation of
Line 628: Line 634:
 
     otDivide  : SetDisplayValue(FirstNumber div GetDisplayValue);
 
     otDivide  : SetDisplayValue(FirstNumber div GetDisplayValue);
 
   end;
 
   end;
end;</Delphi>
+
end;</syntaxhighlight>
  
 
==Loose ends==
 
==Loose ends==
Line 637: Line 643:
 
* Double click on button '+/-' (the event handler is created).
 
* Double click on button '+/-' (the event handler is created).
 
* Add the following code:
 
* Add the following code:
<Delphi>procedure TForm1.btnPlusMinusClick(Sender: TObject);
+
<syntaxhighlight lang=pascal>procedure TForm1.btnPlusMinusClick(Sender: TObject);
 
begin
 
begin
 
   SetDisplayValue(0 - GetDisplayValue);
 
   SetDisplayValue(0 - GetDisplayValue);
end;</Delphi>
+
end;</syntaxhighlight>
 
See here the fruits of our labor. We have reused the procedure SetDisplayValue and GetDisplayValue to implement the negate function.
 
See here the fruits of our labor. We have reused the procedure SetDisplayValue and GetDisplayValue to implement the negate function.
  
Line 649: Line 655:
 
* Double click on button 'C' (the event handler is created).
 
* Double click on button 'C' (the event handler is created).
 
* Add the following code:
 
* Add the following code:
<Delphi>procedure TForm1.btnClearClick(Sender: TObject);
+
<syntaxhighlight lang=pascal>procedure TForm1.btnClearClick(Sender: TObject);
 
begin
 
begin
 
   SetDisplayValue(0);          // Start from scratch
 
   SetDisplayValue(0);          // Start from scratch
 
   SelectedOperation := otNone; // No operation selected yet
 
   SelectedOperation := otNone; // No operation selected yet
 
   doClearDisplay := false;    // The display is already cleared
 
   doClearDisplay := false;    // The display is already cleared
end;</Delphi>
+
end;</syntaxhighlight>
 
The 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.
 
The 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.
  
Line 662: Line 668:
 
* Double click on button '='.
 
* Double click on button '='.
 
* Update the otDivide section:
 
* Update the otDivide section:
<Delphi>procedure TForm1.btnCalculateClick(Sender: TObject);
+
<syntaxhighlight lang=pascal>procedure TForm1.btnCalculateClick(Sender: TObject);
 
begin
 
begin
 
   case SelectedOperation of
 
   case SelectedOperation of
Line 678: Line 684:
 
                   end
 
                   end
 
   end;
 
   end;
end;</Delphi>
+
end;</syntaxhighlight>
  
 
Things noteworthy:
 
Things noteworthy:
Line 711: Line 717:
 
* Double click on button '='.
 
* Double click on button '='.
 
* Change the code in the procedure to this:
 
* Change the code in the procedure to this:
<Delphi>procedure TForm1.btnCalculateClick(Sender: TObject);
+
<syntaxhighlight lang=pascal>procedure TForm1.btnCalculateClick(Sender: TObject);
 
begin
 
begin
 
   // Catch any overflow errors
 
   // Catch any overflow errors
Line 738: Line 744:
 
       end
 
       end
 
   end
 
   end
end;</Delphi>
+
end;</syntaxhighlight>
  
 
Note 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.
 
Note 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.
Line 763: Line 769:
  
 
(Under construction: OO-ify the program)
 
(Under construction: OO-ify the program)
 
[[Category:Tutorials]]
 

Latest revision as of 07:47, 17 February 2020

English (en) suomi (fi) português (pt)

Este 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.

Este 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.

Leitura recomendada antes de começar: uma introdução abrangente da Lazarus IDE.

Vamos começar

É 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.

  • Criar um novo diretório para o projeto de demonstração (digamos, $HOME/CalculatorDemo).

(Texto que descreve ações que precisam ser executadas é marcado, como a linha anterior)

Com isto resolvido, é hora de iniciar um novo projeto Lazarus.

  • Inicie o Lazarus.
  • Escolha Projeto/Novo Projeto… do menu.
  • 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).

Para ter certeza de que todos os arquivos do nosso novo projeto estarão no diretório certo, o projeto precisa ser salvo primeiro.

  • Do menu, escolha Arquivo/Salvar Tudo.
  • Selecione o diretório que foi criado previamente ($HOME/CalculatorDemo).
  • Insira um novo nome de projeto: CalculatorDemo.
  • Pressione Salvar (ou o que corresponder a isto em seu idioma).
  • A IDE quer salvar o formulário principal também: insira ’’ufrmMain’’ como o nome da unidade do formulário.
  • A IDE pergunta se o nome do arquivo deve ser modificado para minúsculas. Confirme isto pressionando o botão 'Renomear para minúsculas'.

Em 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.

  • Do menu, escolha Projeto/Opções de Projeto.
  • Selecione Opções do Compilador/Caminhos (clique no nó da TreeView).
  • Insira o texto ‘‘bin\’‘ antes do nome de arquivo alvo (ou ‘‘bin/’‘ ´para ambientes *nix).
  • Note também o prefixo ‘‘lib’‘ para ‘diretório de saída das unidades’ (não o modifique).
  • Pressione OK.

A imagem abaixo mostra como isto ficaria em uma máquina Windows. tutcal project options.png

Fazendo 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.

Agora, como um primeiro teste, o projeto pode ser compilado e executado.

  • 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.
  • Finalize a execução clicando no ícone Fechar (isto depende do OS).

O primeiro componente

O Lazarus tem uma assim chamada 'paleta de componentes':

tutcal component palette.png

Todos 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.

Para 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

A 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.

# Nota: os componentes visuais são referidos como Controles. 
  Para o nosso propósito, a diferença entre um "componente" e um "controle" não é relevante.

Para 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.

  • Pressione F12 uma vez ou duas para posicionar a janela do formulário no topo (veja imagem abaixo).

tutcal form on top.png

  • Selecione a aba Standard na palete de componentes. Esta é a aba que é selecionada por padrão.
  • Clique sobre o componente TEdit (passe o mouse sobre os ícones de componentes para obter os nomes de classe).
  • 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.

Agora 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. tutcal object inspector.png

As 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.

Faça as seguintes modificações nas propriedades de Edit1:

  • Mude o nome para edDisplay.
  • Mude Align para alTop (não confundir com Alignment).
  • Mude Alignment para alRightJustify (não confundir com Align).
  • Mude BorderSpacing.Around para 6.
  • Mude Text para 0 (o número zero, não a letra "O").

Estas propriedades são bastante autoexplicativas, especialmente quando as modificações acima são feitas e o efeito é monitorado no formulário.

Como 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.

  • 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.
  • 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.
  • Clique no botão com três pontos. Isto abre a caixa de diálogo Fonts.
  • Selecione Verdana, Bold, Tamanho 10.
  • Pressione OK.

O título do formulário não é muito significativo. O padrão é 'Form1'.

  • No Inspetor de Objetos, clique na propriedade Caption.
  • Mude o texto para Calculator Demo.

O resultado de todas estas ações será um formulário que se parece com este: tutcal form with tedit.png

Agora é uma boa hora para salvar o formulário.

  • Selecione do menu Arquivo/Salvar ou pressione a combinação de teclas Ctrl+S.

Lembre-se de salvá-lo e o salve com frequência!

Botões

Para 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.

  • Na paleta de componentes, selecione um controle TPanel.
  • 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.
  • Remova o texto Panel1 da propriedade Caption.
  • Modifique Align par a alClient.
  • Modifique Borderspacing.Around para 6.
  • Aumente a largura (width) do formulário para 300 (primeiro clique no formulário na TreeView do Inspetor de Objetos).
  • Aumente a altura (height) do formulário para 350.

(Aumentando o tamanho do formulário nos dará algum espaço para mover os controles.)

Agora os botões dos dígitos serão adicionados.

  • Na paleta de componentes, selecione um controle TButton.
  • 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.
  • No Inspetor de Objetos, modifique o valor de Caption para 0 (o número zero).
  • Modifique a largura (width) para 32.
  • Modifique a altura (height) para 30 .
  • Modifique o nome (name) para btnDigit0.

Isto 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.

  • Clique com o botão direito do mouse no botão do dígito 0 e selecione Copiar do menu popup.
  • 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.
  • Modifique a propriedade Caption (rótulo) no Inspetor de Objetos para 1.
  • Repita a ação de Colar mais oito vezes para os botões seguintes.
  • Mova os botões no formulário para obter um layout que se pareça com uma calculadora:

tutcal calculator digits.png

Os próximos são os quatro botões para operações matemáticas (soma, subtração, etc).

  • Coloque um novo TButton no formulário.
  • Modifique a largura para 32.
  • Modifique a altura para 30.
  • Modifique a propriedade Caption para +.
  • Modifique o nome para btnFunction.
  • Copie e Cole este botão mais três vezes, modificando o rótulo (caption) para -, * e /.
  • Alinhe os botões verticalmente, ao lado dos botões dos dígitos.

E os botões especiais:

  • Copie e Cole o botão +.
  • Modifique o rótulo para +/-
  • Modifique o nome para btnPlusMinus.
  • Posicione o botão bem embaixo do botão 3.
  • Copie e Cole o botão +.
  • Modifique o rótulo para C.
  • Modifique o nome para btnClear.
  • Posicione o botão à direita do botão +.
  • Copie e Cole o botão +.
  • Modifique o rótulo para =.
  • Modifique o nome para btnCalculate.
  • Posicione o botão à direita do botão /.

Redimensione o formulário para que se ajuste aos botões.

Isto deve resultar em algo assim:

tutcal calculator gui ready.png

Lembre-se de salvar e salve com frequência!

Respondendo a eventos

Uma 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.

  • Certifique-se de que o formulário com todos os botões está visível (se necessário, pressione F12 uma ou duas vezes).
  • 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);
  • Digite o seguinte código entre as palavras-chave begin e end:
edDisplay.Text := edDisplay.Text + '0'

edtDisplay é 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.

  • Dê um duplo-clique no botão com o rótulo 1.
  • Digite o seguinte código entre as palavras-chave begin e end:
edDisplay.Text := edDisplay.Text + '1'

É uma boa ideia compilar o código a intervalos para verificar erros de sintaxe.

  • Selecione do menu: Executar/Executar (ou pressione F9). O projeto é compilado e a aplicação se inicia.
  • 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.
  • Pare o programa de Calculadora.

É 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.

  • No editor, localize as linhas em que a classe TForm1 é declarada. Ele vai se parecer com isto:
  { TForm1 }

  TForm1 = class(TForm)
    btnDigit0: TButton;
    btnDigit1: TButton;
    btnDigit2: TButton;
    btnDigit3: TButton;
    btnDigit4: TButton;
    btnDigit5: TButton;
    btnDigit6: TButton;
    btnDigit7: TButton;
    btnDigit8: TButton;
    btnDigit9: TButton;
    btnFunction: TButton;
    btnFunction1: TButton;
    btnFunction2: TButton;
    btnFunction3: TButton;
    btnCalculate: TButton;
    btnPlusMinus: TButton;
    btnClear: TButton;
    edDisplay: TEdit;
    Panel1: TPanel;
    procedure btnDigit0Click(Sender: TObject);
    procedure btnDigit1Click(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

Note 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).

De 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.

  • Acrescente o procedimento AddDigit à seção private do formulário.
  private
    { private declarations }
    procedure AddDigit(const pDigit: byte);
  public
    { public declarations }
  end;

Em seguida, o código efetivo para o procedimento AddDigit precisa ser inserido. Novamente a IDE vem em nosso auxílio:

  • Posicione o caret na linha AddDigit.
  • Pressione a combinação de teclas Ctrl+Shift+C.

A 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.

  • Acrescente o seguinte código ao procedimento AddDigit:
edDisplay.Text := edDisplay.Text + IntToStr(pDigit)

Note 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.

E para usar este procedimento para todos os dígitos:

  • Abra o formulário (se necessário, pressione F12 uma ou duas vezes).
  • Dê um duplo-clique no botão do dígito '0': isto abrirá o editor no manipulador de eventos para o botão 0.
  • Substituia o código no manipulador de eventos por: AddDigit(0). O procedimento se parecerá com isto:
procedure TForm1.btnDigit0Click(Sender: TObject);
begin
  AddDigit(0)
end;
  • Faça o mesmo para o botão do dígito 1:
procedure TForm1.btnDigit1Click(Sender: TObject);
begin
  AddDigit(1)
end;

Antes de completar esta sequência para todos os outros botões de dígitos, certifique-se de que ele realmente faz o que queremos.

  • Execute o programa (pressione F9).
  • Clique nos botões 0 e 1 algumas vezes e veja se ele realmente funciona.
  • Finalize o programa.

Um bom programador é um programador preguiçoso

Agora 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.

Todos 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...

  • Abra o formulário (se necessário, pressione F12 uma ou duas vezes).
  • Selecione o botão do dígito 1 (clique nele uma vez).
  • No Inspetor de Objetos, localize a propriedade Tag e modifique seu valor par 1.
  • Selecione o botão do dígito 2 (clique nele uma vez).
  • No Inspetor de Objetos, localize a propriedade Tag e modifique seu valor para 2.
  • Repita isto para os botões dos dígitos 3..9.

Agora 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:

  • Abra o formulário.
  • 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).
  • Note que o procedimento btnDigit0Click tem um parâmetro Sender de tipo TObject.
procedure TForm1.btnDigit0Click(Sender: TObject);

O 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.

  • Modifique o código assim:
procedure TForm1.btnDigit0Click(Sender: TObject);
begin
  AddDigit(TButton(Sender).Tag)
end;

Nã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!

Vamos olhar melhor para o Inspetor de Objetos.

  • Abra o formulário.
  • Selecione o botão do dígito 0 (btnDigit0).
  • No Inspetor de Objetos, selecione a aba Events (veja abaixo).


tutcal oi events.png


No 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).

  • 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.
  • Abra a lista dropdown para o evento OnClick e selecione btnDigit0Click.
  • Faça o mesmo para todos os outros botões de dígitos 3..9.

Now 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.

  • On the form: select digit button 1. In the Object Inspector we can now see that the event handler indeed is btnDigit1Click.
  • Open the drop down list for the OnClick event and select btnDigit0Click.

Now all buttons share the same event handler to add digits to the display box.

  • Run the program (press F9).
  • Click all buttons 0..9 a couple of times and see that it actually works.
  • End the program.


For 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.

  • In the menu select Environment/Options.
  • Click on Editor/Completion and Hints.
  • Enable the option 'Auto remove empty methods' (see image below).
  • Press OK.

tutcal auto remove.png

From now on all empty methods are automatically deleted as soon as the source file is saved.

  • Locate method procedure TForm1.btnDigit1Click(Sender: TObject)
  • Delete the line 'AddDigit(1)'
  • Save the file (press Ctrl-S or select from the menu File/Save). The now empty procedure is automatically deleted.

Tuning

So 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.

Removing leading zeroes

Normally 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.

  • Locate method procedure TForm1.AddDigit(const pDigit: byte)
  • Change the code to:
procedure TForm1.AddDigit(const pDigit: byte);
begin
  // Suppress leading zeroes when adding digits
  if edDisplay.Text = '0' then
    edDisplay.Text := IntToStr(pDigit)
  else
    edDisplay.Text := edDisplay.Text + IntToStr(pDigit)
end;

Limit the number of digits

This 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:

  • Locate method procedure TForm1.AddDigit(const pDigit: byte)
  • Change the code to:
procedure TForm1.AddDigit(const pDigit: byte);
begin
  // Limit the number of digits
  if length(edDisplay.Text) < 8 then
  begin
    // Suppress leading zeroes when adding digits
    if edDisplay.Text = '0' then
      edDisplay.Text := IntToStr(pDigit)
    else
      edDisplay.Text := edDisplay.Text + IntToStr(pDigit)
  end;
end;

Operations

Now 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:

  1. The user enters a number
  2. The user presses a function button (e.g. '+')
  3. The user enters a second number
  4. The user presses '='
  5. The program responds with the addition of the first and second number

As soon as the user presses the '=' button the program has to know three things:

  1. What was the operation?
  2. What was the first number?
  3. What was the second number?

What was the operation?

Somehow 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.

  • In the Source Editor, find the location where the form is declared.
  • Just above the form add the type declaration TOperationType for all supported operations:
 type

  { All supported operations for our calculator }
  TOperationType = (otNone, otPlus, otMinus, otMultiply, otDivide);

  { TForm1 }

  TForm1 = class(TForm)
  • To temporarily store the operation, a variable is added to the private section of the form's class declaration.
  private
    { private declarations }
    SelectedOperation: TOperationType;
    procedure AddDigit(const pDigit: byte);

What was the first number?

To temporarily store the first number that was entered we need a variable.

  • Add a variable to the form's private section.
  private
    { private declarations }
    SelectedOperation: TOperationType;
    FirstNumber: longint;
    procedure AddDigit(const pDigit: byte);

What was the second number?

  • Add variable SecondNumber to the form's private section (the same as FirstNumber) and make it a longint as well.

What's on your mind

Now 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.

  • Open the form.
  • Double click on button '+'. The IDE creates an event handler and the text cursor is placed inside the empty begin/end block.

Hold 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.

  • Locate the form's private section and add a new procedure: StoreOperation.
  private
    { private declarations }
    SelectedOperation: TOperationType;
    FirstNumber: longint;
    SecondNumber: longint;
    procedure AddDigit(const pDigit: byte);
    procedure StoreOperation(const pOperation: TOperationType);
  • Place the blinking text cursor on the newly inserted line.
  • Press Ctrl-Shift-C (you know the drill by now…).

The 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.

  • Add the following code to the newly created procedure:
procedure TForm1.StoreOperation(const pOperation: TOperationType);
begin
  // Store the operation so it can be used later
  SelectedOperation := pOperation;

  // Store the number that was entered by the user
  FirstNumber := StrToInt(edDisplay.Text);
end;

Ok, now back to the '+' button:

  • Open the form.
  • Double click on button '+'.
  • Add the following code: StoreOperation(otPlus);
  • Open the form.
  • Double click on button '-'.
  • Add the following code: StoreOperation(otMinus);
  • Open the form.
  • Double click on button '*'.
  • Add the following code: StoreOperation(otMultiply);
  • Open the form.
  • Double click on button '/'.
  • Add the following code: StoreOperation(otDivide);

Now test the program and see what happens:

  • Run the program (press F9)
  • Enter a number (e.g. 21267)
  • Press the '+' button
  • Enter the second number (e.g. 31170)

What 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.

  • Locate the form's private section and add a new variable: doClearDisplay. This variable will be the trigger for erasing the display.
  private
    { private declarations }
    SelectedOperation: TOperationType;
    FirstNumber: longint;
    SecondNumber: longint;
    doClearDisplay: boolean;
    procedure AddDigit(const pDigit: byte);
    procedure StoreOperation(const pOperation: TOperationType);
  • Position the blinking text cursor on the line StoreOperation.
  • Press Ctrl-Shift-CursorDown: this moves the cursor to the body of the StoreOperation procedure.
  • 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:
procedure TForm1.StoreOperation(const pOperation: TOperationType);
begin
  // Store the operation so it can be used later
  SelectedOperation := pOperation;

  // Store the number that was entered by the user
  FirstNumber := StrToInt(edDisplay.Text);

  // The display must start with a new number
  doClearDisplay := true;
end;
  • And where better to clear the display than in the AddDigit procedure? Change the code to:
procedure TForm1.AddDigit(const pDigit: byte);
begin
  // Must the display be cleared first?
  if doClearDisplay then
  begin
    edDisplay.Text := '0';   // Reset the display to zero
    doClearDisplay := false; // Once is enough...
  end;

  // Limit the number of digits
  if length(edDisplay.Text) < 8 then
  begin
    // Suppress leading zeroes when adding digits
    if edDisplay.Text = '0' then
      edDisplay.Text := IntToStr(pDigit)
    else
      edDisplay.Text := edDisplay.Text + IntToStr(pDigit)
  end;
end;

Now let's see what happens:

  • Run the program (press F9).
  • Enter a number.
  • Press an operation button.
  • Enter another number.
  • End the program.

We're almost there: we can enter numbers and select an operation. Now it's time to do the actual calculations.

Doing the math

Now that we have stored a number and an operation, it's time to do the actual calculations. Let's start easy with additions.

  • Open the form.
  • Double click on button '=' (the event handler is created).
  • Change the procedure:
procedure TForm1.btnCalculateClick(Sender: TObject);
var result: longint;
begin
  case SelectedOperation of
    otPlus: begin
              // Retrieve the second number
              SecondNumber := StrToInt(edDisplay.Text);
              // Do the math
              result := FirstNumber + SecondNumber;
              // Display the resulting value
              edDisplay.Text := IntToStr(result);
            end;
  end;
end;

The 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.

We now have a working calculator (that can only add two numbers):

  • Run the program (press F9).
  • Enter a number.
  • Press '+'
  • Enter another number.
  • Press '=' : the sum of the two numbers is displayed.
  • End the program.

We 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:

SecondNumber := StrToInt(edDisplay.Text);

We have seen such a statement before: the StoreOperation procedure contains something similar:

FirstNumber := StrToInt(edDisplay.Text);

Calculating 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.

  • Locate the form's private section and add a new function: GetDisplayValue.
  private
    { private declarations }
    SelectedOperation: TOperationType;
    FirstNumber: longint;
    SecondNumber: longint;
    doClearDisplay: boolean;
    procedure AddDigit(const pDigit: byte);
    procedure StoreOperation(const pOperation: TOperationType);
    function GetDisplayValue: longint;
  • Position the blinking text cursor on the line GetDisplayValue.
  • Press Ctrl-Shift-C.
  • Add this code to extract the value that is in the display field:
function TForm1.GetDisplayValue: longint;
begin
  result := StrToInt(edDisplay.Text);
end;
  • Locate the statement: FirstNumber := StrToInt(edDisplay.Text);
  • Change it to: FirstNumber := GetDisplayValue;

So much for refactoring, back to the calculations.

  • Locate the statement: SecondNumber := StrToInt(edDisplay.Text);
  • Change it to: SecondNumber := GetDisplayValue;

Now look at the following statements:

              // Retrieve the second number
              SecondNumber := GetDisplayValue;
              // Do the math
              result := FirstNumber + SecondNumber;

Variable SecondNumber is used to store the value in the display box. However it is not really necessary. The above code can be simplified:

              // Do the math
              result := FirstNumber + GetDisplayValue;

Now look at the following statements:

              // Do the math
              result := FirstNumber + GetDisplayValue;
              // Display the resulting value
              edDisplay.Text := IntToStr(result);

The same now applies to the result variable. The above code can be simplified to:

              // Display the resulting value
              edDisplay.Text := IntToStr(FirstNumber + GetDisplayValue);

Now we can add the 3 remaining operations.

  • Open the form.
  • Double click on button '=' (the event handler is created).
  • Change the code:
procedure TForm1.btnCalculateClick(Sender: TObject);
begin
  case SelectedOperation of
    otPlus     : edDisplay.Text := IntToStr(FirstNumber + GetDisplayValue);
    otMinus    : edDisplay.Text := IntToStr(FirstNumber - GetDisplayValue);
    otMultiply : edDisplay.Text := IntToStr(FirstNumber * GetDisplayValue);
    otDivide   : edDisplay.Text := IntToStr(FirstNumber div GetDisplayValue);
  end;
end;

We now have a working calculator for all 4 operations:

  • Run the program (press F9).
  • Enter a number.
  • Press an operation button.
  • Enter another number.
  • Press '=' : the result is displayed.
  • End the program.

Intermezzo

After 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.

First let's add another procedure, just for the fun of it (well, there's more to it of course:-)):

  • Locate the form's private section and add a new procedure: SetDisplayValue.
  private
    { private declarations }
    SelectedOperation: TOperationType;
    FirstNumber: longint;
    SecondNumber: longint;
    doClearDisplay: boolean;
    procedure AddDigit(const pDigit: byte);
    procedure StoreOperation(const pOperation: TOperationType);
    function GetDisplayValue: longint;
    procedure SetDisplayValue(const pValue: longint);
  • Position the blinking text cursor on the line SetDisplayValue.
  • Press Ctrl-Shift-C.
  • Add the code below to give the display a new value, as the procedure name suggests:
procedure TForm1.SetDisplayValue(const pValue: longint);
begin
  edDisplay.Text := IntToStr(pValue);
end;

The above code should look pretty familiar. Have a look at the procedure that does all calculations:

procedure TForm1.btnCalculateClick(Sender: TObject);
begin
  case SelectedOperation of
    otPlus     : edDisplay.Text := IntToStr(FirstNumber + GetDisplayValue);
    otMinus    : edDisplay.Text := IntToStr(FirstNumber - GetDisplayValue);
    otMultiply : edDisplay.Text := IntToStr(FirstNumber * GetDisplayValue);
    otDivide   : edDisplay.Text := IntToStr(FirstNumber div GetDisplayValue);
  end;
end;

This procedure can now be simplified.

  • Change the code to:
procedure TForm1.btnCalculateClick(Sender: TObject);
begin
  case SelectedOperation of
    otPlus     : SetDisplayValue(FirstNumber + GetDisplayValue);
    otMinus    : SetDisplayValue(FirstNumber - GetDisplayValue);
    otMultiply : SetDisplayValue(FirstNumber * GetDisplayValue);
    otDivide   : SetDisplayValue(FirstNumber div GetDisplayValue);
  end;
end;

Loose ends

There's a couple of things that need to be done: implement the two loose buttons and make the calculator a bit more robust.

The +/- button

  • Open the form.
  • Double click on button '+/-' (the event handler is created).
  • Add the following code:
procedure TForm1.btnPlusMinusClick(Sender: TObject);
begin
  SetDisplayValue(0 - GetDisplayValue);
end;

See here the fruits of our labor. We have reused the procedure SetDisplayValue and GetDisplayValue to implement the negate function.

Try it by running the program and press the '+/-' button.

The Clear button

  • Open the form.
  • Double click on button 'C' (the event handler is created).
  • Add the following code:
procedure TForm1.btnClearClick(Sender: TObject);
begin
  SetDisplayValue(0);          // Start from scratch
  SelectedOperation := otNone; // No operation selected yet
  doClearDisplay := false;     // The display is already cleared
end;

The 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.

Divide by zero

Let'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).

  • Open the form.
  • Double click on button '='.
  • Update the otDivide section:
procedure TForm1.btnCalculateClick(Sender: TObject);
begin
  case SelectedOperation of
    otPlus     : SetDisplayValue(FirstNumber + GetDisplayValue);
    otMinus    : SetDisplayValue(FirstNumber - GetDisplayValue);
    otMultiply : SetDisplayValue(FirstNumber * GetDisplayValue);
    otDivide   : if GetDisplayValue <> 0 then
                   SetDisplayValue(FirstNumber div GetDisplayValue)
                 else
                   begin
                     // Display an error message to the user
                     MessageDlg('It is not possible to divide a number by 0!', mtWarning, [mbCancel],0);
                     // Reset the calculator to start with a clean slate
                     btnClearClick(nil);
                   end
  end;
end;

Things noteworthy:

  1. 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.
  2. 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.

Overflows

  • Start the calculator
  • Enter 99999999 as the first number (8 digits)
  • Press button '*'
  • Enter 99999999 as the second number
  • Press button '='

Now two things could have happened:

  1. The application gives an incorrect result (something like 1674919425).
  2. The application raises an error.

If it's the first option then overflow errors have been disabled for the project. Enable them by ticking the Overflow option box:

  • Select from the menu Project/Project Options…
  • Select in the treeview Compiler Options/Code Generation.
  • Tick the box Overflow (-Co) (see image below).
  • Click OK.

tutcal compiler overflow.png

Now run the program again and see what happens with overflows.


If 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.

  • Open the form.
  • Double click on button '='.
  • Change the code in the procedure to this:
procedure TForm1.btnCalculateClick(Sender: TObject);
begin
  // Catch any overflow errors
  try
    case SelectedOperation of
      otPlus     : SetDisplayValue(FirstNumber + GetDisplayValue);
      otMinus    : SetDisplayValue(FirstNumber - GetDisplayValue);
      otMultiply : SetDisplayValue(FirstNumber * GetDisplayValue);
      otDivide   : if GetDisplayValue <> 0 then
                     SetDisplayValue(FirstNumber div GetDisplayValue)
                   else
                     begin
                       // Display an error message to the user
                       MessageDlg('It is not possible to divide a number by 0!', mtWarning, [mbCancel],0);
                       // Reset the calculator to start with a clean slate
                       btnClearClick(nil);
                     end
    end;
  except
    on E: EIntOverflow do
      begin
        // Display an error message
        MessageDlg('The result of the calculation was too big for the calculator to process!', mtWarning, [mbCancel], 0);
        // Reset the calculator
        btnClearClick(nil);
      end
  end
end;

Note 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.

Final tweaks

Remember 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.

  • Click on message Note: Private field "TForm1.SecondNumber" is never used.
  • Delete longint variable SecondNumber.


As soon as our demo Calculator is started we see that the number 0 in the display has the focus (see image below).

tutcal calculator focus.png

This 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.

  • Open the form.
  • Select the display control (click on edDisplay).
  • 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.
  • 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.

Run the program to see the result of these changes.


(Under construction: OO-ify the program)