terça-feira, 3 de agosto de 2010

Trabalhando com DBCtrlGrids


Desta vez, apresento-lhes a um componente que traz uma alternativa ao “DBGrid” e também um pouco mais de versatilidade com relação ao seu incansável similar. Dentre suas habilidades, a mais louvável seria a de poder agregar outros componentes, geralmente “DataWare”, pois trata-se de um objeto do tipo “Container”, fazendo assim expandir suas possibilidades. Dentre os objetos que podemos adicionar posso citar:

- DBEdit;
- DBComboBox;
- DBImage;
- DBCheckBox;
- DBChart;
- Chart;
- Entre outros.


Para iniciar os trabalhos, adicionemos os seguintes componentes ao seu projeto:

Componente
Aba
DBCtrlGrid
Data Controls
DBEdit
Data Controls
DBComboBox
Data Controls
DBImage
Data Controls
Table
BDE
DataSource
Data Access
Labels (2)
Standard



Talvez você esteja se perguntando. Como vou adicionar estes componentes ao DBCtrlGrid ?
Resposta: Da mesma forma como adicionaria um componente a um “Painel” por exemplo. Clicando no componente desejado em uma das abas do Delphi, e clicando de volta sobre o componente “DBCtrlGrid”.

Dica: Se você tentar adicionar um componente e uma tela surgir com esta mensagem “Cannot control be used in a DBCtrlGrid.” é porque o componente não implementa as Propriedades e Eventos do Objeto escolhido.

Configurando componentes:

Componente
Propriedade
Valor
DataSource1
DataSet
Table1
Table1
TableName
c:\Arquivos de Programas\Arquivos Comuns\Borland Shared\Data\biolife.db
Table1
Active
True
Table1
ReadOnly
True
DBCtrlGrid1
DataSource
DataSource1
DBCtrlGrid1
Width
633

Ao executar o programa, perceberá que apareceram outras linhas “registros”, tanto com relação à quantidade de peixes, quanto à de exibição por página, que vem configurada para três. Isso pode ser modificado para quantas quiser, mas não aconselho um número muito alto, pois não caberia na tela. A propriedade que altera este valor é “RowCount” e só aceita números inteiros. Outra propriedade interessante é a que altera a orientação “Orientation” e que vem por padrão setada para “goVertical”.

Fiz este projeto com um componente “Table” pela facilidade de configuração, mas poderia ter sido com qualquer outro suportado pelo Delphi.

Curiosidade: Em outros testes realizados posteriormente, percebi que o “DBCtrlGrid” suporta adicionar até mesmo um componente do tipo “QuickRep”, da aba QReport, que para quem não conhece, trata-se de um componente gerador de relatórios. Segue a dica para quem quiser aprofundar seus conhecimentos e usar de criatividade para desenvolver seus sistemas.

Com a leitura desse artigo você aprendeu:

- Configurar e exibir dados de uma fonte de dados em um “DBCtrlGrid”;
- Adicionar componentes em um “DBCtrlGrid”.

segunda-feira, 2 de agosto de 2010

Instalação de Componentes no Delphi

O Delphi nos proporciona várias formas de instalação de componentes, isso confunde um pouco quem vai fazer tal tarefa.

Às vezes, instalamos um componente e só um ano depois, após formatarmos nossas máquinas, iremos reinstalar os mesmos. Por este motivo, esquecemos de como é feita a sua instalação.

Podemos instalar componentes cuja extensão dos arquivos é: *.pas, *.dcu, *.dpk.

Explicando todas as instalações:

1- *.pas

Arquivos que necessitam de um Package (componentes que possuem o *.pas), execute o Delphi e feche o projeto, acesse o menu ‘Component’ e clique na opção ‘install component’, na tela que abrirá clique na aba ‘Into new package’, clique no botão Browse...(figura 1.1). Abra o arquivo com a extensão *.pas, clique em OK e após compile e instale. Após isto, o Delphi irá criar uma aba na barra de componentes com o nome do seu novo componente.

(Figura 1.1)
Clique para ver a imagem em seu tamanho real

2 - *.DPK

Para a instalação de pacotes (arquivos com extensão *.DPK), execute o Delphi, feche o projeto, acesse o menu File e clique na opção ‘Open’, abra o arquivo que contém os componentes. Clique em OK e após clique em ‘Install’. (Figura 2.1).

(Figura 2.1 – exemplo instalação do Crystal Report).
Clique para ver a imagem em seu tamanho real

3- *.DCU

Para a instalação de componentes cujo arquivo tem a extensão *.DCU acesse o menu ‘Component’ e clique na opção ‘Install Package’. Verifique se na lista ‘Design Packages’ existe a opção ‘Borland user component’, se sim, clique nela e em seguida clique no botão ‘Edit’(Figura 3.1), abrirá uma caixa de mensagem, clique no botão ‘Yes’. Na janela que aparece, clique no botão ‘Add’, na janela que se abrirá, clique no botão ‘Browse’ da caixa de texto ‘unit file name’. Na caixa de combinação ‘files of type’ escolha ‘Delphi compiled unit’. Depois, na caixa de texto ‘File Name’ direcione o arquivo a ser instalado, clique no botão ‘Open’. Clique o botão ‘OK’ na janela que aparece e clique no botão ‘install’.

Se na lista Design packages não tiver a opção Borland user component você deverá primeiro instalar componentes que estão em arquivos com extensão *.pas.

(Figura 3.1)
Clique para ver a imagem em seu tamanho real

OBS.: É imprescindível que seja copiado os arquivos de cada componente para a pasta lib do Delphi, do contrário o delphi acusará a falta de arquivos para a compilação/instalação dos componentes.

Um pouco mais...

Quando precisamos de mais alguns recursos no Delphi, ele nos dá a possibilidade de utilizar ActiveX de softwares que já estão instalados em nossa máquina. É necessário importar o ActiveX e o Delphi irá criar uma paleta com o nome ActiveX com o componente.

Acesse o menu ‘Component’, clique em ‘Import ActiveX Control...’. Irá aparecer uma tela(Figura 4.1) com a lista dos ActiveX que podemos instalar, escolha um e clique em Install, para fazer este teste escolhi o Media Player, a tela seguinte (Figura 4.2) irá especificar o Package que será utilizado pelo componente. Clique em OK.

Confirme a instalação do Package e instale o ActiveX (Figura 4.3).

Pronto, agora você pode visualizar vídeos em seus projetos, preencha a URL com o endereço do vídeo.

(Figura 4.1)
Clique para ver a imagem em seu tamanho real

(Figura 4.2)
Clique para ver a imagem em seu tamanho real

(Figura 4.3)
Clique para ver a imagem em seu tamanho real

Função para gerar senhas aleatórias

Essa é uma dica simples e que para os que trabalham com redes sem fio pode ser muito útil. O que a função faz é gerar um string com caracteres hexadecimais, mas que podem ser adaptados para qualquer outro tipo. Esta função recebe como parâmetros o comprimento da senha como um integer, e outros três parametros do tipo boolean que indicam se estarão presentes letras minúsculas, maiúsculas e números.

Aqui vai o código e as explicações seguem logo abaixo.
function GeraSenhaHex(Digitos: Integer; Min: Boolean; Mai: Boolean; Num: Boolean): string;
const
MinC = 'abcdef';
MaiC = 'ABCDEF';
NumC = '1234567890';
var
p, q : Integer;
Char, Senha: String;
begin
Char := '';
If Min then Char := Char + MinC;
If Mai then Char := Char + MaiC;
If Num then Char := Char + NumC;
for p := 1 to Digitos do
  begin
    Randomize;
    q := Random(Length(Char)) + 1;
    Senha := Senha + Char[q];
  end;
Result := Senha;
end;
 
Explicações:

Primeiro criamos as constantes que trarão os caracteres referentes a letras minúsculas, maiúsculas, e números, depois, iniciamos como vazia, só por desencargo de consciência já que o delphi faz isso por padrão, a variável "Char", que conterá todos os caracteres a serem usados para a geração da senha randômica.
Após isso, testamos os parâmetros para letras maiúsculas, minúsculas e números, acrescentando à "Char" cada um dos que forem verdadeiros segundo os parâmetros passados na chamada da função.
E depois, para finalizar, um laço com o número de repetições igual aos dígitos passados também como parâmetro na chamada, que usando a função Random do delphi gera números aleatórios dentro do limite estabelecido pelo cumprimento da variável "Char", lembrando que o fato de acrescentar o "+ 1" é por que as posições dos caracteres dentro de um string iniciam em 1, e a função Random gera números de 0 até o valor estipulado como limite. Por exemplo uma String = 'teste' temos os valores a seguir:
String[1] = 't'
String[2] = 'e'
String[3] = 's'
String[4] = 't'
String[5] = 'e'
Terminado. Simples, fácil e bem útil.
 

Função para criar diretorio na Exportação de dados

Essa é uma dica para gerar um diretório no diretório padrão do sistema em exportação de dados em um txt.

Variaveis
var
arq: TextFile;
pasta: String;
Função que para pegar o Diretorio do sistema e gerar a o Diretorio.
 function IniciaDirArq(dirNome: string; arqNome: string): string;
  begin
    pasta := GetPath(Application.ExeName) + '' + dirNome + '';
    CreateDirectory(PAnsiChar(pasta), nil);
    pasta := pasta + arqNome;
    AssignFile(arq, pasta);
    Rewrite(arq);
  end;

Explicação:
Primeiro Declaramos as variaveis arq do tipo TextFile e pasta do tipo string, a variavel pasta na função receberá o GetPath da aplicação(onde está o executavel) + o nome do diretório a ser criado (dirNome),  em seguida gera o direório (CreateDirectory), apos gerar o diretório é precisso gerar o arquivo cujo nome será passado por parametro da função (arqNome) faz a atribuição ao caminho do arquivo (Assingfile), Rewrite deixa o arquivo pronto para excrita.
Aqui chama a função:
IniciaDirArq('Teste', 'Teste.txt');
A função recebe os parametros Teste (dirNome), teste.txt (arqNome)
Gera um txt com os dados da exportação
  Write(arq,
    '2'+
    'T'+ 
    '0.00' +
    '0.00' +
    '0.00' +
    '0.00');
  CloseFile(arq);
Escrevendo os dados no arquivo teste.txt  e fecha o arquivo (CloseFile)

O DBA de Alta Performance


Hoje conversaremos a respeito do trabalho do profissional responsável por Banco de Dados e seus desafios. Sabemos que o grande objetivo de ter um banco de dados em uma empresa é registrar e manter suas informações mais importantes. O responsável pela manutenção e administração dessas informações e de todos os sistemas que rodam utilizando bancos de dados é o Administrador de Banco de Dados, mais conhecido como DBA
A cada ano que passa o trabalho deste profissional está cada vez mais complexo, pois os fatores que mais o afetam estão em constante crescimento, mais dados, mais bancos de dados, virtualização e carência de profissionais eficientes no mercado de trabalho.

O crescimento de dados, por exemplo, tem sido enorme nos últimos anos. Há alguns anos atrás (década passada), os DBAs se preocupavam com Megabytes, hoje tem que lidar com Terabytes e já em algumas grandes companhias com Pentabytes.

Isto tem gerado tarefas bem complexas para seu gerenciamento. Como, por exemplo, monitorar com segurança seus backups, o monitoramento e planejamento desse crescimento, Multiplataforma, Virtualização, Tunning, Capacitação e muitos outros.

E isso é apenas parte de suas funções, o DBA deve se preocupar se as aplicações que rodam utilizando os bancos de dados foram criadas dentro dos padrões, que o banco exige, com tabelas modeladas, quantidade de índices nas aplicações, usuários que as utilizarão, enfim uma série de outros fatores que podem, e muito, atrapalhar seu desempenho. Digo "seus", pois à medida que crescem o volume dos bancos de dados crescem também os tipos de plataformas e de versões a serem administradas.

Hoje a grande maioria dos DBAs tem que gerenciar ambientes de múltiplos bancos de dados e o seu mais novo desafio é estender seus conhecimentos a outros tipos não conhecidos anteriormente pelo profissional.

Outro fator que esta se tornando cada vez mais comum é a virtualização. Com o grande crescimento de “datacenters” virtualizados, criou-se outro tipo de problema para se administrar banco de dados, que vão desde o planejamento da capacidade de seus servidores ao monitoramento da performance.

Performance, este é o grande desafio. Quem nunca ouviu "minha aplicação esta travada" ou ainda "nosso sistema está com problemas hoje", frases comuns que afetam diretamente os DBAs, pois todos acreditam ser deles a responsabilidade de uma aplicação rodar "a todo vapor". O constante monitoramento da performance de um banco de dados é a garantia que os usuários acessem os dados mais rápidos com facilidade e que o banco de dados utilize seus recursos de maneira eficiente.

Hoje temos uma carência de bons profissionais. Administradores de bancos de dados experientes sempre foram difíceis de encontrar, o treinamento é fundamental na sua formação e para quem deseja estar atualizado para o mercado. Apesar de esses fatores aumentarem os desafios do dia-a-dia, fornecedores de banco de dados têm trabalhado para que seus bancos sejam cada vez mais "autogerenciáveis", algo tremendamente positivo e com melhorias já notadas. Porém, a complexidade do ambiente de SGBD está aumentando e não parece ter um fim para a gestão e intervenção humana.

Independente desses desafios, espera-se que o DBA, sobrecarregado de trabalho, mantenha os sistemas de banco de dados essenciais disponíveis e otimizados para ter alta performance. O DBA de alta performance deve concentrar-se nas áreas fundamentais do gerenciamento de banco de dados, que são: Armazenamento, Capacidade e Performance.

Segundo Scott Walz, diretor sênior de produtos da Embarcadero, "Para cumprir essas metas, o DBA deve adotar uma estratégia e equipar-se com as ferramentas certas para enfrentar esses desafios, pois ainda há muito que aprender, compreender e dominar a cada novo lançamento de banco de dados".

segunda-feira, 26 de julho de 2010

Algumas dicas Uteis

Reproduzir arquivos de som WAV:


{ Inclua na seção uses: MMSystem }

PlaySound('C:\NomedoArquivodeSom.wav', 1, SND_ASYNC);

--------------------------------------------------------------------------------------------------------------

Enviar Arquivo para lixeira :

Inclua na seção uses: ShellApi

{ Coloque a procedure abaixo na seção implementation }

procedure ArqParaLixeira(const NomeArq: string; var MsgErro: string);
var
Op: TSHFileOpStruct;
begin
MsgErro := '';
if not FileExists(NomeArq) then begin
MsgErro := 'Arquivo não encontrado.';
Exit;
end;
FillChar(Op, SizeOf(Op), 0);
with Op do begin
wFunc := FO_DELETE;
pFrom := PChar(NomeArq);
fFlags := FOF_ALLOWUNDO or FOF_NOCONFIRMATION or FOF_SILENT;
end;
if ShFileOperation(Op) <> 0 then
MsgErro := 'Não foi possível enviar o arquivo para a lixeira.';
end;

{ - Coloque um botão no Form;
- Altere o evento OnClick do botão conforme abaixo: }

procedure TForm1.Button1Click(Sender: TObject);
var
S: string;
begin
ArqParaLixeira('c:\Diretorio\Teste.doc', S);
if S = '' then
ShowMessage('O arquivo foi enviado para a lixeira.')
else
ShowMessage(S);
end;

--------------------------------------------------------------------------------------------------------------

Fechar o Windows:

{ Reinicia o Windows }
ExitWindowsEx(EWX_REBOOT, 0);

{ Desliga o Windows }
ExitWindowsEx(EWX_SHUTDOWN, 0);

{ Força todos os programa a desligarem-se }
ExitWindowsEx(EWX_FORCE, 0);

{ Você já observou a caixa "Propriedades", aquela que mostra
as propriedades de um arquivo no Windows Explorer, não
aparece na lista do Alt+Tab e tampouco na barra de tarefas?

Isto ocorre porque ela funciona como uma ToolWindow, enquanto
os demais aplicativos funcionam como AppWindow. Porém podemos
mudar o comportamento de nossos programas feito em Delphi
para que se comportem como uma ToolWindow também.

Para experimentar, crie um novo projeto e altere o
Project1.dpr como abaixo (não esqueça do uses):
}

program Project1;

uses
Forms, Windows,
Unit1 in 'Unit1.pas' {Form1};

{$R *.RES}

var
ExtendedStyle : Integer;
begin
Application.Initialize;

ExtendedStyle := GetWindowLong(Application.Handle, gwl_ExStyle);
SetWindowLong(Application.Handle, gwl_ExStyle, ExtendedStyle or
ws_Ex_ToolWindow and not ws_Ex_AppWindow);

Application.CreateForm(TForm1, Form1);
Application.Run;
end;

--------------------------------------------------------------------------------------------------------------

Obter nome do usuário e a empresa registrada no Windows:

Inclua na seção uses: Registry

{ Coloque um botão no form e altere seu evento OnCkick
como abaixo: }

procedure TForm1.Button1Click(Sender: TObject);
var
Reg: TRegIniFile;
S: string;
begin
Reg := TRegIniFile.Create('SOFTWARE\MICROSOFT\MS SETUP (ACME)\');
try
S := Reg.ReadString('USER INFO','DefName','');
S := S + #13;
S := S + Reg.ReadString('USER INFO','DefCompany','');
ShowMessage(S);
finally
Reg.free;
end;
end;    

Executar programa do DOS e fechá-lo em seguida

{ Coloque isto no evento OnClick de um botão: }

WinExec('command.com /c programa.exe',sw_ShowNormal);

{ Se quizer passar parâmetros pasta adicioná-los após o
nome do programa. Exemplo: }

WinExec('command.com /c programa.exe param1 param2',sw_ShowNormal);

domingo, 25 de julho de 2010

Como gravar posição do form no registro do windows e recuperá-lo



Caros amigos delphianos, esta dica é muito útil para quem precisa gravar a posição do formulário no registro do windows e assim quando o usuário for abrir aquele mesmo form ele irá buscar esta informações no registro do windows e chamar o form do mesmo jeito que o usuário visualizou pela ultima vez
Basta inserir no FormShow do form a seguinte linha de código:
// Recupera a posição da janela no registro do windows
GetRegWindowState(Self, '\Software\Software Teste\Teste\Cadastro\Form\CadTabPreco');

E para gravar as informações insira esta linha de código no FormClose do form. 
// Grava a posição do form para posterior recuperação no "FormShow" 
SetRegWindowState(Self,'\Software\Software Teste\Teste\Cadastro\Form\CadTabPreco');

Observação: Self: é o próprio objeto form que está sendo executado.
'\Software\Software Teste\Teste\Cadastro\Form\CadTabPreco': Caminho no registro do windows aonde será gravada as informações.



sexta-feira, 23 de julho de 2010

Pesquisa entre faixa de valores com Firebird

Fonte: www.activedelphi.com.br

Caso queira abrir uma tabela do Firebird pelo Delphi, começando por exemplo, pelo registro com ID 11200 até o ID 11400, deverá usar esta procedure e chamá-la por um botão
Você poderá usar os componentes de acesso a dados de sua preferência, mas em meu caso, estou trabalhando com DBExpress (um SQLConection, um SimpleDataSet e um DataSource):
procedure TForm1.SQLFiltra(Sender: TObject);
var
  Campo1, Campo2: string;
  Ok1, Ok2: Boolean;
begin
  Campo1:=
'';
  Campo2:=
'';
  //recupera o código inicial na variável Campo1
  InputQuery(
'Código Inicial',
   
'Digite o Código onde começará a ser salvo a tabela',
    Campo1);
  //recupera o código final na variável Campo2
  InputQuery(
'Código Final',
   
'Digite o Código onde terminará a seleção para ser salvo a tabela',
    Campo2);
  //se informou os dois códigos
  if (Campo1 <> '') and (Campo2 <> '') then
  begin

    //monta o select utilizando o operador BETWEEN, que faz a pesquisa
    //entre uma faixa de valores, seja numérico, caracter ou date/hora

    SimpleDataSet1.Close;
    SimpleDataSet1.DataSet.CommandText :=
'SELECT * FROM TABELA' +
     
' WHERE CAMPOTABELA BETWEEN ' + Campo1 + ' AND ' + Campo2;
    SimpleDataSet1.Open;
  end
  else

    ShowMessage(
'Valor(es) não digitado(s)');
end;

quinta-feira, 22 de julho de 2010

Usando funções no MySQL


No nosso exemplo, criaremos e utilizaremos uma função que retornará a idade do cliente
Crie uma pequena tabela com a seguinte estrutura:

CREATE TABLE `clientes` (
`ID` int(11) NOT NULL auto_increment,
`NOME` varchar(45) default NULL,
`DATANASC` date default NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `ID` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;



Inclua as linhas abaixo na tabela, ou digite suas próprias informações:


INSERT I NTO `clientes` (`ID`, `NOME`, `DATANASC`) VALUES
(1, 'SILVIO SANTOS', '1971-09-18'),
(2, 'SILLAS SANTOS', '1993-04-27');


Agora, crie a função conforme o exemplo abaixo:


CREATE FUNCTION `idade`(pDATA DATE)
RETURNS varchar(15) CHARSET latin1
NOT DETERMINISTIC CONTAINS SQL SQL SECURITY DEFINER
COMMENT ''
Begin
DECLARE vIDADE varchar(30);
IF DATE_FORMAT(NOW( ),'00-%m-%d') >= DATE_FORMAT(pDATA,'00-%m-%d') THEN
SET @idade=DATE_FORMAT(NOW( ),'%Y')-DATE_FORMAT(pDATA,'%Y');
ELSE
SET @idade=DATE_FORMAT(NOW( ),'%Y')-DATE_FORMAT(pDATA,'%Y')-1;
END IF;
RETURN(concat(@idade,' anos'));
end;


Esta função, retornará a idade da pessoa de acordo com o campo DATANASC, da tabela ‘clientes’.
Veja que a função receberá um valor (pDATA, que no nosso exemplo será o campo ‘DATANASC’ de nossa tabela) e fará a subtração da “dat a corrente” pela data do campo DATANASC.
Dentro da função, criamos uma variável (vIDADE), onde armazenaremos o valor a ser retornado pela função. Note também que a função possui retorno do tipo String (RETURNS varchar(30)), que deverá ter o tamanho suficiente para comportar o resultado retornado. Ou seja, se o valor retornado tiver 15 caracteres, o ‘RETURNS’ da função precisa ter tamanho de 15 ou mais caracteres, senão, pode acontecer de não retornar nenhum valor.

Após ter criado a tabela, inserido as linhas na mesma e criado a função, execute a seguinte instrução:


SELECT NOME, idade(DATANASC) IDADE FROM clientes;


No SQL acima, a consulta irá retornar o Nome, e a idade assim: “XX anos”, para cada cliente.

Como você viu, utilizar funções dentro do banco de dados por ser muito mais prático do que fazê-lo dentro da aplicação (me refiro a funções para retorno de dados, claro).

Lembrando que este exemplo é apenas uma base, pois podemos criar funções para qualquer necessdiade, como: calculo de preços, juros de mora, datas, horas, concatenação de campos complexos, junções de tabelas (como nos inner, left e right joins (desde que precise retornar apenas um valor), e muito mais.

Utilizando Barras de Progresso


Nesta dica comentarei sobre dois componentes muito utilizados para montarmos barras de progresso no Delphi: o ProgressBar e o Gauge
Para fazermos uma aplicação de exemplo, insira no form de um novo projeto:
3 Componentes TLabel - Paleta Standard;
1 Componente ProgressBar - Paleta Win32;
1 Componente TGauge - Paleta Samples;
1 Componente TTimer – Paleta System;
1 Componente TButton – Paleta Standard;
2 Componentes TRadioButton – Paleta Standard;
Mude a propriedade Caption do formuláo para Barras de Progresso. Mude também a propriedade Interval do componente TTimer para 100 (ou menos) e sua propriedade Enabled para False:

Ainda com o TTimer, no seu evento OnTimer, adicione o seguinte código:
procedure TForm1.Timer1Timer(Sender: TObject);
begin  //Se o RadioButton1 estiver marcado executa o código abaixo 
  if RadioButton1.Checked then
   begin

     label2.Caption := inttostr(ProgressBar1.Position)+ '%';
     progressbar1.position := progressbar1.position+5;
     if progressbar1.position = 10 then
      begin
        Label1.caption:='Carregando Telas: '; //escreva o que quiser
        label1.Repaint;
      end;
     if progressbar1.position = 35 then
      begin
        label1.caption:='Carregando as Tabelas';
        label1.Repaint;
      end;
     if progressbar1.position = 85 then
      begin
        label1.caption:='Finalizando';
        label1.Repaint;
      end;
   end; //FIM IF RADIOBUTTON1

  //Se o RadioButton2 estiver marcado executa o código abaixo
  if RadioButton2.Checked then
   begin
     gauge1.progress := gauge1.progress+5;
     if gauge1.progress = 10 then
      begin
        Label3.caption:='Carregando Telas: '; //escreva o que quiser
        label3.Repaint;
      end;
     if gauge1.progress = 35 then
      begin
        label3.caption:='Carregando as Tabelas';
        label3.Repaint;
      end;
     if gauge1.progress = 85 then
      begin
        label3.caption:='Finalizando';
        label3.Repaint;
      end;
   end; //FIM IF RADIOBUTTON2


 end; 
Para finalizarmos, no evento OnClick do TButton, faça:
ProgressBar1.Position := 0;
  Gauge1.Progress := 0;
  Timer1.Enabled := True;
  if RadioButton1.Checked then
    while ProgressBar1.Position < 100 do
      Application.ProcessMessages;
  if RadioButton2.Checked then
    while Gauge1.Progress < 100 do
      Application.ProcessMessages;
  Timer1.Enabled := False;
Como você pode perceber, esta rotina simplesmente aumenta a posição de uma das barras de progresso em um intervalo de tempo pré definido, o que costuma ser utilizado em telas de Splash e também formuláos de consultas demoradas. Porém em operações de processamento demorados e que se sabe a quantidade de execuções (como percorrer os registros de uma tabela, por exemplo), o correto é fazermos:
//atribuir o valor máximo /qtde de processamento
  Gauge1.MaxValue := DataSet.RecordCount;
  while not DataSet.Eof do
  begin
    //processamento dos dados

    DataSet.Next;
    //ajustar a posição da barra de progresso
    Gauge1.Progress := Gauge1.Progress;
  end;


quarta-feira, 21 de julho de 2010

Listas em Memória

Fonte: www.activedelphi.com.br Tratar estruturas de dados em memória é uma alternativa técnica interessante quando se deseja velocidade no processamento, sendo, às vezes, a única alternativa que se apresenta

Por exemplo, imagine produzir um relatório com uma query envolvendo três tabelas, na ordem de milhões de linhas cada, e dispondo-se de um tempo curto e crítico. Isso pode ser conseguido se transferirmos conscientemente algum trabalho de organização e seleção de dados, para a memória.

A opção pelo tratamento consciente de dados em memória, além de aliviar o trabalho do servidor, poderá dar um aumento significativo de velocidade ao processamento.

Ou seja, são várias as situações que admitem esse tipo de solução, pois nem sempre é conveniente ou viável que se reestruture o banco de dados para atender a uma situação específica. Além disso, existem situações em que é necessária a manipulação de massas de dados que não estão sob controle de um SGBD.

O Delphi disponibiliza um poderoso dispositivo que nos auxilia no tratamento de dados, que são as listas em memória, da classe TSTRINGLIST, as quais permitem criar e manipular listas, de modo simples e eficiente.

Um objeto STRINGLIST dispõe de dois métodos que simplificam o seu uso, Sort e Find, que, combinados, fazem grande parte do trabalho.

Este artigo trata do uso prático desse recurso, e para tanto desenvolveremos 3 casos, de complexidade crescente, dando oportunidade a que sejam apresentadas algumas técnicas interessantes. Os casos são estudados a seguir.

Caso 1. Verificando se existe.

Este primeiro caso é bem simples, meramente ilustrativo dos métodos da classe.

Suponhamos uma tabela, tab_A, que desejamos filtrar, e a condição de seleção é que o dado DADO exista em um arquivo plano.

Seria equivalente ao uso da condição EXISTS, do Sql. sendo que, no nosso caso, a lista com os critérios de seleção está em um arquivo texto.

Vejamos o código, simples, para tratar a situação.

procedure TForm1.pega_do_txt;
var lista : TStringList;
   n,ind : integer;
begin

  // carrega Txt para a lista, e a ordena

  lista := TStringList.Create();
  lista.LoadFromFile('c:\revista_delphi\dado.txt');
  lista.Sorted := true;
  // o SORTED é necessário para uso do FIND, mais adiante

  // lê a tabela A, e verifica se existe o dado na lista - "n" é meramente ilustrativo
  n := 6;
  tab_A.Active := true;
  tab_A.First;
  while not tab_A.eof do
       begin
       if lista.Find(tab_A.Fields[n].Value, ind) then
          begin
          // trata linha selecionada
          end;
       tab_A.Next;
       end;
  tab_A.Active  := false;

  lista.Free;
end;

Comentários:

1. Memória ocupada: Se o arquivo texto tem 10.000 linhas, por exemplo, e o dado buscado tem 20 caracteres de comprimento, a stringlist irá ocupar algo em torno de 0,2MB apenas.

2. A propriedade SORTED foi setada por ser uma condição para o método FIND funcionar.

3. Observe que a posição do DADO na lista ficou anotada no campo ind. Isso será utilizado nos próximos exemplos.

4. Em complemento ao método loadfromfile, que foi utilizado no exemplo, existe o savetofile, que permite que os dados da lista, de uso bem simples, sejam despejados em um arquivo plano.

Caso 2. Indexando.

Suponha agora duas tabelas. Na primeira, tab_A, existe um campo, COD que servirá para buscar um DADO na segunda tabela, tab_B.

Na tab_B, existem dois campos: COD e DADO. Esse dado será recuperado de tab_B sempre que COD for encontrado.

Ou seja: toma-se COD de A e procura-se em B: se encontrado, recupera-se DADO.

O programa fica assim: os dois campos da tabela B, COD e DADO, são carregados em listas na memória; em seguida, para cada linha da tabela A, pesquisa-se COD na lista B, recuperando-se o dado DADO.

(Vamos modificar o exemplo anterior, acrescentando mais uma lista, ficando uma para COD, e outra para DADO. Também, as tabelas serão lidas do banco de dados, e não mais de um arquivo texto.)

Mas, agora surge um problema: se carregarmos as duas listas distintas, e em seguida fizermos a ordenação, perdem-se as relações entre as posições dos itens das duas. Para evitar isso, usaremos duas técnicas diferentes, a seguir:

- alternativa 1: ordenando as duas listas juntas

Uma alternativa é agregar COD + DADO numa só lista, ordenar, e em seguida, desmembrar em duas listas, que manterão a posição relativa entre os itens. O desmembramento é necessário porque, se mantivéssemos os dados agregados, o FIND não iria encontrá-los.

Esquematicamente, fica assim:

Figura 1 - Ordenação das duas listas como um todo (juntas em uma única lista)

No passo I, são agregados COD e DADO numa só lista; No passo II, COD + DADO é ordenado, e;

No passo III, COD e DADO são segregados ficando, porém nas mesmas posições nas respectivas listas.

O código fica:

procedure TForm1.indexando_ordenando_as_duas;
var lst_COD,lst_DADO : TStringList;
   n,ind            : integer;
   dado_recuperado  : string;
begin
  lst_COD  := TStringList.Create();
  lst_DADO := TStringList.Create();


  // carrega COD e DADO da tabela B, usando uma query

  query.SQL.Text := 'select COD, DADO from tabela_B';
  query.Active   := true;
  query.First;
  while not query.eof do
       begin
       lst_COD.Add(query.Fields[0].value + query.Fields[1].value);
       query.Next;
       end;
  query.Active   := false;

  lst_COD.Sort;   // ordena os pares de dados
  lst_COD.Sorted := false;


  // agora, desmembra a lista em duas, com os itens emparelhados

  for ind := 0 to lst_COD.Count - 1 do
     begin
     lst_DADO.add(copy(lst_COD[ind], 4, 99));
     // no caso, o comprimento de COD seria 3
     lst_COD[ind]  := copy(lst_COD[ind], 1, 3);
     end;
  lst_COD.Sorted := true;
  // lê a tabela A, e verifica se existe o dado na lista  -
  //"n" é meramente ilustrativo
  n := 6;
  tab_A.Active  := true;
  tab_A.First;
  while not tab_A.eof do
       begin
       if lst_COD.Find(tab_A.Fields[n].Value, ind) then
          //dado_recuperado := lst_DADO[ind]
       else
          dado_recuperado := '';
       tab_A.Next;
       end;
  tab_A.Active  := false;

  lst_COD.Free;
  lst_DADO.Free;
end;

Comentário:

No exemplo acima, confiamos que o comprimento de COD fosse fixo. Caso seja necessário trabalhar com formatos que não resultem em strings fixos, como inteiros, por exemplo, sugerimos que a “chave” carregada na lista seja formatada pela função FORMAT. Assim também pode ser feito quando a chave de busca é formada por múltiplos campos.

- alternativa 2: indexação relativa

A segunda alternativa é construir um vetor (uma lista), carregada apenas com os índices relativos, apontando para a outra lista donde os dados serão recuperados.

Isso é útil, por exemplo, quando DADO, do exemplo anterior, é uma estrutura ou registro, com vários campos, ou ponteiros, ou marcadores, etc. Enfim, quando não se for conveniente que DADO participe da ordenação.

Nesse caso, ao carregarmos a lista COD, carregamos a lista DADO ao mesmo tempo, e a posição em que o dado foi inserido (ind) ficará agregada junto a COD. Depois da ordenação, COD e o índice de DADO são desmembrados em duas listas. Agora são três listas.

Esquematicamente, fica assim:

Figura 2 - Utilizando uma lista/vetor auxiliar

No passo I, são carregadas as listas, e a posição relativa é anotada junto a COD;

No passo II, COD e seu antigo índice são ordenados, e;

No passo III, COD e o índice são desmembrados, ficando o índice como “ponte” para DADO

Esse exemplo ilustra uma técnica forte, que pode ser útil em várias situações.

procedure TForm1.indexando_relativo;
var lst_COD,
   lst_DADO,
   lst_IND          : TStringList;
   n,ind            : integer;
   dado_recuperado  : string;
begin
  lst_COD  := TStringList.Create();
  lst_DADO := TStringList.Create();
  lst_IND  := TStringList.Create();


  // carrega COD e DADO da tabela B, usando uma query

  query.SQL.Text := 'select COD, DADO from tabela_B';
  query.Active   := true;
  query.First;
  ind := -1;
  while not query.eof do
       begin
       inc(ind);
       lst_COD.Add(query.Fields[0].value + inttostr(ind));
       lst_DADO.Add(query.Fields[1].value);
       query.Next;
       end;
  query.Active   := false;

  lst_COD.Sort;      // ordena os codigos, mais os índices para dados
  lst_COD.Sorted := false;


  // agora, desmembra a lista em duas

  for ind := 0 to lst_COD.Count - 1 do
     begin
     lst_IND.add(copy(lst_COD[ind], 4, 99));
     // no caso, o comprimento de COD seria 3
     lst_COD[ind] := copy(lst_COD[ind],  1, 3);
     end;
  lst_COD.Sorted := true;

  // lê a tabela A, e verifica se existe o dado na lista
  //"n" é meramente ilustrativo

  n := 6;
  tab_A.Active  := true;
  tab_A.First;
  while not tab_A.eof do
       begin
       if lst_COD.Find(tab_A.Fields[n].Value, ind) then
         dado_recuperado := lst_DADO[ strtoint(lst_IND[ ind ]) ]
       else
          dado_recuperado := '';
       tab_A.Next;
       end;
  tab_A.Active  := false;

  lst_COD.Free;
  lst_DADO.Free;
  lst_IND.Free;
end;

Caso 3. Uma aplicação.

Imaginemos agora duas tabelas, uma de ORÇAMENTOS, e outra de VENDAS realizadas, e desejamos saber o percentual de rejeição dos orçamentos, por produto.

Na tabela VENDAS está registrado o número do orçamento que deu origem à venda, mais o código do produto.

Porém, na tabela ORÇAMENTOS, não há referência se o orçamento foi aceito ou rejeitado. Essa tabela contém apenas o número do orçamento e o código do produto,

Ambas as tabelas têm em torno de 1.500.000 linhas. O número do orçamento é um código numérico, de 8 posições. Sabemos também que a tabela de PRODUTOS tem em torno de 5.000 linhas.

Então, se carregarmos todos os números de orçamentos, que estão na tabela VENDAS, para uma lista, tal lista ocupará em torno de 12MB, o que é bastante viável.

Trabalharemos então com a seguinte estrutura:

- duas listas, a primeira com os códigos de todos os produtos, ordenados, e, a segunda, com os códigos dos orçamentos aceitos, ou seja, que se transformaram em vendas.

- e mais um vetor de duas posições inteiras, com uma entrada para cada produto, onde serão armazenados contadores dos orçamentos emitidos, e dos orçamentos aceitos.

A indexação do vetor será relativa à lista de produtos.

Esquematicamente, fica assim:

Figura 3 - Aplicação das ordenações

O programa, então, fica:

- carregam-se todos os códigos de orçamentos que estão na tabela VENDAS para uma lista, que também é ordenada. (Orçamentos de “Vendas”)

- carregam-se os códigos dos produtos para outra lista, que é depois ordenada. (COD)

- tomam-se todos os orçamentos (código do orçamento e código do produto), da tabela ORÇAMENTOS, e:

soma-se 1 ao contador ORÇADO do produto, e:

se o orçamento existe na tabela de VENDAS (ou seja, na lista que foi carregada), soma-se 1 ao contador VENDIDO do produto.

Vamos ao código:

procedure TForm1.contando_os_orcamentos;
var lst_PROD,
   lst_ORCS         : TStringList;
   conta            : array[1..10000,1..2] of integer; // 1-orcado; 2-vendido
   ind,aux          : integer;
   orcado, vendido  : integer;
begin

  orcado := 1;
  vendido:= 2;

  lst_PROD := TStringList.Create();
  lst_ORCS := TStringList.Create();


  // carrega os códigos dos produtos, usando uma query

  query.SQL.Text := 'select PROD from PRODUTOS';
  query.Active   := true;
  query.First;
  ind := 0;
  while not query.eof do
       begin
       lst_PROD.Add(query.Fields[0].Value);
       inc(ind);
       conta[ind,orcado]  := 0;
       conta[ind,vendido] := 0;
       query.Next;
       end;
  query.Active    := false;


  // carrega os códigos dos orçamentos que vingaram

  query.SQL.Text := 'select COD_ORC from VENDAS';
  query.Active   := true;
  query.First;
  ind := -1;
  while not query.eof do
       begin
       lst_ORCS.Add(query.Fields[0].value);
       query.Next;
       end;
  query.Active   := false;

  lst_PROD.Sorted := true;
  lst_ORCS.Sorted := true;


  // carrega os orçamentos, e verifica se foram aceitos ou não

  query.SQL.Text := 'select COD_ORC,PROD from ORCAMENTOS';
  query.Active   := true;
  query.First;
  while not query.eof do
       begin
       lst_PROD.Find(query.Fields[1].value,ind);   //pega o índice do produto
       inc(conta[ind, orcado]);                    //conta orçamento
       if lst_ORCS.Find(query.Fields[0].value, aux) then // conta se foi aceito
          inc(conta[ind, vendido]);
       query.Next;
       end;
  query.Active  := false;

  lst_PROD.Free;
  lst_ORCS.Free;
end;

Comentário:

A lista de produtos poderia ser já uma lista de um componente STRINGBAND, por exemplo.

Esse caso poderia ser modificado para incluir as tuplas (COD, Descrição e contadores, etc) descritas em registros, e o vetor passar a ser uma estrutura com tais registros.

Conclusão.

Utilizando principalmente uma classe e dois métodos da classe, demonstramos como é simples tratar dados, de modo consciente e eficiente, em memória. Ou seja, pode ser simples implementarmos estratégias personalizadas para o tratamento dos dados, o que quase sempre representa um ganho significativo de desempenho para a aplicação.

Então, se juntarmos o conhecimento formal de estruturas de dados, com os poderosos recursos que o Delphi disponibiliza, poderemos primar pela eficiência da programação, sem comprometer os prazos de desenvolvimento. E mais, podemos avançar no tratamento de estruturas complexas, como grafos, por exemplo, lançando mão apenas da nossa ferramenta básica do dia-a-dia.

Por: Pedro Jales - Graduado em Engenharia Química, e pós-graduado em Engenharia de Sistemas e em Administração de Empresas. Começou a programar em 1975, inicialmente em Fortran e Cobol. Trabalhou como Analista de Sistemas de 1978 a 2002, desenvolvendo programação, Projetos de Sistemas e Análise Organizacional. Programa em Delphi desde 1998. Contato: pedrojales@ig.com.br

Dica do Editor

Listas em memória podem ajudam a obter melhor performance em vários casos, porém, os exemplos de pesquisa e ordenação mostrados nesta artigo do amigo Pedro Jales não devem ser considerados como a melhor forma para lidar com banco de dados, seja um SGBD ou um com dados somente em memória.

Nestes casos é melhor utilizar um componente já pronto, como um ClientDataSet, um RxMemoryData ou um JvMemoryData para realizar as operações de ordenação (por índice virtual em memória), filtro (propriedade Filter), busca (Locate, FindKey, FindNearest), etc, para não ter que reinventar a roda e desfrutar de códigos já testados e aprovados por tantas pessoas.

Utilizar listas em memória são úteis em cruzamento de dados, em aplicações críticas ou com certa limitação de hardware.

Além disso, há outros tipos de listas (que não a StringList) que podem ser utilizadas em processamentos críticos. Um exemplo disto é trabalhar com ponteiros e montar os apontamentos entre os endereços diretamente no registro armazenado neles.

Veja abaixo alguns materiais aqui do site, sobre listas e dados em memória que podem ser úteis:

Usando Record, Ponteiro e TList Criando tabelas temporárias Criando arquivos XML com ClientDataSet com Pesquisa e Ordenação de Colunas

Criando Arrays dinâmicos de objetos no Delphi

Fonte: www.activedelphi.com.br Caros amigos delphianos, neste tutorial vou explicar para vocês como manipular objetos num array dinamicamente. Farei uma aplicação demostrando a impressão de cheques

Primeiro criaremos a classe, a classe é criada após o uses e antes da implementation, neste caso a classe criada de exemplo sera a Tcheque.

//-------------------------------------------------------------------// // DESCRIÇÃO : CLASSE CHEQUE // //------------------------------------------------------------------// type TCheque = class protected //----- Get e Set procedure SetBanco(const Value: String); function GetBanco:String; procedure SetNumero(const Value: String); function GetNumero:String; procedure SetValor(const Value: String); function GetValor:String; procedure SetFavorecido(const Value: String); function GetFavorecido:String; procedure SetCidade(const Value: String); function GetCidade:String; procedure SetData(const Value: String); function GetData:String; procedure SetVerso(const Value: String); function GetVerso:String;

private //----- Atributos FBanco : String; FNumero : String; FValor : String; FFavorecido : String; FCidade : String; FData : String; FVerso : String;

public property Banco : String read GetBanco write SetBanco; property Numero : String read GetNumero write SetNumero; property Valor : String read GetValor write SetValor; property Favorecido : String read GetFavorecido write SetFavorecido; property Cidade : String read GetCidade write SetCidade; property Data : String read GetData write SetData; property Verso : String read GetVerso write SetVerso;

//----- Metódos Criar e destruir objeto constructor Create; destructor Destroy; //---- end;

Está é a estrutura da classe que irei usar neste exemplo, não irei colocar as funções delas por que não é objetivo deste tutorial mostrar orientação ao objetos, caso haja um interesse futuramente posso postar um artigo mais completo mostrando como trabalhar com POO no Delphi.

Após a criação da estrutura da classe que iremos utilizar, agora vou mostrar como pegar os dados de um DataSet e jogá-los dentro de um array.

Procedure GeraCheques; var ACheques : array of TCheque; // Array de Cheques dinâmico InInd : Integer; // Indice do array begin //----------------------------------------------------// // Cria o Array de Cheques // //----------------------------------------------------// //Primeiro registro DataSet.First; while not DataSet.Eof do begin //Aloca uma posição no array SetLength(ACheques,Length(ACheques)+1); //Pega o indice do ultimo registro que foi alocado InInd := Length(ACheques) - 1; // Crio o objeto ACheques[InInd] := TCheque.Create;

//Insere os valores no objeto ACheques[InInd].Banco := DataSetBAN_IN_NUMERO.AsString; ACheques[InInd].Numero := DataSetATO_ST_CHEQUE.AsString; ACheques[InInd].Valor := FormatFloat('###########0.00',DataSetATO_RE_VALORPAGO.AsFloat); ACheques[InInd].Favorecido := DataSetATO_ST_NOMINAL.AsString; ACheques[InInd].Cidade := Ed_Local.Text; ACheques[InInd].Data := FormataData('dd/mm/yy',DataSetATO_DT_VALIDOPARA.AsDateTime); ACheques[InInd].Verso := Ed_Memo.Lines.Text;

// Pula para o próximo registro DataSet.Next; end; end;

Em muitas situações trabalhar com obejetos diretos na memória se torna viável em ganho de performance, cabe a cada umverificar a utilidade do seu uso.

Como deletar arquivos temporários da Internet

Esta dica mostra como deletar arquivos temporários da internet:
Uses WinInet;

procedure deletar;
var
  info: PInternetCacheEntryInfo;
  diretorio: LongWord;
  tamanho: LongWord;
begin
  tamanho := 0;
  FindFirstUrlCacheEntry(nil, TInternetCacheEntryInfo(nil^), tamanho) ;
  GetMem(info, tamanho) ;
  if tamanho > 0 then info^.dwStructSize := tamanho;
  diretorio := FindFirstUrlCacheEntry(nil, info^, tamanho) ;
  if diretorio <> 0 then
  begin
    repeat
      DeleteUrlCacheEntry(info^.lpszSourceUrlName) ;
      FreeMem(info, tamanho) ;
      tamanho := 0;
      FindNextUrlCacheEntry(diretorio, TInternetCacheEntryInfo(nil^), tamanho) ;
      GetMem(info, tamanho) ;
      if tamanho > 0 then info^.dwStructSize := tamanho;
    until not FindNextUrlCacheEntry(diretorio, info^, tamanho);
  end;
  FreeMem(info, tamanho) ;
  FindCloseUrlCache(diretorio) ;
end;

Add To Repository: O repositório de objetos do Delphi

Tempo, o maior desafio dos programadores! No mundo atual desenvolver aplicações com qualidade exige um tempo maior do que dispomos. Para resolver este tipo de problema o delphi nos oferece um grande número de ferramentas que tornam o processo de desenvolvimento de software mais rápido. Dentre elas podemos destacar o repositório de objetos muito útil para quem trabalha com aplicações vinculadas a bancos de dados. Ele permite que objetos herdem as propriedades e rotinas de outros que já existem. Tal característica é denominada de herança. Definimos um objeto como ancestral, “pai”, e depois criamos os objetos que irão herdar as características do ancestral, os “filhos”. A função mais importante do repositório é a possibilidade de ao modificar o ancestral todos os filhos recebem as alterações. O Ancestral Para definir um objeto como pai clique com o botão direito do mouse sobre o mesmo e no menu que surge selecione a opção “Add To Repository...”.
Irá surgir uma janela com os seguintes campos: - Forms: objetos que deseja adicionar ao repositório de objetos; - Title: define um título ao objeto que irá funcionar como ancestral; - Description: neste campo você poderá inserir uma descrição de seu objeto. - Page: guia da janela “New Items” ao qual o objeto será exibido; Obs.: caso digite um nome que não exista, o delphi irá criar uma nova guia com este nome automaticamente. - Author: nome do desenvolvedor; - Browse: seleciona um ícone para a exibição do objeto.
Janela do Add To Repository
Após preencher todos os campos basta clicar no botão “Ok” para gerar o ancestral. Os Filhos Para criar o objeto filho clique no menu file > New > other e selecione a guia onde o ancestral foi salvo.
Na parte inferior desta janela existem três opções: - Copy: cria uma cópia do ancestral, qualquer alteração no pai não afeta o filho. - Inherit: cria um objeto que herda as características do ancestral, neste caso qualquer mudança no pai acarreta alteração no filho. - Use: utiliza o próprio formulário ancestral. Esta opção é usada para modificar o ancestral.
Janela New Items
Após selecionar o objeto ancestral e uma das três características acima, basta clicar no botão “Ok” para utilizar o objeto filho com as características do pai ou alterar o próprio ancestral.

Video Aula