![]() |
Calculadora do Windows |
Índice
Introdução
A criação de uma aplicação simples como uma calculadora é um excelente ponto de partida para aprender os conceitos de programação orientada a objetos em Delphi. Neste artigo, vamos desenvolver um clone da calculadora do Windows em Delphi, utilizando orientação a objetos. Vamos explorar o código detalhadamente para que você possa entender cada parte do desenvolvimento. Mas não se engane, apesar de ser um exemplo simples, a calculadora possui operações matemáticas mais avançadas em comparação com as aprendidas anteriormente no projeto de calculadora simples.
Estrutura do Projeto
Vamos criar uma aplicação Delphi com os seguintes componentes principais:
- Um formulário principal (
TfrmCalculator
). - Botões para números (0-9) e operações (+, -, x, ÷, %, +/-, ⅟x, x² e ²√x).
- Um botão de igual (=) para calcular o resultado.
- Um botão de limpar (C) para reiniciar a calculadora.
- Um botão para cancelar a entrada (CE) do número digitado.
- Um botão Õ para apagar o último número digitado.
- Um rótulo (
TLabel
) para exibir os resultados e operações.
Passo a Passo
Passo 1: Configuração Inicial
- Crie um novo projeto VCL no Delphi:
- Selecione File > New > VCL Forms Application - Delphi.
- Salve o projeto como prj
Calculator
. - Salve a unit do formulário como untCalculator e o form como
frmCalculator
. - Adicione uma nova unit e salve como untClassCalculator, é nessa unit que ficará a classe responsável pelos cálculos da calculadora.
- Adicione Componentes ao Formulário:
- Adicione um
TLabel
no topo do formulário para exibir os números e resultados. - Adicione botões (
TButton
) para números de 0 a 9, operações (+, -, x, ÷, %, +/-, ⅟x, x² e ²√x), igual (=) , cancelar a entrada (CE) , limpar (C) e apagar (Õ).
- Adicione um
Passo 2: Definindo a Classe da Calculadora
Vamos criar uma classe para gerenciar as operações da calculadora.
type
TCalculator = class
private
FCurrentValue: Double;
FCurrentValueStr: String;
FStoredValue: Double;
FCurrentOperation: string;
FDecimalEntered: Boolean;
FEqualEntered: Boolean;
FAccumulated: Boolean;
procedure SetCurrentValue(const Value: Double);
procedure SetCurrentValueStr(const Value: String);
function DecimalPlaces(Number: String): String;
function FormatNumberToFloat(Number:String):String;
public
constructor Create;
procedure Clear;
procedure AddDigit(Digit: string);
procedure Calculate;
procedure CalculateAddition;
procedure CalculateSubtraction;
procedure CalculateMultiplication;
procedure CalculateDivision;
procedure CalculatePercent;
procedure CalculateSquareRoot;
procedure CalculateInverse;
procedure CalculatePower;
procedure AddDecimal;
procedure DeleteLastDigit;
procedure InvertSign;
procedure CancelEntry;
procedure SetCurrentOperation(const Value: String);
property CurrentValue: Double read FCurrentValue write SetCurrentValue;
property CurrentValueStr : String read FCurrentValueStr write SetCurrentValueStr;
property CurrentOperation : String read FCurrentOperation write SetCurrentOperation;
end;
Implementação da Classe TCalculator
constructor TCalculator.Create;
begin
Clear;
end;
procedure TCalculator.AddDecimal;
begin
if not FDecimalEntered then
begin
FDecimalEntered := True;
if Pos(',', FCurrentValueStr) = 0 then
FCurrentValueStr := FCurrentValueStr + ',';
end;
end;
procedure TCalculator.AddDigit(Digit: string);
var
valueStr : String;
begin
if FEqualEntered and (FCurrentOperation = '') then
Clear;
if ((FCurrentValue = 0) or FAccumulated) and (not FDecimalEntered) then
begin
FCurrentValue := StrToFloat(Digit);
FCurrentValueStr := Digit;
FAccumulated := False;
end
else
begin
valueStr := FCurrentValueStr + Digit;
FCurrentValueStr := FormatNumberToFloat(valueStr);
FCurrentValue := StrToFloat(StringReplace(FCurrentValueStr,'.','',[rfReplaceAll])); //Remove os pontos, deixando somente número com casas decimais
end;
end;
procedure TCalculator.Calculate;
begin
if FCurrentOperation = '+' then
FCurrentValue := FStoredValue + FCurrentValue
else
if FCurrentOperation = '-' then
FCurrentValue := FStoredValue - FCurrentValue
else
if FCurrentOperation = '*' then
FCurrentValue := FStoredValue * FCurrentValue
else
if FCurrentOperation = '^' then
FCurrentValue := Power(FStoredValue, 2)
else
if FCurrentOperation = '2' then
FCurrentValue := Sqrt(FStoredValue)
else
if FCurrentOperation = '%' then
FCurrentValue := FStoredValue + ((FStoredValue * FCurrentValue) / 100)
else
if FCurrentOperation = '/' then
begin
if FCurrentValue > 0 then
FCurrentValue := FStoredValue / FCurrentValue
else
raise Exception.Create('Divisão por zero não permitida');
end
else
if FCurrentOperation = 'i' then
begin
if FStoredValue > 0 then
FCurrentValue := 1 / FStoredValue
else
raise Exception.Create('Divisão por zero não permitida');
end;
FCurrentValueStr := FormatNumberToFloat(FloatToStr(FCurrentValue));
FCurrentOperation := '';
FEqualEntered := True;
end;
procedure TCalculator.CalculateDivision;
begin
SetCurrentOperation('/');
end;
procedure TCalculator.CalculateAddition;
begin
SetCurrentOperation('+');
end;
procedure TCalculator.CalculateInverse;
begin
SetCurrentOperation('i');
end;
procedure TCalculator.CalculateMultiplication;
begin
SetCurrentOperation('*');
end;
procedure TCalculator.CalculatePercent;
begin
FCurrentOperation := '%';
if FStoredValue = 0 then
begin
Calculate;
FCurrentOperation := '';
end;
end;
procedure TCalculator.CalculatePower;
begin
SetCurrentOperation('^');
end;
procedure TCalculator.CalculateSquareRoot;
begin
SetCurrentOperation('2');
end;
procedure TCalculator.CalculateSubtraction;
begin
SetCurrentOperation('-');
end;
procedure TCalculator.CancelEntry;
begin
FCurrentValue := 0;
FCurrentValueStr := '0';
FDecimalEntered := False;
FEqualEntered := False;
end;
procedure TCalculator.Clear;
begin
FCurrentValue := 0;
FCurrentValueStr := '0';
FStoredValue := 0;
FCurrentOperation := '';
FDecimalEntered := False;
FEqualEntered := False;
FAccumulated := False;
end;
procedure TCalculator.DeleteLastDigit;
begin
if FEqualEntered then
Exit;
if Length(FCurrentValueStr) > 0 then
begin
CurrentValueStr := Copy(FCurrentValueStr, 1, Length(FCurrentValueStr) - 1);
if CurrentValueStr = '' then
begin
FCurrentValue := 0;
FCurrentValueStr := '0';
end
else
FCurrentValue := StrToFloat(StringReplace(FCurrentValueStr,'.','',[rfReplaceAll]));
CurrentValueStr := FormatNumberToFloat(FCurrentValueStr);
end;
end;
function TCalculator.FormatNumberToFloat(Number: String): String;
var
doubleNumber : Double;
begin
doubleNumber := StrToFloat(StringReplace(Number,'.','',[rfReplaceAll]));
Result := FormatFloat('#,##0.'+DecimalPlaces(Number),doubleNumber);
end;
procedure TCalculator.InvertSign;
begin
FCurrentValue := -FCurrentValue;
FCurrentValueStr := FormatNumberToFloat(FloatToStr(FCurrentValue));
end;
procedure TCalculator.SetCurrentOperation(const Value: String);
begin
if (not FEqualEntered) and (FStoredValue <> 0) and (FCurrentValue <> 0) then
begin
Calculate;
FStoredValue := FCurrentValue;
FEqualEntered := False;
FAccumulated := True;
end
else
begin
FStoredValue := FCurrentValue;
FAccumulated := False;
FCurrentValue := 0;
FCurrentValueStr := '0';
end;
FCurrentOperation := Value;
end;
procedure TCalculator.SetCurrentValue(const Value: Double);
begin
FCurrentValue := Value;
end;
procedure TCalculator.SetCurrentValueStr(const Value: String);
begin
FCurrentValueStr := Value;
end;
function TCalculator.DecimalPlaces(Number: String): String;
var
NumberStr: string;
DecimalPos: Integer;
count: Integer;
i: Integer;
firsDecimal : String;
begin
//inicializa a variável com vazio;
firsDecimal := '';
//remove o ponto do número
NumberStr := StringReplace(Number,'.','',[rfReplaceAll]);
// Encontra a posição do ponto decimal
DecimalPos := Pos(FormatSettings.DecimalSeparator, NumberStr);
// Se houver ponto decimal, conta os dígitos após ele
// e encontra o primeiro digito decimal
if DecimalPos > 0 then
begin
count := Length(NumberStr) - DecimalPos;
firsDecimal := Copy(NumberStr, DecimalPos+1, 1);
end
else
count := 0;
//se não houver casas decimais, seta para 2
if count = 0 then
count := 2;
//se as casas decimais forem maior que 16, limita a 16
if count > 16 then
count := 16;
//retorna o carater # que servirá de máscara
//para formatar o número, de acordo com as casas decimais
for I := 1 to count do
begin
if firsDecimal = '0' then
Result := Result + '0'
else
Result := Result + '#';
end;
end;
Passo 3: Integração com o Formulário
No formulário principal (TfrmCalculator
), vamos instanciar a classe TCalculator
e conectar os eventos dos botões.
type
TfrmCalculator = class(TForm)
lblResultado: TLabel;
btnZero: TButton;
btnUm: TButton;
btnDois: TButton;
btnTres: TButton;
btnQuatro: TButton;
btnCinco: TButton;
btnSeis: TButton;
btnOito: TButton;
btnSete: TButton;
btnNove: TButton;
btnDecimal: TButton;
btnPercentual: TButton;
btnInverso: TButton;
btnPotencia: TButton;
btnRaizQuadrada: TButton;
btnLimpar: TButton;
btnDividir: TButton;
btnMultiplicar: TButton;
btnSubtrair: TButton;
btnAdicionar: TButton;
btnIgual: TButton;
btnApagarUltimoDigito: TButton;
btnLimparEntrada: TButton;
btnInverterSinal: TButton;
procedure btnLimparEntradaClick(Sender: TObject);
procedure btnInverterSinalClick(Sender: TObject);
procedure btnApagarUltimoDigitoClick(Sender: TObject);
procedure btnDecimalClick(Sender: TObject);
procedure btnLimparClick(Sender: TObject);
procedure btnIgualClick(Sender: TObject);
procedure btnNumeroClick(Sender: TObject);
procedure btnOperacaoClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormKeyPress(Sender: TObject; var Key: Char);
private
{ Private declarations }
FUltimoDigitado : String;
Calculator : TCalculator;
procedure AtualizaResultado(const Key: String = '');
public
{ Public declarations }
end;
var
frmCalculator: TfrmCalculator;
implementation
{$R *.dfm}
procedure TfrmCalculator.AtualizaResultado(const Key: String = '');
begin
if Key <> '' then
begin
if Calculator.CurrentValueStr <> '0' then
lblResultado.Caption := Calculator.CurrentValueStr + ' ' + Key
else
lblResultado.Caption := FUltimoDigitado + ' ' + Key;
end
else
lblResultado.Caption := Calculator.CurrentValueStr;
end;
procedure TfrmCalculator.btnApagarUltimoDigitoClick(Sender: TObject);
begin
Calculator.DeleteLastDigit;
ActiveControl := nil; //Remove o foco dos botões
AtualizaResultado;
end;
procedure TfrmCalculator.btnDecimalClick(Sender: TObject);
begin
Calculator.AddDecimal;
AtualizaResultado;
end;
procedure TfrmCalculator.btnIgualClick(Sender: TObject);
begin
try
Calculator.Calculate;
AtualizaResultado;
except
on E: Exception do
Application.MessageBox(PWideChar(E.Message), 'Erro', MB_OK+MB_ICONERROR);
end;
end;
procedure TfrmCalculator.btnLimparClick(Sender: TObject);
begin
Calculator.Clear;
ActiveControl := nil;
AtualizaResultado;
end;
procedure TfrmCalculator.btnLimparEntradaClick(Sender: TObject);
begin
Calculator.CancelEntry;
lblResultado.Caption := '0';
ActiveControl := nil;
end;
procedure TfrmCalculator.btnNumeroClick(Sender: TObject);
begin
Calculator.AddDigit((Sender as TButton).Caption);
ActiveControl := nil;
if Calculator.CurrentValueStr <> '0' then
FUltimoDigitado := Calculator.CurrentValueStr
else
FUltimoDigitado := (Sender as TButton).Caption;
AtualizaResultado;
end;
procedure TfrmCalculator.btnOperacaoClick(Sender: TObject);
begin
try
if (Sender as TButton).Name = 'btnMultiplicar' then
Calculator.CalculateMultiplication
else
if (Sender as TButton).Name = 'btnDividir' then
Calculator.CalculateDivision
else
if (Sender as TButton).Name = 'btnAdicionar' then
Calculator.CalculateAddition
else
if (Sender as TButton).Name = 'btnSubtrair' then
Calculator.CalculateSubtraction
else
if (Sender as TButton).Name = 'btnInverso' then
begin
Calculator.CalculateInverse;
Calculator.Calculate;
end
else
if (Sender as TButton).Name = 'btnPotencia' then
begin
Calculator.CalculatePower;
Calculator.Calculate;
end
else
if (Sender as TButton).Name = 'btnRaizQuadrada' then
begin
Calculator.CalculateSquareRoot;
Calculator.Calculate;
end
else
if (Sender as TButton).Name = 'btnInverterSinal' then
Calculator.InvertSign
else
if (Sender as TButton).Name = 'btnPercentual' then
Calculator.CalculatePercent;
ActiveControl := nil;
AtualizaResultado(Calculator.CurrentOperation);
except
on E: Exception do
Application.MessageBox(PWideChar(E.Message), 'Erro', MB_OK + MB_ICONERROR);
end;
end;
procedure TfrmCalculator.btnInverterSinalClick(Sender: TObject);
begin
Calculator.InvertSign;
ActiveControl := nil;
AtualizaResultado;
end;
procedure TfrmCalculator.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if Assigned(Calculator) then
Calculator.Free;
end;
procedure TfrmCalculator.FormCreate(Sender: TObject);
begin
Calculator := TCalculator.Create;
KeyPreview := True; //Habilita o formulário para receber entradas pelo teclado
AtualizaResultado;
end;
procedure TfrmCalculator.FormKeyPress(Sender: TObject; var Key: Char);
begin
case Key of
'0'..'9':
begin
Calculator.AddDigit(Key);
if Calculator.CurrentValueStr <> '0' then
FUltimoDigitado := Calculator.CurrentValueStr
else
FUltimoDigitado := Key;
AtualizaResultado;
end;
'+', '-', '*', '/':
begin
Calculator.SetCurrentOperation(Key);
AtualizaResultado(Key);
end;
'=':
begin
try
Calculator.Calculate;
AtualizaResultado;
except
on E: Exception do
Application.MessageBox(PWideChar(E.Message), 'Erro', MB_OK+MB_ICONERROR);
end;
end;
#8: // Backspace
begin
Calculator.DeleteLastDigit;
AtualizaResultado;
end;
'.', ',':
begin
Calculator.AddDecimal;
AtualizaResultado;
end;
#$1B: //Esc
begin
Calculator.Clear;
AtualizaResultado;
end;
#13: // Enter
begin
try
Calculator.Calculate;
AtualizaResultado;
except
on E: Exception do
Application.MessageBox(PWideChar(E.Message), 'Erro', MB_OK + MB_ICONERROR);
end;
end;
end;
end;
Explicação do Código do Clone da Calculadora do Windows em Delphi
- Classe
TCalculator
:- Campos Privados:
FCurrentValue
, FCurrentValueStr eFStoredValue
para armazenar os valores atuais e armazenados,FCurrentOperation
para armazenar a operação atual, FDecimalEntered para controlar se o ponto decimal foi digitado, FEqualEntered para saber se o botão de igual foi acionado e FAccumulated para saber se está acumulando contas. - Construtor: Inicializa a calculadora chamando o método
Clear
. - Método
Clear
: Reseta os valores a operação e as variáveis de controle(FDecimalEntered, FEqualEntered , FAccumulated) . - Método
AddDigit
: Adiciona um dígito ao valor atual, tratando o caso inicial ondeFCurrentValue
é 0 ou FAccumulated é verdadeiro, e quando o botão de igual é acionado e não tem mais operações para fazer o método clear é chamado para resetar as variáveis. - Método
SetOperation
: Armazena o valor atual e define a operação atual, caso for uma conta acumulado efetua o cálculo. - Método
Calculate
: Realiza a operação armazenada entre os valores armazenados e atuais, e reseta a operação. - Método CalculateAddition: Armazena a operação de adição.
- Método CalculateSubtraction: Armazena a operação de subtração.
- Método CalculateMultiplication: Armazena a operação de multiplicação
- Método CalculateDivision: Armazena a operação de divisão.
- Método CalculatePercent: Armazena a operação de percentual.
- Método CalculateSquareRoot: Armazena a operação de raiz quadrada.
- Método CalculateInverse: Armazena a operação de inversão (1 dividido pelo número digitado).
- Método CalculatePower: Armazena a operação de potência.
- Método AddDecimal: Adiciona o separador de decimal.
- Método DeleteLastDigit: Apaga o último dígito inserido.
- Método InvertSign: Inverte o sinal do número digitado.
- Método CancelEntry: Cancela a entrada;
- Método SetCurrentValue: Seta o valor para a variável FCurrentValue e também é o método da propriedade CurrentValue.
- Método SetCurrentValueStr: Seta o valor para a variável FCurrentValueStr e também é o método da propriedade CurrentValueStr.
- Método SetCurrentOperation: Armazena a operação informada e também é o método da propriedade CurrentOperation.
- Método DecimalPlaces: Resulta a máscara das casas decimais para formatar o valor que será exibido.
- Método FormatNumberToFloat: Formata uma entrada em texto com máscara de número float, e resulta o texto formatado para ser exibido no rótulo.
- Propriedades: CurrentValue, CurrentValueStr e CurrentOperation que podem ser acessadas quando a classe é instanciada.
- Campos Privados:
- Formulário
TfrmCalculator
:- Eventos dos Botões: Cada botão de dígito chama o evento btnNumeroClick que adiciona o dígito à calculadora e atualiza o rótulo.
- Operações: Botões de operação chamam o evento btnOperacaoClick que define a operação atual.
- Igual: Botão de igual chama evento btnIgualClick que realiza a operação e exibe o resultado.
- Limpar: Botão de limpar chama evento btnLimparClick que reseta a calculadora.
- Cancelar Entrada: Botão de cancelar entrada chama o evento btnLimparEntradaClick que cancela a entrada atual.
- Inverter Sinal: Botão de inverter sinal chama o evento btnInverterSinalClick que inverte o sinal do número digitado.
- Apagar último dígito: Botão de apagar o último dígito chama o evento btnApagarUltimoDigitoClick que apaga o último dígito inserido.
- Vírgula: Botão da vírgula chama o evento btnDecimalClick que adiciona a vírgula no número digitado para representar as casas decimais.
- Criação do formulário: Na criação do formulário é chamado o evento FormCreate que cria o objeto Calculator, habilita o formulário para receber dados através do teclado e atualiza os dados no rótulo.
- Fechamento do formulário: No fechamento do formulário é chamado o evento FormClose que limpa o objeto Calculator da memória, caso o mesmo esteja instanciado.
- Tecla pressionada: Quando uma tecla numérica ou equivalente a calculadora é pressionada, é chamado o evento FormKeyPress, que captura a tecla pressionada e executa uma ação. Se for um dígito, ele aparecerá no rótulo, se for um sinal de operação, ela será armazenada para cálculo.
É importante consultar o código completo no link do github abaixo, para pegar todos os detalhes das propriedades dos componentes e formulário, como TabStop e Fontes utilizadas nos botões, e das bibliotecas declaradas na seção uses de cada unit.
Veja abaixo a ilustração do projeto Clone da Calculadora do Windows em Delphi:
![]() |
Ilustração do projeto. |
Código fonte do exemplo
Você pode fazer o download do exemplo do projeto através do repositório do github:
https://github.com/Gisele-de-Melo/CloneCalculator
Conclusão
Criar um clone da calculadora do Windows em Delphi é um ótimo exercício para aprender os conceitos básicos da programação orientada a objetos. Este projeto simples demonstra como estruturar uma aplicação utilizando classes e métodos, além de como manipular componentes da interface gráfica proporcionando uma experiência de usuário fluida e intuitiva. Com esta base, você pode expandir suas habilidades e explorar projetos mais complexos em Delphi.
Nenhum comentário:
Postar um comentário