Gerando Relatórios Dinâmicos usando QuickRep
Neste Artigo o objetivo é criar uma aplicação onde é possível gerar relatórios no QuickRep que o próprio usuário possa escolher os campos desejados e sua ordem de impressão. Vamos utilizar um tabela Funcionarios do tipo Dbase IV com a seguinte estrutura:
Figura 1.
Obs: Este exemplo pode ser utilizado com qualquer banco de dados.
Crie uma Aplicação Delphi e adicione um Data Module com o Nome = Dm. No Data Module coloque um componente Query (paleta BDE). Em DataBaseName coloque o alias ou caminho onde se encontra nossa tabela, no meu caso ficará assim: DataBaseName = C:\Artigo.
Figura 2.
Pré-configurando o Relatório
Como nosso objetivo é utilizar o QuickRep vamos deixar algumas coisas pré-configuradas, para isso adicione um novo Form ao Projeto com Name = FrmRelatorios. Coloque nesse formulário um QuickRep (paleta QReport), em Bands ative HasColumnHeader, HasDetail e HasPageHeader. No Detail e ColumnHeader mude a propriedade Heigth para 20.
Figura 3.
No Page Header vamos colocar um dois QrLabel e dois QrSysData mude suas propriedades conforme mostrado abaixo:
QrLabel1 |
Caption = Gerador de Relatórios Top = 2 Left = 4 |
QrLabel2
|
Top = 21 Left = 4 |
QrSysData1
|
Top = 2 Alignment = taRightJustify AlignToBand = True Data = qrsPageNumber Text = Pagina: |
QrSysData2
|
Top = 2 Alignment = taRightJustify AlignToBand = True Data = qrsDate Text = Data: |
Ainda no Page Header na propriedade Frame Ative DrawBottom, DrawLeft, DrawTop, DrawRigth. Com isso seu QuickRep deve esta com mostrado abaixo.
Figura 4.
Obs: O QRLabel2 será usado para o Titulo do Relatório.
Coloque na sessão USES a unit do Data Module e na propriedade DataSet do QuickRep coloque Dm.Query1.
Criando o Editor de Relatórios
Volte para o formulário principal, mude seu nome para FrmMenu, coloque no formulário um componente PageControl (paleta Win32). Crie duas paginas “Definindo Campos” e “Ordenando”.
Figura 5.
Na Pagina “Definindo Campos” coloque os Componentes: dois Listbox, três Label (ambos da paleta Standard), 4 SpeedButton (Paleta Additional). Organize conforme mostrado abaixo:
Figura 6.
Na pagina “Ordenando” coloque um Label (paleta Standard), um CheckListbox e dois SpeedButton (ambos da paleta Additional). Organize conforme abaixo:
Figura 7.
Para facilitar nosso trabalho vamos mudar os nomes dos SpeedButton:
SpeedButton1 = IncluirCampo
SpeedButton2 = RemoverCampo
SpeedButton3 = CampoUp
SpeedButton4 = CampoDown
SpeedButton5 = OrdemUp
SpeedButton6 = OrdemDown
No formulário logo abaixo ao PageControl coloque um Label, um Edit e um BitBtn e Organize conforme abaixo:
Figura 8.
Mude o nome do BitBtn1 para Gerar. Agora vamos declarar na sessão Public as variáveis abaixo:
public
{ Public declarations }
Pagina:Integer;
QrLabel:array[1..8] of TQrLabel;
QrDbText:array[1..8] of TQrDbtext;
Desc,Campo:array[1..8] of String;
Largura:Array[1..8] of Integer;
end;
Observe que estamos criamos dois array de 8 posições (Nº Maximo de Campos da Tabela) do tipo TQrLabel e TQrDbText. Os QrLabel são os componentes que mostraram as descrições das colunas nos relatórios e os QrDbText são os componentes que mostraram as informações de cada coluna. Assim temos que incluir a QRCtrls na sessão USES.
O Array DESC terá os títulos de cada coluna, o CAMPO o nome dos campos que se deseja usar da tabela e LARGURA aguardará a largura de cada coluna. A variável PAGINA terá a quantidade de caracteres utilizados na pagina, com isso podemos definir se o relatório será impresso em PAISAGEM ou RETRATO.
Vamos até o evento OnCreate do formulário para definirmos os valores dos array e os itens de seleção (ListBox e CheckListBox). Digite o código abaixo:
procedure TFrmMenu.FormCreate(Sender: TObject);
var
i:integer;
begin
// Matriz com Nome de Campo da Tabela
Campo[1] := 'CODIGO';
Campo[2] := 'NOME';
Campo[3] := 'ENDERECO';
Campo[4] := 'BAIRRO';
Campo[5] := 'CIDADE';
Campo[6] := 'UF';
Campo[7] := 'SALARIO';
Campo[8] := 'CARGO';
// Matriz Com Descrição das Colunas do Relatório
Desc[1] := 'Nº Código';
Desc[2] := 'Nome';
Desc[3] := 'Endereço';
Desc[4] := 'Bairro';
Desc[5] := 'Cidade';
Desc[6] := 'UF';
Desc[7] := 'Salário';
Desc[8] := 'Cargo';
// Matriz Com a Largura das Colunas
Largura[1] := 9;
Largura[2] := 40;
Largura[3] := 40;
Largura[4] := 20;
Largura[5] := 20;
Largura[6] := 2;
Largura[7] := 10;
Largura[8] := 15;
// Colocamos as Descrições dos Campos para Seleção no Listbox e CheckListbox
for i := 1 to 8 do
begin
CheckListBox1.Items.Add(Desc[i]);
ListBox1.Items.Add(Desc[i]);
end;
end;
Obs: O Array Largura deve conter o tamanho que se deseja para cada coluna do relatório, podendo ser o mesmo tamanho os campos na tabela ou tamanhos pré-definidos.
Agora vamos criar a função PosCampo que retornará a posição dentro dos arrays dos campos selecionados.
function TFrmMenu.PosCampo(Campo:String):Integer;
var
i:integer;
begin
for i := 1 to 8 do
begin
if Campo = Desc[i] then
begin
Result := i;
Break;
end;
end;
end;
Obs: não esquece de declara a função na sessão public ou private.
Agora vamos colocar o código para a escolha dos campos nos relatórios. Vá até o evento OnClick do botão IncluirCampo e digite o código abaixo:
// Verificamos se existem itens
if Listbox1.ItemIndex >= 0 then
begin
// Verificamos se o nº de caracteres do campo não ultrapassa o total da pagina
if Pagina + Largura[PosCampo(Listbox1.Items.Strings[ListBox1.ItemIndex])] > 125 then
ShowMessage('Campo Supera o Tamanho da Pagina')
else
begin
// Adiciona o Campo escolhido na lista de seleção
Pagina := Pagina + Largura[PosCampo(Listbox1.Items.Strings[ListBox1.ItemIndex])];
ListBox2.Items.Add(ListBox1.Items.Strings[ListBox1.ItemIndex]);
ListBox1.Items.Delete(ListBox1.ItemIndex);
end;
end;
Obs: Estamos definindo 125 como o total de caracteres para o tamanho do papel em paisagem e 94 em retrato.
Digite agora o código do botão RemoverCampo.
if Listbox2.ItemIndex >= 0 then
begin
Pagina := Pagina - Largura[PosCampo(Listbox2.Items.Strings[ListBox2.ItemIndex])];
ListBox1.Items.Add(ListBox2.Items.Strings[ListBox2.ItemIndex]);
ListBox2.Items.Delete(ListBox2.ItemIndex);
end;
Digitando os código dos botões de Organização.
Botão CampoUp.
var
i: integer;
begin
if Listbox2.ItemIndex > 0 then
begin
i := ListBox2.ItemIndex;
ListBox2.Items.Move(i,i-1);
ListBox2.ItemIndex := i-1;
end;
end;
Botão CampoDown.
var
i:integer;
begin
if Listbox2.ItemIndex < ListBox2.Items.Count - 1 then
begin
i := ListBox2.ItemIndex;
ListBox2.Items.Move(i,i+1);
ListBox2.ItemIndex := i+1;
end;
end;
Botão OrdemUp.
var
i:integer;
begin
if CheckListbox1.ItemIndex > 0 then
begin
i := CheckListBox1.ItemIndex;
CheckListBox1.Items.Move(i,i-1);
CheckListBox1.ItemIndex := i-1;
end;
end;
Botão OrdemDown.
var
i:integer;
begin
if CheckListbox1.ItemIndex < CheckListBox1.Items.Count - 1 then
begin
i := CheckListBox1.ItemIndex;
CheckListBox1.Items.Move(i,i+1);
CheckListBox1.ItemIndex := i+1;
end;
end;
Agora é a parte mais importante para a nossa aplicação o código do botão Gerar.
procedure TFrmMenu.BtnGerarClick(Sender: TObject);
var
i, Col, Tamc:integer;
NCampo,Ordem:String;
begin
//Verificamos se existem Campos Selecionados
if Listbox2.Items.Count > 0 then
begin
// Posição inicial da coluna
Col := 5;
// Informamos o Titulo do Relatório
Form2.QRLabel2.Caption := Edit1.Text;
for i := 0 to listbox2.Items.Count -1 do
begin
//Obtendo o Tamanho e Nome do Campo
Tamc := Largura[PosCampo(ListBox2.Items.Strings[i])] ;
NCampo := Campo[PosCampo(ListBox2.Items.Strings[i])];
//Criando Componentes Para os Títulos das Colunas
QrLabel[i+1] := TQrLabel.Create(Form2.QuickRep1.Bands.ColumnHeaderBand);
QrLabel[i+1].Parent := Form2.QuickRep1.Bands.ColumnHeaderBand;
QrLabel[i+1].Left := Col;
QrLabel[i+1].Top := 4;
QrLabel[i+1].Caption := ListBox2.Items.Strings[i];
QrLabel[i+1].Font.Style := [fsunderline,fsbold];
//Criando Componentes de Exibição de Dados das Colunas
QrDbtext[i+1] := TQrDbtext.Create(FrmRelatorios.QuickRep1.Bands.DetailBand);
QrDbText[i+1].Parent := Form2.QuickRep1.Bands.DetailBand;
QrDbText[i+1].Left := Col;
QrDbText[i+1].Top := 8;
QrDbText[i+1].DataSet := Dm.Query1;
QrDbText[i+1].DataField := NCampo;
//Obtendo o Valor da próxima Coluna. Como o valor precisa ser em pixel multiplicamos por
// 5 o tamanho do Campo.
Col := Col + (5 * Tamc);
// Alinhamos a direita o Campo Salario
if NCampo = 'SALARIO' then
begin
QrDbText[i+1].AutoSize := False;
QrDbText[i+1].Alignment := taRightJustify;
end;
end;
// Criando Ordenação
Ordem := '';
for i := 0 to CheckListBox1.Items.Count - 1 do
begin
if CheckListBox1.Checked[i] = True then
begin
if Ordem = '' then
Ordem := Ordem +' Order by ' + Campo[PosCampo(CheckListBox1.Items.Strings[i])]
else
Ordem := Ordem +' , ' + Campo[PosCampo(CheckListBox1.Items.Strings[i])];
end;
end;
//Passamos as instruções SQL
Dm.Query1.Close;
Dm.Query1.SQL.Clear;
Dm.Query1.SQL.Add('select * from funcionarios.dbf');
if Ordem <> '' then
Dm.Query1.SQL.Add(Ordem);
Dm.Query1.Open;
//Chamamos o Relatório
FrmRelatorios.QuickRep1.Preview;
//Liberamos os Componentes utilizados
for i := 0 to listbox2.Items.Count -1 do
begin
QrLabel[i+1].free;
QrDbText[i+1].free;
end;
end;
end;
Agora precisamos definir se o relatório será impresso em paisagem ou retrato. Para isso vamos até o formulário FrmRelatorios e no evento OnBeforePrint do QuickRep digite o código abaixo:
if FrmMenu.Pagina <= 94 then
QuickRep1.Page.Orientation := poPortrait
else
QuickRep1.Page.Orientation := poLandscape;
Inclua a PRINTERS na sessão USES. Pronto agora é só fazer alguns testes.
Selecionado Campos
Figura 9.
Ordenando Por Salário
Figura 10.
Visualizando Relatório
Figura 11.
Conclusão
Mostrei aqui uma forma de como desenvolver um gerador de relatórios onde o próprio usuário pode escolher os dados desejados acredito que seja muito útil em diversas aplicações. Abraços T+
Walbert Castro (walbertsc@hotmail.com) Coordenador de Informática Revenda Ambev do Amapá.