Jump to content
Yamix

Método para se "interromper" um Delay

Recommended Posts

Yamix

Olá,

Trago aqui um método para se "interromper" um Delay.

Para quem não sabe, o Delay é um procedimento que congela o programa por um determinado tempo (em milisegundos).

Procedure Delay(MS: Word); // Cabeçalho do procedimento Delay

 Então, não há meios de se interromper um Delay em tempo de execução (a menos que você finalize o programa de alguma forma :P).

Mas, há uma forma de se trabalhar para que se possa burlar o "Delay", utilizando laços de repetição.

É bem simples, não tem muito mistério. O que é feito, é utilizar repetidas vezes o 

Delay(1);

até somar o tempo total, e por também uma condição de parada. Então, o Delay poderá ser "interrompido" ou não.

Exemplo (onde a condição, é uma tecla ser pressionada) :

Program InterromperDelay;
 Uses Crt;
 
 Const Segundos = 1000;
 
 Var I, TempoDelay: Integer;
     Interrompido: Boolean;
     
Begin 
      Interrompido := False;
      
	  TempoDelay := 3 * Segundos;
	  
	  WriteLn('ENTRANDO NO DELAY..');
	  
	  For I := 1 To TempoDelay Do
	  Begin
	  	  Delay(1);
	  	  
	  	  If (KeyPressed = True) Then
	  	  Begin
	  	  	  ReadKey();
	  	  	  
	  	  	  Interrompido := True;	  	  	  
	  	  	  Break;
	  	  End;
	  End;
	  
	  If (Interrompido = True) Then
	  Begin 
	  	  WriteLn('DELAY INTERROMPIDO!');
	  End
	      Else
	           WriteLn('DELAY NAO FOI INTERROMPIDO!');		  
End.

ou, para quem viu o último tópico:

Program InterromperDelay;
 Uses Crt;
 
 Const Segundos = 1000;
 
 Var I, TempoDelay: Integer;
 
Begin 
	  TempoDelay := 3 * Segundos;
	  
	  WriteLn('ENTRANDO NO DELAY..');
	  
	  For I := 1 To TempoDelay Do
	  Begin
	  	  Delay(1);
	  	  
	  	  If (KeyPressed = True) Then
	  	  Begin	  	  	  
	  	  	  Break;
	  	  End;
	  End;
	  
	  If (KeyPressed = True) Then
	  Begin 
	        ReadKey();
	  	    WriteLn('DELAY INTERROMPIDO!');
	  End
	      Else
	           WriteLn('DELAY NAO FOI INTERROMPIDO!');		  
End.

Espero que tenham entendido a lógica :D

 

PS: Estou começando um canal no youtube, o foco é desenvolver coisas em Pascal. Quem quiser dar uma olhada, segue o link.

Edited by Yamix

Share this post


Link to post
Share on other sites
HappyHippyHippo

sendo normal num sistema operativo, só haver context switching entre os 10ms e os 20ms, esta solução não é muito diferente da mesma sem a instrução Delay(1) lá pelo meio ...


IRC : sim, é algo que ainda existe >> #p@p

Share this post


Link to post
Share on other sites
nunopicado

A minha sugestão é que convertas isso em código reutilizável.
Por exemplo, uma função que aceite o valor de delay e um conjunto de teclas que possam ser aceites como método de interrupção, e retorne um Boolean indicando se foi ou não interrompido antes do tempo! 

Metes isso numa unit, e das próximas vezes que precisares de algo semelhante, está feito, e não terás de andar a copiar/colar código.
Ao mesmo tempo, se um dia chegares à conclusão que podes fazer isso de forma melhor, bastará alterares num sítio, e todos os teus projectos que usem esse código ficaram cobertos pela actualização.


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
Yamix
Em 23/12/2018 às 12:00, nunopicado disse:

A minha sugestão é que convertas isso em código reutilizável.
Por exemplo, uma função que aceite o valor de delay e um conjunto de teclas que possam ser aceites como método de interrupção, e retorne um Boolean indicando se foi ou não interrompido antes do tempo! 

Metes isso numa unit, e das próximas vezes que precisares de algo semelhante, está feito, e não terás de andar a copiar/colar código.

@nunopicado, Dito e feito!

Aqui está, 

Unit DelayInterrupt;
 
 Interface 
           Const PARA_SEGUNDOS = 1000;
           
                 SEG = TRUE;
                 SEGS = TRUE;
                 SEGUNDO = TRUE;
                 SEGUNDOS = TRUE;
                   
                 MILI = FALSE;             
                 MILIS = FALSE;                 
                 MILISSEG = FALSE;
                 MILISSEGS = FALSE;
                 MILISSEGUNDO = FALSE;
                 MILISSEGUNDOS = FALSE;
                 
           Type Conjunto = Set Of Char;

           Function Delay(Tempo: LongInt; ParaSegundos: Boolean): Boolean; OverLoad;            
           Function Delay(Tempo: LongInt; TeclasPermitidas: Conjunto; ParaSegundos: Boolean): Boolean; OverLoad; 
                     
 Implementation                 
                Uses Crt;
                 
                Function Delay(Tempo: LongInt; ParaSegundos: Boolean): Boolean; OverLoad;              
                Begin
                      Delay := False; 
                                   	
                	  If (ParaSegundos = True) Then Tempo := Tempo * PARA_SEGUNDOS;

 	                 For Tempo := Tempo DownTo 1 Do 
 	                 Begin
 	                 	  Delay(1);
 	                 	  
 	                 	  If (KeyPressed = True) Then
 	                 	  Begin
 	                 	        ReadKey();
 	                 	        
 	                 	        Delay := True;
 	                 	        Break;	
 	                 	  End;
 	                 End;	                       	                 
                End;                                                
                
                Function Delay(Tempo: LongInt; TeclasPermitidas: Conjunto; ParaSegundos: Boolean): Boolean; OverLoad;
                Begin
                	  Delay := False;  
                	                       	
                	  If (ParaSegundos = True) Then Tempo := Tempo * PARA_SEGUNDOS;
                	  	  
 	                 For Tempo := Tempo DownTo 1 Do 
 	                 Begin
 	                 	  Delay(1);
 	                 	  
 	                 	  If (KeyPressed = True) Then
 	                 	  Begin
 	                 	        If (UpCase(ReadKey()) In TeclasPermitidas = True) Then 
 	                 	        Begin 	                 	        
 	                 	              Delay := True;
 	                 	              Break;	
 	                 	        End;
 	                 	  End; 	                 	
 	                 End;                 	
                End;                 
End.

Pode ser que tenha como eu melhorar esse código, mas não sei, estou aberto a dicas e opniões. 

  • Vote 1

Share this post


Link to post
Share on other sites
nunopicado

Muito bem... :)

Deixa-me agora dar algumas sugestões, mantendo o core do teu código, mas com algumas optimizações:

unit DelayInterrupt;

interface

const
  AnyKey = [Low(Char)..High(Char)];

type
  TCharSet      = set of Char;
  TUnidadeTempo = (Segundos, Milisegundos);

function Delay(Tempo: LongInt; const Unidade: TUnidadeTempo = Milisegundos; const TeclasPermitidas: TCharSet = AnyKey): Boolean;

implementation

uses
  Crt;

function Delay(Tempo: LongInt; const Unidade: TUnidadeTempo = MiliSegundos; const TeclasPermitidas: TCharSet = AnyKey): Boolean;
const
  PARA_SEGUNDOS = 1000;
begin
  Result := False;
  if (Unidade = Segundos)
    then Tempo := Tempo * PARA_SEGUNDOS;
  while (Tempo > 0) and (not Result) do
    begin
      Crt.Delay(1);
      Result := KeyPressed and (ReadKey in TeclasPermitidas);
      Dec(Tempo);
    end;
end;

end.

 

Começando por cima, todas as tuas constantes desapareceram daqui. Sempre que possível devemos evitar código na secção Interface de uma unit, pois esse código ficará aberto a quem use essa unit. Há outro motivo também para eliminar aquelas constantes, que descrevo mais à frente.

Aparece no entanto uma constante, corresponde ao conjunto predefinido - também explicado mais à frente.

De seguida, mudei o nome do teu Conjunto por TCharSet. Isto não optimiza coisíssima nenhuma, mas há benefícios em manter as normas da linguagem, como por exemplo os tipos de dados começarem por T. Isto não é de todo consensual, e outros programadores dir-te-ão que os prefixos e sufixos são 'lixo visual'. De qualquer das formas, possivelmente todos te dirão que o nome 'Conjunto' é demasiado vago. Um identificador deve dizer exactamente ao que vem, que é como quem diz, quem o lê deve saber com a certeza possível o que ele realmente indica. TCharSet indica-te claramente que é um tipo (T) que representa o conjunto de Char's (Char Set). ;)

De seguida, o tipo TUnidadeTempo substitui todas aquelas constantes que tinhas representadas. Em vez do parâmetro Boolean, tem um parâmetro claramente indicado como aquilo que é - mais uma vez apontando para a clareza de código - com apenas dois valores que representam claramente o que são.

 

Por fim, as tuas duas funções eram praticamente iguais, com apenas um pequeno pormenor de diferença - o uso do ReadKey para testar o conjunto.
O ReadKey pode ser sempre usado, logo que tenhas um conjunto predefinido que abarque todos os caracteres que o utilizador possa usar, no caso de não ser necessário indicar nenhum conjunto (e deste modo ele ter de parar ao toque de qualquer tecla). Daqui já vês a origem da constante AnyKey. ;)

Coloquei também a unidade imediatamente a seguir ao parâmetro Tempo, por uma questão de lógica - é a Unidade de Tempo! 

Por fim, temos o parâmetro TeclasPermitidas, que agora tem um valor predefinido (a constante AnyKey). Ou seja, se nenhum conjunto for indicado, todas as teclas serão aceites para parar a sequência.

 

Quanto à função propriamente dita, pouco muda, mas há alguns pormenores diferentes...
O primeiro é que em vez do nome da função, uso a variável interna Result para definir o valor resultante da função. Usar o nome da função era a forma antiga, e embora ainda funcione, há operações que não são permitidas, pelo menos em alguns compiladores.

Removi-te das condições o "= TRUE" - qualquer condição lógica devolve um valor True ou False. Uma função que devolva um valor Boolean já retorna um valor lógico True ou False, pelo que não há necessidade de meter o "= TRUE" ou "= FALSE".

Também substitui o ciclo FOR por um WHILE, mais para evitar o uso do Break. Não sou purista para te dizer que nunca deves usar este tipo de funções que alteram o fluxo do programa, mas recomendo que as uses só quando não há outra hipótese viável.

 

De qualquer forma, gostei do empenho... :) 
Continua o bom trabalho!

 

Edited by nunopicado

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
Yamix

Nossa, que aula!

Muitississímo obrigado @nunopicado, isso vai ajudar muito em meu aprendizado!!!

Espero um dia com bastante esforço, chegar no seu nível!

Feliz natal!!

Share this post


Link to post
Share on other sites
nunopicado

Já deu para perceber, pelas tuas participações aqui no fórum, que tens gosto em aprender... É o que mais gosto de ver num programador, e é essa característica que te pode levar longe. 

Assim dá gosto tentar ajudar alguém... 

Se precisares de alguma explicação mais detalhada sobre o código que pus, não hesites em dizer! ;)

Continuação de um bom natal... 


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
nunopicado

Fiz mais uns ajustes na função Delay, porque aquele UpCase ali causava problemas com teclas alfabéticas (ele iria testar a letra sempre maiúscula, mas o conjunto de TeclasPermitidas poderia não o incluir.
Também tirei o IF, que no caso é desnecessário, uma vez que assim que Result passa a True, o ciclo acaba.

Agora tenho outra sugestão para ti...
Altera a função de forma a que ela te devolva também a tecla que foi pressionada.

Com isso podes criar por exemplo um menu com timeout, que te seleccione uma função se ao fim de certo tempo não for escolhida nenhuma opção, mantendo no entando o funcionamento esperado se a quiseres usar como um simples delay.

Ficarás com uma função versátil na tua 'caixa de ferramentas' para usar em qualquer projecto. ;)

Edited by nunopicado

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
Yamix

Fiz um outro programinha, usando o que aprendi.. Resumindo: Ele conta o número de teclas pressionadas, e caso esse contador atinja um limite X, começa uma contagem (em segundos) até um tempo Y, e após isso, ele poderá, ou sair do programa, ou apenas seguir o ciclo novamente.

{$mode objfpc} {$H+}

Program pReload;
 Uses Crt, SysUtils;
 
 Type TValor = (Atual, Max);       
      TValores = Array [TValor] Of Integer;
                   
      TContadores = (Cont, Temp);
      TUnidadeTempo = (Seg, Mili); 
      
      TSaida = (Ativada, Desativada);                                                    
      
      TReload = Class
              Private
                      Contador, Temporizador: TValores;        
                      Saida: TSaida;
                      fFinalizar: Boolean;
                      
              Public
                     Constructor Iniciar(ContagemMax, TempoMax: Integer; UnidadeTempo: TUnidadeTempo = Seg; Saida1: TSaida = Desativada);    
                     
                     Procedure Reset();
                     Procedure Temporizar();
                     Procedure Contar();
                     
                     Function Finalizar(): Boolean;  
                     Function GetTempo(Contadores: TContadores): Integer;                     
      End;
      
 Var Reload: TReload;
 
 Constructor TReload.Iniciar(ContagemMax, TempoMax: Integer; UnidadeTempo: TUnidadeTempo = Seg; Saida1: TSaida = Desativada);
 Begin
 	  Reset(); 	
 	  Contador[Max] := ContagemMax;	  
 	  Temporizador[Max] := TempoMax; 
 	  
 	  If (UnidadeTempo = Seg) Then
 	  Begin
 	  	  Temporizador[Max] := Temporizador[Max] * 100;
 	  End;
 	  	  
 	  Saida := Saida1;
 	  fFinalizar := False; 	  
 End;
 
 Procedure TReload.Reset();
 Begin
 	  Contador[Atual] := 0;
 	  Temporizador[Atual] := 0;
 End;
 
 Procedure TReload.Temporizar();
 Begin
 	  If (Contador[Atual] >= Contador[Max]) Then
 	  Begin
 	  	  If (Temporizador[Atual] < Temporizador[Max]) Then
       	  Begin
 	  	        Inc(Temporizador[Atual]);
 	        End 
 	            Else 
 	                 Begin
 	                 	  If (Saida = Ativada) Then 
 	                 	  Begin
 	                 	  	  fFinalizar := True; // Tentei usar o FreeAndNil(Self)
 	                 	  End 
 	                 	      Else Reset();
 	                 End;
 	  End;
 End;  
 
 Procedure TReload.Contar();
 Begin
 	  If (Contador[Atual] < Contador[Max]) Then
 	  Begin
 	  	  Inc(Contador[Atual]);
 	  End;
 End;
 
 Function TReload.Finalizar(): Boolean;
 Begin
 	  Result := fFinalizar;
 End;
 
 Function TReload.GetTempo(Contadores: TContadores): Integer;
 Begin
 	  Case (Contadores) Of
 	       Cont: Result := Contador[Atual];
 	       Temp: Result := Temporizador[Atual];
       End;
 End;
 
Begin
      Reload := TReload.Iniciar(3, 3);
      
	  Repeat	         	  
	         While (Not KeyPressed) And (Not Reload.Finalizar()) Do
	         Begin
	         	  WriteLn('CONTAGEM: ', Reload.GetTempo(Cont), LineEnding, 'TEMPORIZADOR: ', (Reload.GetTempo(Temp) / 100):0:0, 's');
	                       
                   Reload.Temporizar();
	               Delay(10); ClrScr();	          	      	               	                       
	         End; 
	          
             If (Not Reload.Finalizar()) Then
             Begin
	         	  ReadKey();
	               Reload.Contar();             	
             End
                 Else FreeAndNil(Reload);          	         
	  Until (Reload = Nil);
	  
	  WriteLn('FIM');
End.

Eu acho que foi o meu primeiro programa realmente Orientado a Objetos.

Tive uns problemas com o FreeAndNil(self), entrei em um monte de site tentando achar o erro e procurei também como liberava um objeto instanciado de uma classe, mas não conseguia entender muita coisa, tentei e tentei, mas nada.. Então improvisei criando um boolean para fazer o serviço.

Quando fui tentar abrir o código aqui no compilador do meu celular, ele não estava reconhecendo algumas coisas, então fui procurar o motivo, e vi que era necessário usar o {$mode objfpc}, acabei aprendendo também sobre Directives, heheh :D

4 horas atrás, nunopicado disse:

Fiz mais uns ajustes na função Delay, porque aquele UpCase ali causava problemas com teclas alfabéticas (ele iria testar a letra sempre maiúscula, mas o conjunto de TeclasPermitidas poderia não o incluir.

Sim! Eu achei isso enquanto tentava entender o código e consertei, pensei que era uma pegadinha sua para ver se eu estava realmente a prestar atenção no que você havia me passado :P

4 horas atrás, nunopicado disse:

Agora tenho outra sugestão para ti...
Altera a função de forma a que ela te devolva também a tecla que foi pressionada.

Com isso podes criar por exemplo um menu com timeout, que te seleccione uma função se ao fim de certo tempo não for escolhida nenhuma opção, mantendo no entando o funcionamento esperado se a quiseres usar como um simples delay.

Ficarás com uma função versátil na tua 'caixa de ferramentas' para usar em qualquer projecto. ;)

Beleza! Logo, logo lhe dou um retorno com o código. Obrigado mais uma vez,  @nunopicado, está me ajudando muito!

Fico muito feliz em ver que estou aprendendo novas coisas!

Edited by Yamix

Share this post


Link to post
Share on other sites
Yamix
Unit DelayInterrupt;
{$MODE OBJFPC}

 Interface
           Const AnyKey = [Low(Char)..High(Char)]; 
            
           Type TCharSet = Set of Char;
                TUnidadeTempo = (Segundos, Milisegundos);                                                                   

           Function Delay(Tempo: LongInt; Var TeclaPremida: Char; Const TeclasPermitidas: TCharSet = AnyKey; Const Unidade: TUnidadeTempo = Segundos): Boolean; OverLoad;
           Function Delay(Tempo: LongInt; Const TeclasPermitidas: TCharSet = AnyKey; Const Unidade: TUnidadeTempo = Segundos): Boolean; OverLoad;
 
 Implementation
                Uses Crt, SysUtils;

                Function Delay(Tempo: LongInt; Var TeclaPremida: Char; Const TeclasPermitidas: TCharSet = AnyKey; Const Unidade: TUnidadeTempo = Segundos): Boolean;
                Const PARA_SEGUNDOS = 1000;
                Begin
                      Delay := False;
                      
                      If (Unidade = Segundos) Then Tempo := Tempo * PARA_SEGUNDOS;
                                            
                      While (Tempo > 0) And (Not Delay) Do
                      Begin
                            Crt.Delay(1);                          
                            Tempo := Tempo - 1;    
                                                   
                            Delay := (KeyPressed) And (ReadKey In TeclasPermitidas);                           
                      End;
                End;
                
                Function Delay(Tempo: LongInt; Const TeclasPermitidas: TCharSet = AnyKey; Const Unidade: TUnidadeTempo = Segundos): Boolean; 
                Var Aux: Char;
                Begin
                	  Delay := Delay(Tempo, Aux, TeclasPermitidas, Unidade);
                End;                
 End.

Bem fácil :P

Edited by Yamix

Share this post


Link to post
Share on other sites
nunopicado

:) Muito bem

Lembra-te só que, se o utilizador tocar numa tecla especial (setas, por exemplo, ou um dos F's), ele irá retornar sempre #0. ;)


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.