Zegar analogowy

PiXel

Poniższy kod rysuje na wybranym Canvasie prosty, analogowy zegarek.

Najpierw w sekcji type wklejamy następujący typ danych:

TClockOptions = packed record
    HourWidth: Integer;
    HourColor: TColor;
    HourStyle: TPenStyle;
    MinuteWidth: Integer;
    MinuteColor: TColor;
    MinuteStyle: TPenStyle;
    SecondWidth: Integer;
    SecondColor: TColor;
    SecondStyle: TPenStyle;
 end;

Procedura rysująca wskazówki, oraz druga, rysująca reszte zegara:

procedure DrawAnalogClock(T: TDateTime; Canvas: TCanvas; R, Left, Top: Integer; Options: TClockOptions);
var
  h, m, s, x, y: Integer;
begin
  h := StrToInt(FormatDateTime('h', T));
  m := StrToInt(FormatDateTime('n', T));
  s := StrToInt(FormatDateTime('s', T));
  Canvas.MoveTo(Left + R, Top + R);
  x := Round(cos(PI * (30 * h + (m / 2) - 90) / 180) * (R - (R / 100 * 30)) + Left + R);
  y := Round(sin(PI * (30 * h + (m / 2) - 90) / 180) * (R - (R / 100 * 30)) + Top + R);
  Canvas.Pen.Width := Options.HourWidth;
  Canvas.Pen.Color := Options.HourColor;
  Canvas.Pen.Style := Options.HourStyle;
  Canvas.LineTo(x, y);
  Canvas.MoveTo(Left + R, Top + R);
  x := Round(cos(PI * (6 * m - 90) / 180) * (R - (R / 100 * 10)) + Left + R);
  y := Round(sin(PI * (6 * m - 90) / 180) * (R - (R / 100 * 10)) + Top + R);
  Canvas.Pen.Width := Options.MinuteWidth;
  Canvas.Pen.Color := Options.MinuteColor;
  Canvas.Pen.Style := Options.MinuteStyle;
  Canvas.LineTo(x, y);
  Canvas.MoveTo(Left + R, Top + R);
  x := Round(cos(PI * (6 * s - 90) / 180) * (R - (R / 100 * 10)) + Left + R);
  y := Round(sin(PI * (6 * s - 90) / 180) * (R - (R / 100 * 10)) + Top + R);
  Canvas.Pen.Width := Options.SecondWidth;
  Canvas.Pen.Color := Options.SecondColor;
  Canvas.Pen.Style := Options.SecondStyle;
  Canvas.LineTo(x, y);
end;

procedure DrawClockShield(Canvas: TCanvas; R, Left, Top, Width: Integer; Color: TColor);
var
  x1, y1, x2, y2, i: Integer;
begin
  Canvas.Pen.Width := Width;
  Canvas.Pen.Color := Color;
  i := 0;
  while i < 360 do begin
    x1 := Round(cos(PI * i / 180) * R + Left + R);
    y1 := Round(sin(PI * i / 180) * R + Top + R);
    x2 := Round(cos(PI * i / 180) * (R - (R / 100 * 7)) + Left + R);
    y2 := Round(sin(PI * i / 180) * (R - (R / 100 * 7)) + Top + R);
    Canvas.MoveTo(x1, y1);
    Canvas.LineTo(x2, y2);
    i := i + 30;
  end;
end;

Omówie parametry

Procedura DrawAnalogClock:
T - wybrany przez nas czas (aktualny czas możemy pobrac poprzez funkcje Time)
Canvas - płótno na którym będziemy rysowac
R - promień naszego zegara
Left - pozycja x na canvasie
Top - pozycja y na canvasie
Options - Opcje naszego zegara (takie jak grubośc wskazówek, ich kolor, styl)

Procedura DrawClockShield:
Canvas - płótno na którym będziemy rysowac
R - promień naszego zegara
Left - pozycja x na canvasie
Top - pozycja y na canvasie
Width - grubośc kresek wyznaczających godziny
Color - kolor :]

Przykład użycia:

Na formę dajemy Timera i wpisujemy w zdarzeniu onTimer taki kod:

var
  Options: TClockOptions; // Deklarujemy zmienną z opcjami zegara
begin
  Repaint; // Czyścimy canvas formy
  Options.HourWidth := 2; 
  Options.HourColor := clBlack;
  Options.HourStyle := psSolid; 
  Options.SecondWidth := 1; 
  Options.SecondColor := clRed;
  Options.SecondStyle := psSolid; 
  Options.MinuteWidth := 2;
  Options.MinuteColor := clBlack;
  Options.MinuteStyle := psSolid;
  DrawClockShield(Canvas, 100, 20, 20, 4, clBlack);
  // Rysujemy tarczę zegarową na płótnie formy kolorem czarnym i grubości 4 pikseli. Promień 100 pikseli, pozycja x i y 20 pikseli.

  DrawAnalogClock(Time, Canvas, 100, 20, 20, Options); 
  // Rysujemy zegar z aktualnym czasem na płótnie formy. Promień 100 pikseli, pozycja x i y 20 pikseli.
  
  Caption := FormatDateTime('h:nn:ss', Time); // Wyświetlenie czasu w tytule formy
 
end;

7 komentarzy

a jak zrobić, żeby nam się to robiło na obiekcie Image , a nie na formie??

Mam pytanie czy posiada ktoś strukturę do programu ze stoperem analogowym?
Muszę zrobić na zaliczenie, a nie mogę dojść końca z tym programem :/
Jak zrobić żeby stoper zaczynał liczenie od zera, bo tego nie kojarzę

Sory, namieszałem :(

Powinno by na odwrot:

cos(alfa) = x/r
sin (alfa = y/r

i

t = r * sin (alfa)
x = r * cos (alfa)

Ad zuza86

W kodzie wykorzystany jest "aparat matematyczny" na poziomie szkoły średniej :)
Każdy punkt w układzie współrzęnych x-y możemy przedstawić za pomocą modułu (odległości od punktu 0,0) i kąta między:
odcinkiem łączącym dany punkt x,y z punktem 0,0
a
osią x

Narysuj sobie na kartce układ współrzędnych x-y i zaznacz jakiś punkt (najlepiej w 1szej ćwiartce układu). Połącz odcinkiem ten punkt z punktem 0,0.
zaznacz kąt alfa między osią x a odcinkiem.
teraz widać, że:

sin (alfa) = x/r
cos (alfa = y/r

r - długosc odcinka.

Zatem x = r * sin (alfa)
y = r * cos (alfa)

dla tego w równaniach na x i y pojawiają się sinus i cosinus :)

Po wprowadzeniu prostej modyfikacji w kodzie rysującym wskazówkę godzinową, tzn. linijki:

x := Round(cos(PI * (30 * h - 90) / 180) * (R - (R / 100 * 30)) + Left + R);
y := Round(sin(PI * (30 * h - 90) / 180) * (R - (R / 100 * 30)) + Top + R);

uzupełniamy do postaci:

x := Round(cos(PI * (30 * h + (m / 2) - 90) / 180) * (R - (R / 100 * 30)) + Left + R);
y := Round(sin(PI * (30 * h + (m / 2) - 90) / 180) * (R - (R / 100 * 30)) + Top + R);

otrzymujemy w efekcie prawidłowe wychylanie się wskazówki godzinowej, w zależności do tego, która "część" godziny jest aktualnie. W nowej wersji wskazówka godzinowa "spaceruje" pomiędzy poprzednią, a następną godziną. Zaś w oryginale - przeskakuje, gdy jest pełna godzina, na nową wartość (np. pomiędzy 9, a 10), gdzie pozostaje nieruchomo przez całą godzinę.

Dzięki tej drobnej zmianie zegar ów pracuje bardziej, jak realistyczny.

bardzo fajny program tylko trochę za trudny dla osób które poruszają się po delphi mniej swobodnie... i nie za pewnie...

Komentarze mile widziane