Serwis internetowy netmax.pl
Komputery    
Poprzedni artykuł Następny artykuł

Delphi, cz. 10

Adam Boduch

Cóż będziemy robić tym razem? Tym razem trochę języka Pascal i początki animacji. Co w przyszłości? W przyszłym rozdziale omówię odtwarzanie dźwięków, jeszcze bardziej skomplikowane animacje, następnie wykorzystanie rejestru Windows, plików INI i blibliotek DLL. Tak szybko się ode mnie nie uwolnicie :) Później jeszcze komunikaty Windows, kontrolki ActiveX oraz bardziej skompilowane rzeczy jak wykorzystanie informacji o pamięci, ilości klastrów na dysku i inne bajery :) Najnowsze części kursu oraz inne rzeczy związane z programowaniem możesz znaleźć na: www.programowanie.of.pl.

Co to właściwie jest ten uchwyt ( Handle )?

Zanim ruszymy dalej trzeba to omówić. Jest to bardzo ważne. Otóż każde okno, każdy komponent posiada swój uchwyt. Znając ten uchwyt można odwołać się do kontrolki czy okna. Zobaczysz, ze jest to bardzo częste przy programowaniu. Przykładowo: chciałbyś, aby któraś z kontrolek ( np. Edit ) stała się aktywna po naciśnięciu przycisku. Służy do tego funkcja "SetFocus", w której jako parametr należy podać uchwyt okna:

Windows.SetFocus(Edit1.Handle);

Tak jak powiedziałem: każde okno ma swój uchwyt.

Konwersja i rzutowanie

To było już omawiany w części 2, ale tym razem dokładniej. Najpierw konwersja. nieraz niektóre typy, które chcesz przypisać są różnych typów. Przykład: chcesz wyświetlić w okienko aktualną datę i czas:

var
  DateTime: TDateTime; // zmienna przechowuje datę i czas
begin
  DateTime := Now; // przypisanie do zmiennej aktualnego czasu
  ShowMessage(DateTime); // Blad: próba wyświetlenia zmiennej TDateTime

W tym przykładze pobierana jest aktualna data i czas. Następnie następuje próba wyświetlenia tej daty w okienku. Kompilator wskaże błąd. W funkcji "ShowMessage" musi się znaleźć zmienna typu "String", a zmienna "DateTime" jest typu "TDateTime". W takim przypadku z pomocą przychodzi konwersja:

ShowMessage(DateTimeToStr(DateTime));

Polecenie "DateTimeToStr" konwertuje datę i czas do postaci zmiennej "String". Istnieją jeszcze inne konwersje:

IntToStr - konwertuje zmienna "Integer" na "String"
StrToInt - "String" na "Integer"
StrPas - "PChar" na "String"
StrPCopy - "String" na "PChar"
TimeToStr - zmienna "Time" na "String"
DateToStr - zmienna "Date" na "String"
StrToDate - "String" na "Date"
StrToTime - "String" na "Time"
CurrToStr - "Currency" ( zmiennoprzecinkowy ) na "String"
StrToCurr - "String" na "Currency"
FloatToStr - zmiennoprzecinkowy na "String"

Rzutowanie to co innego. Jest to sposób na oszukanie kompilatora.

var
  B : Byte;
  C : Char;
begin
  B := 123; // jakas cyfra
  C := Char(B); // rzutowanie
  ShowMessage(C);

W tym przykładze następuje rzutowanie zmiennej "Byte" na "Char". Przecież zmienna Byte to zupełnie co innego niż Char! A jednak! Jezeli uruchomić program to zobaczysz na ekranie okienko z jakimś znakiem ( chyba: "{" ). Są to tzw. numery ASCII. Każdy znak ma swój numer i właśnie poprzez rzutowanie można ten numer poznać  ( ci którzy programowali w Turbo Pascalu pewnie wiedzą o co chodzi ). Ten sam efekt można osiągnąć stosując funkcję "Chr". Funkcja ta zwraca w postaci znaku numer - np:

var
  B : Byte;
  C : Char;
begin
  B := 123; // jakas cyfra
  C := Chr(B); // zamiana ( uzyskanie znaku poprzez zamiane cyfry )
  ShowMessage(C);

Istnieje też funkcja, która to odwraca - tzn., podajesz znak, a funkcja zamienia ten znak na numer ASCII tego znaku. Do tego służy funkcja "Ord":

var
  B : Byte;
  C : Char;
begin
  C := '{'; // znak
  B := Ord(C); // uzyskanie numeru znaku
  ShowMessage(IntToStr(B)); // wyswietlenie numeru


Wcześniej mówiłem o datach. Może dokończę ten temat. Podstawową funkcją jest "FormatDateTime". Zwraca ona datę w postaci tekstu ( np ): '2 luty 2001'. Oto przykład:

  ShowMessage(
  FormatDateTime('d mmmm yyy', Now));

Pierwszym parametrem tej funkcji jest ciąg znaków. Ciąg ten reprezentuje w jakim stylu mają być wyświetlana data. Np. mmmm oznacza pełny miesiąc: "luty". W ostatnim parametrze zamiast yyy można by było napisać: yy - spowodowałoby to wyświetlenie jedynie dwóch ostatnich cyfr daty ( 01 ). Jest tych parametrów wiele ( ich znaczenia szukaj w systemie pomocy Delphi pod hasłem "FormatDateTime" ).  Z datą wiąże się jeszcze jedna ciekawa funkcja podająca dzień tygodnia. Służy do tego funkcja "DayOfWeek". Ponieważ zwraca ona numer tygodnia musimy zadeklarować tablicę, która zawierała by opis dni:

const
  Dni : array[1..7] of String = // deklracja dni
  (('Niedziela'),
  ('Poniedziałek'),
  ('Wtorek'),
  ('Środa'),
  ('Czwartek'),
  ('Piątek'),
  ('Sobota'));
begin
  ShowMessage(Dni[ // wyświetl aktualny dzień
  DayOfWeek(Now)]);

Na samym początku deklaracja tablicy ( była o tym mowa w poprzednim rozdziale ). Nie wiedzieć czemu w Delphi dni liczone są od niedzieli ( tzn., że środa jest 4 dniem tygodnia ). Stąd taki kształt tablicy :-) No i w końcu następuje wyświetlenie aktualnego dnia tygodnia. Jeżeli coś jest niejasne, czegoś nie rozumiecie to czekam na listy. Czekam także na opinię dotyczące kursu ( prosty, sztywny, trudny, niedokładny ). Mój e-mail: boduch@poland.com

Proste animacje

Tematem tego pod rozdziału są proste animacje. Procedura, którą przedstawię ma za zadanie tekstu od lewej strony do prawej. Teskt będzie płynnie przesuwany, będzie także posiadał cień. W kolejnych rozdziałach stworzymy animację mająca  za zadanie ładować bitmapki i odtwarzać dźwięk ( wszystko w jednym pliku EXE ! ).
Najpierw prosty test. Wyświetli się statyczny tekst posiadający cień. Wklej ten tekst do jakiejś procedury i sprawdź efekt:

  Canvas.Font.Name := 'Courier New'; // czcionka
  Canvas.Font.Size := 20; // rozmiar czcionki
  Canvas.Font.Style := Font.Style + [fsBold]; // pogrubenie
  Canvas.Brush.Style := bsClear; // tlo czyste
  Canvas.Font.Color := clWhite; // kolor czcionki
  Canvas.TextOut(20, 20, 'WWW.PROGRAMOWANIE.OF.PL');
  Canvas.Brush.Style := bsClear; // tlo przezroczyste
  Canvas.Font.Color := clBlack; // kolor czcionki
  Canvas.TextOut(19, 19, 'WWW.PROGRAMOWANIE.OF.PL');

Nie przerażaj się - kod ten nie wygląda tak strasznie! :) Ładny efekt, prawda? Tekst jest rysowany dwa razy innym kolorem co daje efekt cienia. My swoją animację będziemy rysować w trochę inny sposób. Otóż poprzez bitmapę. Stworzona zostanie bitmapa, która będzie wyświetlana w innym miejscu dając efekt animacji. Zacznijmy! Stworzymy uniwersalną procedurę. Dodaj do sekcji "private" taki nagłówek:

  Done : Boolean; 
  procedure DrawAnim(Text: String; X, Y: Integer);

Pierwszym parametrem tej funkcji jest tekst, który ma być przesuwany, kolejne dwa to pozycja od której tekst ma być wyświetlony oraz jaka ma być pozycja Y tego tekstu. Tak więc jeżeli będziesz chciał wyświetlić taką animację będziesz pisał:

DrawAnim('Adam Boduch', 0, 200);

Ok, teraz uzupełnij procedurę w sekcji "Implementation" ( przypominam: najeżdżasz kursorem na nazwę procedury i wciskasz: Shift + Ctrl + C ). W każdym razie wpisz taki tekst w sekcji "Implementation":

procedure TForm1.DrawAnim(Text: String; X, Y: Integer);
var
  B : TBitmap; // Bitmapa
  i : Integer;
begin
  B := TBitmap.Create; // utwórz bitmape
  B.Height := 40;  // wysokosc bitmapy
  B.Width := Canvas.TextWidth(Text) * 10; // szerokosc bitmapy
  B.Canvas.Font.Name := 'Arial Black';  // czcionka
  B.Canvas.Font.Size := 14; // rozmiar czcionki
  B.Canvas.Brush.Color := clSilver; // kolor tla
// stworz obszar na ktorym bitmapa ma byc wyswietlana
  B.Canvas.FillRect(Rect(0, 0, Canvas.TextWidth(Text) * 10, 40));

  while not (Done) do // Jezeli Done = False...
  begin // zacznij odtwarzanie animacji
    for i:= X to Width  do  // zacznij od punktu X to konca formy
    begin
      Application.ProcessMessages; // daj odetchnac systemowi
      Sleep(1); // czekaj 1 milisekunde
      B.Canvas.Font.Color := clWhite; // kolor na bialy
      B.Canvas.TextOut(10, 10, Text); // wyswietlenie tekstu
      B.Canvas.Brush.Style := bsClear; // tlo na przezroczyste
      B.Canvas.Font.Color := clBlack; // kolor czcionki: czarny
      B.Canvas.TextOut(9, 9, Text); // wyswietl tekst jeszcze raz
      Canvas.Draw(I, Y, B); // wyswiet bitmape
      if (Done) then // Jezeli ktos przerwal animacje ( Done = True )...
        Break; // ... to przerwij odtwarzanie animacji
    end;
  end;
  B.Free; // zwolnji pamiec dla zmiennej
end;

Czy ten kod jest skomplikowany? Dużą jego część zajmują komentarze. Zmienna "Done", która stworzyłeś w sekcji "private" informuje o tym, czy zmienna jest uruchomiona, czy też nie. No, ale po kolei zacznijmy analizować tę procedurę. Na samym początku następuje Stworzenie bitmapy i przypisanie jej odpowiednich rozmiarów. Zauważyłeś polecenie "TextWidth" przy określaniu szerokości bitmapy? Funkcja ta podaje szerokość ( w pikselach ) tekstu wpisanego w nawiasie. Ponieważ jest to szerokość w pikselach mnożymy ją przez 10, aby zagwarantować, że cały tekst się zmieści. Istnieje taże funkcja "TextHeight", która podaje wysokość ( w pikselach ) tekstu. Następnie funkcja "FillRect", która określa region na którym będzie można rysować ( bitmape ). W funkcji tej musi się znaleźć parametr typu "TRect". Następnie pętla "While". Jeżeli zmienna "Done" nadal ma wartość "False" to znaczy, ze animacja jest uruchomiona - kontynuuj więc. Następnie pętla "For". Zaczyna się ona od zmiennej "X", czyli pozycji od której animacja ma się rozpoczynać. Tekst będzie "przelatywał" od tej właśnie pozycji do końca formy. Następna ważna funkcja to "Application.ProcessMessages". Jest ona bardzo ważna gdyż daje odetchnąć systemowi. Gdyby tej funkcji nie było to program nie dopuszczał by do siebie żadnych komend dopóki nie zakończy działania pętli. Nie można by było więc zakończyć działania programu ( tylko: Ctrl + Alt + Del ). Następnie pauza to 1 milisekunda ( możesz ją zmienić wedle własnych upodobań ). Późniejsze komendy zmieniają kolor na biały rysują tekst w punkcie 10, 10 [ punkty te teraz nie oznaczają położenia na formie, ale położenie w obszarze, który zadeklarowaliśmy z użyciem funkcji "FillRect" ]. Następnie zmiana koloru czcionki i narysowanie tekstu przesuniętego w lewo i dół. No i na końcu ta bitmapa jest rysowana na formie. To jeszcze nie koniec. Żeby zakończyć animację musisz gdzieś w procedurze ( najlepiej w "OnClose" ) napisać:

Done := True; // zakończ animację.

Dobra. Gotowe. Teraz napisz gdzieś w procedurze:

  DrawAnim('HTTP://WWW.PROGRAMOWANIE.OF.PL', 0, 20);

Zobacz jak działa!

W poprzedniej procedurze zastosowałem instrukcję "if", która sprawdza, czy zmienna "Done" równa jest True. Jeżeli chcemy sprawdzić, czy zmienna równa jest True to nie trzeba pisać:

if Done = True, ale wystarczy tak jak w poprzednim przykładzie. Jeżeli tak jest to następuje wyjście z pętli ( polecenie Break ). Istnieje jeszcze polecenie "Continue", które powoduje ominięcie następnej instrukcji i przejście do kolejnego bloku poza pętlą.

Jeszcze jeden ciekawy efekt z grafiką. Procedura, którą tutaj zaprezentuje ma za zadanie wyświetlać znak po znaku określony tekst. Najlepiej sam sprawdź -wklej ten tekst do procedury:

procedure TForm1.Button2Click(Sender: TObject);
var
  TextLength, I: Integer;
const  // tekst do wyswietlenia
  Text = 'http://www.programowanie.of.pl';
begin
  TextLength := StrLen(Text); // określ dlugosc tekstu ( w znakach )

  with Canvas do
  begin
    for I := 1 to TextLength do // od pierwszego znaku to ostatniego
    begin
      Application.ProcessMessages;
      Sleep(100); // pauza
      Brush.Style := bsClear; // styl przezroczysty
      Font.Name := 'Courier New'; // czcionka
      Font.Color := clWhite; // kolor czcionki
      Font.Size := 14; // rozmiar czcionki
      TextOut((20 + i * 12), 40, Text[i]); // wyswietl znak po znaku
      Brush.Style := bsClear;
      Font.Color :=  clBlack;  // zmien kolor czcionki
      TextOut((20 + i * 12) -2, 40 -2, Text[i]);
  end;
  end;
end;

Procedura jest prostsza i krótsza od poprzedniej. Na początku liczona jest długość tekstu ( w znakach ). Dokonuje tego procedura "StrLen". Następnie pętla - w odstępach 100 milisekundowych ustawia odpowiednie parametry czcionki. Później wyświetlenie tekstu. Ponieważ każdy znak ma być wyświetlony w pewnym odstępie od drugiego potrzebna była specyficzna konstrukcja procedury "TextOut". W pierwszym parametrze X podawana jest pozycja X, a następnie do tego dodawana jest zmienna "i", która później mnożona jest jeszcze przez cyfrę 12, aby zachować odpowiednie odstępy. Kolejna pozycja Y - bez komentarza :). No i wreszcie ostatni parametr, czyli tekst, który ma być wyświetlony. Konstrukcja:
Text[i]  gwarantuje, ze wyświetlany będzie znak po znaku. Bo jeżeli piszesz w nawiasie klamrowym cyfrę to wyświetlony zostaje znak podany w tym nawiasie. Przykład:

const
  Tx = 'Adam';
begin
  ShowMessage(Tx[2]); // wyświetlona zostanie litera 'd'

I tak w poprzedniej procedurze rysowany jest jeszcze raz tekst tyle, że jest on przesunięty o dwa znaki.
I tak dotarłem do końca rozdziału. Najważniejsze jest w programowaniu rozumienie tego, co się robi. Dlatego też piszcie do mnie ( boduch@poland.com ) i odwiedźcie stronę www.programowanie.of.pl - tam znajdziesz odpowiedzi na większość Twoich pytań.


Adam Boduch
e-mail: boduch@poland.com
http://www.programowanie.of.pl

Poprzedni artykuł Następny artykuł


Stare Gry - chcesz pograc w gry sprzed lat?


Copyright 1999-2001 Magazyn internetowy NoName
Wszelkie prawa zastrzeżone