EDITORIAL Editorial Olá amigos, THE CLUB Av. Celso Ferreira da Silva, 190 Jd. Europa - Aé - SP - CEP 18.707-150 Informações: (0xx14) 3732-3689 Suporte: (0xx14) 3733-1588 - Fax: (0xx14) 3732-0987 Internet http://www.theclub.com.br Cadastro: cadastro@theclub.com.br Suporte: suporte@theclub.com.br Informações: info@theclub.com.br Dúvidas Correspondência ou fax com dúvidas devem ser enviados ao - THE CLUB, indicando "Suporte". Opinião Se você quer dar a sua opinião sobre o clube em geral, mande a sua correspondência para a seção "Tire sua dúvida". Reprodução A utilização, reprodução, apropriação, armazenamento em banco de dados, sob qualquer forma ou meio, de textos, fotos e outras criações intelectuais em cada publicação da Revista The Club são terminantemente proibidos sem autorização escrita dos titulares dos direitos autorais. Aqui estamos com mais uma edição da The Club Megazine trazendo até você muita informação para facilitar seu dia-a-dia. Começamos com um excelente artigo do nosso amigo Marcelo Nogueira, onde ele trata sobre a gestão de riscos em projetos de software, não deixe de conferir. Continuando, nosso consultor Claudinei Rodrigues demonstra em seu artigo várias dicas acerca do componente TWebBrowser o qual encapsula o navegador Microsoft Internet Explorer permitindo ao programador adicionar ótimos recursos à sua aplicação. Na seqüência, nosso consultor André Colavite apresenta uma solução para criar arquivos PDFs via Delphi utilizando um componente gratuito o qual também pode ser utilizado em conjunto com o QuickReport, e dessa forma exportar seus relatórios para formato PDF. Ainda falando em QuickReport, nosso consultor Alessandro Ferreira preparou um artigo especial sobre o QuickReport onde demonstra como criar relatórios não triviais com este gerador que ainda é bastante utilizado meio a comunidade Delphi. E finalizando esta edição, trazemos nossa sessão Perguntas & Respostas onde apresentamos algumas das solicitações feitas aos nossos consultores neste mês. Abraço e sucesso à todos, Copyright The Club 2005 Impressão e acabamento: GRAFILAR Tel.: (0xx14) 3841-2587 - Fax: (0xx14) 3841-3346 Rua Cel. Amando Simôes, 779 - Cep 18.650-000 São Manuel - SP Tiragem: 5.000 exemplares Diretor - Presidente Celso Jefferson M. Paganelli Diretor Técnico Mauro Sant Anna Colaboradores Emerson Facunte, Marcelo Nogueira Delphi é marca registrada da Borland International, as demais marcas citadas são registradas pelos seus respectivos proprietários. Celso Jefferson Paganelli Presidente - The Club Editorial... 03 Gestão de riscos em projetos de software... 04 Usando o componente TWebBrowser... 08 Criando um arquivo PDF a partir do Delphi... 12 Explorando o QuickReport... 16 Perguntas & Respostas... 28 MeGAZINE 3
Gestão de riscos em projetos de software Por Marcelo Nogueira Resumo - As empresas de desenvolvimento de software possuem características especiais, diante da demanda a elas submetidas. Com a realização de projetos sobre pressão de prazos e custos, processos fundamentais são ignorados como por exemplo à gestão de riscos. A falta de foco nos riscos dos projetos de software, bem como a determinação de seu grau de exposição, pode causar transtornos e prejuízos ao projeto. A confiabilidade do produto de software é influenciada pelo seu processo de desenvolvimento. Um processo repetitivo, orientado no sentido de monitorar e controlar os riscos, permite que o software desenvolvido, tenha confiabilidade. A não adoção dessas práticas fundamentais da Engenharia de Software por falta de cultura ou por resistência a mudanças, levam projetos de suma relevância ao insucesso e aumentando os casos de fracassos no desenvolvimento de software. Palavras-chave: Desenvolvimento de software, Riscos, Engenharia de Software. Introdução aos Riscos Segundo Robert Charette [1], a definição de risco é: Em primeiro lugar, risco afeta acontecimentos futuros. Presente e passado não preocupam, pois o que colhemos hoje já foi semeado por nossas ações anteriores. A questão é mudando nossas ações hoje, podemos criar oportunidade para uma situação diferente e possivelmente melhor para nós amanhã? Isso significa, em segundo lugar, que risco envolve mudança, como por exemplo, mudança de pensamento, opinião, ações ou lugares..., e em terceiro lugar, o risco envolve escolha e a incerteza que a própria escolha envolve. Assim, paradoxalmente, o risco, como a morte e os impostos, é uma das poucas certezas da vida. Quando o risco é considerado no contexto da Engenharia de Software, as três fundamentações conceituais de Charette estão sempre em evidência: 1. O futuro é nossa preocupação: Que riscos podem causar o insucesso do projeto de software? 2. A mudança é nossa preocupação: Como as mudanças de requisitos do cliente, afetam a pontualidade e o sucesso geral? 3. Devemos cuidar das escolhas: Que métodos e ferramentas devemos usar, quantas pessoas devem ser envolvidas, quanta ênfase em qualidade é suficiente? Peter Drucker disse certa vez, já que é fútil tentar eliminar riscos e questionável tentar minimizá-los, o essencial é que os riscos considerados sejam os certos. Antes que possamos identificar os riscos certos, que acontecerão durante um projeto de software, é importante identificar todos os demais que são óbvios, tanto para gerentes quanto para profissionais. Segundo Higuera [1], um risco 100% provável é uma restrição ao projeto de software. Um grande volume de dados publicados aponta para os riscos que ocorrem os projetos de software executados se a utilização de processos adequados [4]. Um levantamento publicado de uma base de dados de 4.000 projetos, constatou a ocorrência freqüente dos seguintes problemas: 70% dos projetos de grandes aplicativos sofrem instabilidade dos requisitos. Os requisitos crescem tipicamente cerca de 1% ao mês, atingindo níveis de mais de 25% de inchaço ao final do projeto. Pelo menos 50% dos projetos são executados com níveis de produtividade abaixo do normal. Pelo menos 25% dos softwares de prateleira e 50% dos produtos feitos por encomenda apresentam níveis de defeitos superiores ao razoável. Produtos feitos sob pressão de prazos podem quadruplicar o número de defeitos. 4 MeGAZINE
Pelo menos 50% dos grandes projetos de software estouram seu orçamento e seu prazo. 2/3 dos projetos de software muito grandes são cancelados Alto Esquema de Classificação de Riscos Médio Baixo Zero Descrição A correção de irregularidades ou a implementação de melhorias apresentam risco alto de impacto negativo no projeto. A correção de irregularidades ou a implementação de melhorias apresentam risco médio de impacto negativo no projeto. A correção de irregularidades ou a implementação de melhorias apresentam risco baixo de impacto negativo no projeto. A correção de irregularidades ou a implementação de melhorias apresentam risco desprezível de impacto negativo no projeto. Tabela 1 Esquema de classificação de riscos - IEEE [3]. antes do final. Os usuários não ficam satisfeitos com 25% dos produtos comerciais para PC, 30% dos produtos comerciais para mainframe e 40% dos produtos feitos por encomenda. Tipicamente, 50% do patrimônio de software das empresas não são usados. Atritos entre a área de tecnologia da informação e a alta gerência ocorrem em mais de 30% das organizações. Atritos com clientes ocorrem, no desenvolvimento de aplicativos, em 50% dos contratos por administração e 65% dos contratos por empreitada. O risco de projeto pode ser estimado qualitativamente. O principal objetivo da análise de riscos é desenvolver um conjunto de estratégias de prevenção de riscos. Gestão de Riscos Gestão de Riscos é composta por atividades coordenadas para direcionar uma organização em relação ao risco. A gestão de riscos, geralmente inclui avaliação, tratamento, aceitação e comunicação de riscos. A gestão de riscos envolve cinco atividades principais: Planejamento, controle, monitoração, direcionamento e recrutamento [5]. A gestão de riscos é particularmente importante para projetos de software, devido às incertezas inerentes que a maioria dos Figura 1 - Taxonomia da engenharia de riscos. MeGAZINE 5
projetos enfrenta. De modo simplificado, podemos pensar no risco como uma probabilidade de que alguma circunstância adversa realmente venha ocorrer. Os riscos podem ameaçar o projeto, o software que está sendo desenvolvido ou a organização. Essas categorias de riscos podem ser definidas como se segue: 1. Riscos relacionados ao Projeto: São os riscos que afetam a programação ou os recursos do projeto. 2. Riscos relacionados ao Produto: São os riscos que afetam a qualidade ou o desempenho do software que está em desenvolvimento. 3. Riscos para os negócios: São os riscos que afetam a organização que está desenvolvendo ou adquirindo o software. O processo de gestão de riscos envolve vários estágios: 1. Identificação dos riscos: São identificados os possíveis riscos de projeto, produto e negócios. 2. Análise de riscos: São avaliadas as possibilidades e as conseqüências da ocorrência desses riscos. 3. Planejamento de riscos: São traçados planos para enfrentar os riscos, seja evitando-os, seja minimizando seus efeitos sobre o projeto. 4. Monitoramento de riscos: O Risco é constantemente avaliado e os planos para a diminuição dos riscos revisados, à medida que mais informações sobre eles se tornam disponíveis. Segundo Pádua [4], os riscos dever ser estimados e monitorados: A estimativa de riscos é uma atividade muito importante e pouco praticada; um bom planejamento não apenas o que deve acontecer se tudo correr bem, mas também o que pode correr mal, quais as conseqüências dos problemas e o que pode ser feito para combatê-los. Entre os fatores de riscos que devem ser considerados podem ser incluídos: Riscos legais; Riscos Tecnológicos; Riscos devidos ao tamanho e à complexidade do produto; Riscos relativos a pessoal; Riscos relativos à aceitação pelos usuários; Sommerville, descreve os tipos de riscos que podem afetar o projeto e do ambiente organizacional em que o software está sendo desenvolvido. [3] Contudo, muitos riscos são considerados universais e eles envolvem as seguintes áreas: Tecnologia Pessoal Organizacional Ferramentas Requisitos Tabela 2 Exemplo de Estimativa de Riscos Prioridade Risco Gravidade Probabilidade de ocorrência Impacto Previsto Contramedidas Previstas 1 Falta de Equipamentos para testes beta. 2 Defeitos na Engenharia de Software 3 Falta de Usuários responsáveis por testes. 4 Falta de inventário das mercadorias para o cadastramento 5 Falta de povoamento inicial das bases de dados. Alta Média Impossibilidade de realizar os testes beta. Média Média Vários dias de atraso por alteração de requisitos. Alta Baixa Impossibilidade de realizar os testes beta. Alta Baixa Impossibilidade de realizar os testes beta. Alta Baixa Impossibilidade de realizar os testes beta. Cobrar providência do cliente. Incluir na primeira liberação os requisitos mais complexos. Cobrar providência do cliente. Cobrar providência do cliente. Cobrar providência do cliente. 6 Mudança de Legislação Média Baixa Pode ser necessário refazer partes referentes à nota fiscal. Isolar as classes e interfaces susceptíveis de mudança de legislação. 6 MeGAZINE
Estimativa A estimativa dos riscos compreende as seguintes tarefas: Identificação dos riscos possíveis em relação ao projeto; Análise desses riscos, avaliando-lhes a probabilidade e o provável impacto; Previsão de contramedidas curativas ou preventivas; Priorização dos riscos, organizando-os de acordo com a probabilidade e o impacto. Os riscos não permanecem constantes durante a execução de um projeto. Alguns desaparecem, outros novos surgem, e outros sofrem alterações de probabilidade e impacto, mudando portanto a prioridade. Um relatório de acompanhamento do projeto juntamente com uma tabela atualizada para monitoração dos riscos. A tabela de estimativa deve ser repetida e atualizada para refletir as modificações ocorridas, até que os riscos sejam concretizados ou completamente eliminados [4] As questões a seguir foram derivadas de dados de riscos obtidos por levantamento feito com gerentes de projeto de software experientes, em diferentes partes do mundo [1]. As questões estão ordenadas por sua importância relativa em relação ao sucesso de um projeto: 1. A alta administração do software e do cliente empenhou-se formalmente em apoiar o projeto? 2. Os usuários finais estão entusiasticamente empenhados com relação ao projeto? 3. Os requisitos estão plenamente entendidos? 4. Os clientes envolveram-se totalmente na especificação dos requisitos? 5. Os usuários finais têm expectativas realistas? 6. O escopo do projeto é estável? 7. A equipe de projeto tem a combinação de aptidões adequadas? 8. Os requisitos do projeto são estáveis? 9. A equipe de projeto tem experiência com a tecnologia a ser implementada? 10. A quantidade de pessoal é adequada ao projeto? 11. Todos os membros da equipe e usuários envolvidos no projeto concordam com a importância do projeto e com os requisitos do sistema? Se qualquer dessas questões for respondida negativamente, os passos de atenuação, monitoração e gestão devem ser instituídos imediatamente. O grau em que o projeto está em risco é diretamente proporcional ao número de respostas negativas a essas questões. Segundo PMBOK [6], existem ferramentas e técnicas para identificação de riscos. São elas: Listas de Verificação: Questões do produto, tecnologia e pessoas envolvidas no projeto; Fluxogramas: Melhor compreensão das causas e efeitos dos riscos do projeto; Entrevistas: Entrevistas orientadas aos riscos com participação de várias partes envolvidas; Conclusão Aqui foi possível verificar a importância da gestão de riscos nos projetos de software. O Fracasso ou o sucesso estão diretamente ligadas a essas iáveis e contudo serão objetos de estudo e análise para que possam ser monitoradas e controlados durante os projetos de software. A partir do estudo do Estado da Arte, podemos identificar os riscos e analisá-los diante do seu grau de ocorrência. Para validar este processo utiliza-se o para-analisador, construído através da Lógica Paraconsistente. Referências Bibliográficas [1] PRESSMAN, ROGER S., Engenharia de Software, Rio de Janeiro, Ed. McGraw-Hill, 2002. [2] REZENDE, DENIS ALCIDES, Engenharia de Software e Sistemas de Informações, Rio de Janeiro, Ed. Brasport, 1999. [3] SOMMERVILLE, IAN, Engenharia de Software, São Paulo, Ed. Pearson Education, 2003. [4] FILHO, WILSON DE PÁDUA PAULA, Engenharia de Software, Rio de Janeiro, Ed. LTC, 2003. [5] PETERS, JAMES F. et al. Engenharia de Software,Rio de Janeiro, Ed. Campus,2001. [6]PMI, PMBOK, Project Management Institute, 2000. Sobre o autor Marcelo Nogueira é Mestre em Engenharia de Produção com ênfase em Gestão da Informação, bacharel em Análise de Sistemas, Professor Universitário, Instrutor e Desenvolvedor Delphi desde 1995, Membro fundador do DUG-BR. e-mail: marcelo@noginfo.com.br MeGAZINE 7
Usando o componente TWebBrowser Por Claudinei Rodrigues Olá amigos. Nos últimos dias alguns sócios têm entrado em contato com o nosso suporte técnico solicitado informações sobre como utilizar o componente TWebBrowser que fica localizado na palheta Internet. Sendo assim eu reuni algumas dicas bem interessantes, as quais você pode ver a seguir. Usando o TWebBrowser para visualizar e imprimir documentos do Microsoft Word. Através do componente TWebBrowser nós podemos utilizar o Microsoft Word como uma ferramenta de impressão para a nossa aplicação. Aqui está como você pode utilizar o TWebBrowser para controlar a tanto a visualização quanto a impressão dos documentos do Microsoft Word. // Evento OnClick do componente Button procedure TForm1.Button1Click(Sender: TObject); // Abre um documento do Word no componente WebBrowser WebBrowser1.Navigate( C:\Meus documentos\exemplo.doc ) ; Como verificar se um documento mostrado no TWebBrowser está localizado no seu HD. Se você precisa saber a localização de um documento mostrado no componente TWebBrowser, você irá precisar acessar a propriedade Protocol. Veja abaixo como é a rotina: // Função que irá localizar o arquivo function ProcuraDoc(wb: TWebBrowser): boolean; const fileprotocol = file: ; protocol : string; if Assigned(wb.Document) then protocol := wb.oleobject.document.location.protocol; result := protocol = fileprotocol; // Aqui é o evento OnClick do componente // Button que mostra como utilizar a função // ProcuraDoc procedure TForm1.Button2Click(Sender: TObject); if ProcuraDoc(WebBrowser1) then ShowMessage( O documento está no drive local ou em sua rede. ) ; Como localizar e destacar uma informação de uma WebPage mostrada no TWebBrowser. Veja aqui como você pode localizar uma informação em um documento web através do componente TWebBrowser e quando encontrá-lo, poder destacá-lo para que a visualização fique mais fácil. A função ProcuraTexto irá receber dois parâmetros. O primeiro é o próprio componente TWebBrowser e o segundo parâmetro é o texto a ser localizado. Ao executar a função você 8 MeGAZINE
notará que o texto que for localizado ficará com o fundo amarelo. Veja abaixo a nossa função ProcuraTexto e também como você pode utilizá-la. // Esta função irá localizar e destacar o texto procedure ProcuraTexto(WB: TWebBrowser; Texto: string) ; const prefix = <span style= color:black; background-color: yellow; > ; suffix = </span> ; tr: IHTMLTxtRange; if Assigned(WB.Document) then tr := ((wb.document AS IHTMLDocument2).body AS IHTMLBodyElement).createTextRange; while tr.findtext(texto, 1, 0) do tr.pastehtml(prefix + tr.htmltext + suffix) ; tr.scrollintoview(true) ; // Aqui é o evento OnClick do componente // Button que mostra como utilizar a função // ProcuraTexto procedure TForm1.Button3Click(Sender: TObject); ProcuraTexto(WebBrowser1, Template ) ; Como sal uma WebPage que está sendo mostrada no componente TWebBrowser em um arquivo HTML em seu drive local Aqui nós vamos mostrar como uma WebPage que está sendo mostrada dentro do componente TWebBrowser pode ser salva como um arquivo.html no seu HD. // Esta função tem a finalidade de sal a / / página exibida no componente TWebBrowser em // um arquivo.html no seu drive local procedure Salva_Em_HTML (WB:TWebBrowser; const Arquivo : string); PersistStream: IPersistStreamInit; Stream: IStream; FileStream: TFileStream; if not Assigned(WB.Document) then ShowMessage ( Documento não foi encontrado! ); Exit; PersistStream := WB.Document as IPersistStreamInit; FileStream := TFileStream.Create (Arquivo, fmcreate); try Stream := TStreamAdapter. Create(FileStream, soreference) as IStream; if Failed(PersistStream.Save (Stream, True)) then ShowMessage( Não foi possível sal o arquivo! ); finally FileStream.Free; // Aqui é o evento OnClick de um botão que // mostra como você deve utilizar esta função procedure TForm1.Button4Click(Sender: TObject); //Primeiro chame a página desejada WebBrowser1.Navigate ( http://www.theclub.com.br ); //Depois basta salvá-la Salva_Em_HTML(WebBrowser1, c:\theclub.html ); Para que este código funcione corretamente você deve declarar a unit ActiveX. Como chamar o FindDialog para localizar um texto em uma WebPage que está sendo mostrada no componente TWebBrowser. Quando você está navegando na internet utilizando um navegador como o Internet Explorer por exemplo, as vezes você quer localizar um texto. Daí você vai ao menu do Internet Explorer e clica em Editar Localizar ou simplesmente digita CTRL + F. Daí aparece uma janela de diálogo onde você digita o que quer pesquisar. Esta função que está sendo demonstrada a MeGAZINE 9
seguir faz a mesma coisa. // Esta procedure irá chamar a tela // de dialogo onde você irá digitar // a informação a ser pesquisada. procedure FindDialog (WB: TWebbrowser) ; const CGID_WebBrowser: TGUID = {ED016940-BD5B-11cf-BA4E-00C04FD70816} ; HTMLID_FIND = 1; CmdTarget : IOleCommandTarget; vain, vaout: OleVariant; PtrGUID: PGUID; New(PtrGUID) ; PtrGUID^ := CGID_WebBrowser; if WB.Document <> nil then try WB.Document.QueryInterface (IOleCommandTarget, CmdTarget) ; if CmdTarget <> nil then try CmdTarget.Exec(PtrGUID, HTMLID_FIND, 0, vain, vaout) ; finally CmdTarget._Release; except Dispose(PtrGUID) ; // Aqui é o evento OnClick de um botão // que mostra como você deve utilizar // a função FindDialog procedure TForm1.Button5Click(Sender: TObject); FindDialog(WebBrowser1) ; Como ver o código fonte de uma WebPage que está sendo mostrada no componente TWebBrowser. Quando você acessa uma Home Page através do Internet Explorer por exemplo, você pode clicar com o botão direito sobre a página e selecionar o item Exibir código fonte e ver o código fonte utilizado naquela página. Veja a seguir a rotina que podemos utilizar para obter este mesmo efeito. // Esta procedure tem a finalidade de // mostrar o código fonte de uma home page procedure Mostra_Codigo_Fonte (WB: TWebbrowser) ; const CGID_WebBrowser: TGUID = {ED016940-BD5B-11cf-BA4E-00C04FD70816} ; HTMLID_VIEWSOURCE = 2; CmdTarget : IOleCommandTarget; vain, vaout: OleVariant; PtrGUID: PGUID; New(PtrGUID) ; PtrGUID^ := CGID_WebBrowser; if WB.Document <> nil then try WB.Document.QueryInterface (IOleCommandTarget, CmdTarget) ; if CmdTarget <> nil then try CmdTarget.Exec(PtrGUID, HTMLID_VIEWSOURCE, 0, vain, vaout) ; finally CmdTarget._Release; except Dispose(PtrGUID) ; // Aqui é o evento OnClick de um botão // que mostra como você deve utilizar // a função Mostra_Codigo_Fonte procedure TForm1.Button6Click(Sender: TObject); Mostra_Codigo_Fonte(WebBrowser1); Como imprimir uma WebPage que está sendo mostrada no componente TWebBrowser. Veja a seguir várias formas de imprimir utilizando o componente TWebBrowser. // Manda diretamente para a impressora procedure Imprime_Sem_Janela_Dialogo(WB: TWebBrowser) ; vin, vout: OleVariant; 10 MeGAZINE
WB.ControlInterface.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER, vin, vout) ; //Imprime depois de chamar a tela de diálogo procedure Imprime_Com_Janela_Dialogo(WB: TWebBrowser) ; vin, vout: OleVariant; WB.ControlInterface.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER, vin, vout) ; // Chama o preview procedure Mostra_Preview(WB: TWebBrowser) ; vin, vout: OleVariant; WB.ControlInterface.ExecWB(OLECMDID_PRINTPREVIEW, OLECMDEXECOPT_DONTPROMPTUSER, vin, vout) ; //Chama o Print Setup procedure Chama_PrintSetup(WB: TWebBrowser) ; vin, vout: OleVariant; WB.ControlInterface.ExecWB(OLECMDID_PAGESETUP, OLECMDEXECOPT_PROMPTUSER, vin, vout) ; Como desabilitar o Context Menu em um TWebBrowser Veja abaixo como podemos desabilitar o menu de contexto, que é aquele que você clica com o botão direito do mouse. // Esta é a função que desabilita function MouseProc(nCode: Integer; wparam, lparam: Longint): LongInt; stdcall; classbuf: array[0..255] of Char; const ie = Internet Explorer_Server ; case ncode < 0 of True: Result := CallNextHookEx (MouseHook, ncode, wparam, lparam); False: case wparam of WM_RBUTTONDOWN, WM_RBUTTONUP: GetClassName(PMOUSEHOOKSTRUCT(lParam)^. HWND, classbuf, SizeOf(classbuf)) ; if lstrcmp(@classbuf[0], @ie[1]) = 0 then Result := HC_SKIP else Result := CallNextHookEx (MouseHook, ncode, wparam, lparam) ; end else Result := CallNextHookEx (MouseHook, ncode, wparam, lparam) ; // Aqui é o evento OnCreate do form onde // estamos chamando a função que desabilita // o Context Menu procedure TForm1.FormCreate(Sender: TObject); MouseHook := SetWindowsHookEx(WH_MOUSE, MouseProc, 0, GetCurrentThreadId()) ; // Aqui é o OnDestroy do form onde // estamos habilidando novamente // o Context Menu procedure TForm1.FormDestroy(Sender: TObject); if MouseHook <> 0 then UnHookWindowsHookEx(MouseHook) ; Conclusão Estas foram algumas dicas que temos disponíveis a respeito do componente TWebBrowser. Se você tiver mais alguma dica e quiser vê-la publicada em nossa revista, mande um e-mail para suporte@theclub.com.br que teremos o maior prazer em publicálas. Um abraço a todos e até o próximo mês. Sobre o autor Claudinei Rodrigues, Consultor Técnico do The Club nei@theclub.com.br MeGAZINE 11
Criando arquivo PDF a partir do Delphi. Introdução Nesta matéria iremos verificar como criar arquivos PDF a partir do Delphi, esse tópico é bastante abordado pois é muito importante que nosso sistema possa exportar dados de uma forma segura e de fácil visualização. Alguns geradores de relatórios já trazem embutidas essas particularidades, mas em alguns casos não temos esses gerados a disposição ou a estrutura que iremos montar não se encaixa num gerador de relatório. Sendo assim, temos a possibilidade de montar o PDF via programação, incluindo assim as informações conforme a necessidade. Componente Externo Para a criação do arquivo PDF utilizaremos um componente externo e free chamado TNPDF. Esse componente é bastante simples composto de apenas uma unit onde o PDF é gerado. Junto com esse componente temos um outro componente também free chamando ZLIB, que será utilizado para fazer a compactação do arquivo PDF gerado. Antes de iniciar o projeto de exemplo vamos baixar esses dois componentes, para isso acesse o seguinte link: www.theclub.com.br/revista/download/tnpdf.zip Descompacte o arquivo zip e poderá encontrar os seguintes arquivos: TNPDF.PAS: Contem a programação do componente para gerar o PDF; READ_ME.TXT: Contem algumas informações importantes; PASZLIB.ZIP: Contém os arquivos do componente para compactação do PDF. Descompacte esse arquivo junto com o arquivo TNPDF.PAS. Observação: Não iremos instalar o componente TNPDF no Delphi, pois mais adiante ele será instalado junto com outro componente para exportação a partir do QuickReport. Sendo assim para utilizá-lo deixaremos os arquivos.pas no mesmo diretório onde o nosso projeto será criado. Criando um simples exemplo Crie um novo projeto e coloque em seu form os seguintes componentes: Button1, Image1, Memo1, Label1, SaveDialog1 No componente Memo1 carregue o conteúdo do arquivo readme.txt e no arquivo Image1 carregue uma imagem de exemplo. Ao término o nosso form ficará parecido com a figura1. Na seção implementation declare a unit TNPDF. implementation uses tnpdf; Em seguida criaremos uma procedure contendo as instruções para gerar o arquivo PDF: procedure TForm1.Relat1(NomeArq: String); PDF:TPrintPDF; 12 MeGAZINE
Figura 1: { Cria o componente TPrintPDF } PDF := TPrintPDF.Create(self); try { Configurar o título do documento } PDF.TITLE := Label1.Caption; { Usando o componente para compactação do arquivo } PDF.Compress := true; { Configurar o tamanho da página } PDF.PageWidth := 612; PDF.PageHeight := 792; { Indica o nome do arquivo a ser gerado } PDF.FileName := NomeArq; { Inicia a impressão } PDF.BeginDoc; { Configura o tamanho da linha } PDF.LineWidth := 1; { Desenha uma linha no PDF } PDF.DrawLine(12,50,600,50); { Configura o tamanho da linha } PDF.LineWidth := 2; { Desenha um retângulo no PDF } PDF.DrawRectangle(12,12,600,780); { Configura a fonte a ser impressa. Essa fonte é definida dentro do componente devido ao nome a ser utilizado na estrutura do PDF } PDF.Font.Name := pohelveticabold; PDF.Font.Size := 20; { Imprime Texto no corpo do PDF } PDF.TextOut(15,30,Label1.Caption); PDF.Font.Size := 10; PDF.TextOut(15,45, THE CLUB, O maior clube de programadores do Brasil ); { Configura a Fonte } PDF.Font.Name := potimesroman; PDF.Font.Size := 12; { Imprime o conteúdo de um memo } PDF.MemoOut(15,60, Memo1); { Adiciona nova página } MeGAZINE 13
PDF.NewPage; { Imprime um retângulo } PDF.DrawRectangle(12,12,600,780); { Imprime uma imagem } PDF.Draw(15,15,Image1); { Finaliza o documento } PDF.EndDoc; finally { Destroi o componente da memória } PDF.Free; No evento onclick do botão coloque a instrução para chamar a procedure: procedure TForm1.Button1Click(Sender: TObject); if SaveDialog1.Execute then Relat1( SaveDialog1.FileName ); Pronto agora podemos compilar esse projeto e executar, ao qual irá gerar o arquivo PDF normalmente. Esse projeto de exemplo está disponível para download através do seguinte link: www.theclub.com.br/revista/download/exemplo.zip Gerando PDF a partir do QuickReport. Neste segundo caso iremos utilizar outro componente chamado ExportQR, também externo e Freeware que permitirá a partir do QuickReport gerar o arquivo PDF. Para gerar o PDF esse componente pega o Metafile que é a imagem do relatório gerada pelo Quickreport e transfere para o componente que faz a exportação, podendo através desse componente exportar o relatório para os formatos PDF, JPG, BMP, WMF e EMF. Antes de iniciar-mos os trabalhos devemos baixar esse novo componente através do seguinte link: www.theclub.com.br/revista/download/expack12.zip Observação: Esse componente ExportQR foi criado para trabalhar com o QuickReport e com o FastReport, mas nesta versão que estou disponibilizando no site do THE CLUB o componente estará trabalhando somente com o quickreport. Caso alguém queira pegar a versão que também trabalha com o FastReport recomendo que acesse o site do fabricante no seguinte link: http:// usuarios.lycos.es/isma A instalação desse componente é bastante simples, abra o arquivo ExPackD6.dpk (Delphi 6) ou ExPackD7.dpk (Delphi 7) através do delphi e em seguida compile e instale. Esse componente já está trazendo os dois componentes utilizados no primeiro exemplo, isso é, o componente TNPDV e o componente de compactação. Após efetuar a instalação acesse o menu Tools, do Delphi, e depois a opção Environment Options. Selecione a aba Library e depois acesse o botão da Library path. Na janela que será aberta indique o caminho onde estão os arquivos.pas do componente e depois clique em Add e em seguida em Ok. Agora estando com o componente instalado podemos colocá-lo no form do nosso projeto que contem o relatório criado no QuickReport e depois através de um botão colocar a instrução que irá fazer a exportação. A instrução do botão para exportação ficará da seguinte forma: procedure TMainForm.BtnExportQRClick(Sender: TObject); { Liga o componente do QuickReport ao componente de exportação } EXQR.Report := Rep; { Faz a exportação } EXQR.ExportQR; Este componente também nos permite exportar relatórios a partir do componente QRPreview que é o componente ao qual criamos preview personalizado no QuickReport. Para isso podemos colocar um botão no form de preview e neste botão colocar a seguinte instrução: procedure TFPreviewQR.TBSaveClick(Sender: TObject); { Faz a ligação do QRPreview com o componente } EXQR.Preview := Preview; { Faz a exportação do relatório } 14 MeGAZINE
EXQR.ExportQRFromPreview; Esses códigos acima podem ser encontrados no projeto de Exemplo1 que acompanha o componente ExportQR. Outros formatos A exportação dos relatórios para os demais formatos é bastante simples também, veja abaixo as sintaxes utilizadas para cada tipo: PDF com Alta Compressão: EXQR.ExportQRPDF(edFileName.Text, True); PDF com Baixa Compressão EXQR.ExportQRPDF(edFileName.Text, False); JPG EXQR.ExportQRJPG(edFileName.Text); BMP EXQR.ExportQRBMP(edFileName.Text); WMF EXQR.ExportQRWMF(edFileName.Text); EMF EXQR.ExportQREMF(edFileName.Text); Esses códigos acima podem ser encontrados no projeto de Exemplo3 que acompanha o componente ExportQR. Observação Não entrei em detalhes sobre a criação dos exemplos, para que o foco principal que é conhecer o componente não seja perdido. Os projetos de exemplo aqui apresentados foram criados pelos próprios fabricantes dos componentes e somente sofreram algumas poucas alterações como, por exemplo, tradução dos forms para o português. Conclusão Particularmente gostei muito desses componentes e acredito que eles irão nos ajudar bastante na criação de arquivos PDF através dos projetos em Delphi, sem aquela dependência dos geradores de relatório. Um grande abraço a todos e até a próxima. Sobre o autor André Colavite Consultor Técnico do The Club colavite@theclub.com.br MeGAZINE 15
Explorando o QuickReport Criando relatórios não triviais Sobre o autor Alessandro Ferreira, Consultor Técnico do The Club alessandro@theclub.com.br Introdução Entra e sai versão do Delphi e o QuickReport continua tendo uma legião de usuários que, por conseguirem atender seus clientes com este gerador, não vêem necessidade de estar migrando. Particularmente confesso que gosto do QuickReport, apesar de todos os bugs nestes vários anos de caminhada, contudo, até hoje, não tive nenhuma situação onde o QuickReport me deixou na mão e acredito ser este o motivo de muitos programadores ainda o utilizarem. Neste artigo irei demonstrar algumas técnicas não triviais na confecção de relatórios com o QuickReport, como por exemplo, a montagem de um relatório de referência cruzada, um relatório muito utilizado na impressão de etiquetas onde você poderá estar repetindo um mesmo registro por N vezes, etc. Instalando o QuickReport no Delphi 7 A Borland não traz o QuickReport adicionado a IDE, porém, traz o pacote dclqrt70.bpl referente o QuickReport na pasta: C:\Arquivos de programas\borland\delphi7\bin Para adicionar, acesse o menu Component Install Packages Add e adicione o referido pacote. Após adicionar, recomendo efetuar atualização do QuickReport que está disponível para download no site do fabricante no endereço: Delphi 7 http://www.qusoft.com/ getfile.asp?level=0&filename=qrstd351d7.exe Delphi 6 http://www.qusoft.com/ getfile.asp?level=0&filename=qr351sd6.exe O evento OnNeedData Antes de começarmos, gostaria de explicar o evento OnNeedData do QuickReport, pois, este evento será muito utilizado nestes exemplos. Por padrão, quando ligamos um componente DataSet na propriedade DataSet do QuickReport, ele automaticamente assume o controle e vai movendo o ponteiro do DataSet até atingir o último registro e mostrando os dados em uma banda DetailBand, por exemplo. Em muitas situações não é este o resultado que necessitamos e poderemos precisar assumir o controle da navegação nos registros e, é exatamente isso que o evento OnNeedData nos possibilita. A seguir, veja o escopo deste evento: procedure TForm1.QuickRep1NeedData(Sender: TObject; MoreData: Boolean); // Observe que temos um parâmetro chamado MoreData. No momento da impressão, o QuickReport dispara este evento e verifica o valor do parâmetro MoreData e caso este seja verdadeiro (true) irá forçar a impressão de mais uma DetailBand, ou seja, irá gerar mais uma linha em nosso relatório e no momento que encontrar esta iável falsa, irá encerrar o relatório. Banco de Dados Iremos utilizar o Firebird 1.5.2 onde a princípio vamos criar duas tabelas, sendo uma contendo os dados dos clientes e outra contendo as compras efetuadas por estes clientes. Sugiro utilizar o IBExpert (www.ibexpert.com) que é uma ferramenta gratuita 16 MeGAZINE
para manutenção em bancos Interbase/Firebird para criar o banco de dados e suas tabelas. (Poderá encontrar referência sobre o IBExpert em nossa revista de Dezembro/2004). Vamos criar as estrutura da tabela Clientes, veja o código na listagem 1. CREATE TABLE CLIENTES ( ID INTEGER NOT NULL PRIMARY KEY, NOME VARCHAR(70), ENDERECO VARCHAR(70), BAIRRO VARCHAR(30), CIDADE VARCHAR(30), UF CHAR(2), CEP CHAR(10), CNPJ_CPF VARCHAR(18), RG_IE VARCHAR(18), TELEFONE VARCHAR(18), EMAIL VARCHAR(80) ); Listagem 1 Tabela Clientes Agora vamos criar uma tabela chamada Vendas, a qual terá uma estrutura bem simples apenas para ilustrar nosso relatório, veja a listagem 2. CREATE TABLE VENDAS ( NF CHAR(6) NOT NULL PRIMARY KEY, DATA DATE NOT NULL, CLIENTE_ID INTEGER NOT NULL, VALOR_TOTAL NUMERIC(15,2) NOT NULL ); ALTER TABLE VENDAS ADD CONSTRAINT FK_VENDAS_CLIENTES FOREIGN KEY (CLIENTE_ID) REFERENCES CLIENTES (ID); Listagem 2 Tabela Vendas Para podermos testar nosso relatório, vamos acionar alguns registros em ambas as tabelas Para isso, poderá utilizar a aba Data do IBExpert e vamos inserir alguns clientes, veja a figura 1. Agora vamos adicionar alguns registros que irão representar as compras destes clientes, veja a figura 2. Figura 2 Vendas Obs. Adicione dados para todos os clientes alternando valores e datas. Se preferir, poderá baixar o projeto de exemplo apresentado neste artigo no qual já adicionamos dados nas tabelas para testes, veja o endereço no final deste artigo. 1. Construindo um relatório de referência cruzada (CrossTab) Nosso primeiro exemplo será a construção de um relatório de cruzamento de dados, ou seja, um relatório CrossTab. Diferente de outros gerados de relatórios que trazem este recurso embutido, no QuickReport teremos que fazer tudo no braço utilizando entre outros, o eventos OnNeedData anteriormente descrito. Primeiro, vamos criar um projeto no Delphi 7 onde iremos através da dbexpress acessar o banco de dados e gerar uma consulta SQL para agrupar os dados de acordo com nossas necessidades e depois utilizá-los em nosso relatório. Adicione um componente SQLConnection (dbexpress), altere sua propriedade Name para cnxcrosstab, a propriedade Figura 1 Clientes MeGAZINE 17
LoginPrompt para False, dê um duplo clique no componente e adicione uma conexão apontando para nosso banco de dados de exemplo, conforme sugere a figura 3. Adicione um componente DataSetProvider, configure Name para dspcrosstab e DataSet para sdscrosstab. Após isso, adicione um componente ClientDataSet, configure Name para cdscrosstab, ProviderName para dspcrosstab e depois clique com o botão direito no mesmo e selecione a opção: Fetch Params. Para testes, se executar esta sentença SQL no IBExpert, terá o resultado apresentado na figura 4. Figura 3 Conexão dbexpress. Prosseguindo, adicione um componente SQLDataSet, configure a propriedade SQLConnection para cnxcrosstab, Name para sdscrosstab e na propriedade CommandText adicione a sentença SQL apresentada na listagem 3. Select CLI.NOME AS CLIENTE, EXTRACT(MONTH FROM VEN.DATA) AS MES,SUM(VEN.VALOR_TOTAL) AS TOTAL From VENDAS VEN Left Outer Join CLIENTES CLI on CLI.ID = VEN.CLIENTE_ID Where EXTRACT(YEAR FROM VEN.DATA) = :ANO Group By CLI.NOME, EXTRACT(MONTH FROM VEN.DATA) Listagem 3 Sentença SQL. Figura 4 Select executado Observe que os dados ficam arranjados contendo mês/total linha a linha para cada cliente. Contudo, um relatório de referência cruzada consiste em arranjar os dados na estrutura apresentada na tabela 1. Dando continuidade a implementação do relatório, adicione um componente QuickReport, altere Name para QR_CrossTab. Em cima deste QuickReport, adicione três componentes QRBand, configurando a propriedade BandType para rbpageheader, rbdetail e rbsummary, respectivamente. Devido a estrutura deste relatório, vamos necessitar de mais espaço na transversal, por isso, selecione o QuickReport e altere a propriedade Page Orientation para polandscape. Cliente Jan Fev Mar Abr Mai Jun Jul Ago Set Out Nov Dez Total Alessand... 420,00 180,00 420,00 280,00 500,00 420,00 180,00 100,00 320,00 180,00 100,00 500,00 3.600,00 Emerson... 1267,00 487,00 1850,00 1190,00 1190,00 530,00 650,00 540,00 221,00 311,00 123,00 1205,00 9.564,00 Total 13.164,00 Tabela 1 Dados arranjados em cross-tab 18 MeGAZINE
Tendo como base de layout a tabela 1 anteriormente apresentada, vamos adicionar componentes QRLabel os quais serão responsáveis pela apresentação dos dados. Na banda referente o cabeçalho, adicione 14 QRLabels ajustando o primeiro com o Caption Nome do Cliente, os próximos 12 deverão conter o nome de cada mês começando por Janeiro e o último QRLabel terá como Caption Total ; Agora vamos adicionar os QRLabel na banda detalhe (DetailBand) a qual será responsável pela apresentação dos dados. Mais uma vez iremos necessitar de 14 QRLabels, seguindo a mesma lógica da banda anterior, sendo o primeiro para apresentação do nome do cliente, os 12 subseqüentes para apresentação dos valores respectivos à cada mês e o último para uma totalização dos meses. Vale ressaltar que iremos utilizar uma nomenclatura específica para estes componentes, pois, mais adiante na codificação iremos implementar uma rotina genérica para facilitar a implementação, assim sendo, nomeie os componentes QRLabels da banda detalhe: qr_nome, qr_01, qr_02, qr_03, qr_04, qr_05, qr_06, qr_07, qr_08, qr_09, qr_10, qr_11, qr_12, qr_13, respectivamente. E para finalizar, vamos novamente adicionar mais 14 QRLabels, agora em cima da banda sumário, seguindo a mesma estrutura utilizada nas bandas anteriores e tendo o cuidado de nomear os QRLabels para: tot_01, tot_02, tot_03, tot_04, tot_05, tot_06, tot_07, tot_08, tot_09, tot_10, tot_11, tot_12, tot_13. Adicionando código ao nosso relatório Antes de iniciarmos a codificação de nosso relatório, adicione um componente Bitbtn e um MaskEdit ajustando o layout do formulário como apresenta a figura 5. Figura 5 Formulário para chama do QuickReport Obs. Apenas a título de informação, o QuickReport está neste mesmo formulário, porém encontra-se escondido e para visualizá-lo bastará aumentar o tamanho do formulário. Iniciando a codificação, faça as seguintes declarações abaixo da sessão Private do objeto Form, conforme apresenta a listagem 4. private { Private declarations } TotColunas: Array[1..12] of Extended; TotCliente, TotGeral: Extended; NomeDoCliente: String; ImpTitulo: Boolean; procedure ZeraTotColunas; procedure LimpaValores; procedure PreencheValores; procedure LimpaSumario; procedure PreencheSumario; public { Public declarations } Listagem 4 Declarações. Dica: Após efetuar estas declarações, tecle a combinação CTRL+SHIFT+C e automaticamente o editor irá criar o cabeçalho dos procedimentos abaixo da sessão implementation. Observe que declaramos um Array o qual será responsável em armazenar a totalização de cada coluna (mês) o qual iremos apresentar no final do relatório, na banda sumário e para não ter que fazer referência a cada elemento do Array, implementamos um método para inicializar este Array, acompanhe a listagem 5. { Limpa Array que irá armazenar a totalização das colunas. } procedure TForm1.ZeraTotColunas; i: Integer; for i := Low(TotColunas) to High(TotColunas) do TotColunas[i] := 0; Listagem 5 Iniciar Array Prosseguindo, na listagem 6 apresentamos outros dois métodos: LimpaValores e LimpaSumario. O primeiro, será responsável em limpar o conteúdo dos QRLabels existentes na banda detalhe atribuindo 0,00 ao seu caption e veja que não fazemos referência direta ao nome dos QRLabels e sim dentro de um laço FOR pesquisamos o componentes pelo seu nome, por isso a importância da nomenclatura anteriormente sugerida. O método LimpaSumario tem a mesma finalidade, contudo, aplica-se aos MeGAZINE 19
QRLabels existentes na banda sumário. { Limpar os QRLabel da banda detalhe } procedure TForm1.LimpaValores; i: Integer; C: TComponent; for i := 1 to 13 do C := FindComponent( qr_ + FormatFloat( 00, i)); if (C <> nil) and (C is TQRLabel) then TQRLabel(C).Caption := 0,00 ; TotCliente := 0; { Limpar os QRLabel da banda sumário } procedure TForm1.LimpaSumario; i: Integer; C: TComponent; for i := 1 to 13 do C := FindComponent( tot_ + FormatFloat( 00, i)); if (C <> nil) and (C is TQRLabel) then TQRLabel(C).Caption := 0,00 ; TotGeral := 0; Listagem 6 Métodos para limpar os QRLabels O método seguinte apresentado na listagem 7 é um dos mais importantes dentro deste contexto, pois, será o responsável em preencher os QRLabels (da banda detalhe) com os valores advindos do ClientDataSet: { Preenche os QRLabel da banda detalhe } procedure TForm1.PreencheValores; C: TComponent; Mes: Integer; ImpTitulo := False; while (NomeDoCliente = cdscrosstabcliente.asstring) and not (cdscrosstab.eof) do Mes := cdscrosstabmes.asinteger; // Totaliza por coluna. TotColunas[Mes] := TotColunas[Mes] + cdscrosstabtotal.asfloat; // Totaliza por cliente. TotCliente := TotCliente + cdscrosstabtotal.asfloat; C := FindComponent( qr_ + FormatFloat( 00, cdscrosstabmes.asinteger)); if (C <> nil) and (C is TQRLabel) then TQRLabel(C).Caption := FormatFloat( ###,##0.00, cdscrosstabtotal.asfloat); qr_nome.caption := cdscrosstabcliente.asstring; cdscrosstab.next; // Mostra total do Cliente. qr_13.caption := FormatFloat( ###,##0.00, TotCliente); // Acumula total geral. TotGeral := TotGeral + TotCliente; ImpTitulo := True; Listagem 7 Método que alimenta os QRLabels Vamos explicar o método PreencheValores, o qual começa atribuindo false para a iável ImpTitulo. Esta iável será responsável em controlar quando a banda detalhe deverá ser impressa, pois a mesma deverá ser impressa apenas quando todos os meses do referido cliente estiverem preenchidos. Você deve estar lembrando que o controle de navegação dos registros é por nossa conta e a fim de obtermos todos os registros referente o cliente atual, efetuamos um laço enquanto o cliente permanecer o mesmo e enquanto não atingir o final do arquivo. Dentro deste laço, vamos acumulando o valor de cada coluna (mês) e o valor referente ao cliente corrente. Mais uma vez recorremos ao FindComponent para pesquisar e retornar os QRLabels existentes na banda detalhe através do seu nome e caso seja encontrado, alimentamos sua propriedade caption. Ao término do while, verificamos que todos os dados cliente armazenado na iável NomeDoCliente foram obtidos e nos resta efetuar a totalização deste cliente e armazenar seu total na iável TotaGeral que irá ser apresentada na banda sumário, e atribuir true à iável ImpTitulo a qual irá liberar a impressão da banda detalhe com todos os dados já carregados para o respectivo cliente. 20 MeGAZINE