LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho 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 Form1 Panel1 Panel2 Image1 ! Propriedades Caption Color Position Height Width BorderStyle BorderIcons Caption Height Align Caption Height Align Align Descrição/Valor DESENHO LIVRE clWhite poScreenCenter 478 639 bsSingle BiMaximize = false (deixar em branco) 41 alTop (deixar em branco) 41 alBottom AlClient 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: { Private declarations } public { Public declarations } end; var Form1: TForm1; Tam, - Página CorPreench: integer; implementation {$R *.DFM} end. 1- LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho " Digite apenas o que está em negrito, exatamente no local indicado ! 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. 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: (x-tam,y-tam) (x+tam,y+tam) (x,y) 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). ! 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. - Página 2 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho Agora, digite as instruções (que estão em negrito) a seguir, dentro do evento OnKeyPress do formulário: 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; 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). ! 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). - Página 3 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho Insira dentro de Panel1 um SpeedButton e um objeto ColorDialog e altere as algumas de suas propriedades: SpeedButton1 Glyph Height Width Left Top Hint ShowHint = = = = = = = Brush.bmp 36 36 3 3 Escolhe a cor de preenchimento 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 procedure TForm1.SpeedButton1Click(Sender: TObject); begin If ColorDialog1.execute then CorPreench:= ColorDialog1.color; end; diálogo. Nota-se na rotina acima que alteramos a cor de preenchimento (CorPreench), atribuindo à mesma o valor da propriedade Color do objeto ColorDialog1. ! 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 Width Height Color BevelWidth BevelInner Top Left = = = = = = = = (deixar em branco) 37 37 clWhite 3 bvLowered 2 3 Agora, acrescente dentro deste Panel3 um objeto Image, e altere as suas seguintes propriedades: Image2 Width Height Top Left = = = = 25 25 7 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; - Página 4 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho 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, é atribuido 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: procedure TForm1.SpeedButton1Click(Sender: TObject); begin If ColorDialog1.execute then CorPreench:= ColorDialog1.color; Pincel; end; 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: 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; 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.FormCreate(Sender: TObject); begin Tam:= 1; CorPreench:= clBlack; Pincel; end; ! 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. Altere as seguintes propriedades dos mesmos: SpeedButton2 Glyph Height Width Left Top Hint ShowHint = = = = = = = - Página 5 - Floppy.bmp 37 37 41 3 Grava o desenho atual True LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho SaveDialog1 DefaultExt Filter = = Title = *.bmp BitMaps | *.bmp Todos os arquivos | *.* Grava figura Agora, temos que programar o método correspondente ao evento Onclick do objeto SpeedButton2: procedure TForm1.SpeedButton2Click(Sender: TObject); begin If SaveDialog1.execute then begin Image1.Picture.SaveToFile(SaveDialog1.Filename); end; end; 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. ! 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. Se quizermos 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 da seguinte forma: procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin If not Termina_programa then Abort; O comando Abort, “aborta” este evento, isto é, o formulário não é fechado, caso a função end; “Termina_programa” (que descreveremos a seguir) retorne o valor False. 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, pela primeira vez, 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. - Página 6 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho • BOTÕES: são códigos que identificam quais os botões 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 MB_ABORTRETRYIGNORE MB_OK MB_OKCANCEL MB_RETRYCANCEL MB_YESNO MB_YESNOCANCEL MB_ICONEXCLAMATION, MB_ICONWARNING MB_ICONINFORMATION, MB_ICONASTERISK MB_ICONQUESTION MB_ICONSTOP, MB_ICONERROR, MB_ICONHAND MB_DEFBUTTON1 MB_DEFBUTTON2 MB_DEFBUTTON3 MB_DEFBUTTON4 MB_APPLMODAL Descrição A caixa de mensagem conterá três botões: Abort, Retry, e Ignore. A caixa de mensagem conterá um botão OK. A caixa de mensagem conterá dois botões: OK e Cancel. A caixa de mensagem conterá dois botões: Retry e Cancel. A caixa de mensagem conterá dois botões: Yes e No. A caixa de mensagem conterá três botões: Yes, No, e Cancel. Um ícone com um ponto de exclamação será colocado na caixa de mensagem. Um ícone com a letra i dentro de um círculo será colocado na caixa de mensagem. Um ícone com um ponto de interrogação será colocado na caixa de mensagem. Um ícone com o sinal STOP será colocado na caixa de mensagem. O primeiro botão será o default. O segundo botão será o default. O terceiro botão será o default. O quarto botão será o default. 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. - Página 7 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho MB_SYSTEMMODAL MB_TASKMODAL MB_DEFAULT_DESKTOP_ONLY MB_HELP MB_RIGHT MB_RTLREADING MB_SETFOREGROUND MB_TOPMOST • O mesmo que o MB_APPLMODAL, exceto que a caixa de mensagem tem o estilo WS_EX_TOPMOST. Use a caixa de mensagem systemmodal para informar o usuário sobre erros sérios que devem ser reparados imediatamente. 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. O desktop que recebe a caixa de mensagem deve ser o desktop padrão, se não for, a função falha. Acrescenta um botão de Help na caixa de mensagens. Acionando este botão ou pressionando F1 gerará um evento de Help. O texto é justificado à direita. Mostra a mensagem e o caption da direita para a esquerda no sistema Hebreu e Árabe. 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. 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_ALBANIAN LANG_ARABIC LANG_BASQUE LANG_BELARUSIAN LANG_BULGARIAN LANG_CATALAN LANG_CHINESE LANG_CROATIAN LANG_CZECH LANG_DANISH LANG_DUTCH LANG_ENGLISH LANG_ESTONIAN LANG_FAEROESE LANG_FARSI LANG_FINNISH LANG_FRENCH LANG_GERMAN LANG_GREEK LANG_HEBREW LANG_HUNGARIAN LANG_ICELANDIC LANG_INDONESIAN LANG_ITALIAN LANG_JAPANESE LANG_KOREAN LANG_LATVIAN LANG_LITHUANIAN LANG_NEUTRAL LANG_NORWEGIAN LANG_POLISH LANG_PORTUGUESE LANG_ROMANIAN LANG_RUSSIAN LANG_SERBIAN LANG_SLOVAK LANG_SLOVENIAN LANG_SPANISH LANG_SWEDISH LANG_THAI LANG_TURKISH LANG_UKRANIAN LANG_VIETNAMESE SUBLANG_ARABIC_SAUDI_ARABIA SUBLANG_ARABIC_IRAQ SUBLANG_ARABIC_EGYPT SUBLANG_ARABIC_LIBYA SUBLANG_ARABIC_ALGERIA SUBLANG_ARABIC_MOROCCO SUBLANG_ARABIC_TUNISIA SUBLANG_ARABIC_OMAN SUBLANG_ARABIC_YEMEN SUBLANG_ARABIC_SYRIA SUBLANG_ARABIC_JORDAN SUBLANG_ARABIC_LEBANON SUBLANG_ARABIC_KUWAIT SUBLANG_ARABIC_UAE SUBLANG_ARABIC_BAHRAIN SUBLANG_ARABIC_QATAR SUBLANG_CHINESE_TRADITIONAL SUBLANG_CHINESE_SIMPLIFIED SUBLANG_CHINESE_HONGKONG SUBLANG_CHINESE_SINGAPORE SUBLANG_DEFAULT SUBLANG_DUTCH SUBLANG_DUTCH_BELGIAN SUBLANG_ENGLISH_UK SUBLANG_ENGLISH_AUS SUBLANG_ENGLISH_CAN SUBLANG_ENGLISH_NZ SUBLANG_ENGLISH_EIRE SUBLANG_GERMAN SUBLANG_GERMAN_SWISS SUBLANG_GERMAN_AUSTRIAN SUBLANG_GERMAN_LUXEMBOURG SUBLANG_GERMAN_LIECHTENSTEIN SUBLANG_ITALIAN SUBLANG_ITALIAN_SWISS SUBLANG_KOREAN SUBLANG_KOREAN_JOHAB SUBLANG_NEUTRAL SUBLANG_NORWEGIAN_BOKMAL SUBLANG_NORWEGIAN_NYNORSK SUBLANG_PORTUGUESE SUBLANG_PORTUGUESE_BRAZILIAN SUBLANG_SERBIAN_LATIN SUBLANG_SERBIAN_CYRILLIC SUBLANG_SPANISH SUBLANG_SPANISH_MEXICAN SUBLANG_SPANISH_MODERN SUBLANG_SPANISH_GUATEMALA SUBLANG_SPANISH_COSTA_RICA SUBLANG_SPANISH_PANAMA SUBLANG_ENGLISH_US SUBLANG_SPANISH_COLOMBIA SUBLANG_SPANISH_PERU SUBLANG_SPANISH_ARGENTINA SUBLANG_SPANISH_ECUADOR SUBLANG_SPANISH_CHILE - Página 8 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho 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 IDCANCEL IDIGNORE IDNO IDOK IDRETRY IDYES • ! O botão Abort foi acionado O botão Cancel foi acionado O botão Ignore foi acionado O botão No foi acionado O botão OK foi acionado O botão Retry foi acionado O botão Yes foi acionado Veja o aspecto da caixa de mensagem deste programa: 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: var Form1: TForm1; Tam, CorPreench: integer; Gravou: boolean; 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 asalteraçã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; - Página 9 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho Faça a alteração, que aparece em negrito, no evento OnMouseDown do objeto Image1: 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; 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: 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; A novidade que 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. ! 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. Vamos incluir estes objetos e alterar algumas de suas propriedades: SpeedButton3 Glyph Height Width Left Top Hint ShowHint = = = = = = = FldrOpen.bmp 37 37 79 3 Abre uma figura existente True OpenPictureDialog1 Filter = Title = BitMaps | *.bmp Todos os arquivos | *.* Qual figura deseja abrir ? O evento OnClick do SpeedButon3 ficaria assim: - Página 10 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho procedure TForm1.SpeedButton3Click(Sender: TObject); begin If OpenPictureDialog1.execute then Image1.Picture.LoadFromFile(OpenPictureDialog1.filename); end; Não vejo necessidade de maiores explicações, pois, este tipo de procedimento já foi detalhado anteriormente. ! 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). 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 4 # 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. - Página 11 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho 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. 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. Durante o nosso projeto, outros ícones deverão ser criados, por exemplo, um balde para indicar o preenchimento: - Página 12 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho ! 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 ficaria assim: procedure TForm1.SpeedButton4Click(Sender: TObject); begin Circulo:= not Circulo; Pincel; end; 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.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; No método OnMouseDown do objeto Image1 devemos também fazer uma modificação, para testar o formato do ponto a desenhar: procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin - Página 13 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 Também teremos que fazer uma ligeira modificação no método do evento OnCreate do objeto Form1, onde é inicializada a variável “circulo com o valor true: procedure TForm1.FormCreate(Sender: TObject); begin Tam:= 1; CorPreench:= clBlack; Circulo:= true; Pincel; end; ! 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). 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: 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; 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, atribuimos 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. - Página 14 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho ! 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, o retângulo é desenhado fazendo um XOR (ou-exclusivo) com 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 quizermos, 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 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; end; seleção: 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; If SpeedButton5.down then {curva} If shift = [ssLeft] then begin Image1.Canvas.Pen.color:= CorPreench; - Página 15 - ………. Etc ………… LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho 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 guardades 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 nesta cor. d) É desenhado o retângulo de seleção com as coordenadas: (x,y) (Sx,Sy) 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; Sx, Sy, Tam, CorPreench: integer; Circulo, Gravou: boolean; RetSelect: TRect; ! 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: - Página 16 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho 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} …………. ETC …………… 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 Sx < x Sy > y Trocax = false Trocay = true Sx < x Sy < y Trocax = false Trocay = false ! Sx > x Sy < y Trocax = true Trocay = false As setas indicam o sentido do movimento FASE 14: COLORINDO UMA ÁREA DO DESENHO: Para este tipo de efeito, utilizaremos o método FloodFill do Canvas. A sintaxe deste método está descrita na página 81. Para isto, iremos acrescentar mais um botão (SpeedButton7), dentro do objeto Panel2, alterando a sua propriedade GroupIndex para 1. Para este botão, sugerimos o ícone: 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 - Página 17 begin Cor:= Image1.Canvas.Pixels[x,y]; Image1.Canvas.Brush.Color:= CorPreench; Image1.Canvas.FloodFill(x,y,Cor,fsSurface); Exit; end; If SpeedButton6.down then … etc … LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho 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. ! 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; O ícone utilizado foi o DoorShut.bmp encontrado na pasta: \Arquivos de programas\Arquivos comuns\Borland Shared\Images\Buttons. ! 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. 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: - Página 18 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho 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 Fx:= x; Fy:= y; end; If shift = [ssRight] then begin . . . . etc . . . . . Na rotina acima, atribuímos a Fx o valor atual da abcissa x do mouse e a Fy o valor atual da ordenada y do mouse. Se rodássemos o programa agora, provavelmente daria um erro de sintaxe, pois, não declaramos ainda as variáveis Fx e Fy. Devemos fazê-lo agora: var Form1: TForm1; Sx,Sy, // coordenadas do canto superior esquerdo de uma área selecionada Fx,Fy, // coordenadas do canto inferior direito de uma área selecionada Tam, CorPreench: integer; Circulo, Gravou: boolean; RetSelect: TRect; - Página 19 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho 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á mencionamos na página 78. 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. 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 <> Fx) or (Sy <> Fy) 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:= Fx; P[2].Y:= Sy; P[3].X:= Fx; P[3].Y:= Fy; P[4].X:= Sx; P[4].Y:= Fy; p[5]:= p[1]; Image1.Canvas.PolyLine(P); Exit; end; - Página 20 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho // Desenha a elipse If SpeedButton10.Down and (button = mbRight) then If (Sx <> Fx) or (Sy <> Fy) 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,Fx,Fy,Fx,Fy,Fx,Fy); Exit; end; // Desenha a reta If SpeedButton11.Down and (button = mbRight) then If (Sx <> Fx) or (Sy <> Fy) 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(Fx,Fy); Exit; end; end; Funcionamento da rotina: Retângulo: a. b. c. d. e. f. g. Se o botão de retângulo está acionado, então pode desenhar o retângulo. 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. Zeramos “RetSelect” para que o retângulo de seleção desapareça ao movermos o mouse. 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) 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). O desenho é efetivamente realizado ao ser acionado o método PolyLine do Canvas. Agora, saímos da rotina (Exit). Elipse/Círculo: a) b) c) d) e) Primeiramente, como no retângulo fizemos os testes iniciais. Também, conforme o retângulo, “apagamos” o retângulo de seleção. Atribuímos as cores e tamanho da pena Desenhamos o arco fechado (ellipse ou círculo) Saímos da rotina. Linha Reta: a) b) c) Para a reta, repetimos as operações iniciais. Através do método MoveTo posicionamos o primeiro ponto da reta (Sx,Sy). Através do método LineTo desenhamos a reta do primeiro ponto até o atual (Fx,Fy). - Página 21 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho ! FASE 17: DESENHANDO TEXTO: Acrescente ao projeto mais um SpeedButton no Panel2, e altere as seguintes propriedades do mesmo: GroupIndex Caption Font.Name Font.Size Font.Style = = = = = 1 T Times New Roman 20 fsBold Acrescente também um objeto FontDialog, que está na aba “Dialogs”. No método do seu evento OnClick 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: 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 Visible BorderStyle = = = (deixar em branco) false 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; Exit; end; If SpeedButton7.Down then . . . . . ETC . . . . . - Página 22 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho 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. ! 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- Página 23 begin Sx:= x; Sy:= y; exit; end; If SpeedButton13.Down then begin CorPreench:= Image1.Canvas.Pixels[x,y]; Pincel; Exit; LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho 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. ! 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: procedure TQRListForm.TitleBand1BeforePrint(Sender: TQRCustomBand; var PrintBand: Boolean); begin QRImage1.Picture:= Form1.Image1.picture; end; 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. - Página 24 - LINGUAGEM DE PROGRAMAÇÃO DELPHI Prof. Alberto Cezar de Carvalho Para realmente conseguirmos imprimir alguma coisa, temos que programar o evento OnClick do SpeedButton14. Digite o que aparece em negrito: procedure TForm1.SpeedButton14Click(Sender: TObject); begin QRListForm.QuickRep1.Preview; // para imprimir direto coloque Print ao invés // de Preview end; 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. ! 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 ... $$$$$$$$$$$$$$$$$$$$$$$$$$$ - Página 25 -