imagem na tela por um determinado tempo (era: Dúvida...)
[quote:74830e66ff=´Moderação´][color=red:74830e66ff]Título editado por Massuda
Por favor, procure usar um título descritivo.
Leia as :arrow: [url=http://forum.clubedelphi.net/viewtopic.php?t=6689]Regras de Conduta[/url] do fórum.[/color:74830e66ff][/quote:74830e66ff]
Olá.
Estou com problema para criar um algoritmo que resolva uma questão envolve tempo.
Preciso que uma imagem seja mantida visível na tela por um determinado tempo, depois deste tempo entre outra imagem por outro tempo determinado e assim por diante. Até aí tudo bem e já fiz da seguinte forma:
[b:74830e66ff]Importante[/b:74830e66ff]: tem que ser utilizado o GetTickCount como base de tempo. Não pode ser um Timer.
O PROBLEMA:
Quando o valor de GetTickCount estourar, o que ocorre a cada 49 dias aproximadamente se considerarmos o tempo a partir do zero, a referência de tempo é perdida e as imagens não serão mais trocadas, ou seja, ficará travada a exibição da última imagem antes de zerar o GetTickCount.
Alguém tem idéia de como resolver este problema?
vlw!
Por favor, procure usar um título descritivo.
Leia as :arrow: [url=http://forum.clubedelphi.net/viewtopic.php?t=6689]Regras de Conduta[/url] do fórum.[/color:74830e66ff][/quote:74830e66ff]
Olá.
Estou com problema para criar um algoritmo que resolva uma questão envolve tempo.
Preciso que uma imagem seja mantida visível na tela por um determinado tempo, depois deste tempo entre outra imagem por outro tempo determinado e assim por diante. Até aí tudo bem e já fiz da seguinte forma:
type TImgParam = packed record Path : ShortString; Tempo: Cardinal; end; Var i: Integer; tPERCORRIDO, tEXPIRAR, tINICIO: Cardinal; ImgAtual: TImgParam; begin tINICIO := GetTickCount; tEXPIRAR := 0; i := 0; while not Interromper do begin ImgAtual := ImagensList[i]; // "ImagensList" vem de outro lugar do programa, que não importa. FuncExibirImagem(ImgAtual); // Isso é apenas para exeplificar como ocrre o processo. tEXPIRAR := tEXPIRAR + ImgAtual.Tempo repeat Sleep(1); tPERCORRIDO := GetTickCount - tINICIO; until tPERCORRIDO > tEXPIRAR; Inc(i); if i >= ImagensList.Count then i := 0; end; end;
[b:74830e66ff]Importante[/b:74830e66ff]: tem que ser utilizado o GetTickCount como base de tempo. Não pode ser um Timer.
O PROBLEMA:
Quando o valor de GetTickCount estourar, o que ocorre a cada 49 dias aproximadamente se considerarmos o tempo a partir do zero, a referência de tempo é perdida e as imagens não serão mais trocadas, ou seja, ficará travada a exibição da última imagem antes de zerar o GetTickCount.
Alguém tem idéia de como resolver este problema?
vlw!
Rtava
Curtidas 0
Respostas
Massuda
23/10/2007
Use um TTimer, caso contrário seu programa vai ficar preso nesse seu loop. Cada vez que o timer estourar, você troca a imagem.
GOSTEI 0
Rtava
23/10/2007
Olá Massuda.
Quanto ao título, como se tratava realmente de uma dúvida sobre lógica e não sobre Delphi propriamente, imaginei que o título estivesse adequado.
Quanto ao TTimer, conforme comentei a base de tempo tem que ser o GetTickCount e já estou usando esta rotina com sucesso, sem travar, de dentro de uma thread.
O problema que estou tendo não é detectar quando estourou o contador, mas sim o que fazer quando estourar. O problema é que, supondo que uma imagem tenha que ficar, por exemplo, 1 hora sendo exibida e quando está com 2 minutos de exibição o contador estoura e eu faço a troca de imagem. E os outros 58 minutos? Eu simplesmente perco. É isso que eu preciso evitar e corrigir.
Quanto ao título, como se tratava realmente de uma dúvida sobre lógica e não sobre Delphi propriamente, imaginei que o título estivesse adequado.
Quanto ao TTimer, conforme comentei a base de tempo tem que ser o GetTickCount e já estou usando esta rotina com sucesso, sem travar, de dentro de uma thread.
O problema que estou tendo não é detectar quando estourou o contador, mas sim o que fazer quando estourar. O problema é que, supondo que uma imagem tenha que ficar, por exemplo, 1 hora sendo exibida e quando está com 2 minutos de exibição o contador estoura e eu faço a troca de imagem. E os outros 58 minutos? Eu simplesmente perco. É isso que eu preciso evitar e corrigir.
GOSTEI 0
Massuda
23/10/2007
Você não mencionou thread no seu post inciial.
Não testei, mas algo assim deve funcionar...
...note que isso independe de contadores.
Não testei, mas algo assim deve funcionar...
uses ... SyncObjs, ... // isso deve ir no Execute da sua thread var Timer: TEvent; // não é exatamente um timer... ... Timer := TEvent.Create(nil, False, False, ´´) while Timer.WaitFor(.TempoAEsperarEmMilissegundos.) = wrTimeout do begin // faz alguma coisa periódica end; Timer.Free; end;
...note que isso independe de contadores.
GOSTEI 0
Rtava
23/10/2007
Massuda,
A base de tempo precisa ser exclusivamente por meio de GetTickCount, pois só assim é possível manter uma sincronia perfeita. Que sincronia? Associado às imagens também são exibidos textos. Para exibir os textos eu uso objetos comuns como Label, Edit, etc. Este objetos de texto têm que existir. Quando uma imagem é exibida, um texto associado à imagem é exibido junto. Se a imagem trocar, o texto também tem que trocar exatamente ao mesmo tempo (ao menos visualmente ao mesmo tempo).
Agradeço o exemplo que você me enviou, que aliás pode me ser muito útil para outras coisas. Porém, para esta aplicação não funcionou.
Para testar eu fiz o seguinte teste: Criei este código abaixo no evento de um TButton mesmo e adicionei à tela um Memo que dei o nome de ´mm´. Depois rodei o programa e enquanto isso abri também alguns outros programas pesados (Photoshop, AutoCad, etc).
O problema ocorre que o TEvent, assim como o TTimer, é afetado pelo processamento da máquina, o que leva a um problema de sincronia na exibição de imagens e textos. Se você reproduzir o teste poderá verificar que no Memo os registros de tempo ficam certinhos enquanto o processamento da máquina não sofrer nenhum distúrbio. Quando o distúrbio ocorre, a contagem de tempo exibida no Memo fica totalmente fora.
A base de tempo precisa ser exclusivamente por meio de GetTickCount, pois só assim é possível manter uma sincronia perfeita. Que sincronia? Associado às imagens também são exibidos textos. Para exibir os textos eu uso objetos comuns como Label, Edit, etc. Este objetos de texto têm que existir. Quando uma imagem é exibida, um texto associado à imagem é exibido junto. Se a imagem trocar, o texto também tem que trocar exatamente ao mesmo tempo (ao menos visualmente ao mesmo tempo).
Agradeço o exemplo que você me enviou, que aliás pode me ser muito útil para outras coisas. Porém, para esta aplicação não funcionou.
Para testar eu fiz o seguinte teste: Criei este código abaixo no evento de um TButton mesmo e adicionei à tela um Memo que dei o nome de ´mm´. Depois rodei o programa e enquanto isso abri também alguns outros programas pesados (Photoshop, AutoCad, etc).
O problema ocorre que o TEvent, assim como o TTimer, é afetado pelo processamento da máquina, o que leva a um problema de sincronia na exibição de imagens e textos. Se você reproduzir o teste poderá verificar que no Memo os registros de tempo ficam certinhos enquanto o processamento da máquina não sofrer nenhum distúrbio. Quando o distúrbio ocorre, a contagem de tempo exibida no Memo fica totalmente fora.
procedure TfPrincipal.Button1Click(Sender: TObject); var Tmr: TEvent; Init: Cardinal; begin Tmr := TEvent.Create(nil, False, False, ´´); Init := GetTickCount; mm.Lines.Add(inttostr(GetTickCount - Init)); while Tmr.WaitFor(2000) = wrTimeout do begin mm.Lines.Add(´ok´); mm.Lines.Add(inttostr(GetTickCount - Init)); end; Tmr.Free; end;
GOSTEI 0
Massuda
23/10/2007
Quando uma imagem é exibida, um texto associado à imagem é exibido junto. Se a imagem trocar, o texto também tem que trocar exatamente ao mesmo tempo (ao menos visualmente ao mesmo tempo).
Por que simplesmente não atualizar os elementos da UI imediatamente antes/depois de trocar a imagem?GOSTEI 0
Rtava
23/10/2007
Vamos lá...
O que precisa ser feito é:
1- Exibir imagens associadas e textos, sincronizados por tempo. São várias imagens e vários textos exibidos ao mesmo tempo, cada um com seu par e cada par com tempos diferentes;
2- Garantir que a sincronia não seja influenciada pelo processamento da máquina, pois o processamento pode sofrer a influência de outros processos que a máquina execute (isso foi o que me levou a usar uma base de tempo única, o GetTickCount). Se a imagem/texto foram programados para serem exibidos durante 10 minutos, 30 segundos e 347 milésimos por exemplo, é esse o tempo que tem que permanecer na tela, nem mais nem menos, independente de tudo mais que a máquina esteja processando;
3- Garantir que a cada troca de imagem, seu respectivo par de texto também seja trocado e vice-versa;
4- Garantir que quando a base de tempo estoure, que neste caso é formada por um tipo inteiro sem sinal de 4 bytes, as imagens e textos já em exibição não percam a referência de quando começaram a serem exibidas e quando devem terminar de serem exibidas.
Independente do que eu já fiz, se garantir estes quatro requisitos o problema estará solucionado. Em todos os testes que fiz, com TTimer, com GetTickCount e agora com TEvent, somente com o GetTickCount (base única de tempo para todos) o sistema funcionou como deveria.
O que precisa ser feito é:
1- Exibir imagens associadas e textos, sincronizados por tempo. São várias imagens e vários textos exibidos ao mesmo tempo, cada um com seu par e cada par com tempos diferentes;
2- Garantir que a sincronia não seja influenciada pelo processamento da máquina, pois o processamento pode sofrer a influência de outros processos que a máquina execute (isso foi o que me levou a usar uma base de tempo única, o GetTickCount). Se a imagem/texto foram programados para serem exibidos durante 10 minutos, 30 segundos e 347 milésimos por exemplo, é esse o tempo que tem que permanecer na tela, nem mais nem menos, independente de tudo mais que a máquina esteja processando;
3- Garantir que a cada troca de imagem, seu respectivo par de texto também seja trocado e vice-versa;
4- Garantir que quando a base de tempo estoure, que neste caso é formada por um tipo inteiro sem sinal de 4 bytes, as imagens e textos já em exibição não percam a referência de quando começaram a serem exibidas e quando devem terminar de serem exibidas.
Independente do que eu já fiz, se garantir estes quatro requisitos o problema estará solucionado. Em todos os testes que fiz, com TTimer, com GetTickCount e agora com TEvent, somente com o GetTickCount (base única de tempo para todos) o sistema funcionou como deveria.
GOSTEI 0
Micheus
23/10/2007
[b:82287590a5]Massuda[/b:82287590a5], será que não daria para o colega [b:82287590a5]rtava[/b:82287590a5] utilizar a função da API, [i:82287590a5]SetTimer[/i:82287590a5], onde ele define o tempo em milisegundos em que sua callback seria chamada?
GOSTEI 0
Massuda
23/10/2007
O timer mais preciso do Windows é o usado pelo sistema de multimídia do SO.
Experimente isso (como sempre, não testado)...
Experimente isso (como sempre, não testado)...
uses MMSystem, ... type TForm1 = .... ... mm: TMemo; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private Timer: MMRESULT; ... public Inicio: Cardinal; ... .... procedure CallBackDoTimer(ID, Msg: Uint; dwUser, dw1, dw2: DWORD); pascal; var Form: TForm1; begin Form := TForm1(dwUser); Form.mm.Lines.Add(IntToStr(GetTickCount - Form.Inicio)); end; procedure TForm1.FormCreate(Sender: TObject); begin Inicio := GetTickCount; Timer := TimeSetEvent(2000 , 0, @CallBackDoTimer, DWORD(Self), TIME_PERIODIC); end; procedure TForm1.FormDestroy(Sender: TObject); begin TimeKillEvent(Timer); end;
GOSTEI 0
Rtava
23/10/2007
Massuda,
Parabens pela dica. Muito boa! Assim como no código que estou usando, este também não trava o programa, funciona com sincronia e não é afetado por qualquer distúrbio de processamento. E ainda nem precisa de thread... muito bom mesmo.
Porém, e sempre tem um porém, a menos que eu esteja enganado ainda há o problema de perda da referência quando o contador estoura. Como não tenho como agir sobre o contador GetTickcount, então reproduzi o problema da seguinte forma: inseri um TTimer (com 100ms de Interval) para servir de base de tempo e criei uma variável ´Contador´ para substituir o GetTickCount. Conforme comentei, a menos que eu tenha me enganado, o problema ocorre também.
Coloquei o código inteiro da Unit para vc verificar. O problema ocorrido é o mesmo que relatei na abertura deste tópico.
Parabens pela dica. Muito boa! Assim como no código que estou usando, este também não trava o programa, funciona com sincronia e não é afetado por qualquer distúrbio de processamento. E ainda nem precisa de thread... muito bom mesmo.
Porém, e sempre tem um porém, a menos que eu esteja enganado ainda há o problema de perda da referência quando o contador estoura. Como não tenho como agir sobre o contador GetTickcount, então reproduzi o problema da seguinte forma: inseri um TTimer (com 100ms de Interval) para servir de base de tempo e criei uma variável ´Contador´ para substituir o GetTickCount. Conforme comentei, a menos que eu tenha me enganado, o problema ocorre também.
Coloquei o código inteiro da Unit para vc verificar. O problema ocorrido é o mesmo que relatei na abertura deste tópico.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, MMSystem, ExtCtrls;
type
TForm1 = class(TForm)
mm: TMemo;
tmr: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure tmrTimer(Sender: TObject);
private
{ Private declarations }
Timer: MMRESULT;
public
{ Public declarations }
Inicio: Cardinal;
Contador: Cardinal;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TForm1 }
procedure CallBackDoTimer(ID, Msg: Uint; dwUser, dw1, dw2: DWORD); pascal;
var
Form: TForm1;
begin
Form := TForm1(dwUser);
Form.mm.Lines.Add(IntToStr(Form.Contador - Form.Inicio));
//Form.mm.Lines.Add(IntToStr(GetTickCount - Form.Inicio));
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Contador := 3;
Inicio := Contador;
// Inicio := GetTickCount;
Timer := TimeSetEvent(100 , 0, @CallBackDoTimer, DWORD(Self), TIME_PERIODIC);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
TimeKillEvent(Timer);
end;
procedure TForm1.tmrTimer(Sender: TObject);
begin
Inc(Contador);
if Contador >= 20 then Contador := 0;
end;
end.GOSTEI 0
Rtava
23/10/2007
Só um observação: o contador foi inicializado com 3 apenas para simular o que ocorreria com o GetTickCount, em que praticamente nunca conseguiremos pegá-lo em Zero.
GOSTEI 0
Massuda
23/10/2007
...o problema de perda da referência quando o contador estoura.
Não entendo porque você precisa do GetTickCounter. Poderia explicar?GOSTEI 0
Rtava
23/10/2007
Desculpe, acabei esquecendo de comentar outra coisa. No lugar onde você colocou 2000ms, se substituir por valores menores que 500ms, começa a dar problema para execução. A rotina passa a levar mais que o tempo programado. Tente com 100ms por exemplo. Claro que para esta minha aplicação não são necessários tempos tão baixos. Só estou comentando a título de curiosidade.
GOSTEI 0
Rtava
23/10/2007
Não entendo porque você precisa do GetTickCounter. Poderia explicar?
Suponha que existam dois Timers temporizando a exibição de duas imagens, uma com 10 segundos e outra com 10 segundos, OK? Agora suponha que algum processo que a máquina executou tenha feito dar aqueles ´trancos´ de processamento que demorou 2 segundos (como ocorre na abertura de um programa pesado). O efeito deste ´tranco´ de processamento age de formas diferentes sobre o Timer1 e Timer2, pois eles têm bases de tempo independentes. Assim, o tempo de exibição da imagem 1 será diferente de 10 segundos (não se sabe quanto) e da imagem 2 também será diferente de 10 segundos (da mesma forma, não se sabe quanto). Ou seja, as imagens que começaram a ser exibidas juntas, poderão não terminar a exibição juntas (já testei isso). Além disso haverá o acréscimo de parte do tempo desse ´tranco´ ao tempo dos dois Timers, mudando de 10 segundos para 10+X.
Da forma como estou fazendo a rotina de sincronia, como postei lá no início, como a base de tempo é única por meio do GetTickcount, mesmo que o tranco influenciasse neste tempo, o que não ocorre, ainda assim o distúrbio seria igualmente atribuído ao tempo das duas imagens. Dessa forma a exibição sempre começa e termina perfeitamente sincronizada.
Por isso estou usando o GetTickCount.
GOSTEI 0
Rtava
23/10/2007
sobe!
GOSTEI 0
Rtava
23/10/2007
sobe!
GOSTEI 0
Rtava
23/10/2007
Resolvido.
Criei um GetTickCount com 64 bits.
Criei um GetTickCount com 64 bits.
GOSTEI 0