Odliczanie

Adam Boduch

Ostatnio dostałem list z prośbą napisania programu, który odliczałby czas od
podanej wartości do zera. Przy czym czas musiałbyć zapisany w postaci:
HHSS. Czyli program wyglądałby jak stoper tyle, że od podanej wartości
czasowej do 0.

Co nam będzie potrzebne? Przede wszystkim komponent Timer. Umieść go na
formularzu i zmień
jego wartość Enabled na False. Wszystko co nam będzie potrzebne to komponent
Timer oraz
polecenia EncodeTime i DecodeTime. Te polecenia służą do rozdzielania
zmiennej czasowej
typu TTime na oddzielne wartości (godziny, minuty, sekundy), a następnie na
podstawie podanych wartości połączenie ich w zmienną typu TTime.

Na formularzu umieść dodatkowo komponent MaskEdit, który będzie dbał o
poprawność
wprowadzanych danych. Czyli użytkownik nie będzie mógł wpisać żadnych liter,
a tylko
cyfry. Ustaw maskę tego komponentu na LongTime.

Dekodowanie czasu na poszczególne "człony" wygląda tak:

BeforeTime := StrToTime(meTime.Text); // pobierz tekst z kontrolki iprzeksztalc go na zmienna TTime
DecodeTime(BeforeTime, wHour, wMin, wSec, wMSec); // rozłącz czas nagodziny, minuty i sekundy

Natomiast kodowanie wartości na zmienną czasową następująco:

{ tak zmodyfikowane wartosci przeksztalc do postaci zmiennej typu TTime iwyswietl
    na kontrolce TMaskEdit }
  AfterTime := EncodeTime(wHour, wMin, wSec, wMSec);
  meTime.Text := TimeToStr(AfterTime);

Zdarzenie OnTimer komponentu Timer występować będzie co 1000 milisekund,
czyli co jedną sekundę. Następować w niej będzie
zmniejszanie wartości wSec (która to oznacza ilość sekund) i ew.
zmniejszenie pozostałych wartości, czyli wMin oraz wHour.
Przy tym trzeba będzie zastosować trochę warunków if aby wszystko było tak
jak trzeba.

Cała procedura OnTimer wygląda tak:

procedure TMainForm.TimerTimer(Sender: TObject);
var
  BeforeTime, AfterTime : TTime;
  wHour, wMin, wSec, wMSec : Word;
begin
  BeforeTime := StrToTime(meTime.Text); // pobierz tekst z kontrolki i przeksztalc go na zmienna TTime
  DecodeTime(BeforeTime, wHour, wMin, wSec, wMSec); // rozłącz czas na godziny, minuty i sekundy

  Dec(wSec); // zmniejsz liczbe sekund
  { tutaj nastepuje sprawdzenie, czy odliczanie sie zakonczylo. Jezeli liczba godzin
  minut i sek rowna sie 0 to nastepuje wylaczenie timera i uruchomienie procedury "Run"
  }
  if ((wSec = 0) and (wMin = 0) and (wHour = 0)) then
  begin
    Timer.Enabled := False; // wylacz timer
    btnGo.Caption := 'Odliczaj...'; // zmień wartość Caption
    Run; // wywolaj procedure
    Exit;
  end;
  
  if wSec = 0 then  // sprawdz, czy liczba sekund nie rowna sie przypadkiem0
  begin
    wSec := 59; // w takim wypadku zmien te wartosc na 59

  { jezeli liczba godzin oraz liczba minut rowna sie 0 to zmniejsz liczegodzin,
    a zmiennej wMin przypisz wartosc 60 }
    if ((wHour > 0) and (wMin = 0)) then begin dec(wHour); wMin := 60;end;
  { jezeli liczba minut jest wieksza od 0 to zmniejsz te wartosc o jeden }
    if wMin > 0 then dec(wMin);
  end;

  { tak zmodyfikowane wartosci przeksztalc do postaci zmiennej typu TTime iwyswietl
    na kontrolce TMaskEdit }
  AfterTime := EncodeTime(wHour, wMin, wSec, wMSec);
  meTime.Text := TimeToStr(AfterTime);
end;

Czyli następuje tutaj sprawdzenie czy przypadkiem odliczanie się nie
zakończyło. Później w przypadku, gdy wartość wSec wyniesie 0 trzeba będzie
sprawdzić parę warunków. Bowiem liczba minut może wynieść 0 - wtedy trzeba
będzie zmniejszyć o jeden
wartość godzin. Także w przypadku, gdy ilość sekund wyniesie 0 to trzeba
będzie ponownie przypisać tej wartości liczbę 59,
a później zmniejszyć o jeden liczbę minut. Itd., itp.

Cały kod źródłowy programu wygląda tak:

(****************************************************************)
(*                                                              *)
(*            Copyright (c) 2002 by Adam Boduch                 *)
(*                http://4programmers.net                       *)
(*                 [email protected]                        *)
(*                                                              *)
(****************************************************************)

unit MainFrm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, Mask;

type
  TMainForm = class(TForm)
    meTime: TMaskEdit;
    Timer: TTimer;
    btnGo: TButton;
    procedure btnGoClick(Sender: TObject);
    procedure TimerTimer(Sender: TObject);
  private
    procedure Run;
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

procedure TMainForm.Run;
begin
{ wyświetl okienko informacyjne w przypadku skończenia odliczania }
  ShowMessage('Skończone');
end;

procedure TMainForm.btnGoClick(Sender: TObject);
begin
{
   tutaj nastepuje ew. sprawdzenie, czy Timer jest uruchomiony - jezelitak
   to jest mozliwosc zatrzymania jego - w przeciwnym wypadku - nastepujeuruchomienie
}
  if not Timer.Enabled then
  begin
    Timer.Enabled := True;
    btnGo.Caption := 'Zatrzymaj...';
  end else
  begin
    Timer.Enabled := False;
    btnGo.Caption := 'Odliczaj...';
  end;
end;

procedure TMainForm.TimerTimer(Sender: TObject);
var
  BeforeTime, AfterTime : TTime;
  wHour, wMin, wSec, wMSec : Word;
begin
  BeforeTime := StrToTime(meTime.Text); // pobierz tekst z kontrolki iprzeksztalc go na zmienna TTime
  DecodeTime(BeforeTime, wHour, wMin, wSec, wMSec); // rozłącz czas nagodziny, minuty i sekundy

  Dec(wSec); // zmniejsz liczbe sekund
  { tutaj nastepuje sprawdzenie, czy odliczanie sie zakonczylo. Jezeliliczba godzin
  minut i sek rowna sie 0 to nastepuje wylaczenie timera i uruchomienieprocedury "Run"
  }
  if ((wSec = 0) and (wMin = 0) and (wHour = 0)) then
  begin
    Timer.Enabled := False; // wylacz timer
    btnGo.Caption := 'Odliczaj...'; // zmień wartość Caption
    Run; // wywolaj procedure
    Exit;
  end;
  
  if wSec = 0 then  // sprawdz, czy liczba sekund nie rowna sie przypadkiem0
  begin
    wSec := 59; // w takim wypadku zmien te wartosc na 59

  { jezeli liczba godzin oraz liczba minut rowna sie 0 to zmniejsz liczegodzin,
    a zmiennej wMin przypisz wartosc 60 }
    if ((wHour > 0) and (wMin = 0)) then begin dec(wHour); wMin := 60;end;
  { jezeli liczba minut jest wieksza od 0 to zmniejsz te wartosc o jeden }
    if wMin > 0 then dec(wMin);
  end;

  { tak zmodyfikowane wartosci przeksztalc do postaci zmiennej typu TTime iwyswietl
    na kontrolce TMaskEdit }
  AfterTime := EncodeTime(wHour, wMin, wSec, wMSec);
  meTime.Text := TimeToStr(AfterTime);
end;

end.

Cały kod programu możesz ściągnąć tutaj:

4 komentarzy

W tym programie coś brakuje bo jak się ustawi pełną godzinę lub minutę to wyrzuca błąd. Nie dzieje się to tylko jak się doda przynajmniej jedną sekundę.

Nigdy nie ufaj, ze timer wywola ci metode rowno po sekundzie, co jesli w tym momecie twoj program nie bedzie mial dostepu do procesora (co jest wysoce prawdopodobne) i co jesli to sie powtorzy kilka tysiecy razy? Do takich rzeczy uzywa sie RTC (choc zauwazylem, ze w moim laptopie zegarek potrafi sie presunac az o 10 sek w ciagu dnia! ale to inna historia ;)

Nie jestem pewien, czy dobrze zrozumialem problem, ale wydaje mi sie ze ponizszy kod bedzie robil cos podobnego (opuszczam graficzna otoczke, zeby nie dublowac kodu).

program KoncoweOdliczanie;

{$APPTYPE CONSOLE}

uses
  SysUtils;

var
  tekst : String;
  czas_kon, czas : TDateTime;

begin
  Writeln('Program stosuje koncowe odliczanie (max. 23h59m59s).');
  Write('Podaj czas odliczania w formacie h:m:s : ');
  Readln(tekst);

  czas := StrToTime(tekst);   czas_kon := Now + czas;

  Writeln;
  while Now<czas_kon do
   begin
     Write(#13, TimeToStr(czas_kon-Now));
     Sleep(250); // to nie jest niezbedne
   end;
  Writeln(#13#10#13#10'Odliczanie zakonczone. Nacisnij [ENTER], aby zakonczyc program...');
  Readln;
end.

Pozdrawiam, BcbMan.

Link nie dziala. Blad 404???