linguagem de programaÇÃo delphi prof. alberto cezar …alberto.acsoft.com.br/apostilas/18 -...

34
LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho Desenho Livre - Página 1 - DESENHO LIVRE OBJETIVO: Mostrar ao estudante como desenhar através do método Canvas de alguns objetos. APARÊNCIA DO PROJETO: OBJETOS A ACRESCENTAR/MODIFICAR PROPRIEDADES INICIALMENTE: Objetos Propriedades Descrição/Valor Form1 Caption DESENHO LIVRE Color clWhite Position poScreenCenter Height 478 Width 639 BorderStyle bsSingle BorderIcons BiMaximize e biHelp = false Panel1 Caption (deixar em branco) Height 41 Align alTop Panel2 Caption (deixar em branco) Height 41 Align alBottom Image1 Align AlClient

Upload: vulien

Post on 27-Nov-2018

245 views

Category:

Documents


4 download

TRANSCRIPT

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 1 -

DESENHO LIVRE

q OBJETIVO:

Mostrar ao estudante como desenhar através do método Canvas de alguns objetos.

q APARÊNCIA DO PROJETO: q OBJETOS A ACRESCENTAR/MODIFICAR PROPRIEDADES INICIALMENTE:

Objetos Propriedades Descrição/Valor

Form1

Caption DESENHO LIVRE Color clWhite Position poScreenCenter Height 478 Width 639

BorderStyle bsSingle BorderIcons BiMaximize e biHelp = false

Panel1 Caption (deixar em branco) Height 41 Align alTop

Panel2 Caption (deixar em branco) Height 41 Align alBottom

Image1 Align AlClient

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 2 -

q FASE 01: PRIMEIROS PONTOS:

Nesta primeira fase iremos permitir o desenho de pontos de tamanhos variáveis. Para que possamos desenhar qualquer ponto, na realidade teremos que desenhar um círculo

com um raio muito pequeno. Para se desenhar um círculo, usa-se o método Ellipse da propriedade Canvas de um objeto onde se vai desenhar. No nosso caso, iremos desenhar dentro do objeto Image1; mas poderíamos desenhar em outros objetos do Windows, inclusive até o formulário.

O evento que iremos utilizar será o OnMouseDown do objeto Image1. Antes de programar o evento citado, vamos declarar algumas variáveis globais que

utilizaremos no decorrer do projeto:

è Digite apenas o que está em negrito, exatamente no local indicado !

{ Private declarations } public { Public declarations } end; var Form1: TForm1; Tam, CorPreench: integer; implementation {$R *.DFM} end.

Agora, acione o evento OnMouseDown do objeto Image1 e digite os comandos que aparecem em negrito:

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin Image1.Canvas.Pen.color:= CorPreench; Image1.Canvas.Brush.Color:= CorPreench; Image1.Canvas.Ellipse( x – tam , y – tam , x + tam , y + tam); end; end.

Notem que nesta rotina, utilizamos os valores de “X” e “Y”, que são as coordenadas do

mouse ora somados ora subtraídos de um valor representado pela variável “Tam” (tamanho). Esta variável “Tam” iremos inicializá-la com o valor 1, dentro do evento OnCreate do

formulário.

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 3 -

Ao atribuirmos os valores (X-tam, Y-tam) para o ponto do canto superior esquerdo do retângulo que circunscreve o círculo (elipse), estamos supondo que o ponteiro do mouse encontra-se no centro do círculo:

A variável “Tam” representa o raio do círculo. Vamos inicializá-la no evento OnCreate do

objeto Form1:

procedure TForm1.FormCreate(Sender: TObject); begin Tam:= 1; CorPreench:= clBlack; end;

è Aproveitamos o ensejo para inicializar outra variável, que utilizaremos nas rotinas deste

projeto para definir a cor da linha (pen.color) e a cor de preenchimento (brush.color).

q FASE 02: TAMANHO DOS PONTOS:

Nesta fase iremos implementar a mudança do tamanho dos pontos que são desenhados na tela. Para isto, iremos possibilitar a alteração do valor da variável “tam” (tamanho) que foi

inicializada com o valor 1. Aumentaremos o valor de “tam” toda vez que o usuário acionar a tecla “+” e diminuiremos seu valor toda vez que acionar a tecla “-“. Com isto, os valores das coordenadas X-tam, Y-tam, X+tam e Y+tam que representam o retângulo que circunscreve o círculo (elipse) irão aumentar ou diminuir o mesmo.

Para que o windows dê prioridade ao teclado, temos que alterar a propriedade KeyPreview do

formulário para True, pois, a partir daí todo evento gerado pelo teclado será examinado primeiramente pelos métodos do formulário. Caso não exista nenhum método de tratamento de teclado programado, o controle passará para o próximo objeto.

Vamos exemplificar o que acabamos de afirmar: Vamos supor que você esteja digitando algo dentro de um objeto Edit. A cada tecla que você digita, são acionados primeiramente os eventos OnKeyPress, OnKeyDown, OnKeyUp, etc... do formulário e não do Edit. Se existir algum tratamento para estes eventos do formulário, eles são executados antes de se atender ao objeto Edit.

Isto é importante, pois, assim, por exemplo, podemos encerrar um determinado programa a qualquer momento que a tecla “Esc” for acionada, bastando testar o acionamento da mesma dentro de um evento OnKeyPress de um formulário, mesmo que no momento estejamos digitando um texto em um objeto “Memo” por exemplo.

Portanto, mude a propriedade KeyPreview do formulário Form1 para True.

(x-tam,y-tam)

(x,y)

(x+tam,y+tam)

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 4 -

Agora, digite as instruções (que estão em negrito) a seguir, dentro do evento OnKeyPress do formulário:

Na rotina acima podemos verificar que ao ser acionada qualquer tecla do teclado, verifica-se qual

é a mesma: Se for a tecla “+” aumenta-se o valor de “Tam” em uma unidade até o valor máximo de 12,

através do procedimento Inc(tam). Se for a tecla “-“ verifica-se primeiramente se o valor de “Tam” é maior que 1, para evitar

valores negativos do mesmo, se for, diminui-se o valor de “Tam” de um unidade através do procedimento Dec(tam).

Colocamos também, o teste da tecla Esc, cujo código ASCII é 27. Se esta tecla for acionada, o

programa é encerrado (ao se fechar o formulário principal). q FASE 03: COR DOS PONTOS:

Podemos criar a possibilidade de se alterar a cor dos pontos que estão sendo desenhados. Para isto, iremos incluir um botão (SpeedButton) que irá chamar um diálogo de cores (ColorDialog).

Insira dentro de Panel1 um SpeedButton e em qualquer lugar do formulário um objeto

ColorDialog e altere as algumas de suas propriedades: SpeedButton1 Glyph = Brush.bmp Height = 37 Width = 37 Left = 3 Top = 3 Hint = Escolhe a cor de preenchimento ShowHint = True ColorDialog1 nenhuma de suas propriedades serão alteradas Agora, vamos ao evento OnClick do SpeedButton1. Ele deverá chamar a execução do diálogo

ColorDialog. A função “Execute” deste diálogo, retorna o valor true se uma cor for selecionada ou false caso contrário. Se uma cor for selecionada, ela será informada na propriedade Color deste objeto de diálogo.

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char); begin If key = '+' then If tam < 12 then Inc(Tam); If key = '-' then If tam > 1 then Dec(tam); If key = #27 then Close; end;

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 5 -

procedure TForm1.SpeedButton1Click(Sender: TObject); begin If ColorDialog1.execute then CorPreench:= ColorDialog1.color; end;

Nota-se na rotina acima que alteramos a cor de preenchimento (CorPreench), atribuindo à

mesma o valor da propriedade Color do objeto ColorDialog1.

q FASE 04: PREVIEW DOS PONTOS:

Acho que é necessário neste momento, que incluamos no nosso projeto um “preview” do ponto que será desenhado, para que possamos ter uma idéia do seu tamanho e cor atuais.

Para isto, iremos incluir no nosso projeto um objeto Panel3 dentro do Panel2. Alterando as seguintes propriedades do mesmo:

Panel3 Caption = (deixar em branco) Width = 37 Height = 37 Color = clWhite BevelWidth = 3 BevelInner = bvLowered Top = 2 Left = 3 Agora, acrescente dentro deste Panel3 um objeto Image, e altere as suas seguintes

propriedades: Image2 Width = 25 Height = 25 Top = 7 Left = 7

Dentro deste objeto Image2 é que colocaremos um preview do ponto que será desenhado. Para que isto ocorra, é melhor digitarmos um procedimento que faça isto acontecer (Pincel), e depois, colocar o comando de chamada do mesmo em lugares estratégicos dentro do programa. Não esqueça de além de digitar as linhas abaixo, colocar a definição deste procedimento dentro do escopo public da classe TForm1:

procedure TForm1.Pincel; var x, y: integer; begin x:= image2.width div 2; y:= image2.height div 2; Image2.picture:= nil; If corpreench <> Panel3.color then Image2.Canvas.Pen.color:= CorPreench else Image2.Canvas.Pen.color:= clBlack; Image2.Canvas.Brush.Color:= CorPreench; Image2.canvas.ellipse(x-tam,y-tam,x+tam,y+tam); end;

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 6 -

Na rotina acima, observamos que foram criadas 2 variáveis locais: “x” e “y” que recebem as coordenadas do centro do objeto Image2. Em seguida, a imagem que porventura esteja desenhada em Image2 é apagada atribuindo-se Nil à propriedade Picture. Depois, verifica-se a cor do ponto, se é a mesma do Panel3, pois, se isto acontecer, o “preview” não mostrará nada, pois o fundo do Panel3 seria da mesma cor. Daí, a cor da linha que envolve o ponto é desenhada na cor preta, caso contrário, continua com a mesma cor do preenchimento. A seguir, é atribuído o valor da cor de preenchimento e finalmente é desenhado o ponto dentro do objeto Image2. Um dos “locais estratégicos” para colocarmos a chamada a Pincel, é no botão SpeedButton1, pois, com isto, se a cor for alterada, a cor do ponto no preview será alterada:

Outro local estratégico, é no evento OnKeyPress do formulário, pois, ao acionarmos as teclas “+” ou “-“ temos uma mudança no tamanho do ponto, o que também deverá ser mostrado no preview do mesmo:

Existe mais um local estratégico: no método OnCreate do formulário. Neste local, a chamada é necessária para que o ponto apareça no “preview” tão logo inicie o programa:

procedure TForm1.SpeedButton1Click(Sender: TObject); begin If ColorDialog1.execute then CorPreench:= ColorDialog1.color; Pincel; end;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char); begin If key = '+' then If tam < 12 then Inc(Tam); If key = '-' then If tam > 1 then Dec(tam); Pincel; If key = #27 then Close; end;

procedure TForm1.FormCreate(Sender: TObject); begin Tam:= 1; CorPreench:= clBlack; Pincel; end;

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 7 -

q FASE 05: GRAVANDO O DESENHO:

Você já deve ter percebido que todo o desenho é perdido depois que nós fechamos o programa. Claro que você já deve ter chegado à conclusão que necessitamos de gravá-lo. Para fazer isto, precisamos introduzir no nosso projeto mais 2 objetos: um SpeedButton dentro do Panel1 e um SaveDialog em qualquer lugar do formulário.

Altere as seguintes propriedades dos mesmos: SpeedButton2 Glyph = Floppy.bmp Height = 37 Width = 37 Left = 41 Top = 3 Hint = Grava o desenho atual ShowHint = True SaveDialog1 DefaultExt = *.bmp Filter = BitMaps | *.bmp Todos os arquivos | *.* Title = Grava figura Agora, temos que programar o método correspondente ao evento Onclick do objeto

SpeedButton2:

Na rotina acima destacada, verificamos a execução da função Execute do objeto SaveDialog1. Esta função retornará o valor True se o usuário escolher um nome de arquivo para gravar a figura, ou o valor False se ele acionar o botão “Cancel”. O método SaveToFile grava a figura contida em Image1 no arquivo que foi especificado no diálogo SaveDialog1, e cujo nome foi guardado na propriedade FileName do mesmo. q FASE 06: PERGUNTANDO ANTES DE TERMINAR O PROGRAMA:

Toda vez que um formulário é fechado, o seu evento “OnClose” é chamado. Como devemos lembrar, na rotina de tratamento do evento “onKeyPress” do formulário,

fechamos o formulário toda vez que a tecla “Esc” é acionada, e com isto, encerramos o programa.

procedure TForm1.SpeedButton2Click(Sender: TObject); begin If SaveDialog1.execute then begin Image1.Picture.SaveToFile(SaveDialog1.Filename); end; end;

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 8 -

Se quisermos perguntar ao usuário se ele realmente deseja encerrar o aplicativo, podemos interceptar esta decisão, programando o evento “OnClose” do formulário, onde podemos Abortar o encerramento do mesmo. O comando Abort, “aborta” este evento, isto é, o formulário não é fechado, caso a função “Termina_programa” (que descreveremos a seguir) retorne o valor False.

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin If not Termina_programa then Abort; end;

A rotina “Termina_Programa”, que deverá ser declarada no escopo public da classe TForm1 no

início da Unit1, deverá ser digitada conforme aparece a seguir:

Function TForm1.Termina_programa: boolean; begin Result:= true; If MessageBoxEx(0,'Deseja mesmo sair do programa ?', 'ENCERRAMENTO ...', MB_YESNO+MB_ICONSTOP+MB_DEFBUTTON2,

lang_portuguese) = idNo then Result:= false; end;

A palavra Result colocada na função faz com que retorne no nome da mesma

(Termina_programa) o resultado (true ou false). Ao invés de utilizar a palavra Result, você poderia utilizar o próprio nome da rotina: Termina_programa:= true, etc...

Utilizei a palavra Result, pois, economiza digitação. Nesta rotina, também fizemos uso, da função interna do Delphi, MessageBoxEx, esta função tem

a seguinte sintaxe:

MessageBoxEx( 0, pergunta , caption , botões , língua ) : identificador do botão acionado; • PERGUNTA: um texto no formato PChar contendo o texto que será perguntado. • CAPTION: também um texto PChar que será colocado na faixa superior da janela da caixa

de mensagem. • BOTÕES: são códigos que identificam quais os botões que serão mostrados, qual ícone será

colocado na caixa, e qual dos botões ficará selecionado. No nosso programa escolhemos os botões Yes e No, que serão mostrados em português em virtude do próximo parâmetro (língua) informar a língua que queremos. Escolhemos o ícone Stop e o botão default selecionado é o segundo (Não). Se não informarmos o botão a ser selecionado o botão mais a esquerda é selecionado como padrão. Observe que as constantes devem ser somadas.

Os valores possíveis para este parâmetro são:

Flag Descrição MB_ABORTRETRYIGNORE A caixa de mensagem conterá três botões: Abort, Retry, e Ignore. MB_OK A caixa de mensagem conterá um botão OK. MB_OKCANCEL A caixa de mensagem conterá dois botões: OK e Cancel. MB_RETRYCANCEL A caixa de mensagem conterá dois botões: Retry e Cancel. MB_YESNO A caixa de mensagem conterá dois botões: Yes e No. MB_YESNOCANCEL A caixa de mensagem conterá três botões: Yes, No, e Cancel. MB_ICONEXCLAMATION, MB_ICONWARNING

Um ícone com um ponto de exclamação será colocado na caixa de mensagem.

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 9 -

MB_ICONINFORMATION, MB_ICONASTERISK

Um ícone com a letra i dentro de um círculo será colocado na caixa de mensagem.

MB_ICONQUESTION Um ícone com um ponto de interrogação será colocado na caixa de mensagem.

MB_ICONSTOP, MB_ICONERROR, MB_ICONHAND

Um ícone com o sinal STOP será colocado na caixa de mensagem.

MB_DEFBUTTON1 O primeiro botão será o default. MB_DEFBUTTON2 O segundo botão será o default. MB_DEFBUTTON3 O terceiro botão será o default. MB_DEFBUTTON4 O quarto botão será o default. MB_APPLMODAL A caixa de mensagem é mostrada na forma Modal, isto é, o usuário é

obrigado a responder à pergunta antes de passar para o próximo passo do programa.

MB_SYSTEMMODAL O mesmo que o MB_APPLMODAL, exceto que a caixa de mensagem tem o estilo WS_EX_TOPMOST. Use a caixa de mensagem system-modal para informar o usuário sobre erros sérios que devem ser reparados imediatamente.

MB_TASKMODAL O mesmo que o MB_APPLMODAL, exceto que todas as demais janelas que estiverem abertas serão desabilitadas se o primeiro parâmetro da caixa de mensagem for NULL.

MB_DEFAULT_DESKTOP_ONLY O desktop que recebe a caixa de mensagem deve ser o desktop padrão, se não for, a função falha.

MB_HELP Acrescenta um botão de Help na caixa de mensagens. Acionando este botão ou pressionando F1 gerará um evento de Help.

MB_RIGHT O texto é justificado à direita. MB_RTLREADING Mostra a mensagem e o caption da direita para a esquerda no sistema

Hebreu e Árabe. MB_SETFOREGROUND A caixa de mensagem torna-se uma janela de fundo (foreground

window). Internamente, o Windows chama a função SetForegroundWindow para a caixa de mensagem.

MB_TOPMOST A caixa de mensagem é criada no estilo de janela WS_EX_TOPMOST. • LÍNGUA: aqui colocamos uma constante que identifica a língua que queremos utilizar nos

captions dos botões. Mostramos as línguas disponíveis, e as sub-línguas quando for o caso:

LANG_AFRIKAANS LANG_ICELANDIC LANG_ALBANIAN LANG_INDONESIAN LANG_ARABIC LANG_ITALIAN LANG_BASQUE LANG_JAPANESE LANG_BELARUSIAN LANG_KOREAN LANG_BULGARIAN LANG_LATVIAN LANG_CATALAN LANG_LITHUANIAN LANG_CHINESE LANG_NEUTRAL LANG_CROATIAN LANG_NORWEGIAN LANG_CZECH LANG_POLISH LANG_DANISH LANG_PORTUGUESE LANG_DUTCH LANG_ROMANIAN LANG_ENGLISH LANG_RUSSIAN LANG_ESTONIAN LANG_SERBIAN LANG_FAEROESE LANG_SLOVAK LANG_FARSI LANG_SLOVENIAN LANG_FINNISH LANG_SPANISH LANG_FRENCH LANG_SWEDISH LANG_GERMAN LANG_THAI LANG_GREEK LANG_TURKISH

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 10 -

LANG_HEBREW LANG_UKRANIAN LANG_HUNGARIAN LANG_VIETNAMESE SUBLANG_ARABIC_SAUDI_ARABIA SUBLANG_GERMAN SUBLANG_ARABIC_IRAQ SUBLANG_GERMAN_SWISS SUBLANG_ARABIC_EGYPT SUBLANG_GERMAN_AUSTRIAN SUBLANG_ARABIC_LIBYA SUBLANG_GERMAN_LUXEMBOURG SUBLANG_ARABIC_ALGERIA SUBLANG_GERMAN_LIECHTENSTEIN SUBLANG_ARABIC_MOROCCO SUBLANG_ITALIAN SUBLANG_ARABIC_TUNISIA SUBLANG_ITALIAN_SWISS SUBLANG_ARABIC_OMAN SUBLANG_KOREAN SUBLANG_ARABIC_YEMEN SUBLANG_KOREAN_JOHAB SUBLANG_ARABIC_SYRIA SUBLANG_NEUTRAL SUBLANG_ARABIC_JORDAN SUBLANG_NORWEGIAN_BOKMAL SUBLANG_ARABIC_LEBANON SUBLANG_NORWEGIAN_NYNORSK SUBLANG_ARABIC_KUWAIT SUBLANG_PORTUGUESE SUBLANG_ARABIC_UAE SUBLANG_PORTUGUESE_BRAZILIAN SUBLANG_ARABIC_BAHRAIN SUBLANG_SERBIAN_LATIN SUBLANG_ARABIC_QATAR SUBLANG_SERBIAN_CYRILLIC SUBLANG_CHINESE_TRADITIONAL SUBLANG_SPANISH SUBLANG_CHINESE_SIMPLIFIED SUBLANG_SPANISH_MEXICAN SUBLANG_CHINESE_HONGKONG SUBLANG_SPANISH_MODERN SUBLANG_CHINESE_SINGAPORE SUBLANG_SPANISH_GUATEMALA SUBLANG_DEFAULT SUBLANG_SPANISH_COSTA_RICA SUBLANG_DUTCH SUBLANG_SPANISH_PANAMA SUBLANG_DUTCH_BELGIAN SUBLANG_ENGLISH_US SUBLANG_ENGLISH_UK SUBLANG_SPANISH_COLOMBIA SUBLANG_ENGLISH_AUS SUBLANG_SPANISH_PERU SUBLANG_ENGLISH_CAN SUBLANG_SPANISH_ARGENTINA SUBLANG_ENGLISH_NZ SUBLANG_SPANISH_ECUADOR SUBLANG_ENGLISH_EIRE SUBLANG_SPANISH_CHILE SUBLANG_ENGLISH_SOUTH_AFRICA SUBLANG_SPANISH_URUGUAY SUBLANG_ENGLISH_JAMAICA SUBLANG_SPANISH_PARAGUAY SUBLANG_ENGLISH_CARIBBEAN SUBLANG_SPANISH_BOLIVIA SUBLANG_ENGLISH_BELIZE SUBLANG_SPANISH_EL_SALVADOR SUBLANG_ENGLISH_TRINIDAD SUBLANG_SPANISH_HONDURAS SUBLANG_FRENCH SUBLANG_SPANISH_NICARAGUA SUBLANG_FRENCH_BELGIAN SUBLANG_SPANISH_PUERTO_RICO SUBLANG_FRENCH_CANADIAN SUBLANG_SWEDISH SUBLANG_FRENCH_SWISS SUBLANG_SWEDISH_FINLAND SUBLANG_FRENCH_LUXEMBOURG SUBLANG_SYS_DEFAULT SUBLANG_SPANISH_DOMINICAN_REPUBLIC SUBLANG_SPANISH_VENEZUELA

• IDENTIFICADOR DO BOTÃO ACIONADO: aqui colocamos uma constante que identifica qual foi a tecla acionada. Dentre os identificadores podemos enumerar:

IDABORT O botão Abort foi acionado IDCANCEL O botão Cancel foi acionado IDIGNORE O botão Ignore foi acionado IDNO O botão No foi acionado IDOK O botão OK foi acionado IDRETRY O botão Retry foi acionado IDYES O botão Yes foi acionado

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 11 -

• Veja o aspecto da caixa de mensagem deste programa:

q FASE 07: VERIFICANDO GRAVAÇÃO DO DESENHO: Seria bom que o seu programa testasse, antes de ser encerrado, se a figura que está na tela já foi gravada. Para isto, iremos declarar uma variável “Booleana”, de nome Gravou, globalmente e com o valor inicial True:

var Form1: TForm1; Tam, CorPreench: integer; Gravou: Boolean = true; implementation {$R *.DFM}

Esta variável vai receber o valor True toda vez que a figura for efetivamente gravada, e o valor False toda vez que se der um clique no objeto Image1. Vamos ver como vão ficar estes procedimentos: Faça a alteração que aparece em negrito, no evento OnClick do botão SpeedButton2:

procedure TForm1.SpeedButton2Click(Sender: TObject); begin If SaveDialog1.execute then begin Image1.Picture.SaveToFile(SaveDialog1.Filename); Gravou:= true; end; end;

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 12 -

Faça a alteração, que aparece em negrito, no evento OnMouseDown do objeto Image1:

Agora, antes de sair do programa, devemos testar o valor da variável Gravou. Se for false é porque ainda não gravamos, e deveremos dar uma mensagem de advertência. O melhor local para colocarmos esta advertência, talvez seja, na função Termina_programa:

Podemos notar nesta rotina, o aparecimento de uma chamada a um procedimento gerado pelo próprio Delphi (SpeedButton2Click) e que em cujo parâmetro de entrada, colocamos a palavra Self para indicar que o objeto atual (Sender) é o próprio. Esta palavra substitui o parâmetro Sender. Esta chamada evita termos que digitar novamente os comandos para gravação da figura. q FASE 08: CARREGANDO UM DESENHO PRONTO:

Para ler uma figura já gravada, iremos acrescentar mais 2 objetos do Delphi: outro SpeedButton que deverá ser colocado no Panel1 e um objeto OpenPictureDialog em qualquer lugar do formulário.

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin Image1.Canvas.Pen.color:= CorPreench; Image1.Canvas.Brush.Color:= CorPreench; Image1.Canvas.Ellipse(x-tam,y-tam,x+tam,y+tam); Gravou:= false; end;

function TForm1.Termina_programa: boolean; begin If not Gravou then begin If MessageBoxEx(0, 'A figura não foi gravada. Deseja fazê-lo agora ?', 'ATENÇÃO !!!', mb_YesNo+mb_IconWarning, lang_portuguese) = idYes then SpeedButton2Click(Self); end; Result:= true; If MessageBoxEx(0, 'Deseja mesmo sair do programa ?', 'ENCERRAMENTO ...', MB_YESNO+MB_ICONSTOP+MB_DEFBUTTON2, lang_portuguese) = idNo then Result:= false; end;

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 13 -

Vamos incluir estes objetos e alterar algumas de suas propriedades: SpeedButton3 Glyph = FldrOpen.bmp Height = 37 Width = 37 Left = 79 Top = 3 Hint = Abre uma figura existente ShowHint = True OpenPictureDialog1 Filter = BitMaps | *.bmp Todos os arquivos | *.* Title = Qual figura deseja abrir ? O evento OnClick do SpeedButon3 ficaria assim:

Não vejo necessidade de maiores explicações, pois, este tipo de procedimento já foi detalhado

anteriormente.

q FASE 09: DESENHANDO NOVOS ÍCONES:

Vamos dar uma pausa no nosso projeto para podermos explicar como se cria novos ícones. Estou vendo esta necessidade neste momento porque alguns novos botões que iremos implementar necessitam de ícones que não estão entre os fornecidos pelo Delphi.

Na realidade, no nosso programa precisaremos de BitMaps e não de ícones, pois, a propriedade Glyph trabalha com BitMaps, da seguinte forma:

BITMAPS PARA GLYPHS: As Glyphs admitem até 4 imagens em um mesmo BitMap. Estas imagens são colocadas lado a

lado, sendo que o componente sabe que deverá considerar como uma figura a largura igual à altura da mesma, isto é, a figura será sempre quadrada. Para se ter uma glyph com 4 figuras, esta deverá ter uma largura igual a até 4 vezes a altura.

Por exemplo, uma figura com 18 pixels de altura poderá ter: 18 ou 36 ou 54 ou 72 pixels de largura, dependendo se representará 1, 2, 3 ou 4 figuras.

Porque 4 figuras ? a resposta é simples: cada uma delas representa uma determinada situação que se encontra o botão acionado:

- A primeira figura é usada quando o botão está habilitado - A segunda, quando o botão está desabilitado - A terceira, quando o botão foi clicado - A quarta, somente para o SpeedButton (não funciona no BitBtn), quando o botão estiver

afundado (Down = true).

procedure TForm1.SpeedButton3Click(Sender: TObject); begin If OpenPictureDialog1.execute then Image1.Picture.LoadFromFile(OpenPictureDialog1.filename); end;

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 14 -

A utilização das imagens é automática, e a quantidade delas é determinada na propriedade NumGlyphs. Outra característica importante deve ser mencionada: a cor do pixel situado no canto inferior

esquerdo da figura indica a cor que representará a transparência, isto é, se por exemplo, este pixel for vermelho, todas as partes da figura que forem vermelhas serão transparentes e não vermelhas !

DESENHANDO NOVOS BITMAPS/ICONES/CURSORES: Para isto, acione no menu do ambiente Delphi a opção Tools à Image Editor, ou diretamente na

barra de tarefas do Windows: Iniciar à Programas à Borland Delphi à Image Editor. O programa Image Editor tem a seguinte aparência:

Se você abrir no menu a opção File à New, você irá deparar com 5 opções, dentre as quais

convém citar: BitMap File, Icon File e Cursor File, que correspondem, respectivamente, à confecção de BitMaps, Ícones e Cursores.

Escolhendo BitMap Files, surge a tela:

Poderemos informar a largura e a altura de nossa imagem. Vamos usar os valores 30 e 30, para

desenharmos uma figura com somente 1 imagem (botão habilitado). Se fôssemos desenhar 2 imagens, colocaríamos Width = 60 e Height = 30, e assim por diante.

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 15 -

Dando vários Ctrl I poderemos ampliar a nossa área de desenho para facilitar nossa visualização:

Vamos desenhar um ícone que possa representar a mudança do formato do ponto do nosso

desenho, de círculo para quadrado e vice-versa:

Em seguida, grave-o com um nome sugestivo, por exemplo: Circ-Quad.bmp.

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 16 -

Durante o nosso projeto, outros ícones deverão ser criados, por exemplo, um balde para indicar o preenchimento:

q FASE 10: ALTERANDO A FORMA DO PONTO:

Vamos agora, possibilitar ao usuário alterar o formato do ponto: entre o circular (como já está) e o quadrado.

Para executar isto, temos que acrescentar mais um objeto SpeedButton, agora dentro do Panel2 e colocar na propriedade Glyph do mesmo o ícone recém criado:

O seu evento OnClick ficará assim:

Você pode perceber o aparecimento da variável “circulo”. Esta variável deverá ser declarada globalmente como do tipo boolean e ela irá informar se a figura do ponto é um CIRCULO (true) ou QUADRADO (false). Nota-se também na rotina, a chamada ao procedimento Pincel. Este procedimento deverá ser modificado para reconhecer o formato do ponto:

procedure TForm1.SpeedButton4Click(Sender: TObject); begin Circulo:= not Circulo; Pincel; end;

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 17 -

No método OnMouseDown do objeto Image1 devemos também fazer uma modificação, para testar o formato do ponto a desenhar:

Mostramos a seguir a declaração global da variável Circulo assim como a sua inicialização:

var Form1: TForm1; Tam, CorPreench: integer; Gravou: boolean = true; Circulo: boolean = true;

q FASE 11: DESENHANDO UMA CURVA CONTÍNUA:

Para tal, acrescente ao projeto mais um SpeedButton (SpeedButton5), também dentro do Panel2. Sugerimos o seguinte ícone para o mesmo:

Altere a propriedade GroupIndex colocando o valor 1. Esta propriedade numera botões que

farão parte de um grupo onde apenas um deles ficará “afundado” (propriedade Down = true).

procedure TForm1.Pincel; var x, y: integer; begin x:= image2.width div 2; y:= image2.height div 2; Image2.picture:= nil; If corpreench <> Panel4.color then Image2.Canvas.Pen.color:= CorPreench else Image2.Canvas.Pen.color:= clBlack; Image2.Canvas.Brush.Color:= CorPreench; If circulo then Image2.canvas.ellipse(x-tam,y-tam,x+tam,y+tam) else Image2.canvas.rectangle(x-tam,y-tam,x+tam,y+tam); end;

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin Image1.Canvas.Pen.color:= CorPreench; Image1.Canvas.Brush.Color:= CorPreench; Image1.Canvas.Ellipse( x - tam , y - tam , x + tam , y + tam); If circulo then Image1.canvas.ellipse(x-tam,y-tam,x+tam,y+tam) else Image1.canvas.rectangle(x-tam,y-tam,x+tam,y+tam); Gravou:= false; end;

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 18 -

Quando Down é verdadeiro o botão fica “afundado”, até que outro botão do mesmo grupo seja acionado, quando então este último “afunda”, liberando o anterior.

Os demais botões do grupo ainda serão criados. Este é o primeiro deles. Para haver esta liberação, devemos acrescentar mais um SpeedButton (SpeedButton6), a ser

colocado no Panel2, contendo o seguinte ícone (sugestão):

Neste último botão inserido, deveremos alterar as propriedades: GroupIndex = 1 Down = true Para que uma curva seja traçada, é necessário repetir o desenho de um ponto à medida que o

mouse se desloca na tela. Podemos deduzir que o melhor evento a utilizar é o OnMouseMove do objeto Image1:

Observamos na rotina acima que a primeira coisa que fizemos foi verificar se o SpeedButton5

está “afundado” (down). Se estiver, e o botão esquerdo do mouse estiver acionado, deveremos desenhar o ponto nas cores da linha e do preenchimento atuais, assim como, na forma de um círculo ou de um quadrado.

Finalmente, atribuímos false ao valor da variável Gravou, para indicar que ainda não foi gravada a alteração que o usuário está fazendo no desenho.

Observação: As linhas traçadas desta forma, terão que ser feitas bem devagar para evitar o

aparecimento de muitos espaços entre os pontos. Existe uma maneira mais correta de desenhar de forma contínua, utilizando 2 métodos do objeto Image. O OnMouseDown seria o responsável para guardar em duas variáveis a posição inicial (x1,y1) da curva contínua. E no método OnMouseMove iremos programar o traçado de linhas utilizando o procedimento MoveTo(x1,y1), para marcar o ponto inicial da reta e LineTo(x2,y2) para indicar o ponto final da reta. Com isto, ao invés de termos pontos isolados, teríamos pequenas retas unindo os pontos, o que daria uma continuidade nas curvas.

Veja como ficariam estes eventos:

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin If SpeedButton5.down then {curva} If shift = [ssLeft] then begin Image1.Canvas.Pen.color:= CorPreench; Image1.Canvas.Brush.Color:= CorPreench; If circulo then Image1.Canvas.Ellipse(x-tam,y-tam,x+tam,y+tam) else Image1.Canvas.Rectangle(x-tam,y-tam,x+tam,y+tam); Gravou:= false; end; end;

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 19 -

var Form1: TForm1; X1,Y1, Tam, CorPreench: integer; Gravou: boolean = true; Circulo: boolean = true; :

: procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin Image1.Canvas.Pen.color:= CorPreench; Image1.Canvas.Brush.Color:= CorPreench; If circulo then Image1.canvas.ellipse(x-tam,y-tam,x+tam,y+tam) else Image1.canvas.rectangle(x-tam,y-tam,x+tam,y+tam); Gravou:= false; X1:= X; Y1:= Y; end; procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin If SpeedButton5.down then {curva} If shift = [ssLeft] then Begin

Image1.Canvas.Pen.color:= CorPreench; Image1.Canvas.Brush.Color:= CorPreench; If circulo then Image1.Canvas.Ellipse(x-tam,y-tam,x+tam,y+tam)

else Image1.Canvas.Rectangle(x-tam,y-tam,x+tam,y+tam); Image1.Canvas.Pen.Width:= 2 * Tam; Image1.Canvas.MoveTo(X1,Y1); Image1.Canvas.LineTo(X,Y); X1:= X; Y1:= Y; Gravou:= false; end; end; Devo comentar o seguinte: a) As variáveis X1 e Y1 são globais inteiras. b) Foram retiradas as linhas que se referem ao círculo ou quadrado (ver linhas riscadas) no

método do evento OnMouseMove de Image1. c) A espessura da linha é na verdade o diâmetro do círculo, ou seja, o dobro do valor de Tam. d) O método MoveTo indica o início da pequena reta e LineTo o ponto final. e) Finalmente, X1 e Y1 assumem as coordenadas do último ponto da pequena reta para que

na próxima vez que esta rotina for chamada, isto é, depois do próximo movimento do mouse, uma nova pequena reta seja desenhada, agora à partir da nova posição (X1,Y1).

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 20 -

q FASE 12: SELECIONANDO UMA ÁREA RETANGULAR:

Image1.Canvas.DrawFocusRect(RetSelect); Utilizaremos o procedimento DrawFocusRect da propriedade Canvas do objeto Image1 para desenhar um retângulo dado pelas coordenadas definidas na variável RetSelect cuja declaração será feita a seguir. Através desta rotina, um retângulo é desenhado fazendo um XOR (ou-exclusivo) entre a cor de Brush do Canvas com a cor dos pontos por onde passa o retângulo, o que nos proporcionará a oportunidade de apaga-lo sempre que quisermos, bastando chamar novamente tal procedimento, isto é, a execução de dois XOR seguidos faz retornar à condição inicial . Para desenhar o retângulo na tela, faremos uso de 2 eventos do objeto Image1: No evento OnMouseDown iremos acrescentar as linhas em negrito (colocadas abaixo), que farão com que as variáveis Sx e Sy recebam os valores atuais da posição do “mouse”, caso estejamos acionando o botão direito do mouse. Estas variáveis irão guardar as coordenadas do canto superior esquerdo do retângulo de seleção:

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin If Shift = [ssRight] then begin Sx:= x; Sy:= y; exit; end; Image1.Canvas.Pen.color:= CorPreench; Image1.Canvas.Brush.Color:= CorPreench; If circulo then Image1.canvas.ellipse(x-tam,y-tam,x+tam,y+tam) else Image1.canvas.rectangle(x-tam,y-tam,x+tam,y+tam); Gravou:= false; X1:= X; Y1:= Y; end;

No evento OnMouseMove, iremos traçar o retângulo de seleção enquanto movemos o mouse. Para isto, o retângulo será desenhado e apagado sucessivamente, para dar este efeito de movimento:

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin If shift = [ssRight] then begin Image1.Canvas.DrawFocusRect(RetSelect); RetSelect:= Rect(Sx,Sy,x,y); Image1.Canvas.Brush.color:= clAqua; Image1.Canvas.DrawFocusRect(RetSelect); exit; end;

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 21 -

If SpeedButton5.down then {curva} If shift = [ssLeft] then begin Image1.Canvas.Pen.Width:= 2 * Tam; Image1.Canvas.MoveTo(X1,Y1); Image1.Canvas.LineTo(X,Y); X1:= X; Y1:= Y; Gravou:= false; end; end;

Na rotina, o programa irá fazer o teste se estamos acionando o botão direito do mouse. Se estiver, o

programa fará o seguinte:

a) desenha o retângulo com as coordenadas atuais guardadas na variável RetSelect, que na primeira vez não tem significado. Nas vezes seguintes, servirá para apagar o último retângulo desenhado pelo mesmo procedimento repetido na terceira linha logo abaixo.

b) A variável RetSelect, que nós inventamos, irá guardar as coordenadas do retângulo que serão transformadas para o formato TRect através da função Rect, para a qual informamos as coordenadas do canto superior esquerdo do retângulo (Sx,Sy), obtidas no evento OnMouseDown do objeto Image1 e a do canto inferior direito (x,y), posição atual do ponteiro do mouse.

c) Atribuímos à propriedade Brush a cor azul claro (clAqua), para desenhar o retângulo de seleção na cor resultante do Xor entre esta cor e a do preenchimento da figura pela qual se passa o retângulo ou do fundo da tela (branco), o que resultará uma cor diferente.

d) É desenhado o retângulo de seleção com as coordenadas:

e) O comando Exit é utilizado para sair da rotina sem executar as demais linhas da mesma.

Agora, não podemos esquecer de declarar as 3 últimas variáveis incluídas no programa:

var Form1: TForm1; X1,Y1,Sx, Sy, Tam, CorPreench: integer; Gravou: boolean = true; Circulo: boolean = true; RetSelect: TRect;

(Sx,Sy)

(x,y)

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 22 -

q FASE 13: PERMITINDO SELECIONAR ÁREA EM QUALQUER DIREÇÃO:

Você poderá facilmente constatar que a seleção ora implementada, só permite que arrastemos o retângulo no sentido sudeste. Nas demais direções o retângulo não se forma.

Para que o retângulo seja desenhado em qualquer direção, temos que alterar algumas linhas do

trecho que desenha tal retângulo no método OnMouseMove do objeto Image1: A solução encontrada é a de criarmos 2 variáveis “booleanas” que nos informarão se a abcissa

e/ou a ordenada de um dos cantos do retângulo está do outro lado. Toda vez que iniciamos o desenho do retângulo, atribuímos a Sx e a Sy os valores fixos do

primeiro ponto do retângulo (ver método OnMouseDown do objeto Image1). Só que tal retângulo só pode ser desenhado no sentido sudeste, e portanto, toda vez que isto não acontecer temos que trocar a posição das abcissas e/ou ordenadas para que sempre o desenho seja feito no sentido sudeste. Para o usuário, isto será transparente, pois, o desenho é feito tão rapidamente que ele não perceberá que foi feito neste sentido.

Portanto, digite as instruções que aparecem em negrito e que causam este efeito, além de

eliminar a linha sugerida:

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); var trocax, trocay: boolean; begin If shift = [ssRight] then begin Image1.Canvas.DrawFocusRect(RetSelect); If Sx > x then trocax:= true else trocax:= false; If Sy > y then trocay:= true else trocay:= false; If trocax and trocay then RetSelect:= Rect(x,y,Sx,Sy); If not trocax and not trocay then RetSelect:= Rect(Sx,Sy,x,y); If not trocax and trocay then RetSelect:= Rect(Sx,y,x,Sy); If trocax and not trocay then RetSelect:= Rect(x,Sy,Sx,y); RetSelect:= Rect(Sx,Sy,x,y); Image1.Canvas.Brush.color:= clAqua; Image1.Canvas.DrawFocusRect(RetSelect); exit; end; If SpeedButton5.down then {curva} If shift = [ssLeft] then begin Image1.Canvas.Pen.Width:= 2 * Tam; Image1.Canvas.MoveTo(X1,Y1); Image1.Canvas.LineTo(X,Y); X1:= X; Y1:= Y; Gravou:= false; end; end;

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 23 -

Verificamos que o valor da variável RetSelect será de acordo com a posição do ponto móvel em relação ao fixo, do retângulo de seleção:

Sx > x Sy > y Trocax = true Trocay = true

q FASE 14: COLORINDO UMA ÁREA DO DESENHO:

Para este tipo de efeito, utilizaremos o método FloodFill do Canvas. A sintaxe deste comando é a seguinte:

procedure FloodFill(X, Y: Integer; Color: TColor; FillStyle: TFillStyle); É utilizado para preencher uma área não retangular de uma imagem com o valor atual de

Brush. Os limites da região de preenchimento são determinados imaginando que um “balde de tinta” é

entornado na posição do ponto interno à figura (X,Y) e começa a escorrer até encontrar a cor determinada por Color. A interpretação desta cor depende do terceiro e último parâmetro: FillStyle.

FillStyle determinará se o preenchimento irá até encontrar a cor especificada pelo parâmetro Color ou até que encontre uma cor diferente deste parâmetro. O valor fsSurface para este parâmetro indica que irá preencher com a cor de Brush enquanto estiver sobre a cor do parâmetro Color (surface = superfície) e se este valor for fsBorder, irá espalhar enquanto a cor for diferente de Color.

Explicando de uma forma diferente:

FillStyle = fsBorder (cor da borda) preenche até achar a cor Color. FillStyle = fsSurface (cor da superfície) preenche enquando a cor for Color.

Voltando ao assunto: Acrescente mais um botão (SpeedButton7), dentro do objeto Panel2,

alterando a sua propriedade GroupIndex para 1. Para este botão, sugerimos o ícone:

Sx < x Sy > y Trocax = false Trocay = true

Sx > x Sy < y Trocax = true Trocay = false

Sx < x Sy < y Trocax = false Trocay = false

As setas indicam o sentido do movimento

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 24 -

No evento OnMouseDown do objeto Image1 digite o trecho que aparece em negrito:

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var Cor: TColor; begin If Shift = [ssRight] then begin Sx:= x; Sy:= y; exit; end; If SpeedButton7.Down then begin Cor:= Image1.Canvas.Pixels[x,y]; Image1.Canvas.Brush.Color:= CorPreench; Image1.Canvas.FloodFill(x,y,Cor,fsSurface); Exit; end; Image1.Canvas.Pen.color:= CorPreench; Image1.Canvas.Brush.Color:= CorPreench; If circulo then Image1.canvas.ellipse(x-tam,y-tam,x+tam,y+tam) else Image1.canvas.rectangle(x-tam,y-tam,x+tam,y+tam); Gravou:= false; X1:= X; Y1:= Y; end;

Traduzindo o trecho acrescentado (em negrito): Se o botão SpeedButton7 estiver “afundado”, o programa realizará os seguintes passos: a) A variável local “Cor” irá pegar a cor do pixel onde aconteceu o clique do ponteiro do mouse.

Esta cor será um dos parâmetros do método FloodFill, isto é, irá informar qual é a cor que será substituída pela cor atual de preenchimento (CorPreench).

b) Atribuímos a cor de preenchimento atual à propriedade Brush do Canvas do objeto Image1. c) Executamos o método FloodFill que irá preencher toda a área que contiver a cor “Cor” com a

cor “CorPreench” até encontrar uma cor diferente de “Cor” (fsSurface). d) O comando “Exit” faz com que o controle do programa saia deste método sem executar as

demais linhas do mesmo. q FASE 15: BOTÃO PARA SAIR DO PROGRAMA:

Desta vez, iremos sair do programa fechando o formulário principal, para que tenhamos certeza que o evento OnClose do formulário seja chamado, pois, neste evento, programamos a saída do programa testando se o desenho foi gravado e perguntando se realmente o usuário deseja sair !

Para implentar isto, acrescente mais um botão SpeedButton no Panel1 e digite no seu evento OnClick a seguinte linha de comando:

Form1.close;

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 25 -

O ícone utilizado foi o DoorShut.bmp encontrado na pasta: \Arquivos de programas\Arquivos

comuns\Borland Shared\Images\Buttons. q FASE 16: DESENHANDO FIGURAS GEOMÉTRICAS SIMPLES:

Vamos acrescentar mais 3 botões SpeedButton: o primeiro irá desenhar retângulos, o segundo elipses e o terceiro linhas retas. Em todos eles altere a propriedade GroupIndex para 1. Sugerimos para ícones os seguintes desenhos:

Abaixo descrevemos as modificações realizadas nos eventos OnMouseDown e OnMouseMove

do objeto Image1, pois, é aqui que os desenhos acontecem:

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var cor: TColor; begin If (Shift = [ssRight]) or (SpeedButton9.down) or (SpeedButton10.down) or (SpeedButton11.down) then begin Sx:= x; Sy:= y; exit; end; If SpeedButton7.Down then … etc …

No evento OnMouseMove de Image1 faça as alterações que aparecem em negrito:

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); var trocax, trocay: boolean; begin If SpeedButton9.down or SpeedButton10.down or SpeedButton11.down then begin X1:= x; Y1:= y; end; If shift = [ssRight] then begin

. . . . etc . . . . .

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 26 -

Na rotina acima, atribuímos a X1 o valor atual da abcissa x do mouse e a Y1 o valor atual da ordenada y do mouse.

Você já deve ter percebido que o que fizemos não irá desenhar nenhuma figura geométrica. O

problema é que temos que escolher um evento para realizar isto. Se imaginarmos como o usuário estará operando o programa neste momento, poderemos concluir que o melhor evento é o OnMouseUp, pois, após ter “esticado” o retângulo, provavelmente ele irá soltar o botão do mouse, o que fará acionar o evento escolhido.

Por esta razão, apresentamos a seguir os comandos de desenho de figuras, colocados dentro do

evento OnMouseUp do objeto Image1.

Para desenhar um retângulo, não utilizamos o método Rectangle, pois, o mesmo desenha o retângulo opaco, isto é, preenchido com a cor de brush. Para desenharmos apenas o contorno do retângulo, escolhemos o método PolyLine que desenha uma série de linhas interligadas, cujos pontos são dados por um vetor contendo dados do tipo Tpoint.

O tipo Tpoint é um registro contendo os campos X e Y do tipo inteiro. Internamente, Tpoint é declarado como: Tpoint = Record X, Y: integer; end; Para guardarmos os pontos da “polyline” declaramos a variável P da seguinte forma:

var P: array[1..5] of Tpoint;

Para desenhar uma elipse, não utilizamos o método Ellipse, pois, da mesma forma que o

retângulo, este desenho será opaco, ou seja, preenchido com a cor de brush. Conseguiremos desenhar apenas o contorno da elipse se fizermos uso do método Arc, cuja sintaxe já é conhecida.

Para desenhar a reta, utilizamos dois métodos: MoveTo, para colocar o início da reta nas

coordenadas especificadas; e o método LineTo, que desenha a reta do último ponto marcado até o ponto informado nos parâmetros deste método. Resumindo: uma reta será traçada do ponto (Sx,Sy) até ao ponto (Fx,Fy), pontos que correspondem à diagonal do retângulo de seleção.

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 27 -

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var P: array[1..5] of Tpoint; begin // Desenha o retângulo If SpeedButton9.Down and (button = mbRight) then If (Sx <> X1) or (Sy <> Y1) then begin RetSelect:= Rect(0,0,0,0); Image1.Canvas.Pen.color:= CorPreench; Image1.Canvas.pen.Width:= 2 * Tam; P[1].X:= Sx; P[1].Y:= Sy; P[2].X:= X1; P[2].Y:= Sy; P[3].X:= X1; P[3].Y:= Y1; P[4].X:= Sx; P[4].Y:= Y1; p[5]:= p[1]; Image1.Canvas.PolyLine(P); Gravou:= false; Exit; end; // Desenha a elipse If SpeedButton10.Down and (button = mbRight) then If (Sx <> X1) or (Sy <> Y1) then begin Image1.Canvas.DrawFocusRect(RetSelect); RetSelect:= Rect(0,0,0,0); Image1.Canvas.Pen.color:= CorPreench; Image1.Canvas.pen.Width:= 2 * Tam; Image1.Canvas.Arc(Sx,Sy,X1,Y1,X1,Y1,X1,Y1); Gravou:= false; Exit; end; // Desenha a reta If SpeedButton11.Down and (button = mbRight) then If (Sx <> X1) or (Sy <> Y1) then begin Image1.Canvas.DrawFocusRect(RetSelect); RetSelect:= Rect(0,0,0,0); Image1.Canvas.Pen.color:= CorPreench; Image1.Canvas.pen.Width:= 2 * Tam; Image1.Canvas.MoveTo(Sx,Sy); Image1.Canvas.LineTo(X1,Y1); Gravou:= false; Exit; end; end;

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 28 -

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 29 -

Funcionamento da rotina: Retângulo:

a. Se o botão de retângulo está acionado, então pode desenhar o retângulo. b. Testamos, em seguida, se o ponteiro do mouse foi movido, verificando se os valores

de “Sx” e de “Sy” estão diferentes da posição do mouse ao soltar o botão. Isto foi feito para evitar desenhar o retângulo em cima do ponto inicial, ou seja, apareceria um ponto no desenho.

c. Zeramos “RetSelect” para que o retângulo de seleção desapareça ao movermos o mouse.

d. Atribuímos à cor da linha a mesma cor de preenchimento e alteramos o tamanho do ponto (width) para 2 vezes o valor de “Tam” (lembre que Tam representa o raio do ponto e não o seu diâmetro)

e. Para desenhar o retângulo, atribui-se à variável “P” as coordenadas de cada um dos cantos do mesmo, fazendo um polígono fechado (por isto tem 5 valores).

f. O desenho é efetivamente realizado ao ser acionado o método PolyLine do Canvas. g. Agora, saímos da rotina (Exit).

Elipse/Círculo:

a) Primeiramente, como no retângulo fizemos os testes iniciais. b) Também, conforme o retângulo, “apagamos” o retângulo de seleção. c) Atribuímos as cores e tamanho da pena d) Desenhamos o arco fechado (ellipse ou círculo) e) Saímos da rotina.

Linha Reta:

a) Para a reta, repetimos as operações iniciais. b) Através do método MoveTo posicionamos o primeiro ponto da reta (Sx,Sy). c) Através do método LineTo desenhamos a reta do primeiro ponto até o atual

(X1,Y1). q FASE 17: DESENHANDO TEXTO:

Acrescente ao projeto mais um SpeedButton no Panel2, e altere as seguintes propriedades do mesmo:

GroupIndex = 1 Caption = T Font.Name = Times New Roman Font.Size = 20 Font.Style = fsBold

Acrescente também um objeto FontDialog, que está na aba “Dialogs”. No método do evento OnClick do botão, digite os comandos necessários a alterar a fonte dos

caracteres que serão desenhados no Canvas do objeto Image1, assim como a dos caracteres que serão digitados dentro de um objeto Edit que será acrescentado ao projeto a seguir:

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 30 -

procedure TForm1.SpeedButton12Click(Sender: TObject); begin If FontDialog1.execute then begin Image1.Canvas.Font:= FontDialog1.font; Edit1.Font:= FontDialog1.font; end; end;

Acrescente agora um objeto Edit colocando-o dentro do objeto Image1. Este objeto Edit1

será o meio pelo qual o usuário poderá digitar o texto que quer colocar no desenho, desfrutando de todas as possibilidades de edição, sem que nós tenhamos que nos preocupar com elas.

Depois que o usuário digitar o seu texto é que o colocaremos no Canvas do Image1, tomando o cuidado de fazer com que o usuário não perceba este “macete”. Para isto, iremos colocar o objeto Edit1 exatamente no local onde o usuário escolheu para colocar o texto, e tornando-o invisível depois que o texto for digitado.

Para conseguir estes efeitos, altere as seguintes propriedades de Edit1: Text = (deixar em branco) Visible = false BorderStyle = bsNone Inicialmente o Edit1 está invisível. Para torná-lo visível e ter o foco do cursor, iremos utilizar o

método do evento OnMouseDown do Image1, da seguinte forma:

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var cor: TColor; begin If (Shift = [ssRight]) or (SpeedButton9.down) or (SpeedButton10.down) or (SpeedButton11.down) then begin Sx:= x; Sy:= y; exit; end; If SpeedButton12.Down then begin Edit1.left:= x; Edit1.top:= y+panel1.height; Edit1.width:= Image1.width-x; Edit1.Height:= Trunc(Edit1.font.Size * 1.5); Edit1.visible:= true; Edit1.setfocus; Gravou:= false; Exit; end; If SpeedButton7.Down then . . . . . ETC . . . . .

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 31 -

Se o SpeedButton12 estiver “afundado”, significa que queremos escrever um texto. Por isto

alteramos a posição do objeto Edit1 através dos valores de suas propriedades Left e Top. Estas propriedades recebem o valor da coordenada atual do mouse acrescida da altura do painel 1, pois, as mesmas se referem à distancia do objeto Edit1 até uma das laterais do formulário, e não do objeto Image1.

Tivemos o cuidado também de aumentar o comprimento de Edit1 para que o texto não fique

“rolando” horizontalmente, assim como a sua altura.. O comprimento é representado pela propriedade Width, cujo valor será igual ao comprimento horizontal do Image1 subtraído da abcissa do ponteiro do mouse. Já a altura é representada pela propriedade Height, cujo valor é de uma vez e meia o tamanho da fonte.

Em seguida, tornamos o objeto Edit1 visível e depois colocamos o foco no mesmo (onde o

cursor de texto irá ficar). Finalmente, saímos da rotina sem executar as demais linhas. Isto não faz com que o texto seja desenhado ! Para desenharmos o texto, iremos utilizar o método TextOut. Iremos fazer uso do evento

OnKeyPress do objeto Edit1 para executá-lo, pois, é fácil de deduzir que a transposição do texto digitado em Edit1 para o desenho deve acontecer quando o usuário acionar a tecla [Enter]. E o melhor local para testar se tal tecla foi acionada é no evento mencionado:

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); begin If key = #13 then begin Edit1.visible:= false; Image1.Canvas.Brush.color:= Image1.Canvas.Pixels[Edit1.left,Edit1.top]; Image1.Canvas.TextOut(Edit1.left,Edit1.top-panel1.Height,Edit1.text); Edit1.text:= ''; Image1.Canvas.Brush.color:= CorPreench; end; end;

Se a tecla acionada for o [Enter], cujo código ASCII é 13, faremos:

a) O objeto Edit1 se tornará novamente invisível. b) A cor de fundo do texto será a mesma cor do pixel localizado na posição do canto

superior esquerdo do objeto Edit1, que agora não está mais lá. c) O texto é “desenhado” no mesmo local onde estava o Edit1 através do método

TextOut do Canvas do objeto Image1. d) Em seguida, apagamos o texto do objeto Edit1, embora o objeto já esteja invisível. e) Retornamos a cor de preenchimento do Canvas para a cor corrente.

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 32 -

q FASE 18: CAPTURANDO A COR DO PIXEL:

Vamos agora, possibilitar ao programa “pegar” uma cor que já esteja na tela para aplicá-la em outro local:

Acrescente ao projeto mais um SpeedButton no Panel2, e altere a sua propriedade GroupIndex para 1. Para colocar na propriedade Glyph sugerimos você criar um ícone como este:

Para conseguir o efeito desejado, basta que altere mais uma vez o método do evento

OnMouseDown, acrescentando as linhas que aparecem em negrito na listagem a seguir:

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var cor: TColor; begin If (Shift = [ssRight]) or (SpeedButton9.down) or (SpeedButton10.down) or (SpeedButton11.down) then begin Sx:= x; Sy:= y; exit; end; If SpeedButton13.Down then begin CorPreench:= Image1.Canvas.Pixels[x,y]; Pincel; Gravou:= false; Exit; end; If SpeedButton12.Down then begin . . . . ETC . . . .

No trecho acima destacado, verifica-se se o SpeedButton13 está “afundado”. Se estiver, é

porque foi selecionado o “conta-gotas”: a) Primeiramente, altera-se a cor de preenchimento (CorPreench), atribuindo-se à mesma a cor

do pixel onde está o cursor do mouse (“chupando” a cor). b) Em seguida, chama-se a rotina “Pincel” para atualizar a forma e a cor do preview do ponto. c) Finalmente, saímos da rotina sem executar as demais linhas da mesma.

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 33 -

q FASE 19: IMPRIMINDO O DESENHO:

Para completar o nosso projeto vamos implementar a possibilidade de imprimir o desenho que estiver na tela.

Basta que criemos um formulário de relatório (Quick Report) e nele coloquemos um objeto

QRImage, e transportemos para o mesmo a imagem que se encontra no objeto Image1. O melhor evento para se fazer isto é o Onclick de um novo objeto SpeedButton que iremos

acrescentar ao projeto. Acrescente, portanto, ao projeto mais um SpeedButton, colocando-o no Panel1 e coloque na

sua propriedade Glyph o ícone “Printer.bmp” que podemos encontrar na pasta: \Arquivos de Programas\Arquivos Comuns\Borland Shared\Images\Buttons.

Agora, vá ao menu File è New è aba Forms è duplo clique no ícone Quick Report List Um novo formulário será criado. Neste novo formulário faça:

a) Apague as “Bands”, deixando apenas a de nome Title. b) Aumente o tamanho da mesma e acrescente a ela um objeto QRImage. Com o

tamanho que gostaria de imprimir o desenho. c) Dê um clique em TitleBand e acione o método do evento Before Print da mesma,

acrescentando nele a linha que aparece em negrito:

A linha acrescentada, transporta a imagem que se encontra no objeto Image1 que está no

formulário Form1, para o objeto QRImage1 que está no formulário QRListForm. O evento BeforePrint, é acionado “antes de iniciar” a impressão da “band” Title. Para realmente conseguirmos imprimir alguma coisa, temos que programar o evento OnClick

do SpeedButton14. Digite o que aparece em negrito: Ao rodar o programa irão surgir duas caixas de mensagens perguntando se pode declarar a

Unit1 dentro da Unit2, assim como a Unit2 dentro da Unit1. Responda afirmativamente às duas solicitações.

procedure TQRListForm.TitleBand1BeforePrint(Sender: TQRCustomBand; var PrintBand: Boolean); begin QRImage1.Picture:= Form1.Image1.picture; end;

procedure TForm1.SpeedButton14Click(Sender: TObject); begin QRListForm.QuickRep1.Preview; // para imprimir direto coloque Print ao invés

// de Preview end;

LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho

Desenho Livre - Página 34 -

q FASE 23: OUTRAS IMPLEMENTAÇÕES:

Vamos considerar o nosso projeto terminado !!! Outras implementações podem ser incorporadas. Se tiver interesse, tente fazê-las. Por exemplo:

- Poder modificar o tipo de linha do desenho; - Poder alterar o estilo de preenchimento; - Poder mover uma área selecionada dentro do desenho; - Poder inserir outros desenhos dentro de uma área previamente selecionada; - Poder girar o desenho; - Poder trocar uma cor por outra em todo o desenho; - Poder alterar o tamanho do desenho na impressão; - Poder desenhar retângulos ou elipses já preenchidos; - Poder desenhar linhas sucessivas (polylines); - Etc ...

JJJJJJJJJJJJJJJJJJJJJJJJJJ