Páginas

quarta-feira, 23 de junho de 2010

Lazarus - Dicas de LazReport

Neste post vou apresentar algumas dicas para criar relatórios com mais eficiência em LazReport. No post Lazarus - Criando relatórios com FortesReport (Parte I) eu mostrei como instalar o LazReport e o link para um tutorial que ensina passo a passo como criar um relatório.

Exportando um relatório para PDF

Esse é um procedimento bastante simples, mas é necessário instalar dois pacotes. Para exportar um relatório para PDF você precisa do LazReportPDFExport. Este componente está em source\addons\pdfexport no mesmo diretório do LazReport. Mas este componente depende do PowerPDF. Você pode fazer o download do pacote PowerPDF no repositório de componentes e código Lazarus. Instale o PowerPDF e depois o LazReportPDFExport. O PowerPDF irá aparecer em uma nova aba de componentes e o LazReportPDFExport será um novo componente -TfrTNPDFExport - na aba do LazReport.
Supondo que você já criou o relatório, coloque um TfrTNPDFExport no mesmo form onde está o TfrReport. Execute o seu programa e visualize o relatório. Clique no botão Save report. Clique na caixa Tipo e deve aparecer a opção Adobe Acrobat PDF (*.pdf). Selecione esta opção, dê um nome ao arquivo e pressione o botão Salvar. Agora você tem um arquivo pdf com o seu relatório. Também existem componentes que permitem você exportar para HTML ou CSV.

Object Inspector

Semelhante ao Object Inspector do Lazarus, com ele podemos manipular propriedades de alguns objetos, tais como: nome, posição, tamanho e visibilidade. No LazReport ele é tratado como uma ToolBar. Pode ser mostrado ou ocultado. Para mostrar a janela do Object Inspector selecione Tools -> ToolBars -> Object Inspector. Depois simplesmente selecione um objeto no relatório para visualizar/alterar suas propriedades.

Destacando objetos

Em certos relatórios pode ser necessário mudar a cor da fonte e/ou do fundo de objetos, baseado em certas condições. Para isso selecione o objeto alvo e pressione o botão Highlight attributes na barra de ferramentas de formatação de texto, como mostra a figura abaixo.


Vamos supor que tenhamos um relatório de vendas mensais efetuadas pelos vendedores de uma loja e que a meta para aquele mês seja 10.000 reais. Então, queremos que os valores abaixo de 10.000 apareçam com fonte em vermelho. Na caixa de texto Condition da janela Highlight attributes informe Value < 10000. Selecione a cor da fonte e/ou a cor de fundo e pressione o botão OK. Veja a figura seguinte. Agora o relatório irá mostrar os valores de venda menores que 10.000 em vermelho. Condições mais complexas podem ser criadas usando o recurso de script que será apresentado mais adiante.


Programando eventos

Em certos casos pode ser necessário imprimir dados que não estão em bancos de dados. Uma opção é usar os eventos do componentes TfrReport. Dois desses eventos são OnGetValue e OnEnterRect. OnGetValue ocorre toda vez que um objeto Text encontra uma variável e precisa atribuir um valor a ela. Se a variável não possuir um valor então esse valor é definido através de código, por exemplo:

procedure TForm1.frReport1GetValue(const ParName: String; var ParValue: Variant);
begin
  if ParName = 'Variavel1' then
     ParValue := 100;
end;

Onde ParName tem o nome da variável como string e ParValue recebe o valor dessa variável. Por sua vez o evento OnEnterRect acontece antes que um objeto seja desenhado. Podemos, por exemplo, imprimir um dado bem específico em uma linha ou coluna do relatório.

procedure TForm1.frReport1EnterRect(Memo: TStringList; View: TfrView);
begin
  if (FCol=3)and(FRow=3) then
    view.Memo.Text := 'ALO';
end;

FCol e FRow definem uma coluna e uma linha do relatório respectivamente. No exemplo será impressa a palava ALO na linha 3 e coluna 3.

Programando um script

LazReport possui um interpretador interno semelhante ao Pascal que tem as seguintes características:

  • Operador de atribuição (:=);
  • Estruturas if ... then ... else, while ... do e repeat ... until;
  • Delimitadores de blocos begin e end;
  • Variávies não tipadas;
  • Acesso aos objetos do LazReport e suas propriedades.
Sempre que um objeto ou banda for impresso, o script associado a ele é executado. O editor de script é acessível no editor memo sempre que a caixa Script estiver marcada. Para visualizar o editor memo de um objeto como Text, por exemplo, basta dar um duplo clique nele. Em uma banda tecle CTRL + ENTER. Veja o editor memo abaixo com o editor de script habilitado.


Algumas propriedades de objetos visuais que podem ser usadas nos scripts são:

Visible - determina a visibilidade do objeto (boolean);
FillColor - determina a cor de fundo do objeto. Valores possíveis: clRed, clBlue, etc.;
Text - conteúdo do memo;

Objetos Text tem também as seguintes propriedades:

FontName, FontSize, FontColor - nome, tamanho e cor da fonte;
FontStyle - estilo da fonte (1 - itálico, 2 - negrito e 3 - sublinhado);
Adjust - alinhamento do texto (1 - direita, 2 - centro).

Por exemplo, voltando ao nosso relatório de vendas, vamos supor que queremos imprimir em verde os valores acima do dobro da meta, em vermelho os valores abaixo da meta e em azul os demais. O código ficaria como abaixo, supondo que o valor seja dado por [TotalVendas]:

if [TotalVendas] < 10000 then 
   FontColor := clGreen 
else if [TotalVendas] < 20000 then 
   FontColor := clBlue 
else 
   FontColor := clRed;

Vamos continuar explorando os recursos do LazReport. No próximo post vamos mostrar como criar um relatório mestre-detalhe.

domingo, 13 de junho de 2010

Java - Strings, memória e imutabilidade

O entendimento sobre pilha e heap pode facilitar em muuito a compreensão de temas como passagem de parâmetros, polimorfismo, garbage collector, etc. Mas neste post darei apenas uma rápida visão para poder falar o assunto principal que é a forma como Java trata os objetos String.
Em geral, variáveis de instância, objetos e arrays - que também são objetos - residem no heap. Já as variáveis locais, tanto de tipos primitivos quanto as referências, residem na pilha.
Sabemos que uma String pode ser instanciada de várias maneiras, como nestes exemplos:

String s = new String("abcd");   // ou
s = "abcd";

Para que o uso da memória seja mais eficiente em Java, a JVM mantém uma área especial de memória chamada pool de Strings constantes. Quando o compilador encontra um literal String, ele verifica se existe uma String idêntica no pool. Se existir, a referência ao novo literal é direcionada à String existente e não é criado novo objeto String. Mas quando um objeto String é criado usando new, por exemplo:

String s = new String("abc"));

Esse novo objeto é criado no heap - a memória onde residem todos os objetos em Java - e "abc" é inserida no pool de Strings constantes, se não existir.. Além disso, as Strings são imutáveis. Por exemplo:

String s = "abc";
s.concat("def"); // concatena o argumento ("def") à String "abc"

Nesse momento Java cria uma nova String com o valor "abcdef", mas ela fica perdida na memória, pois a variável s não muda seu valor e continua valendo "abc". No entanto se a segunda linha for alterada para:

s = s.concat("def");

Então s passará a valer "abcdef". A variável s será uma nova String. A String "abc" fica perdida na memória. Ou seja, se o resultado da expressão não for atribuido a uma variável seu valor será perdido. Analise esse código:

String s1 = "abc";
String s2 = "abc";
String s3 = "abc";
s3 = s3.concat("def");
System.out.println(s1 + " " + s2 + " " + s3);

Inicialmente as Strings s1, s2, s3 referem-se ao mesmo objeto no pool, pelo que foi exposto acima.  No entanto, diferente do que possamos pensar, quando criamos um novo objeto e o referenciamos com s3, os valores de s1 e s2 não se alteram pelo princípio da imutabilidade.
A classe StringBuffer se comporta de forma diferente. Ela não é imutável.

String sb1 = new StringBuffer("abc");
String sb2 = new StringBuffer("abc");
String sb3 = new StringBuffer("abc");
sb3.concat("def");
System.out.println(sb1 + " " + sb2 + " " + sb3);


Mesmo o código acima produzindo o mesmo resultado do código usando String, a explicação é que sb1, sb2 e sb3 são três objetos diferentes. Observe que este código produz algo diferente, pois agora só existe um objeto e três referências:

StringBuffer sb1 = new StringBuffer("abc");
StringBuffer sb2 = sb1;
StringBuffer sb3 = sb2;
sb3.append("def");
System.out.println(sb1 + " " + sb2 + " " + sb3);

Espero que o comportamento de Strings em Java tenha ficado mais claro para você. Leia mais sobre strings no tutorial sobre strings no site da Sun.

segunda-feira, 7 de junho de 2010

Lazarus - Conectando Oracle com SQLdb ou ZeosLib

A Oracle oferece uma versão free do Oracle Database 10g. É o Oracle Database 10g Express Edition. Utilizamos esta versão neste artigo. Além disso, utilizamos a versão 0.9.29 do Lazarus, Free Pascal 2.4.1 e ZeosLib 7.0.0.
Para os testes usamos o usuário do Oracle hr, que é criado em uma nova instalação. Depois de instalado o banco de dados, o usuário hr deve ser desbloqueado pelo DBA e definida uma senha. Esse usuário tem acesso às tabelas do schema hr, também instalado junto com o banco. No entanto criamos uma nova tabela para os testes, de forma a manter as tabelas originais inalteradas. Criamos então nossa famosa tabela CIDADE.

CREATE TABLE  "CIDADE" 
   ( "ID_CIDADE" NUMBER NOT NULL ENABLE, 
 "NOME" VARCHAR2(40), 
  CONSTRAINT "CIDADE_PK" PRIMARY KEY ("ID_CIDADE") ENABLE
   )

Existe ferramenta de administração visual na instalação do Oracle, mas não é objetivo deste artigo expor a utilização da mesma.

Conexão com SQLdb

Para fazer a conexão vamos usar como sempre um Data Module onde os componentes de banco de dados serão colocados.

Localize na aba SQLdb o componente TOracleConnection e coloque no Data Module. Defina suas propriedades como segue:

DatabaseName - informe aqui o SID do banco de dados. No nosso caso é XE, o SID do Oracle Database Express Edition.
Hostname - informe o nome ou IP da máquina onde está o servidor do banco de dados. Como meu teste foi feito na máquina onde está o servidor, eu informei localhost, ou poderia ter informado 127.0.0.1.
Password - informe a senha do usuário hr. Ou de qualquer usuário com acesso ao banco de dados.
UserName - informe hr, ou outro usuário com acesso ao banco de dados.
Para fazer um teste na conexão, mude a propriedade Connected para True. Depois volte para False.

Coloque um TSQLTransaction e defina a propriedade Database com o nome do TOracleConnection que foi colocado anteriormente.

Coloque um TSQLQuery. Defina suas propriedades como mostrado a seguir:

Database - selecione o TOracleConnection anterior.
SQL - informe SELECT * FROM CIDADE.

Finalize colocando um TDataSource da aba Data Access. Defina a propriedade DataSet com a SQLQuery colocada antes.
No evento AfterPost da TSQLQuery digite o código:

SQLQuery1.ApplyUpdates;
SQLTransaction1.CommitRetaining;

No evento AfterDelete selecione o mesmo método para não repetir código.
Pronto. Está feita a configuração de acesso, tanto para leitura quanto para escrita, em uma tabela do Oracle. Crie uma interface seguindo os passos descritos no post Lazarus - Acessando banco de dados com SQLdb - Parte I.
Lembre de abrir a conexão. Pode ser no evento OnCreate do Data Module, com o código:

OracleConnection1.Open;

Conexão com ZeosLib

Coloque um TZConnection no Data Module. Defina as propriedades:

AutoCommit - deixe em True para que as transações no banco sejam comitadas automaticamente.
Database - informe o nome ou IP da máquina do servidor do banco de dados, por exemplo: localhost se estiver na mesma máquina.
HostName - informe o SID do banco de dados. XE no nosso caso. Observe que as propriedades HostName e Database são informadas de forma invertida em relação ao SQLdb.
Password - informe a senha do usuário com acesso ao banco. Neste caso foi usado hr.
Protocol - informe oracle ou oracle-9i. Ambos funcionam.
User - informe o nome do usuário. Neste caso é hr.

Mude Connected para True para testar a conexão. Depois de conectar com sucesso volte Connected para False.

Coloque um TZQuery e um TZUpdateSQL e configure-os como foi feito no artigo Lazarus - Conectando PostegreSQL com ZeosLib.
No TZUpdateSQL defina essas propriedades assim:

DeleteSQL - DELETE FROM CIDADE WHERE ID_CIDADE = :OLD_ID_CIDADE;
InsertSQL - INSERT INTO CIDADE VALUES (:ID_CIDADE, :NOME);
ModifySQL - UPDATE CIDADE SET NOME = :NOME WHERE ID_CIDADE = :OLD_ID_CIDADE;


Coloque um TDataSource da aba Data Access e na propriedade DataSet selecione a TZQuery anterior. Está pronta nossa conexão e acesso a uma tabela. Agora crie a interface com nos posts anteriores. Não esqueça de abrir a conexão com o comando:

ZConnection1.Connect;

Pode ser no evento OnCreate do Data Module.
 
Creative Commons License
This work by Carlos Alberto P. Araújo is licensed under a Creative Commons Atribuição-Uso não-comercial-Compartilhamento pela mesma licença 3.0 Brasil License.