Wyższa Szkoła Morska
w Gdyni
KATEDRA PODSTAW INFORMATYKI I SIECI KOMPUTEROWYCH
Środowisko programowania DELPHI
Przykłady i ćwiczenia
(fragment obszernego opracowania)
Włodzimierz Filipowicz
Środowisko Delphi
Włodzimierz Filipowicz
strona 2
Wprowadzenie
Współczesne aplikacje tworzone są w środowiskach zorientowanych obiektowo i z
użyciem obiektów. Częstokroć korzystają z komunikacji sieciowej. Ich realizacja
ma miejsce w warunkach systemów operacyjnych sterowanych zdarzeniami. Przy-
gotowanie takiej aplikacji możliwe jest w środowisku dostarczającym odpowiednich
mechanizmów i interfejsów. Jednym z wielu dostępnych dla użytkowników popu-
larnych komputerów jest system określanym nazwą Delphi. Środowisko to już w
pierwszej wersji zyskało sobie uznanie wśród twórców oprogramowania. Obecnie
dostępna jest już kolejna, bardziej udoskonalona wersja tego środowiska.
Dzięki wykorzystaniu składni języka Pascal użytkownik nie musiał opanować do-
datkowej umiejętności posługiwania się jeszcze innym językiem. Składnia Pascala
jest na tyle elastyczną, że spełnia wymagania sporej grupy twórców oprogramowa-
nia użytkowego. Wyraźny podział tworzenia aplikacji na dwie fazy projektowania i
realizacji okazał się praktycznym. Już w fazie wstępnej można zaobserwować za-
chowanie się niektórych obiektów. Tworząc aplikację programista posługuje się
zbiorem gotowych elementów zdefiniowanych przez twórców środowiska. Ele-
menty, które posiadają własny identyfikujący je piktogram nazywa się komponen-
tami. Zbiór wszystkich komponentów podzielono na kategorie i umieszczono pod
odpowiednimi zakładkami na głównym panelu. Programista może tworzyć aplikacje
umieszczając wybrane komponenty na obiekcie bazowym, którym jest forma. For-
ma to komponent o zasadniczym znaczeniu dla wizualnej strony aplikacji. To wła-
śnie forma pojawia się na ekranie w momencie uruchomienia jakiegokolwiek pro-
gramu w środowisku Windows. Można powiedzieć, że jest ona pojemnikiem dla
innych komponentów. Programista umieszcza potrzebne mu elementy na formie i
oprogramowuje - definiuje procedury reakcji na odpowiednie zdarzenia. Każdej
formie towarzyszy kod formalnie ją opisujący, zawiera on stosowne deklaracje i
definicje. Kod taki zawarty jest w odpowiadającym formie module. Forma bez mo-
dułu nie może istnieć, sytuacja odwrotna jest natomiast możliwa. Moduł bez formy
pojawia się często wtedy kiedy programista chce w jednym miejscu zgromadzić
potrzebne mu deklaracje czy definicje potrzebnych procedur. Aplikacja może zawie-
rać wiele modułów i wiele form, jedna z nich jest główną to znaczy taką, która po-
jawia się jako pierwsza. Tworzący jedną całość zespół modułów i form nazywa się
projektem.
Materiał przedstawiony w opracowaniu ma za zadanie pomóc w zapoznaniu się
ze środowiskiem i sposobem tworzenia nowoczesnych aplikacji wykorzystujących
programowanie obiektowe. Mniej uwagi poświęcono kreowaniu obiektów w kon-
tekście potrzeb rozwiązywanych problemów. Przejście od programowania z uży-
ciem obiektów do programowania obiektowego jest obecnie swego rodzaju wyzwa-
niem dla twórców oprogramowania użytkowego. Autor ma też nadzieję, że przesta-
Środowisko Delphi
Włodzimierz Filipowicz
strona 3
wiony materiał da studiującemu niezbędne podstawy do samodzielnego projektowa-
nia i budowy aplikacji. Zakres prezentowanego materiału dotyczącego składni języ-
ka Pascal ograniczono do niezbędnego minimum, zakładając, że wiele elementów
student poznał wcześniej podczas realizacji programu szkoły średniej. Sporo uwagi
poświęcono organizacji środowiska jak również sprawom koncepcji przetwarzania
we współczesnych komputerach. Czytelnik będzie miał okazję zapoznać się z reali-
zacją wielowątkową procesów, dynamiczną wymianą danych, strukturami warian-
towymi czy też pokrótce z komunikacją w systemach rozproszonych.
Interfejs użytkownika
Po uruchomieniu oprogramowania i przystąpieniu do tworzenia nowej aplikacji
użytkownik ma do dyspozycji cztery zasadnicze elementy:
•
panel główny środowiska
•
okno inspektora obiektów
•
okno kodu programu
•
formę (główną) nowotworzonej aplikacji
Na panelu głównym znajduje się rząd zakładek udostępniających poszczególne ka-
tegorie gotowych komponentów. Kategorie takie jak: standard, additional czy
win32 będą prawdopodobnie używane najczęściej przez początkujących użytkowni-
ków. Pozostałe dostarczają elementy wizualne bardziej zaawansowanym programi-
stom. Ci ostatni mają potencjalne możliwości rozbudowy galerii komponentów.
Zresztą zawodowi twórcy oprogramowania udostępniają własne produkty i wystar-
czy postarać się o kod źródłowy, a następnie przeprowadzić procedurę instalacji,
aby wzbogacić posiadaną galerię dostępnych komponentów.
Rysunek 1 Panel główny środowiska Delphi – wersja 3.0
zakładki udostępniające
grupy komponentów
Środowisko Delphi
Włodzimierz Filipowicz
strona 4
W celu umieszczenia komponentu na formie należy
uczynić ją aktywną (kliknąć lewym przyciskiem myszki
lub wybrać z listy w inspektorze obiektów), na panelu
głównym środowiska wybrać zakładkę zawierającą wła-
ściwy komponent i nacisnąć piktogram potrzebnego ele-
mentu. Kolejne naciśnięcie lewego przycisku myszki nad
obrazem formy spowoduje automatyczne umieszczenie
właściwego komponentu we wskazanym miejscu. Moż-
liwe jest też umieszczenie komponentu w komponencie.
Ciekawym, praktycznym przykładem niech będzie utwo-
rzenie klastra paneli. Taka rodzina elementów stanowi
całość, można je przemieszczać czy likwidować operując
na komponencie „rodzicu”. Aby umieścić komponent na
innym należy uczynić go aktywnym, na panelu głównym
środowiska wybrać zakładkę zawierającą właściwy kom-
ponent i nacisnąć piktogram potrzebnego elementu. Ko-
lejne naciśnięcie lewego przycisku myszki nad obrazem
wyselekcjonowanego obiektu spowoduje automatyczne
umieszczenie nowego we wskazanym miejscu istniejące-
go komponentu.
Niestety nie zawsze wskazanie komponentu rodzica po-
woduje umieszczenie na nim nowego obiektu. Pomimo
wykonania wszystkich czynności nowy element może
pozostać związany z formą. Istnieje grupa komponentów
„pojemników” dla innych. Są też komponenty dla któ-
rych obiektem rodzicielskim musi być forma. Często
wykorzystywanym przykładem tych pierwszych może
być panel. Do drugich zaliczyć trzeba komponenty
umożliwiające wykorzystanie tak zwanych standardo-
wych okien dialogowych, na przykład wyboru plików.
Umieszczenie komponentu na formie wymaga:
a) uaktywnienia formy (naciśnięcie lewego klawisza
myszki nad obrazem formy lub wybór z listy w oknie
inspektora)
b) zaznaczenia komponentu na panelu głównym środo-
wiska (wybranie odpowiedniej zakładki i naciśnięcie
lewego klawisza myszki nad piktogramem kompo-
nentu),
c) wskazania miejsca położenia komponentu na formie
(naciśnięcie lewego klawisza myszki nad obrazem
formy).
Rysunek 2 Okno
inspektora obiektów,
zakładka właściwości dla
kratki (Stringgridu)
Środowisko Delphi
Włodzimierz Filipowicz
strona 5
Umieszczenie komponentu na innym komponencie wy-
maga:
a) upewnienia się, że komponent rodzic jest widoczny
przynajmniej w małym fragmencie
b) zaznaczenia komponentu na panelu głównym środo-
wiska (wybranie odpowiedniej zakładki i naciśnięcie
lewego klawisza myszki nad piktogramem kompo-
nentu),
c) wskazania miejsca położenia komponentu na wybra-
nym elemencie (naciśnięcie lewego klawisza myszki
nad obrazem komponentu rodzica).
Komponenty, których obraz prezentowany jest na formie podczas realizacji aplikacji
powinny być właściwie rozmieszczone i opisane gdyż stanowi to o przejrzystości
interfejsu. Chaotycznie rozlokowane aktywne elementy zmuszą użytkownika do
odgadywania intencji programisty. Warto nadmienić, że istnieją obiekty niewidocz-
ne podczas realizacji. Precyzyjnie rzecz ujmując każdy komponent można uczynić
ukrytym, ale tylko niektóre mają piktogram możliwy do prezentowania podczas
wykonywania programu.
Po wprowadzeniu na formę wymaganej liczby komponentów można definiować ich
właściwości i określać procedury reakcji na zdarzenia. W tym celu wykorzystuje się
okno inspektora obiektów. W oknie inspektora obiektów, którego przykład pokaza-
no na Rysunek 2, prezentowane są właściwości i zdarzenia wyselekcjonowanego
komponentu. Wybór dokonywany jest za pomocą myszki, naciśnięcie nad właści-
wym piktogramem powoduje jego wyróżnienie czarną obwódką z ośmioma charak-
terystycznymi małymi prostokątami. Nazwa i typ wyróżnionego komponentu poja-
wiają się w nagłówku okna inspektora. Odpowiednie okno skojarzone jest z listą
rozwijaną (widoczną na rysunku) pozwalająca na selekcję komponentów poprzez
zaznaczenie odpowiedniej pozycji na liście. Różne komponenty mają odmienne
zestawy właściwości, określanie właściwości za pomocą inspektora obiektów w
momencie projektowania aplikacji jest jednym ze sposobów ich definiowania. Moż-
liwe jest też użycie operacji podstawienia i zmiany wartości większości właściwości
w czasie realizacji programu (ang. runtime). Należy pamiętać, że zbiór właściwości
czasu wykonywania jest najczęściej różny od dostępnego za pomocą inspektora
obiektów. Nazwy niektórych właściwości w oknie inspektora zaczynają się znakiem
+, oznacza to zagnieżdżenie. Podwójne kliknięcie nazwy takiej właściwości rozwi-
nie listę dostępnych pozycji, a znak + zostanie zamieniony na -. Kolejne podwójne
kliknięcie nazwy właściwości przywróci stan pierwotny.
Chociaż użytkownik może zmienić dowolną właściwość używanego obiektu to na-
leży pamiętać o pewnych konsekwencjach takiego postępowania. Dotyczy to na
przykład zmiany nazwy (ang. name). Ponieważ nazwy metod definiowanych dla
Środowisko Delphi
Włodzimierz Filipowicz
strona 6
każdego obiektu tworzy się automatycznie z użyciem identyfikatora komponentu to
zmiana tego ostatniego prowadzić może do nieporozumień. Jest to szczególnie
uciążliwe kiedy decydujemy się na modyfikacje po opracowaniu chociażby części
kodu programu. Należy starać się określić ostateczne nazwy używanych obiektów
zaraz po ich przeniesieniu na formę.
Właściwości obiektów można zmieniać zarówno podczas projektowania
jak i dynamicznie w czasie realizacji aplikacji. Zbiór właściwości czasu
realizacji może być różny od dostępnego podczas projektowania. Nie-
które właściwości przeznaczone są tylko do odczytu.
Podstawowym obiektem w Delphi jest forma. Jedna z nich tworzona jest automa-
tycznie w momencie rozpoczęcia budowy nowego projektu. Posiada ona status
głównej czego konsekwencją jest jej wyświetlenie w momencie uruchomienia apli-
kacji. Taka forma, kopia obiektu zdefiniowanego przez twórców systemu, jest pusta.
Nie posiada żadnych danych, zespół właściwości jest określony domyślnie, a zbiór
metod obejmuje wszystkie dziedziczone, dostępne z pierwowzoru. Niemniej auto-
matycznie wygenerowany program wykorzystujący pustą formę jest poprawnym ze
składniowego punktu widzenia i może być zrealizowany. Wynikiem jego wykonania
jest pojawienie się obrazu formy opatrzonej domyślną ikoną, zestawem standardo-
wych przycisków na belce tytułowej i pozbawionego jakiegokolwiek elementu pola
formy. Zamknięcie pokazywanej formy kończy realizację „programu”.
Środowisko Delphi
Włodzimierz Filipowicz
strona 7
Automatycznie ge-
nerowany kod, do-
stępny w oknie edy-
cyjnym w momencie
tworzenia nowej
aplikacji pokazano
na wydruku -
Rysunek 3. Zwraca
uwagę nieco inny
schemat od obowią-
zującego na przy-
kład w Turbo Pas-
calu. Występował
tam wyraźny podział
na dwa zasadnicze
bloki, z których
jeden zawierał de-
klaracje drugi zaś
instrukcje programu
głównego. Przypo-
mnijmy, że po uru-
chomieniu komputer
zawsze rozpoczynał
sekwencyjną reali-
zację instrukcji drugiego bloku. Aplikacje tworzone dla środowiska Windows skła-
dają się z elementów, które nie posiadają programu głównego (patrz rysunek 3). Czy
całkowicie zrezygnowano z dotychczas obowiązującego schematu?. Aby się prze-
konać, że tak nie jest należy wybrać opcję:
View|Project Source
Pokazane zostanie wtedy okno edycyjne z kodem aplikacji. Przedstawiona na ry-
sunku 4 zawartość takiego okna wskazuje na zachowanie poprzednich form.
Wszystko to dzieje się na poziomie aplikacji jako całości i początkujący programista
nie powinien dokonywać tam żadnych zmian. Twórcy środowiska przewidywali
konsekwencje ewentualnych ingerencji w kod aplikacji niezbyt doświadczonych
użytkowników i uczynili go „trudno” dostępnym. Podstawowa postać kodu aplikacji
jest generowana w sposób automatyczny i obejmuje zestaw działań na niewidocz-
nym obiekcie aplikacji. Występują tam kolejno: inicjowanie, tworzenie wszystkich
stron oraz uruchomienie aplikacji.
Po rozpoczęciu realizacji aplikacja musi reagować na zachodzące przynajmniej nie-
które zdarzenia. „Martwa” aplikacja jest źle zaprojektowaną. Każde oprogramowane
zdarzenie, na przykład kliknięcie piktogramu komponentu, powoduje wywołanie
związanej z nim procedury. Sekwencja zdarzeń steruje przebiegiem realizacji apli-
kacji.
Rysunek 3 Okno edycji kodu programu – wygląd w momencie
rozpoczęcia tworzenia nowej aplikacji
Środowisko Delphi
Włodzimierz Filipowicz
strona 8
Aplikacje tworzone w omawianym środowisku składają się jednostek zwanych mo-
dułami. Moduł składa się z kilku części wśród, których występuje tak zwana część
widoczna albo interfejs oraz implementacyjna. Część widoczna rozpoczynająca się
od słowa interface zawiera deklaracje zmiennych, stałych, typów, procedur oraz
funkcji dostępnych również dla innych jednostek. Przez pryzmat zbioru deklaracji
umieszczonych w sekcji interfejsu dany moduł jest „widziany” przez inne. Część
implementacyjna rozpoczynająca się po słowie implementation wprowadza nie-
zbędne do funkcjonowania modułu szczegóły. Może ona zawierać w swoim składzie
dodatkowe deklaracje ale przede wszystkim muszą tu wystąpić faktyczne algorytmy
procedur i funkcji zadeklarowanych w części interfejsu. Wszystkie elementy zade-
klarowane w tej części są lokalne dla modułu.
Moduł może również posiadać sekcję określania wartości początkowych opatrzoną
nagłówkiem initialization.
Elementy składowe aplikacji tworzonych dla środowiska Windows nie
posiadają programu głównego. Po rozpoczęciu realizacji aplikacja za-
czyna reagować na zachodzące zdarzenia. Sekwencja zdarzeń steruje
przebiegiem realizacji aplikacji.
Po przystąpieniu do tworzenia nowej aplikacji środowisko Delphi au-
tomatycznie generuje kod aplikacji jak również fragment modułu za-
wierającego kompletną deklarację pustej formy.
Kod aplikacji nie powinien być zmieniany przez początkujących pro-
gramistów.
Środowisko Delphi
Włodzimierz Filipowicz
strona 9
Zwróćmy uwagę na tekst występujący na pokazanym wydruku, fragmenty zawarte
w nawiasach klamrowych są komentarzami. Wyjątkiem od tej reguły są dyrektywy
dla kompilatora na przykład
{$R *.DFM}
. Łatwo odróżnić dyrektywę od notatek
programisty. Charakterystycznym elementem jest symbol waluty. Komentarze ma-
jące na celu popra-
wienie czytelności
intencji autora i ogra-
niczone do pojedyn-
czych linii tekstu mo-
gą rozpoczynać się
podwójnym ukośni-
kiem (//). W przykła-
dach pojawiających
się w tekście opraco-
wania komentarze
dodatkowo wyróżnio-
no kursywą.
Po zakończeniu pracy
tworzony projekt na-
leży zachować na
dysku. W tym celu
można wykorzystać
okna dialogowe gene-
rowane przez system
przy jego zamykaniu
albo też wcześniej
wybrać opcję
Fi-
le|Save all
. Użytkownik proszony będzie o podanie nazw pliku, w którym
przechowywane będą dane o projekcie oraz pliku z kodem źródłowym aplikacji.
Plik projektu posiada rozszerzenie nazwy .dpr a sugerowana, domyślna nazwa to
Project1.dpr. Takie określenie prowadzi najczęściej do nieporozumień i nie sugeruje
przeznaczenia aplikacji, dlatego dobrą praktyką jest zmiana sugerowanej nazwy na
taką, która uwolni programistę od tego typu kłopotów. Podobnie przedstawia się
sprawa nazwy pliku z kodem źródłowym, tu obowiązuje rozszerzenie .pas a stan-
dardowo sugerowana nazwa to unit(n).pas. Przy czym (n) odpowiada kolejnej licz-
bie naturalnej, przy projekcie jednomodułowym domyślna nazwa plik zawierającego
kod modułu to unit1.pas.
Rysunek 4 Okno kodu aplikacji – wygląd w postaci
generowanej przez środowisko
Środowisko Delphi
Włodzimierz Filipowicz
strona 10
Środowisko tworzy jeszcze kilka plików (patrz rysunek 5), z których najważniejszy
to ten którego nazwa główna pokrywa się z nazwą pliku zawierającego kod źródło-
wy a rozszerzeniem jest .dfm. Zawiera on dane dotyczące definicji formy. Część
główna nazwy tego pliku jest kopią nazwy pliku modułu tworzoną przez system, a
użytkownik pod
groźbą niesprawności
aplikacji nie może jej
zmieniać. Dla poka-
zanego na rysunku 5
przypadku na kom-
pletny projekt skła-
dają się pliki: Pno-
wy.dpr, nowy.pas
oraz nowy.dfm. Dla
potrzeb odtworzenia
projektu w dowol-
nych warunkach
wystarczy skopiować
właśnie wymieniony
zestaw plików. Pod-
czas wznowienia
pracy nad projektem
należy zawsze wy-
brać plik z rozsze-
rzeniem przypisanym
projektowi to znaczy .dpr. W takich warunkach system automatycznie udostępni
wszystkie moduły bez potrzeby ich oddzielnego otwierania. W przypadku „dziwne-
go” zachowania się środowiska należy zajrzeć do menedżera projektów, opcja
view|Project Manager
.
Widok okna za pomocą którego
można zarządzać tworzeniem aplika-
cji pokazano na Rysunek 6. W części
tego okna poświęconej modułom
(unitom) musi pojawić się nazwa
przynajmniej jednego modułu. Pro-
jekt bez jakiegokolwiek modułu jest
pusty i nie daje się uruchomić. Nale-
ży pamiętać o tym, że środowisko
umożliwia otwarcie i edycję wielu
programów. Otwarcie pliku(ów) i
obecność kodu źródłowego w oknie
edycji programu nie jest równo-
znaczne z faktem, że jest on ele-
mentem składowym projektu. Początkujący programiści czasami uruchamiają apli-
kację i ze zdziwieniem obserwują, że forma, która się im ukazuje nie jest formą
Rysunek 5 Kompletna lista plików projektu Pnowy złożonego
z pojedynczego modułu nowy
Rysunek 6 Okno menedżera projektu
plik Pnowy.dpr
zawiera definicję
projektu
plik nowy.dfm zawie-
ra definicję formy
unitu (modułu)
nowy.pas zawiera kod
unitu (modułu)
Środowisko Delphi
Włodzimierz Filipowicz
strona 11
zaprojektowaną dodatkowo zaś dokonywane zmiany nie wpływają na zachowanie
aplikacji. Wyjaśnienie tej kwestii jest takie, że projekt zawiera inny moduł główny
niż ten, na którym dokonywane są modyfikacje. Aby przekonać się czy aktualnie
edytowany kod programu należy do projektu powinniśmy:
√
wybrać opcję
view|Project Manager,
√
na liście modułów projektu wybrać odpowiednią pozycję (na początku powinna
być tam wyszczególniona tylko jedna nazwa) i podwójnie ją kliknąć,
√
powrót do okna edytowanym kodem oznacza, że to nad czym pracujemy należy
do projektu. Pojawienie się nowej zakładki w oknie edycyjnym z nową zawarto-
ścią to znak, że przedmiot pracy jest niewłaściwy.
Zarządca projektu umożliwia, między innymi, usuwanie modułów wchodzących w
skład projektu (przycisk remove), odpowiednie pliki mogą być dodane za pomocą
przycisku add.
Na projekt aplikacji składa się szereg plików, najważniejsze to te, któ-
rych rozszerzeniami są: .dpr, .pas, .dfm. W momencie wznowienia pra-
cy nad projektem należy przywołać plik z rozszerzeniem .dpr. Wtedy
wszystkie pozostałe pliki projektu będą udostępniane automatycznie.
Jeżeli uruchomiana aplikacja zachowuje się dziwnie, czy też dokonane
zmiany kodu nie wpływają na zachowanie programu to przyczyna może
tkwić strukturze projektu. Edytowany program prawdopodobnie do
niego nie należy. Celem sprawdzenia należy skorzystać z menedżera
projektu.
Środowisko Delphi
Włodzimierz Filipowicz
strona 12
Stan w jakim znajduje się środowisko i uru-
chomiona aplikacja sygnalizowany jest przez
odpowiednie komunikaty na belce panelu
głównego. Podczas tworzenia aplikacji w
stanie określonym jako roboczy (edycji pro-
gramów) na belce formy środowiska Delphi
występuje, oprócz napisu określającego wer-
sję systemu, tylko nazwa projektu. Po uru-
chomieniu aplikacji pojawia się dodatkowo
słowo [Running]. Chociaż w takim stanie
użytkownik może dokonywać zmian w kodzie
źródłowym to nie jest to wskazane z wielu
powodów. Zasadniczym jest fakt, że jakie-
kolwiek zmiany aby były uwzględnione wy-
magają ponownej kompilacji, która jest moż-
liwa tylko w stanie edycji. Nie dostępny jest
także inspektor obiektów. Z tych powodów
należy zawsze zakończyć aplikację przed
dokonywaniem jakichkolwiek zmian. Zakoń-
czenie realizacji aplikacji jest równoznaczne z zamknięciem jej formy głównej. Inne
próby zakończenia, na przykład za pomocą przycisku opatrzonego ikoną w postaci
dwóch pionowych belek, mogą doprowadzić do sytuacji pokazanej na rysunku 7 w
części dolnej. Środowisko jest wtedy w stanie zatrzymania sygnalizowanym dodat-
kowym napisem [Stopped]. Nie jest to jednak normalny stan roboczy, do którego
przejście możliwe jest po tak zwanym skasowaniu polegającym na użyciu klawiszy
Ctrl F2 lub opcji
Run|Program Reset
.
I.1 Umieszczanie komponentów na formie – co i gdzie jest
zapisane?
Dla początkującego użytkownika środowiska Delphi pewne rzeczy realizowane są w
trochę tajemniczy sposób. Dotyczy to
głównie czynności związanych z
umieszczaniem komponentów na
formie podczas projektowania apli-
kacji. W niniejszym punkcie posta-
ramy się naświetlić pewne aspekty
tak aby komunikaty typu: field xxxxx
does not have a corresponding com-
ponent. Remove the declaration? nie
rodziły zakłopotania.
Aby to zrobić zacznijmy od tego, że
komplet informacji na temat każdego
modułu zawarty jest w dwóch pli-
Rysunek 7 Belka panelu głównego
środowiska (wersja 3.0) w
przypadku realizacji aplikacji (na
górze) i wymuszonego zatrzymania
(na dole)
Rysunek 8 Na pustą formę naniesiono panel, na
którym dodatkowo umieszczono inny
przycisk do zatrzymania realizacji
przycisk do uruchomienia aplikacji
Środowisko Delphi
Włodzimierz Filipowicz
strona 13
kach. Jeden posiada rozszerzenie .pas drugi zaś .dfm. Nazwa główna w obydwu
przypadkach jest taka sama i odpowiada nazwie modułu (unitu). Z modułem o na-
zwie Test związane będą dwa pliki Test.pas i Test.dfm. Pierwszy z plików zawiera
tekst (kod programu) i można go obejrzeć przy użyciu każdego dostępnego edytora,
w tym oczywiście tego dostępnego w środowisku. Drugi plik zaś zawiera dane o
elementach umieszczonych na formie, w postaci tekstowej pojawi się po wybraniu
menu podręcznego formy (prawy przycisk myszki wciśnięty nad piktogramem for-
my) i wybraniu opcji
View as text
. Wtedy to zamiast formy pojawi się okno
edycyjne zawierające zestaw podstawowych danych o elementach formy. Powrót do
oryginalnego widoku odbywa się również poprzez menu podręczne ale okna pre-
zentującego komponenty. Tym razem należy wybrać opcję
View as form.
W przypadku umieszczenia nowego komponentu na formie realizowane są nastę-
pujące czynności. W pliku zawierającym kod w sekcji definicji klasy formy doda-
wane jest pole o nazwie mnemonicznej generowanej automatycznie. Unikalna na-
zwa składa się z identyfikatora, rodzajowego określenia komponentu i pierwszej nie
wykorzystywanej, dla danej klasy, liczby (cyfry), na przykład panel1, stringgrid3
itp. Oprócz nazwy pola, w sekcji definicji środowisko umieszcza nazwę klasy (Tpa-
nel, Tstringgrid itp.). Kompletny wiersz, który pojawi się po umieszczeniu na for-
mie nowego panelu, w części definicji klasy formy to na przykład:
Panel2 : TPanel;
Po zmianach dokonanych w pliku zawierającym kod dokonuje się zapisu do pliku z
komponentami (.dfm). Przykładowy zapis dla nowego panelu może wyglądać tak jak
na wydruku poniżej. Podobnie jak w pliku z kodem wpisuje się tu automatycznie
utworzoną nazwę, identyfikator klasy oraz dodatkowo zestaw podstawowych, do-
myślnych właściwości.
object Panel2: TPanel
Left = 280
Top = 1
Width = 80
Height = 39
Align = alRight
Caption = 'Panel2'
TabOrder = 0
end
Poniżej pokazano fragment kodu po umieszczeniu na formie dwóch paneli w sposób
pokazany na Rysunek 8. Wszystkie zmiany rejestrowane są automatycznie i odby-
wają się poza kontrolą programisty.
type
TForma = class(TForm)
Panel1 : TPanel;
Panel2 : TPanel;
private
{ Private declarations }
W pliku zawierającym dane o kom-
ponentach formy występują: nazwa i
identyfikator klasy każdego elementu
oraz podstawowy zestaw właściwości
Środowisko Delphi
Włodzimierz Filipowicz
strona 14
public
{ Public declarations }
end;
Zawartość pliku opisującego formę jest taki jak poniżej. Oprócz danych liczbowych,
pośrednio zawarto tu informacje o wzajemnych powiązaniach komponentów (dane
dla panel2 umieszczone w ramach definicji obiektu panel1). Zwróćmy uwagę na
fakt, że niektóre elementy na przykład nazwy komponentów czy ich klasy pojawiają
się w obydwu plikach i muszą być identyczne.
object Forma: TForma
Left = 244
Top = 195
Width = 369
Height = 164
Caption = 'Forma'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = []
Position = poScreenCenter
OnCreate = FormCreate
PixelsPerInch = 120
TextHeight = 16
object Panel1: TPanel
Left = 0
Top = 91
Width = 361
Height = 41
Align = alBottom
Caption = 'Panel1'
TabOrder = 0
object Panel2: TPanel
Left = 280
Top = 1
Width = 80
Height = 39
Align = alRight
Caption = 'Panel2'
TabOrder = 0
end
end
end
Komponent nałożony
na inny w pliku .dfm
zapisany jest jako jego
element składowy
Środowisko Delphi
Włodzimierz Filipowicz
strona 15
Chociaż zawartość obydwu plików może być dowolnie zmieniana, to można to robić
pamiętając o konieczności zachowania wymaganej zgodności nazw i typów. Docie-
kliwych zachęcam do zmiany, na przykład nazwy panelu w pliku .dfm. Wtedy przy
próbie uruchomienia aplikacji, zawierającej odwołanie do odpowiedniego elementu
zobaczymy cytowany już komunikat:
field xxxxx does not have a corresponding component. Remove the declaration?
informujący że pole definicji klasy formy nie posiada odpowiednika wśród kompo-
nentów. Po czym ma miejsce sugestia usunięcia pola. Jeżeli użytkownik zatwierdzi
propozycję to odpowiednia deklaracja zostanie skasowana. W każdym tego typu
przypadku, pomijając zamierzone i świadome ingerencje programisty, podobny
komunikat świadczy o niespójności danych i najprawdopodobniej doprowadzi do
niemożliwości poprawnego uruchomienia aplikacji.
Początkującym gorąco zaleca się polegać na możliwościach jakie daje inspektor
obiektów. Dzięki niemu wszelkie dokonywane zmiany, w szczególności dotyczące
nazw obiektów, będą rejestrowane wszędzie tam gdzie potrzeba i stosownie do
obowiązujących reguł. Uwolni to również użytkownika od obowiązku pamiętania co
i gdzie zostało zapisane. Powtórzyć należy też wcześniejszą uwagę o potrzebie
ustalenia nazw używanych elementów na etapie ich tworzenia. W każdym przypad-
ku kiedy modyfikacje będą dokonywane po opracowaniu nawet niewielkiego frag-
mentu kodu programu istnieje ryzyko powstania niespójności.
Komplet informacji na temat tworzonego modułu zawarty jest w dwóch
plikach. Jeden posiada rozszerzenie .pas drugi zaś .dfm. Nazwa główna
w obydwu przypadkach jest taka sama i odpowiada nazwie modułu
(unitu). Zawartości obydwu plików może być dowolnie zmieniana jed-
nak można to robić pamiętając o obowiązku zachowania zgodności nie-
których elementów. Korzystanie z inspektora obiektów uwalnia nas od
konieczności pamiętania tego typu szczegółów.
Zdarzenia
Zdarzenia (ang. events)
Zdarzenia łączą wystąpienie pewnego zjawiska w systemie z kodem programu.
Zjawiska takie mogą wynikać z określonej akcji użytkownika komputera, na przy-
kład kliknięcie myszką
widocznego na ekranie
obiektu. Mogą one być też
powiązane z realizowa-
nym procesem, przykła-
dem niech będzie inicja-
tywa nawiązania połącze-
nia przez zdalnego partne-
ra. Program (procedura)
reagujący w określony
przez programistę sposób
na wybrane zjawiska na-
zywa się procedurą reakcji
na zdarzenie (ang. event
handler). W powszechnym
rozumieniu zdarzeniem
określa się zarówno zjawi-
sko jak i procedurę reakcji.
Bardziej precyzyjnie, zda-
rzenia, w rozumieniu śro-
dowiska programowania,
powinny być kojarzone ze
wskaźnikami (adresami)
do procedur, które wywo-
łują. Dla użytkownika
komponentów zdarzenie
kojarzy się z nazwą zjawi-
ska, takiego jak na przykład kliknięcie przycisku myszki OnClick (nazwa składa się
z przedrostka On i właściwego określenia zdarzenia Click) i z przyporządkowaniem
odpowiedniej metody reakcji na to zjawisko.
OnClick
Akcja
użytkownika
(kliknięcie,
naciśnięcie
klawisza
klawiatury);
bądź
wydarzenie
związane z
realizacją
OnKeyPress
OnDblClick
OnKeyDown
OnError
Rysunek 9 Akcje i zdarzenia
Zdarzenia
Włodzimierz Filipowicz
strona 17
Skojarzenie zjawiska (akcji) z procedurą może wystąpić na etapie projektowania
aplikacji. Wykorzystuje się do tego celu zakładkę Events inspektora obiektów, po-
kazuje ona zdarzenia, na które może reagować wybrany obiekt. Na Rysunek 10 po-
kazano listę zdarzeń dla obiektu formy. Wy-
stępująca z prawej strony nazwa oznacza po-
wiązanie zdarzenia z procedurą (reakcją). Wa-
runkiem możliwości takiego przypisania jest
zgodność deklaracji parametrów formalnych
zdefiniowanej procedury i metody reakcji na
wybrane zdarzenie. Aby uwolnić programistę
od znajomości wszystkich szczegółów uak-
tywnienie pola edycji nazwy metody, na ry-
sunku biały prostokąt obok nazwy zdarzenia,
udostępnia wykaz wszystkich procedur mogą-
cych mieć zastosowanie dla konkretnego przy-
padku. Odpowiednią listę można rozwinąć
naciskając strzałkę z prawej strony pola edy-
cyjnego. Dla zdarzenia związanego z tworze-
niem formy (OnCreate) część wykazu proce-
dur spełniających warunek zgodności parame-
trów formalnych jest pokazana na Rysunek 10.
Chociaż nie jest to widoczne to wszystkie wy-
mienione w pokazanym oknie procedury mają
jeden parametr identyfikujący nadawcę w po-
staci Sender : Tobject. Odkładając na później
dyskusję parametrów formalnych warto za-
uważyć, że nadawca chociaż czasami bardzo
istotny nie wyczerpuje wszystkich oczekiwań
ani też nie stwarza szansy pełnej obsługi zda-
rzenia. W przypadku akcji związanej z operowaniem myszką dobrze byłoby dodat-
kowo znać położenie kursora, obsługując klawiaturę programista prawdopodobnie
zechciałby wiedzieć jaki klawisz został użyty itd. Z powyższego wnosić należy o
potrzebie dostosowania zestawu parametrów procedur do obsługi konkretnego zda-
rzenia. Mnogość zdarzeń i związanych z nimi potrzeb wprowadziłaby jednak w
zakłopotanie nawet doświadczonego programistę. Z tego powodu wprowadzono
mechanizm generowania szkieletu procedury dla każdej sytuacji. Chcąc na przykład
zdefiniować reakcję na zdarzenie onMouseDown wybranego komponentu należy w
oknie inspektora podwójnie kliknąć na polu obok nazwy zdarzenia. Spowoduje to
automatyczne przeniesienie do okna edycji i umieszczenie w nim kodu podobnego
do:
procedure TForm1.Panel2MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y:
Integer);
begin
end;
Rysunek 10 Lista zdarzeń dla
obiektu formy
Zdarzenia
Włodzimierz Filipowicz
strona 18
Jak widać dla tego przykładu zbiór parametrów jest dość złożony oprócz nadawcy
(sender) i współrzędnych kursora myszki (X, Y) mamy także określenie użytego
przycisku (Button) oraz wykorzystywanych razem z myszką klawiszy klawiatury
(Shift).
Podsumowując, obiekt reaguje na pewien zbiór zdarzeń. Dla każdego zdarzenia
określono parametry formalne metody reakcji. Aby zdarzenie zostało „zauważone”
użytkownik obiektu musi powiązać metodę z jedną z dostępnych procedur, przy
czym wymaga się zgodności parametrów formalnych. Schemat postępowania dla
przykładowego obiektu, jest nim przycisk o nazwie START, pokazano na Rysunek
11
naciśnięcie klawisza
myszki na przycisku
START
Powiązanie może mieć miejsce na
etapie projektowania za
pośrednictwem inspektora obiektów
bądź dynamicznie podczas realizacji
aplikacji. Należy pamiętać o
zgodności parametrów formalnych
Metoda obiektu
reagująca na zdarzenie,
w tym przypadku
Start.OnClick, związana z
odpowiednią procedurą
Procedura reakcji
Obiekt "reaguje" na
zdarzenie
Zdarzenie
Zdarzenie bez metody reakcji
pozostaje "nie zauważonym"
Rysunek 11 Reakcja na zdarzenie jest możliwa po związaniu jego wystąpienia z
odpowiednią procedurą
Użytkownik obiektu, w szczególności komponentu, postrzega zdarzenie
jako specyfikację metody, która musi być uaktywniona w następstwie
wystąpienia danego zjawiska. Wybierane są przy tym zdarzenia wyni-
kające z manipulowania myszką bądź klawiaturą lub zachodzące w re-
alizowanym procesie, będące przy tym przedmiotem zainteresowania
użytkownika. Nie obsługiwane zdarzenia nie inicjują żadnej akcji.
W potocznym rozumieniu pojęcia zdarzenie i zjawisko są używane zamiennie. Tak
też będzie to miało miejsce w niniejszym opracowaniu chociaż z formalnego punktu
widzenia jest to nadużycie.
Zdarzenia
Włodzimierz Filipowicz
strona 19
Wyróżnia się następujące zdarzenia standardowe spowodowane manipulowaniem
myszką nad piktogramem obiektu:
OnClick
Naciśnięcie (kliknięcie) jednego z przycisków myszki
OnDragDrop
Ciągnięcie i upuszczenie
OnEndDrag
Koniec ciągnięcia
OnMouseMove
Przesuwanie kursora myszki
OnDblClick
Podwójne naciśnięcie (kliknięcie) lewego z przycisku myszki
OnDragOver
Przeciąganie, ruch myszki z wciśniętym przyciskiem
OnMouseDown
Naciskanie jednego z przycisków
OnMouseUp
Zwalnianie jednego z przycisków
Dodatkowo dla niektórych komponentów zdefiniowano zdarzenia generowane przez
klawiaturę (w warunkach aktywności obiektu) lub fakt jego uaktywnienia. Należą
do nich:
OnEnter
Uaktywnienie, „wejście” do, obiektu
OnExit
Wyjście, dezaktywacja obiektu
OnKeyPress
Naciśnięcie dowolnego klawisza
OnKeyUp
Zwalnianie dowolnego klawisza
OnKeyDown
Przyciskanie dowolnego klawisza
Zdarzenia
Włodzimierz Filipowicz
strona 20
Ćwiczenie 1
Celem ćwiczenia jest zbudowanie aplikacji demonstrującej reakcje na wybrane zda-
rzenia. Należy nanieść na formę komponenty Memo, Image i Panel, tak aby otrzy-
mać formę w postaci
pokazanej na rysun-
ku. Proszę zmienić
nazwy komponentów
na Notatnik, Panel i
Obrazek. Zmiany
należy dokonać przy
pomocy inspektora
obiektów. Propono-
wane rozmieszczenie
komponentów jest
możliwe dzięki ope-
rowaniu na właści-
wości align każdego
z nich. W tym przy-
padku ustawiono:
Notatnik : alLeft;
Panel :
alBottom;
Obrazek : alClient;
Ruch myszki czy naciśnięcie jednego z przycisków nad każdym z obiektów powin-
ny być sygnalizowane komunikatem na panelu. I tak ruch kursora myszki nad kom-
ponentem Image powinien spowodować pojawienie się na panelu komunikatu mysz-
ka nad obrazkiem, myszka nad notatnikiem sygnalizować powinien przemieszczanie
kursora nad komponentem Memo itd.
W ramach rozwinięcia ćwiczenia należy:
•
zrealizować klawiaturę z „klikiem”, każde naciśnięcie klawisza powinno być
akcentowane sygnałem akustycznym (procedura messagebeep(1)). Dla którego
komponentu można to zrobić?. Odpowiedź będzie oczywista po zapoznaniu się
z listą zdarzeń dla każdego komponentu.
•
na dolnym panelu należy umieścić dodatkowy panel, który wskazywałby poło-
żenie kursora myszki na formie,
•
zmienić nazwy komponentów w kodzie źródłowym.
UWAGA!
Należy oprogramować zdarzenia onMouseMove każdego z komponentów. Dla po-
trzeb akustycznej sygnalizacji naciśnięcia klawisza zdarzenie onKeyPress.
Rysunek 12 Przykładowy wygląd formy do wykorzystania w
ćwiczeniu 1
Notatnik:
TMemo
Panel: Tpanel
Obrazek:
TImage
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 21
Elementy języka Pascal
Środowisko Delphi umożliwia pisanie własnych aplikacji z użyciem składni podob-
nej do języka Pascal. Wszyscy, którzy mieli okazję poznać zasady konstrukcji pro-
gramów z użyciem tego języka będą mieli okazję wykorzystać swoje doświadczenia.
Zasadnicze elementy pozostają podobne, chociaż zakres wykorzystania wielu opera-
cji w nowych warunkach znacznie się poszerza. Niniejsze opracowanie rozpoczęto
od rzeczy najważniejszej, od krótkiego omówienia środowiska i wskazania takich
pojęć jak obiekt oraz zdarzenie. W tym i kolejnych punktach będą omówione pod-
stawowe konstrukcje języka.
Typ zmiennej czy właściwości definiuje zbiór wartości jakie mogą one przyjmować,
pośrednio określa on zbiór operacji jakie można wykonywać na i z udziałem odpo-
wiednich elementów. Tak jak w poprzednich wersjach języka można powiedzieć, że
dopuszcza się użycie typów: prostych, złożonych i wskazujących. Najczęściej poja-
wiającymi się w początkowej fazie poznawania środowiska i nauki programowania
są typy proste, wśród nich wyróżnia się tak zwane typy standardowe, do których
należą:
integer - typ całkowity, przechowywany na czterech bajtach w pamięci, zakres
wartości <-2147483648..2147483647>. Do tego typu należą dodatkowo
smallint, longint i word. Różnią się długością ciągu binarnego użytego do
reprezentacji wartości i obecnością bądź brakiem znaku.
single
- krótki typ rzeczywisty, w tej rodzinie istnieją także real, double i exten-
ded. Różnią się długością ciągu binarnego użytego do reprezentacji warto-
ści. Pociąga to za sobą różną ilość cyfr znaczących mantysy i rozszerzenie
zakresu wartości,
boolean - typ logiczny, odpowiednia zmienna może przyjmować dwie wartości:
true i false.
string
- typ napisowy, zmienna tego typu zawiera ciąg znakowy. Występują dwa
rodzaje napisów: shortstring i string. Pierwszy przypadek jest całkowicie
zgodny z typem łańcuchowym z poprzednich wersji Pascala. Organizacja
przechowywania symboli dopuszczała ich co najwyżej 255. Delphi umoż-
liwia użycie długich ciągów znakowych (typ string) pod warunkiem wy-
brania odpowiedniej opcji projektu. Możliwe jest też skorzystanie z 16-
bitowej reprezentacji symboli zwanej Unicode’em. Szerokie stringi (ang.
wide strings) mają duże znaczenie przy tworzeniu aplikacji realizowanych
w heterogenicznych warunkach rozproszonych.
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 22
Wymienione powyżej typy należą do elementów, z których programista może ko-
rzystać bez potrzeby ich wyraźnego definiowania. Istnieje więcej takich elementów i
będą one omawiane stosownie do potrzeb. Program składa się jednak również ze
składników, które muszą być definiowane przez użytkownika. Najczęściej pojawia-
jącym się składnikiem takiego typu jest identyfikator albo nazwa. Za pomocą iden-
tyfikatorów programista określa zmienne, typy, stałe, funkcje i wiele innych po-
trzebnych elementów składających się na program. Nazwa będzie związana z pewną
wartością, której typ należy zadeklarować, powstały w ten sposób obiekt nazywa się
zmienną i stanowi niewątpliwie najważniejszy składnik wszystkich programów. O
zmiennych przyjmujących pojedyncze wartości popularnie mówi się, że są zmien-
nymi prostymi.
Identyfikatorem albo nazwą może być dowolny, nieprzerwany ciąg znakowy rozpo-
czynający się od litery i składający się oprócz liter alfabetu anglosaskiego także z
podkreślenia (_) oraz cyfr. Długość takiego ciągu ograniczona jest względami prak-
tycznymi. Zbyt długie nazwy są niewygodne z uwagi na czasochłonność wpisywa-
nia oraz potencjalne błędy redakcyjne. Nazwa powinna w zwięzły sposób oddawać
swoje przeznaczenie. Zbyt krótkie nazwy na przykład jednoliterowe, aczkolwiek
poprawne nic nie mówią o swoim zastosowaniu.
Za poprawne przykłady nazw można uznać: suma, maksimum, znalazl itp. Niepo-
prawnymi formalnie są: suma elementów (nazwa zawiera spację oraz polską literę),
23mak (nazwa zaczyna się od cyfry). Identyfikatory o wątpliwym znaczeniu prak-
tycznym to te zbyt krótkie (składające się z pojedynczej litery) lub zbyt długie.
Poprzednie środowiska Turbo Pascala traktowały nazwy o identycznym zestawie
liter jako identyczne niezależnie od wielkości poszczególnych znaków. Zmienne
SUma i suma oznaczały dokładnie to samo. W omawianym środowisku jest to jedna
z opcji konfiguracyjnych, można zachować dotychczasowy brak rozróżniania du-
żych i małych liter, dopuszcza się jednak inną możliwość.
Aby w programie móc wykorzystywać zmienną o określonej nazwie to należy ją
zadeklarować. Miejsce deklaracji zmiennych to blok interfejsu modułu, blok imple-
mentacji lub obszar pomiędzy nagłówkiem procedury lub funkcji a pierwszym sło-
wem begin. Na przykład:
procedure zmiana;
var suma, najwiekszy : single;
begin
to poprawna i umieszczona we właściwym miejscu deklaracja dwóch lokalnych dla
procedury zmiennych typu rzeczywistego. Zmienne te mogą być wykorzystane tylko
w ramach procedury, w której zostały zadeklarowane. Inne elementy struktury apli-
kacji nie mogą z nich korzystać, z tego powodu tak zadeklarowane zmienne mają
zasięg lokalny i są określane mianem lokalnych dla procedur. W odróżnieniu od
globalnych, które mogą być wykorzystane przez wszystkie procedury modułu.
Zmienne globalne należy deklarować w sekcji interfejsu modułu, dokładnie pomię-
dzy słowami kluczowymi interface i implementation. Jednak istnieją tu pewne
dodatkowe ograniczenia, z których zasadnicze wymaga aby sekcja opatrzona sło-
wem kluczowym uses musi być pierwszą w module. Z praktycznych względów
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 23
należy też pozostawić niezmienionym fragment kodu poświęcony definicji klasy
formy. Kod ten jest generowany automatycznie przez środowisko, a próba jego
zmiany przez początkującego programistę może mieć przykre konsekwencje. Dodać
też należy, że w zakresie definicji klasy deklarujemy pola a nie zmienne w rozumia-
nym tu sensie. Na rysunku poniżej zielonym kolorem zaznaczono dozwolone miej-
sca deklaracji zmiennych globalnych.
Do tak zadeklarowanych zmiennych globalnych można odwoływać się również w
innych modułach, w których ten zawierający deklarację pojawia się w dyrektywie
uses. Na czerwono zaznaczono miejsca zabronione.
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 24
Zmienne
iloczyn, najmniejszy
z powyższego wydruku mogą być dostęp-
ne z każdego fragmentu składającego się na moduł (unit). Widoczne one są też z
innych modułów pod warunkiem odpowiedniego odwołania. W przytoczonym niżej
przykładzie zawierającym fragment modułu o nazwie innymodul można wykorzy-
stywać zmienne globalne innego o nazwie poprzednimodul.
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
StringGrid1: TStringGrid;
BitBtn1: TBitBtn;
RadioGroup1: TRadioGroup;
procedure BitBtn1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
iloczyn, najmniejszy : single;
implementation
procedure ...
begin
...
end;
procedure ...
begin
...
end; end.
Rysunek 13 Zielonym kolorem zaznaczono dopuszczalne miejsca deklarowania
zmiennych globalnych. Na niebiesko wyróżniono możliwe miejsca delaracji
zmiennych lokalnych modułu. Kolorem żółtym wskazano miejsca deklaracji
zmiennych lokalnych procedur
Ten fragment kodu jest defi-
nicją klasy formy i na począt-
ku kursu nie będzie wykorzy-
stywany jako miejsce dekla-
rowania zmiennych
Ten fragment kodu jest de-
klaracją używanych modu-
łów i zawsze musi być w
tym miejscu
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 25
unit innymodul;
interface
uses
..., poprzednimodul;
•
•
•
Identyfikatory globalne mogą zostać ponownie zdefiniowane lokalnie, powoduje to
ich „przykrycie” w ten sposób staną się niedostępne. Oprócz zmiennych lokalnych
procedur istnieją zmienne lokalne modułów, których miejsce deklaracji zaznaczono
na rysunku 12 kolorem niebieskim. Z tych zmiennych mogą korzystać struktury
programów położone poniżej miejsca deklaracji. W ten sposób wszystko to co zo-
stanie wyspecyfikowane zaraz po słowie implementation jest dostępne we wszyst-
kich procedurach i funkcjach modułu. W pewnym sensie takie zmienne można uwa-
żać za globalne. W odróżnienie od tych ostatnich dostęp do tak zadeklarowanych
zmiennych z innych modułów jest niemożliwy.
Miejsce deklaracji elementów programu decyduje o ich zasięgu.
Wyróżnia się elementy lokalne procedur, lokalne modułów i globalne.
Globalne deklaruje się w interfejsie (wyklucza się niektóre obszary). Z
elementów globalnych mogą również korzystać inne struktury
złożonego projektu pod warunkiem deklaracji wykorzystania modułu
zawierającego właściwą definicję. Elementy lokalne modułów definiuje
się w sekcji implementacji (po słowie implementation). Lokalność w
ramach procedury czy funkcji osiąga się umieszczając deklaracje po
nagłówku procedury (funkcji) i przed nawiasem otwierającym begin.
I.2 Instrukcja
podstawienia
Najczęstszą czynnością występującą podczas pisania programów jest przypisanie
wartości właściwościom bądź zmiennym. Zmiana liczby wierszy kratki w przykła-
dzie pokazanym wcześniej może być dokonana za pomocą inspektora obiektów,
natomiast podczas realizacji można zastosować następującą instrukcję podstawienia:
stringgrid1.rowcount:=12
;
Operator składający się z dwóch symboli := służy do nadania (zmiany) wartości
elementowi położonemu z jego lewej strony. Nowa wartość to wynik obliczenia
wyrażenia znajdującego się po prawej stronie. Obowiązuje przy tym potrzeba zgod-
ności typu elementu określanego z rezultatem zwracanym przez wyrażenie. Pojęcie
wyrażenia jest zgodne z tradycyjnym, powszechnym rozumieniem. Jest to zbiór
stałych zmiennych i funkcji z operatorami jako elementami separującymi. Użycie
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 26
nawiasów (okrągłych) jest również dopuszczalne. Wszystkie operatory można po-
dzielić na pięć kategorii, stosownie do kolejności realizowanych operacji są to:
−
jednoargumentowy operator zmiany znaku,
−
jednoargumentowy operator NOT,
−
operatory typu mnożenie: *, /, div, mod, AND, shl, shr,
−
operatory typu dodawanie: +, -, OR, XOR,
−
operatory relacji: =, <>, >=, >, <=, <, IN.
Porządek wykonywania operacji dla operatorów tego samego typu wynika z kolej-
ności ich użycia.
Operatory jednoargumentowe dokonują przekształceń zgodnie z powszechnie zna-
nymi zasadami. Zmianę znaku można odnieść zarówno do typu całkowitego jak i
rzeczywistego. Operator NOT w zastosowaniu do argumentu logicznego zmienia jej
wartość na przeciwną. Ten operator ma również zastosowanie do argumentu typu
liczba całkowita i dokonuje wtedy przekształcenia reprezentacji binarnej. W ciągu
binarnym tam gdzie występują zera wstawiane są jedynki i odwrotnie.
Poniżej pokazano tabele, w których omówiono poszczególne operatory oraz okre-
ślono typy argumentów oraz wyniku.
Operatory typu mnożenie:
operator
nazwa operacji
typy argumentów
typ wyniku
*
mnożenie
real, real
real
*
mnożenie
integer, integer
integer
*
mnożenie
real, integer (integer, real) real
/
dzielenie
real, real
real
/
dzielenie
integer, integer
real
/
dzielenie
real, integer (integer, real) real
div
dzielenie całkowite
integer, integer
integer
mod
modulo, reszta z dzielenia integer, integer
integer
AND
binarne AND
integer, integer
integer
AND
logiczne AND
boolean, boolean
boolean
shl
przesunięcie w lewo
integer, integer
integer
shr
przesunięcie w prawo
integer, integer
integer
Zastosowanie operatora AND do argumentów typu całkowitego sprowadza się do
działania na ich reprezentacjach binarnych. Wykonywane są operacje iloczynu na
kolejnych parach bitów ciągów binarnych wykorzystanych do zapisu wartości.
Przykłady:
5 AND 4 = 4
8 AND 7 = 0
00000101
(2)
00000100
(2)
00000100
(2)
00001000
(2)
00000111
(2)
00000000
(2)
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 27
Działania na reprezentacjach binarnych dotyczą również operacje przesunięć w lewo
shl i w prawo shr. Wynik przesunięcia o jedną pozycję w lewo jest równoważny
pomnożeniu przez 2. Odpowiednie przemieszczenie ciągu binarnego o jedną pozy-
cję w prawo to podzielenie przez 2. Przesunięcie o wiele pozycji odpowiada pomno-
żeniu lub podzieleniu przez potęgę dwójki, przy czym wykładnik potęgi odpowiada
wielkości przesunięcia.
64 shr 3 = 8; 01000000
(2)
Þ
3
00001000
(2)
;
Operatory typu dodawanie:
operator
nazwa operacji
typy argumentów
typ wyniku
+
dodawanie
real, real
real
+
dodawanie
integer, integer
integer
+
dodawanie
real, integer (integer, real) real
-
odejmowanie
real, real
real
-
odejmowanie
integer, integer
real
-
odejmowanie
real, integer (integer, real) real
OR
binarne OR
integer, integer
integer
OR
logiczne OR
boolean, boolean
boolean
XOR
binarne XOR
integer, integer
integer
XOR
logiczne XOR
boolean, boolean
boolean
Zastosowanie operatora OR do argumentów typu całkowitego sprowadza się do
działania na ich reprezentacjach binarnych. Wykonywane są operacje sumy na ko-
lejnych parach bitów ciągów binarnych wykorzystanych do zapisu wartości.
Przykłady:
5 OR 4 = 5
5 XOR 4 = 1
8 OR 7 = 15
Rozbieżności między typem obliczonej wartości a wielkością określaną można usu-
nąć stosując odpowiednie funkcje dokonujące zmiany typu lub też uciec się do me-
chanizmu konwersji typów (ang. type cast).
Większość komponentów posiada właściwość typu napisowego (string) o nazwie
caption, chcąc aby napis na pewnym obiekcie p_obiekt odpowiadał wartości pewnej
00000101
(2)
00000100
(2)
00000101
(2)
00001000
(2)
00000111
(2)
00001111
(2)
00000101
(2)
00000100
(2)
00000001
(2)
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 28
zmiennej typu rzeczywistego należy skorzystać z funkcji dokonującej zmiany repre-
zentacji floattostr, tak jak pokazano to w poniższym przykładzie:
var zmienna : single;
begin
zmienna:=175.1543;
p_obiekt.caption:=floattostr(zmienna);
end;
Przy odwołaniu do elementu związanego z pewnym obiektem obowiązuje konwen-
cja typu: obiekt.element_obiektu. W odwołaniu występują dwa człony pierwszy,
przed kropką, określa obiekt drugi właściwość (także inne składowe obiektu). Kon-
wencja ta dopuszcza zagnieżdżenia, co oznacza, że właściwość może być obiektem.
Posługiwanie się gotowymi elementami w programie, takimi jak na przykład wła-
ściwości komponentów, wymaga znajomości ich typów a co za tym idzie sposobu
reprezentacji. W związku z tym duże praktyczne znaczenie mają następujące funkcje
dokonujące zmiany reprezentacji wszędzie tam gdzie jest to konieczne. Pojawiający
się w polu edycyjnych zbiór cyfr wcale nie musi i w rzeczywistości nie jest liczbą a
zbiorem znaków, z których każdy zapisano oddzielnie stosując kod ASCII (patrz
rysunek 14). Podstawowy zestaw funkcji konwersji typów obejmuje pozycje podane
w tabeli.
„123” jako liczba całkowita może być
zapisana na 16 bitach w następujący
sposób:
0000000001111011
Ciągi binarne
Kod ASCII
znaku
00110001
1
00110010
2
00110011
3
00001101
CR
00001010
LF
„123” jako napis wymaga zakodowania każdej
cyfry i użycia znacznika końca napisu.
Może to wyglądać tak:
strtoint
inttostr
Rysunek 14 Posługiwanie się gotowymi elementami w programie wymaga
znajomości ich typów a co za tym idzie sposobu reprezentacji. Tam gdzie jest to
potrzebne należy skorzystać z funkcji konwersji
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 29
inttostr(x);
zamienia wartość typu całkowitego x na łańcuch (napis),
strtoint(np.);
zamienia wartość typu napisowego np na wartość typu cał-
kowitego,
floattostr(x);
zamienia wartość typu rzeczywistego x na łańcuch (napis),
strtofloat(np);.
zamienia wartość typu napisowego np na wartość typu rze-
czywistego
Zastosowanie funkcji floattostr(x) może przynieść mało elegancki i nieprecyzyjny
wynik zawierający nieoczekiwanie wiele pozycji znaczących po separatorze dzie-
siętnym. Efekt ten może wynikać z ograniczeń związanych z konwersją systemów
zapisu wartości. Daleko bardziej czytelną prezentację wartości typu rzeczywistego
zapewnia funkcja formatowanej zamiany floattostrf. Na przykład zastosowana w
postaci: floattostrf(x, fffixed,6,2); daje zawsze dwie cyfry znaczące po separatorze
dziesiętnym. Część całkowita zapisana jest na sześciu polach.
Instrukcja podstawienia jest jedną z podstawowych we wszystkich języ-
kach programowania. Służy do nadania wartości zmiennej bądź wła-
ściwości. Podstawienie możliwe jest tylko w przypadku zgodności typów
(jedynym odstępstwem od tej reguły jest przypisanie zmiennej rzeczywi-
stej wartości typu całkowitego) elementu określanego i typu wartości
wyrażenia stojącego po prawej stronie operatora podstawiania :=.
Zgodność typów można osiągnąć poprzez wykorzystanie jednej z funkcji
zmieniającej sposób reprezentacji.
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 30
I.3 Ćwiczenie 2
W ramach ćwiczenia
należy zmieniać właści-
wości wskazanych kom-
ponentów oraz wykonać
proste operacje arytme-
tyczne na kilku zmien-
nych. W pierwszej wersji
należy przygotować for-
mę podobną do tej na
rysunku 15. Wykorzy-
stywanymi elementami
są: kratka kolorów (kom-
ponent selekcji kolorów)
Colorgrid i dwa panele.
Komponent wyboru kolo-
ru posiada właściwości
foregroundcolor
i
backgroungcolor odpo-
wiadające kolorowi klikniętego prostokąta kratki odpowiednio przy pomocy lewego
i prawego przycisku myszki. Na przykład kliknięcie lewym klawiszem protokąta
czerwonego ustawi kolor pierwszego planu (właściwość foregroundcolor) na czer-
wony, kolor tła (właściwość backgroundcolor) pozostanie bez zmian. Zmiana wy-
branego koloru generuje zdarzenie onChange. Proszę opracować metodę przypisują-
cą wybrane kolory tła i pierwszego planu odpowiednim panelom.
Zestawienie właściwości obiektów wykorzystanych w przykładzie:
Obiekt
Właściwość
Objaśnienie
Typ
panele
color
kolor tła panelu
Tcolor
(integer)
ColorGrid
foregroundcolor
kolor pierwszego planu, zmieniany lewym
przyciskiem myszki
Tcolor
(integer)
ColorGrid
backgroundcolor kolor drugiego planu (tła), zmieniany prawym
przyciskiem myszki
Tcolor
(integer)
Zestawienie zdarzeń wykorzystanych w przykładzie:
Obiekt
Zdarzenie
Objaśnienie
ColorGrid
onChange
zainicjowanie zmian kolorów paneli
Rysunek 15 Wygląd formy do ćwiczenia 2
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 31
W kolejnym ćwiczeniu należy
przygotować formę jak na rysunku
16. Składowymi formy są w tym
przypadku cztery pola edycyjne
Edit, dwa przyciski oraz panel. Po
uruchomieniu aplikacji należy do
pól edycyjnych wpisać pewne
wartości. Naciśnięcie przycisku
oblicz powinno spowodować do-
danie wpisanych liczb, wynik
sumowania powinien zostać
umieszczony na panelu. Przycisk
opatrzony etykietą zwiększ służy
natomiast do powiększania warto-
ści prezentowanej na panelu. Każ-
de jego naciśnięcie powinno spo-
wodować posumowanie wartości w oknach edycyjnych i o tą sumę należy zwięk-
szyć wartość pokazywaną na panelu.
Właściwości pól edycyjnych udostępniające wpisywane przez użytkownika liczby
nazywają się text i są typu napisowego (string). Z tego powodu pobraniu danej z
pola musi towarzyszyć zmiana reprezentacji, przekształcenie realizowane przez
funkcję strtoint lub strtofloat w zależności od typu wykorzystywanej zmiennej. Na
przykład można zastosować podstawienie:
x:=strtoint(edit1.text);
które pobierze napis z okna edycyjnego edit1, zamieni go na wartość typu całkowi-
tego i wynik zwiąże ze zmienną x. Funkcja strtoint (ang. string to integer) dokonuje
zamiany stringu na wartość typu całkowitego. Problem może pojawić się wtedy
kiedy konwersja taka jest po prostu niemożliwa. Jeżeli na przykład użytkownik apli-
kacji zamiast liczby wpisał jakiś ciąg znaków, którego nie da się zamienić na war-
tość lub pozostawił pole edycyjne puste. Aby rozwiązać taki problem można wyko-
rzystać konstrukcję ostrożnego działania try ... except ... end;.
Na przykład:
try
x:=strtoint(edit1.text)
except
x:=0
end;
spowoduje, że zostanie podjęta próba konwersji napisu w oknie edycyjnym edit1 i
jeżeli zakończy się ona sukcesem to x otrzyma stosowną wartość. W przypadku
niemożności zamiany napisu na wartość powyższa konstrukcja spowoduje, że x
otrzyma wartość 0. W części po except ma miejsce stosowne podstawienie. Ten
właśnie fragment zapisu realizuje się w przypadku niepowodzenia w wykonywaniu
operacji występujących po słowie try.
Rysunek 16 Wygląd formy do ćwiczenia 2
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 32
W przypadku kiedy masz wątpliwości czy pewien fragment programu
może nie zostać zrealizowany z uwagi na warunki przetwarzania zasto-
suj konstrukcję try ... except ... end;
Niepowodzenie przy takim postępowaniu wiązać się może tylko z uak-
tywnieniem opcji TOOLS|ENVIRONMENT OPTIONS|PREFERENCES
Break on exception, która wymusza zatrzymania mimo stosowania kon-
strukcji ostrożnego działania.
W ramach rozwinięcia ćwiczenia należy:
•
Przekształcić program tak aby akceptował zarówno wartości całkowite jak i
rzeczywiste. Do tego celu należy zmienić funkcję przekształcającą zawartość
pola edycyjnego na typ rzeczywisty, jest to funkcja strtofoat. Zastosowana na
przykład w podstawieniu:
x:=strtofloat(edit1.text);
powoduje zamianę napisu z pola edycyjnego edit1 na wartość rzeczywistą i pod-
stawienie jej pod zmienną x, zmienna ta musi być typu rzeczywistego (real albo
single).
•
opracować metodę kliknięcia pola edycyjnego powodującą jego wyczyszczenie
•
opracować metodę kliknięcia panelu powodującą pojawienie się na nim zera
Zestawienie właściwości obiektów wykorzystanych w przykładzie:
Obiekt
Właściwość
Objaśnienie
Typ
panel
caption
etykieta komponentu (napis pojawiający się na jego
piktogramie)
string
pola edycji text
zawartość pola edycyjnego komponentu, najczęściej
efekt działania użytkownika
string
Zestawienie zdarzeń wykorzystanych w przykładzie:
Obiekt
Zdarzenie
Objaśnienie
przyciski
onClick
zainicjowanie obliczeń
pola
edycyjne
onClick
wyczyszczenie tych pól (w ramach rozwinięcia ćwi-
czenia)
panel
onClick
wyzerowanie napisu (w ramach rozwinięcia ćwicze-
nia)
I.4 Instrukcje
warunkowe
Instrukcja warunkowa specyfikuje operacje, które mają zostać wykonane w przy-
padku spełnienia pewnego warunku.
if warunek then instrukcja;
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 33
Możliwa jest również specyfikacja operacji dla nieprawdziwości warunku w kon-
strukcji:
if warunek then instrukcja_dla_prawdy
else instrukcja_dla_fałszu;
Należy zwrócić uwagę na brak średnika przed słowem else.
Słowo warunek oznacza wyrażenie którego wynik jest typu boolowskiego. Przykła-
dami poprawnych instrukcji warunkowych są:
if x>y then x:=x+3;
if znaleziono then zamien(p,q);
if (suma>2) and (suma<20) then w_przedziale
else poza_przedzialem;
Warunkiem może być więc:
1. relacja, to znaczy porównanie dwóch wielkości z wykorzystaniem operatorów:
>, >=, <, <=, <>, i =. (w przykładzie x>y)
2. zmienna typu boolowskiego (w przykładzie znaleziono)
3. konstrukcja wykorzystująca operatory logiczne OR, AND i NOT (w przykładzie
(suma>2) and (suma<20)
). Ważną rzeczą jest zamknięcie poszczegól-
nych relacji w nawiasy (patrz operatory i ich hierarchia).
Pod pojęciem instrukcja (instrukcja_dla_...) rozumie się pojedynczą operację, lecz
również dowolną ich ilość zamkniętą w nawiasy programowe begin end;.
I.5 Ćwiczenie 3
W ćwiczeniu należy przygotować formę
jak na rysunku 15. Składowymi formy
są w tym przypadku cztery komponenty
edycyjne Edit, dwa przyciski, dwa pa-
nele oraz dwa komponenty wyboru
opcji (ang. checkbox). Po uruchomieniu
aplikacji należy do pól edycyjnych wpi-
sać pewne wartości. Naciśnięcie przyci-
sku oblicz powinno przynieść efekty
zależne od zaznaczenia (wybrania)
opcji poziomo/ pionowo. Przez wybra-
ną rozumie się pierwszą jeżeli na ma-
łym białym polu występuje znaczek
√
,
w przypadku przeciwnym wybraną jest
opcja druga. Obecność znaczka wyboru
wiąże się z wartością właściwości chec-
ked odpowiedniego komponentu równą
Rysunek 17 Wygląd formy do ćwiczenia 3
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 34
true. Po naciśnięciu przycisku oblicz i wybranej opcji poziomo w lewym panelu
należy umieścić sumę dwóch górnych a w prawym dwóch dolnych pól edycyjnych.
Przy wybranej opcji pionowo należy w lewym panelu należy umieścić sumę dwóch
lewych a w prawym dwóch prawych pól edycyjnych.
Poniżej pokazano przykładowy kod metody związanej z kliknięciem przycisku Ob-
licz:
procedure Tform1.Button1Click(Sender: Tobject);
var x,y,z,w
: real;
begin
x:=strtofloat(edit1.text);
y:=strtofloat(edit2.text);
z:=strtofloat(edit3.text);
w:=strtofloat(edit4.text);
if checkbox1.checked then
begin
panel2.caption:=floattostr(x+y);
panel3.caption:=floattostr(z+w)
end
else
begin
panel2.caption:=floattostr(x+z);
panel3.caption:=floattostr(y+w)
end;
end;
Przycisk opatrzony etykietą zwiększ służy natomiast do zmian wartości prezentowa-
nych na panelach. Każde jego naciśnięcie powinno spowodować modyfikację war-
tości pokazywanych na panelach. Jeżeli wybrano opcję zwiększ odpowiednie sumy
będą zwiększane stosownie do wybranej opcji poziomo lub pionowo. Przy wybranej
opcji zmniejsz odpowiednie sumy będą zmniejszane stosownie do zaznaczonego
kierunku.
W ramach rozwinięcia ćwiczenia należy przekształcić program tak aby do okien
edycyjnych można było wpisać tylko cyfry i pojedynczy znak separatora dziesiętne-
go. Procedurę obsługi zdarzenia onKeyup okna edycyjnego akceptującą tylko cyfry
pokazano poniżej. Zastosowany algorytm polega na sprawdzeniu czy naciśniety
klawisz jest odpowiada cyfrze. Parametr key procedury onKeyup zawiera kod ASCII
naciśniętego klawisza. Cyfra 0 w tym kodzie posiada odpowiednik dziesiętny równy
48, natomiast 9 ma kod odpowiadający wartości 57. Parametr key powinien być
zawarty w przedziale <48, 57>. Instrukcja, która dokonuje stosownego sprawdzenia
to:
if (key>=48)and(key<=57)then exit;
Prawdziwość warunku związana z naciśnięciem klawisza cyfry powoduje wykona-
nie instrukcji exit i opuszczenie procedury. Jeżeli użyty klawisz nie jest związany z
cyfrą ma miejsce usunięcie ostatniego znaku z właściwości text obiektu wywołują-
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 35
cego. Takim obiektem może być dowolne pole edycyjne. Pozwala to zastosowanie
tej samej procedury do kontroli wpisywanych wartości do wszystkich okien edycyj-
nych. Procedurę o nazwie Edit1Keyup można związać ze zdarzeniem zwalniania
klawisza we wszystkich komponentach edycyjnych. Jak w takim razie określić z
którym obiektem mamy do czynienia w konkretnym przypadku? Okazuje się, że
obiekt wywołujący procedurę obsługi zdarzenia przekazuje swoje dane za pośred-
nictwem parametru sender. Dzięki temu procedura zawsze „wie” kto ją wywołał.
Omawiany parametr należy jednak do bardzo szerokiej i słabo określonej klasy,
dlatego przed jego użyciem należy uściślić o obiekt jakiej konkretnej klasy chodzi,
na przykład w sposób polegający na dostosowaniu typu:
Tedit(sender)
. Jeżeli
użyty klawisz nie odpowiada cyfrze to odpowiedni znak jest usuwany z z tekstu
zawartego w danym polu.
procedure TForm1.Edit1KeyUp(Sender: TObject; var Key:
Word; Shift: TShiftState);
var s : shortstring;
begin
if (key>=48)and(key<=57)then exit;
s:=Tedit(sender).text;
delete(s,length(s),1);
Tedit(sender).text:=s;
Tedit(sender).selstart:=length(s)
end;
Proszę zmodyfikować procedurę w ten sposób aby akceptowała symbol separatora
dziesiętnego. Parametr key równa się 190 dla kropki i 188 dla przecinka. Zmienna
systemowa o nazwie decimalseparator typu znakowego pozwala ustalić ustawienia
konfiguracyjne. Stosownie do nich we wpisywanych do pola edycyjnego znakach
może pojawić się tylko jedna kropka bądź pojedynczy przecinek. Do rozwiązania
tego przykładu pomocną będzie dwuargumentowa funkcja pos(wzorzec, przeszuki-
wany_napis). Funkcja ta zwraca pozycje początkową wzorca w przeszukiwanym
stringu. Brak odpowiedniego wzorca powoduje, ze funkcja zwraca wartość zero. Na
przykład pos(decimalseparator, edit.text) wyszuka pozycję separatora dziesiętnego
w napisie umieszczonym w polu edycyjnym. Brak separatora objawi się wartością
zerową funkcji. Należy pamiętać, że właściwość text dla pola edycyjnego zawiera
wszystkie umieszczone tam symbole włącznie z ostatnio wpisanym.
Proponowana konstrukcja fragmentu programu to:
if (key=190)or(key=188)then
if pos(decimalseparator,edit1.text)<>0 then
// wcze
ś
niej wpisano znak separatora nie akcetuj
// wpisanego symbolu
else
// akceptuj znak jest on pierwszym separatorem
Wyjdź z procedury
jeżeli wpisano cyfrę
Usuń ostatni znak (nie
jest on cyfrą)
Wskaźnik wpisywanych
znaków ustaw na końcu
Odczytaj tekst z aktyw-
nego pola edycyjnego
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 36
Parametr sender identyfikuje obiekt wywołujący. Jednak odwołanie się
do dowolnego elementu w tym właściwości tego obiektu wiąże się z ko-
niecznością zastosowania funkcji adaptacji (rzutowania) typu (ang. ty-
pe cast). Zmiana typu musi mieć swoje uzasadnienie i być możliwą do
realizacji. Programista stosując rzutowanie powinien znać typ obiektu
kryjącego się pod parametrem sender, powinien znać obiekt wywołują-
cy. W przypadku wątpliwości należy poczynić kroki zabezpieczające
przed powstaniem błędu.
Zestawienie właściwości obiektów wykorzystanych w przykładzie:
Obiekt
Właściwość
Objaśnienie
Typ
panele
caption
etykieta komponentu (napis pojawiający się na
jego piktogramie)
string
checkbox
checked
identyfikator „zaznaczenia”, obecności symbolu
√
w polu edycji
boolean
pola edycyjne
text
zawartość pola edycyjnego komponentu, najczę-
ściej efekt działania użytkownika
string
Zestawienie zdarzeń wykorzystanych w przykładzie:
Obiekt
Zdarzenie
Objaśnienie
przyciski
onClick
zainicjowanie obliczeń
pola
edycyjne
onKeyup
w ramach rozwinięcia ćwiczenia przy akceptacji
określonego zbioru znaków.
Jedna procedura może być wykorzystana do obsługi zdarzeń wielu
obiektów. Niezbędna w takich warunkach identyfikacja obiektu wywo-
łującego daną procedurę możliwa jest dzięki parametrowi sender. Pa-
rametr ten występuje we wszystkich metodach obsługujących dowolne
zdarzenie każdego komponentu. Właściwości obiektu wołającego do-
stępne są po dostosowaniu typu.
I.6 Instrukcje iteracyjne - for
Wśród instrukcji iteracyjnych występują trzy różniące się zarówno składnią jak i
sposobem funkcjonowania. Jedną z takich instrukcji jest for, której postać jest nastę-
pująca:
for
nazwa_zmiennej_calkowitej
:=
wartosc_poczatkowa
to
wartosc_koncowa do instrukcja;
Na przykład:
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 37
for i := 1 to 10 do suma:=suma+i;
Instrukcja ta powoduje, że zmienna sterująca, obowiązkowo typu całkowitego,
przyjmuje kolejne wartości z przedziału wyznaczonego przez wartość początkową i
końcową. Dla każdej nowej wartości tej zmiennej realizowana jest instrukcja
umieszczona po słowie do. W składni Pascala za instrukcję uważa się również do-
wolnie złożoną konstrukcję zamkniętą w nawias programowy begin end. W przyto-
czonym przykładzie zmienna i przyjmie kolejno wartości 1, 2, 3, 4, 5, 6, 7, 8, 9 i na
końcu 10, dodając je każdorazowo do zmiennej suma. W ten sposób wyznaczono
sumę kolejnych liczb naturalnych.
W przytoczonej wyżej postaci instrukcji for wymagane jest aby wartość końcowa
była większa lub równa początkowej. Możliwa jest również sytuacja odwrotna, wte-
dy jednak instrukcja ta przybiera postać:
for
nazwa_zmiennej_calkowitej
:=
wartosc_poczatkowa
to
wartosc_koncowa downto instrukcja;
Na przykład:
for i := 10 to 1 do suma:=suma+i;
Instrukcja ta powoduje, że zmienna sterująca, tak jak poprzednio obowiązkowo typu
całkowitego, przyjmuje kolejne wartości z przedziału wyznaczonego przez wartość
początkową i końcową. W przytoczonym przykładzie zmienna i przyjmie kolejno
wartości 10, 9, 8, 7, 6, 5, 4, 3, 2 i na końcu 1.
I.6.1 Typy tablicowe
Tablica jest uporządkowaną kolekcją danych określonego typu. Każdy element
posiada ściśle określoną pozycję w ramach kolekcji. Popularnie o takich zbiorach
danych mówi się jak o tablicach lub macierzach. Tablice mogą być jedno-, dwu- lub
wielowymiarowe. Liczba wymiarów może być dowolna. Najczęściej jednak ograni-
cza się do co najwyżej struktur trójwymiarowych.
Aby tablice używać w programie należy je zadeklarować zgodnie ze schematem:
var nazwa : array[zakres_wskaznikow] of typ_elementu;
Zakres wskaźników określa rozmiar tablicy oraz identyfikatory kolejnych elemen-
tów i najczęściej występuje w postaci dolny_wskaznik .. gorny_wskaznik. Gdzie
dolny_wskaznik oznacza indeks pierwszego a gorny_wskaznik ostatniego elementu
w jednowymiarowej tablicy. Indeksy te muszą być podane jako stałe.
Na przykład:
var mojatablica : array[1..10] of integer;
deklaruje dziesięcioelementową tablicę, w której pierwszy wyraz ma numer 1 a
ostatni 10. W programie, w którym wystąpiła powyższa deklaracja można odwoły-
wać się do elementów tablicy, na przykład:
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 38
mojatablica[3,5]:=1346;
Instrukcja podstawienia oznacza przypisanie stosownej wartości elementowi w trze-
cim rzędzie i piątej kolumnie. O elementach tablic, adresowanych w pokazany spo-
sób mówi się jako o zmiennych indeksowanych. Mogą one występować prawie we
wszystkich miejscach gdzie mogła pojawić się „zwykła” zmienna.
W tablicach wielowymiarowych liczba zakresów wskaźników oddzielonych od sie-
bie przecinkami wskazuje na wymiary. Deklaracja:
var mojatablica : array[1..10,-1..1] of integer;
nakazuje zarezerwować miejsca na dziesięciowierszową i trzy kolumnową macierz,
w której pierwszy wiersz ma numer 1 a pierwsza kolumna numer –1.
Specjalnym typem tablicowym jest ciąg znakowy zwany stringiem. Elementami
takiego ciągu są znaki. Deklaracja:
var napis : string[10];
rezerwuje miejsce na ciąg znaków złożony z 10 elementów. Pierwszy znak zawsze
posiada numer 1.
I.7 Ćwiczenie 4
W pierwszej części ćwiczenia na-
leży przygotować formę jak na
rysunku 18. Składowymi formy są:
kratka stringów - komponent
umożliwiający prezentacje zawar-
tości tabel Stringgrid, przycisk,
panel, grupa przełączników - tak
zwany radiogroup.
Naciśnięcie przycisku wpisz po-
winno wyzwalać akcję wypełnia-
nia kratki wartościami losowymi.
Zadanie ma być realizowane w
sposób zależny od zaznaczonego
przełącznika. W pierwszym przy-
padku (pozycja górna wciśnięta)
do losowo wybranej komórki zo-
stanie wpisana pewna, losowa wartość, w drugim należy wypełnić losowo wybrany
wiersz, w trzecim losowo wybraną kolumnę w ostatnim zaś należy wypełnić cały
grid.
Podczas ćwiczenia należy wykorzystać właściwości: cells, rowcount i colcount krat-
ki. Właściwość o nazwie cells udostępnia zawartość komórki o ustalonych współ-
rzędnych [kolumna, wiersz]. Z tego powodu konstrukcje programu odwołujące się
do komórek kratek będą musiały zawierać odpowiednie wskaźniki, na przykład:
stringgrid1.cells[0,2]:=’STAT’;
Rysunek 18 Wygląd formy do pierwszej części
ćwiczenia 4
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 39
spowoduje umieszczenie napisu STAT w komórce w zerowej kolumnie i drugim
wierszu. Z kolei:
edit1.text:=stringgrid1.cells[1,3];
przeniesie zawartość komórki w kolumnie pierwszej i wierszu trzecim gridu do pola
edycyjnego edit1.
W komponentach klasy Tstringgrid przyjęto określać współrzędne w porządku ko-
lumna, wiersz dokładnie odwrotnie niż obowiązuje to przy odwołaniu do elementów
macierzy. Pierwszy wiersz i pierwsza kolumna mają wskaźniki równe zero. Komór-
kę w lewym górnym rogu adresujemy jako cells[0,0]. Rowcount oraz colcount to
właściwości typu całkowitego zawierające odpowiednio liczbę wierszy i liczbę ko-
lumn gridu. Element leżący w ostatnim wierszu i ostatniej kolumnie można zaadre-
sować jako cells[st.colcount-1, [st.rowcount-1]. Przy czym st jest nazwą kratki.
Zawartość komórki jest typu napisowego (string), w związku z tym umieszczenie w
niej wartości liczbowej będzie wymagało użycia jednej z funkcji konwersji inttostr
lub floattostr w zależności od typu użytej zmiennej.
Funkcja, która zwraca wartości losowe z określonego przedziału to random (para-
metr). Gdzie parametr wyznacza górną granicę otwartego przedziału wartości, dolna
granica to zero.
Przykładową procedurę realizującą postawione zadanie pokazano poniżej. Wyko-
rzystuje ona właściwość itemindex komponentu radiogroup. Właściwość ta równa
się 0 jeżeli zaznaczono pierwszy przełącznik, 1 jeżeli drugi i tak dalej. Wartość wła-
ściwości itemindex jest selektorem dla instrukcji case. Instrukcja ta pozwala zreali-
zować odpowiedni fragment programu (zamknięty w nawiasy programowe begin
end) w zależności od wartości selektora.
procedure TForm1.BitBtn1Click(Sender: TObject);
var i, j, max : integer;
begin
for i:=0 to stringgrid1.rowcount-1 do
stringgrid1.rows[i].clear;
case radiogroup1.ItemIndex of
0 : begin
i:=random(stringgrid1.rowcount);
j:=random(stringgrid1.colcount);
stringgrid1.cells[j,i]:=inttostr(random(100))
end;
1 : begin
i:=random(stringgrid1.rowcount);
for j:=0 to stringgrid1.colcount-1 do
stringgrid1.cells[j,i]:=inttostr(random(100))
end;
2 : begin
j:=random(stringgrid1.colcount);
for i:=0 to stringgrid1.rowcount-1 do
stringgrid1.cells[j,i]:=inttostr(random(100))
end;
Te instrukcje powo-
dują wyczyszczenie
gridu
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 40
3 : begin
for i:=0 to stringgrid1.rowcount-1 do
for j:=0 to stringgrid1.colcount-1 do
stringgrid1.cells[j,i]:=inttostr(random(100))
end;
end; // koniec case
end;
Instrukcja selekcji case przyjmuje jedną z postaci:
case wyrazenie of
stala1 : instrukcja;
stala2 : instrukcja;
•
•
•
end;
lub też:
case wyrazenie of
stala1 : instrukcja;
stala2 : instrukcja;
•
•
•
stalan : instrukcja;
else
instrukcja
end;
Przy czym stala jest pojedynczą stałą lub zakresem zapisanym przy użyciu dwóch
kropek. Zapis 2..6 oznacza obustronnie domknięty przedział wartości <2, 6>.
Instrukcja case składa się z selektora w postaci wyrażenia arytmetycznego i listy
instrukcji, każdy element tej listy jest opatrzony etykietą. Typ etykiety musi być
zgodny z typem zwracanym przez wyrażenie. Wykonywana jest ta instrukcja, której
etykieta odpowiada wartości selektora. Każda instrukcja może być poprzedzona
kilkoma etykietami. Dopuszcza się etykiety w postaci zakresu wartości. Jeżeli żadna
z etykiet nie odpowiada wartości selektora to wykonywana jest instrukcja występu-
jąca po słowie else - jeżeli takie występuje.
Inny przykład instrukcji case:
case I of
0, 2, 4, 6, 8 : Edit1.Text := 'Parzysta cyfra';
1, 3, 5, 7, 9 : Edit1.Text := 'Nieparzysta cyfra';
10..100
: Edit1.Text := 'Liczba w przedziale 10,100';
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 41
else
Edit1.Text := 'ujemna lub wi
ę
ksza od 100';
end;
Zakresy w instrukcji case nie mogą się pokrywać. Poniższa konstrukcja nie jest do-
puszczalną:
case selektor of
5
: Edit1.Text := 'specjalny przypadek';
1..10 : Edit1.Text := 'ogólny przypadek';
end;
W dalszej części ćwiczenia należy
przygotować formę jak na rysunku 19.
Składowymi formy są w tym przy-
padku dwa komponenty umożliwiają-
ce prezentacje tabel Stringgrid, przy-
cisk oraz panel. Po uruchomieniu
aplikacji należy do lewego gridu wpi-
sać pewne wartości. Wpisywanie cze-
gokolwiek, czynność związana z edy-
cją zawartości gridu możliwa jest po
ustawieniu opcji Options|goEditing na
true. Właściwość Options dla Strin-
ggridu ma charakter zagnieżdżonej
(znak + przed nazwą w inspektorze
obiektów), należy ją rozwinąć przez
podwójne kliknięcie nazwy i odpo-
wiednio zmienić właściwość goEditing. Naciśnięcie przycisku oblicz powinno wy-
brać z każdego wiersza największą wartość i wpisać ją na odpowiednią pozycję
gridu prawego.
Przykładowy kod procedury obsługującej kliknięcie przycisku i rozwiązującej po-
stawiony problem pokazano poniżej:
procedure TForm1.BitBtn1Click(Sender: TObject);
var i, j, max, tym : integer;
begin
for i:=0 to stringgrid1.rowcount-1 do
begin
max:=strtoint(stringgrid1.cells[0,i]);
for j:=1 to stringgrid1.colcount-1 do
begin
tym:= strtoint(stringgrid1.cells[j,i]);
if max<tym then
max:=tym;
stringgrid2.cells[0,i]:=inttostr(max)
Rysunek 19 Wygląd formy do drugiej części
ćwiczenia 4
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 42
end;
end;
end;
W ramach rozwinięcia ćwiczenia należy:
•
wprowadzić przełącznik wyboru maksimum/minimum
•
wartości z gridu przepisać do dwuwymiarowej tabeli. Wszystkie operacje zwią-
zane z poszukiwaniem ekstremum realizować na danych liczbowych. Dodatko-
wo prawy grid rozbudować o drugą kolumnę, do której należy wpisać numer
pozycji (kolumny) ekstremum.
Przy tworzeniu tabeli proszę zwrócić uwagę na inny porządek wskazywania
wiersza i kolumny. W strukturach typu tablicowego, na przykład tablica[i,j] i
jest identyfikatorem wiersza natomiast j kolumny, odwrotnie niż w gridzie.
Zestawienie właściwości wykorzystanych w przykładzie:
Właściwość
Komponent
Objaśnienie
Typ
colcount
stringgrid
liczba kolumn gridu (pierwsza kolumna ma
numer 0 ostatnia zaś colcount-1)
integer
rowcount
stringgrid
liczba wierszy gridu (pierwszy wiersz ma nu-
mer 0 ostatni zaś rowcount-1)
integer
cells[i,j]
stringgrid
zawartość komórki w i-tej kolumnie i j-tym
wierszu
string
rows[i]
stringgrid
lista napisów w całym wierszu gridu
lista
stringów
itemindex
radiogroup
wskaźnik zanaczonej pozycji przełączników
integer
Zestawienie zdarzeń wykorzystanych w przykładzie:
Obiekt
Zdarzenie
Objaśnienie
przycisk
onClick
zainicjowanie obliczeń
I.8 Instrukcje iteracyjne - repeat ... until
Inna spośród instrukcji iteracyjnych repeat ... until, ma następującą postać:
repeat
instrukcja
until warunek;
Instrukcja repeat wymusza powtórzenie fragmentu programu zawartego pomiędzy
słowami repeat i until tak długo jak długo warunek pozostaje fałszywym. Ograni-
czenia w postaci słów repeat i until pełnią dodatkową rolę nawiasów programo-
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 43
wych. Z tego powodu powtarzana instrukcja może stanowić fragment programu
dowolnej wielkości. Skrajny przypadek stanowi instrukcja pusta, konstrukcja repeat
until keypressed; jest tu dobrym przykładem znanym zapewne użytkownikom Turbo
Pascala. Instrukcja ta wprowadzała program w stan jałowego oczekiwania na naci-
śnięcie przez użytkownika dowolnego klawisza na klawiaturze. Naciśnięcie powo-
dowało zmianę funkcji boolowskiej keypressed na true i tym samym przerwanie
iteracji.
Z zasady konstrukcji tej instrukcji wynika, że testowanie warunku odbywa się po
fakcie realizacji instrukcji czy fragmentu programu. Z tej przyczyny jest ona wyko-
nywana przynajmniej raz, jest to cecha, która wyróżnia tą konstrukcję wśród
wszystkich instrukcji iteracyjnych.
Instrukcja iteracyjna repeat ... until warunek; kończy swoje działanie
gdy warunek jest prawdziwy. Aby nie dopuścić do nieskończonej reali-
zacji takiej pętli programowej należy w powtarzanym fragmencie za-
wrzeć instrukcje wpływające na zmianę wartości warunku, jeżeli ten
ostatni nie zależy od stanu zachodzącego w otoczeniu komputera, patrz
konstrukcja repeat until keypressed;.
I.9 Ćwiczenie 5
Przygotować formę o wyglądzie po-
kazanym na rysunku 20. Zawiera ona
dwie etykiety (label), dwa pola edy-
cyjne (edit), panel z przyciskiem.
Naciśnięcie przycisku powinno zaini-
cjować obliczenie wartości funkcji
trygonometrycznej sinus dla kąta,
którego wielkość wyrażona w stop-
niach podana jest w górnym oknie
edycyjnym. Wynik należy umieścić w
dolnym oknie.
Funkcję sinus obliczamy na podstawie
jej rozwinięcia w szereg potęgowy:
...
!
7
!
5
!
3
!
1
)
sin(
7
5
3
+
−
+
−
≈
x
x
x
x
x
Obliczenia należy kontynuować tak
długo aż wartość bezwzględna ostat-
niego wyrazu rozwinięcia będzie mniejsza od pewnej stałej.
Rysunek 20 Wygląd formy do ćwiczenia 5
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 44
Przykładowa procedura obliczająca wartość funkcji sinus może wyglądać następują-
co:
procedure TForm1.Button1Click(Sender: TObject);
var kolejna_potega, sinus, kolejna_silnia, x : real;
n, znak : integer;
parzysta : boolean;
const
NAJMNIEJSZY_WYRAZ
=
0.00001;
WSP
=
57.3;
begin
x:=strtofloat(edit1.text)/WSP;
kolejna_silnia:=1;
n:=1;
sinus:=0;
znak:=0; if x<0 then znak:=1;
kolejna_potega:=abs(x);
parzysta:=false;
repeat
if parzysta then
sinus:=sinus-kolejna_potega/kolejna_silnia
else
sinus:=sinus+kolejna_potega/kolejna_silnia;
parzysta:= not parzysta;
kolejna_potega:=kolejna_potega*x*x;
kolejna_silnia:=kolejna_silnia*(n+1)*(n+2);
n:=n+2;
until kolejna_potega/kolejna_silnia<NAJMNIEJSZY_WYRAZ;
if znak = 0 then
edit2.text:=floattostrf(sinus,ffFixed,6,2)
else
edit2.text:=floattostrf(-sinus,ffFixed,6,2)
end;
W pokazanym przykładzie pojawiła się deklaracja stałej poprzedzona słowem klu-
czowym const. Dobrą praktyką jest stosowanie tak zdefiniowanych stałych zamiast
umieszczania w kodzie liczb i innych nie zmieniających się obiektów. Znaczenie
obydwu sposobów jest bardzo podobne ale użycie stałych jest bardziej eleganckie i
poprawia elastyczność algorytmu. Zmiana wartości takiej stałej jest ograniczona do
miejsca deklaracji. Dobrze jest również wyróżnić identyfikatory stałych a to ze
względu na niemożliwość stosowania stałych wszędzie tam gdzie występują zmien-
ne (stwierdzenie odwrotne jest również prawdziwe), na przykład z lewej strony in-
strukcji podstawienia. Z tego powodu często identyfikatory stałych zapisuje się du-
żymi literami.
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 45
I.10 Instrukcje iteracyjne - while
Instrukcja iteracyjna while, ma następującą postać:
while warunek do instrukcja;
Instrukcja while wymusza powtórzenie instrukcji (prostej lub złożonej objętej na-
wiasami programowymi) tak długo jak długo warunek pozostaje prawdziwy. W
odróżnieniu od instrukcji repeat testowanie warunku ma miejsce przed realizacją
powtarzanej instrukcji. Oznacza to, że fałszywy warunek spowoduje rezygnację z jej
wykonania. W przeciwieństwie do wspomnianej instrukcji iteracyjnej repeat powtó-
rzenie uwarunkowane jest prawdziwością warunku a nie jak poprzednio fałszem.
Instrukcja iteracyjna while warunek do ...; kończy swoje działanie gdy
warunek jest fałszywy. Aby nie dopuścić do nieskończonej realizacji ta-
kiej pętli programowej należy w powtarzanym fragmencie zawrzeć in-
strukcje wpływające na zmianę wartości warunku, jeżeli ten ostatni nie
zależy od stanu zachodzącego w otoczeniu komputera, na przykład kon-
strukcja while not keypressed do;.
I.11 Ćwiczenie 6
W ćwiczeniu zostanie wykorzystany algorytm Euklidesa obliczanie największego
wspólnego dzielnika dwóch liczb naturalnych. Algorytm ten wykorzystuje operator
mod obliczania reszty z dzielenia całkowitego, można przedstawić w postaci:
1.
wprowadź liczby a i b,
2.
dopóki a i b są większe od zera, powtarzaj:
a)
jeżeli a>b to przyjmij a równe a mod b, w przeciwnym przypadku
przyjmij b równe b mod a,
3.
największy wspólny podzielnik równy liczbie większej od zera.
Fragment programu, który realizuje drugi krok algorytmu może być następujący:
while (a>0) and (b>0) do
if a>b then a:=a mod b else b:=b mod a;
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 46
Przygotować formę o wyglądzie pokaza-
nym na rysunku 21. Zawiera ona trzy
etykiety (label), trzy pola edycyjne
(edit), panel i przycisk. Naciśnięcie
przycisku powinno zainicjować oblicze-
nie największego wspólnego dzielnika
zgodnie z przedstawionym powyżej al-
gorytmem Euklidesa. Wynik obliczeń
należy umieścić w dolnym polu edycyj-
nym. Wartości zmiennych A i B należy
pobrać odpowiednio z górnego i środ-
kowego pola edycyjnego. Odpowiednie
pola opatrzono etykietami.
Przykładowa procedura obliczająca naj-
większy wspólny dzielnik może wyglą-
dać następująco:
procedure TForm1.Button1Click(Sender: TObject);
var a, b, nwd : integer;
begin
// *********************************************
// Obliczanie najwi
ę
kszego wspólnego podzielnika
// *********************************************
a:=strtoint(edit1.text);
b:=strtoint(edit2.text);
while (a>0) and (b>0) do
if a>b then a:=a mod b else b:=b mod a;
nwd:=a+b;
edit3.text:=inttostr(nwd)
end;
I.12 Operacje continue i break
Trzy powyżej omówione konstrukcje iteracyjne pozwalają na powtórzenie określo-
nego fragmentu programu albo zadaną liczbę razy (for) lub też powtórzenia zależą
od wartości pewnego warunku. Daje to dosyć sztywny schemat postępowania, w
niektórych przypadkach mogą zaistnieć warunki aby przejść do kolejnego kroku
iteracyjnego z pominięciem określonego fragmentu kodu. Pożądaną byłaby instruk-
cja kontynuj w kolejnym kroku (continue). Jeszcze w innych okolicznościach pro-
gramista zechciałby zakończyć proces iteracyjny mimo, że normalnie dalej mógłby
być on kontynuowany, potrzebna jest więc operacja przerwij (break). Jako przykład
rozważmy problem przeszukiwania liniowego, w którym chodzi o odpowiedź na
pytanie czy w zbiorze istnieje element o określonej wartości. Napotkanie pierwszego
Rysunek 21 Wygląd formy do ćwiczenia 6
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 47
odpowiedniego elementu powinno zakończyć poszukiwanie. Podobne przeszukiwa-
nie realizuje poniżej podany fragment kodu:
for i:=0 to lista.count-1 do
if pos(nazwa,lista.strings[i])<>0 then
begin
pozycja:=i;
break
end;
Przeszukuje on obiekt o nazwie lista celem wyszukania w nim napisu zawierającego
ciąg znakowy związany z identyfikatorem nazwa. Funkcja pos(x,y) zwraca wartość
różną od zera jeżeli pierwszy parametr x występuje w drugim y. W momencie na-
potkania odpowiedniego wiersza na liście napisów rejestrowany jest numer tego
wiersza
pozycja:=i;
oraz realizowana operacja break. Właśnie ona powoduje przerwanie dalszych po-
wtórzeń wymuszanych przez instrukcję for a związanych z osiągnięciem przez
zmienną i wartości pokrywającej się z liczbą elementów listy lista.count.
Takie same zastosowanie ma operacja break do instrukcji while oraz repeat. Iteracje
są przerywane mimo, że warunki ich kontynuowania są spełnione.
Następny fragment programu zawiera operację continue.
var i: integer;
begin
for i := 0 to (ListaPlikow.Items.Count - 1) do
begin
if not FileExists(ListaPlikow.Items.Strings[i]) then
Continue;
Listaistniejacych.Items.Add(ListaPlikow.strings[i]);
end;
end;
Zadaniem tego programu jest pobieranie nazw plików z listaplikow i w przypadku
stwierdzenia istnienia odpowiedniego zbioru, FlieExists zwraca wtedy wartość true,
ma miejsce wpisanie go na listę istniejących listaistniejacych.items.add. Fałszywa
wartość funkcji FlieExists powoduje przejście do następnego elementu listy.
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 48
I.13 Ćwiczenie 7 - Obliczenie wartości wyznacznika
Problem obliczenia wyznaczni-
ka pojawia się w wielu nume-
rycznych algorytmach. Jeżeli
zaś chcemy obliczyć wyznacz-
nik to trudno nie skorzystać z
metody eliminacji Gauess’a.
Polega ona na przekształceniu
macierzy wyjściowej do postaci
trójkątnej to znaczy takiej, która
posiada elementy o wartościach
zerowych pod główną przekąt-
ną. Przekształcenie musi za-
chować właściwości macierzy w
tym wartość wyznacznika musi
pozostać bez zmian. Wartość
wyznacznika macierzy trójkąt-
nej jest iloczynem elementów
leżących na głównej przekątnej.
W kolejnych krokach iteracji
dobierane są współczynniki tak
aby na wybranej pozycji poja-
wiło się zero. W pierwszym
kroku dla macierzy o początko-
wych elementach pokazanych
na rysunku 22 dobieramy współczynnik równy -a
21
/a
11
i do elementów wiersza dru-
giego dodajemy odpowiednie z wiersza pierwszego mnożąc je przez ten współczyn-
nik. Wynik pokazano na następnym rysunku 23. Cała operacja, uzasadniona mate-
matycznie, doprowadziła do zmiany elementów wiesza drugiego. Co najważniejsze
w pierwszej kolumnie pojawiło się zero. Jest to pojedynczy krok na drodze do prze-
kształcenia całej macierzy. Krok ten realizuje następujący fragment programu:
k:=2;
ab[k,1]:=0;
wspol:=-ab[k,1]/ab[1,1];
for i:=k to l_e do ab[k,i]:=ab[k,i]+wspol*ab[1,i]
przy czym l_e odpowiada liczbie wieszy (kolumn) macierzy o nazwie ab.
a
11
a
12
a
13
a
14
a
15
a
21
a
22
a
23
a
24
a
25
a
31
a
32
a
33
a
34
a
35
a
41
a
42
a
43
a
44
a
45
a
51
a
52
a
53
a
54
a
55
Rysunek 22 Macierz – stan początkowy
a
11
a
12
a
13
a
14
a
15
a
21
-
a
21
/a
11
*
a
11
= 0
a
22
-
a
21
/a
11
*
a
12
= a
1
22
a
23
-
a
21
/a
11
*
a
13
= a
1
23
a
24
-
a
21
/a
11
*
a
14
= a
1
24
a
25
-
a
21
/a
11
*
a
15
= a
1
25
a
31
a
32
a
33
a
34
a
35
a
41
a
42
a
43
a
44
a
45
a
51
a
52
a
53
a
54
a
55
Rysunek 23 Macierz – krok 1
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 49
W drugim kroku dla macierzy o zmodyfikowanych elementach dobieramy współ-
czynnik równy –a
31
/a
11
i do elementów wiersza trzeciego dodajemy odpowiednie z
wiersza pierwszego mnożąc je
przez ten współczynnik. Wynik
pokazano na rysunku 24. W
następnych krokach współ-
czynnik –a
41
/a
11
należy zasto-
sować do modyfikacji wiersza
czwartego, a wyrażenie –a
51
/a
11
posłuży do wprowadzenia zera
do ostatniego wiesza pierwszej
kolumny. W ten sposób w tej
kolumnie poniżej głównej
przekątnej są wartości zerowe.
Jednocześnie kończy to etap
pierwszy przekształcenia. Można go zrealizować modyfikując nieco przytoczony
fragment programu realizujący przekształcenie wiersza drugiego. Program może być
następujący:
for k:=2 to l_e do
begin
ab[k,1]:=0;
wspol:=-ab[k,1]/ab[1,1];
for i:=k to l_e do ab[k,i]:=ab[k,i]+wspol*ab[1,i]
end;
Wynik pokazano na kolejnej ilustracji.
Drugi etap powinien dopro-
wadzić do pojawienia się
wartości zerowych pod głów-
ną przekątną w kolumnie
drugiej. W tym etapie warto-
ści współczynników to kolej-
no:
√
-a
1
32
/ a
1
22
√
-a
1
42
/ a
1
22
√
-a
1
52
/ a
1
22
a
11
a
12
a
13
a
14
a
15
0
a
1
22
a
1
23
a
1
24
a
1
25
a
31
-
a
31
/a
11
*
a
11
= 0
a
32
-
a
31
/a
11
*
a
12
= a
1
32
a
33
-
a
31
/a
11
*
a
13
= a
1
33
a
34
-
a
31
/a
11
*
a
14
= a
1
34
a
35
-
a
31
/a
11
*
a
15
= a
1
35
a
41
a
42
a
43
a
44
a
45
a
51
a
52
a
53
a
54
a
55
Rysunek 24 Macierz – krok 2
a
11
a
12
a
13
a
14
a
15
0
a
1
22
a
1
23
a
1
24
a
1
25
0
a
1
32
a
1
33
a
1
34
a
1
35
0
a
1
42
a
1
43
a
1
44
a
1
45
0
a
1
52
a
1
53
a
1
54
a
1
55
Rysunek 25 Przekształcona macierz – koniec etapu
pierwszego
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 50
Trzeci etap, z kolei, powinien doprowadzić do pojawienia się wartości zerowych
pod główną przekątną w ko-
lumnie trzeciej. W tym etapie
wartości współczynników to
kolejno:
√
-a
2
43
/ a
2
33
√
-a
2
53
/ a
2
33
Ostatni, dla tego rozmiaru
macierzy, czwarty etap powi-
nien doprowadzić do poja-
wienia się wartości zerowych
pod główną przekątną w ko-
lumnie czwartej, jest tam
tylko jeden element. W tym
etapie wartość współczynnika wynosi:
√
-a
3
54
/ a
3
44
W wyniku realizacji kolejnych kroków przekształcania macierzy na głównej prze-
kątnej mogą pojawić się zera. Oznacza to, że w kolejnych etapach obliczenie warto-
ści kolejnych współczynników nie będzie możliwe. Sytuacja taka nie oznacza jesz-
cze, że macierz ma wartość wyznacznika równą zero czyli jest macierzą osobliwą.
Stwierdzenie tego faktu zależy od poszukiwania wiersza z odpowiednim elementem
różnym od zera. Poszukiwanie prowadzi się od elementu na głównej przekątnej w
dół. Znalezienie właściwego wiersza powinno spowodować zamianę z tym, który na
głównej przekątnej ma wartość zero. Negatywny wynik poszukiwań jest jedno-
znaczny z uznaniem przekształ-
canej macierzy za osobliwą.
Poniżej zostanie pokazany kod
aplikacji, która przekształca
zadaną macierz korzystają z
opisanej metody. Realizacja
przekształcenia przebiega eta-
powo, numer etapu wskazywa-
ny jest przez specjalne pole
edycyjne. Przedstawiony kod
nie zawiera kontroli elementów
na głównej przekątnej, dlatego
obliczanie kolejnych współczynników może zakończyć się niepowodzeniem.
Przygotować formę o wyglądzie pokazanym na rysunku 28. Zawiera ona kratkę
napisów oraz przewijane pole edycji (spinedit). Komponent ten wyposażony jest w
dwie strzałki służące do krokowej zmiany zawartości w zakresie ograniczonym
przez właściwości MinValue i MaxValue. Naciśnięcie przycisku powinno zainicjo-
wać obliczenia wartości wyznacznika na etapie wskazywanym w polu edycyjnym.
a
11
a
12
a
13
a
14
a
15
0
a
1
22
a
1
23
a
1
24
a
1
25
0
a
1
32
-
a
1
32
/a
1
22
*
a
1
22
=0
a
1
33
-
a
1
32
/a
1
22
*
a
1
23
=a
2
33
a
1
34
-
a
1
32
/a
1
22
*
a
1
24
=a
2
34
a
1
35
-
a
1
32
/a
1
22
*
a
1
25
=a
2
35
0
a
1
42
a
1
43
a
1
44
a
1
45
0
a
1
52
a
1
53
a
1
54
a
1
55
Rysunek 26 Przekształcanie macierzy – krok 1, etap
2.
a
11
a
12
a
13
a
14
a
15
0
a
1
22
a
1
23
a
1
24
a
1
25
0
0
a
2
33
a
2
34
a
2
35
0
0
a
2
43
a
2
44
a
2
45
0
0
a
2
53
a
2
54
a
2
55
Rysunek 27 Przekształcona macierz – koniec
etapu drugiego
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 51
Kod aplikacji realizującej krokowe
przekształcenie zadanej macierzy
podano poniżej. Zwraca uwagę
sposób przypisania wartości ele-
mentom macierzy, zastosowano
konstrukcję opatrzoną słowem
kluczowym const:
const ab : t_ab =
((6,4,3,7),
(7,9,6,4),
(4,8,9,3),
(3,5,9,1));
W zasadzie słowo to poprzedza
deklarację elementów mających
charakter niezmiennych, stałych.
Wtedy jednak odpowiedni frag-
ment deklaracji nie zawiera specy-
fikacji typu. Na przykład deklaracja:
const STALA = 57.2958;
Wprowadza do programu element STALA o stałej wartości. Przypadek, ze specyfi-
kacją typu, służy do określenia wartości początkowych.
unit wyznacz;
interface
uses
Windows,
Messages,
SysUtils,
Classes,
Graphics,
Controls,
Forms,
Dialogs,
StdCtrls,
Spin,
Grids,
ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
StringGrid1: TStringGrid;
SpinEdit1: TSpinEdit;
Label1: TLabel;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
end;
var
Form1 : TForm1;
wspol, wyznacznik : real;
const L_E=4;
type t_ab = array[1..L_E,1..L_E] of real;
Rysunek 28 Wygląd formy do ćwiczenia 7
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 52
const ab : t_ab =((6,4,3,7),
(7,9,6,4),
(4,8,9,3),
(3,5,9,1));
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var i,j : integer;
begin
for i:=1 to L_E do
for j:=1 to L_E do stringgrid1.cells[j-1,i-1] :=
format('%7s',[floattostrf(ab[i,j],fffixed,6,2)])
end;
procedure TForm1.Button1Click(Sender: TObject);
var i,j,k : integer;
begin
j:=spinedit1.value;
for k:=j+1 to L_E do
begin
wspol:=-ab[k,j]/ab[j,j];
ab[k,j]:=0;
for i:=k to L_E do ab[k,i]:=ab[k,i]+wspol*ab[j,i]
end;
formcreate(nil);
spinedit1.value:=spinedit1.value+1;
if spinedit1.value=l_e then
button1.enabled:=false
else
begin
panel1.caption:='realizuj nastepny etap';
exit
end;
wyznacznik:=1;
for i:=1 to L_E do wyznacznik:=wyznacznik*ab[i,i];
panel1.caption:='Wyznacznik = '+ floattostrf(
wyznacznik,fffixed,10,2);
end;
end.
W ramach rozwinięcia ćwiczenia należy wprowadzić macierz wyrazów wolnych i
rozwiązać układ równań liniowych.
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 53
I.14 Rozwiązanie równań różniczkowych
Zachowanie się układów fizycznych można opisać za pomocą równań różnicz-
kowych. Taki model najczęściej naśladuje mniej lub bardziej wiernie obiekt rzeczy-
wisty. W praktyce często pojawia się potrzeba znalezienia odpowiedniego modelu.
Większość modeli została już opracowana zaś zadaniem inżyniera jest interpretacja
uzyskiwanych wyników. Przykładem niech będzie model opisujący zmiany wartości
prędkości statku po zmianie charakterystyki napędu. Model ten jest oparty na ukła-
dzie równań różniczkowych. Dla przykładu rozważmy równanie drugiego rzędu
postaci y’’=f(x,y,y’). Jedna z metod numerycznego rozwiązania zwana metodą Run-
gego-Kutty wykorzystuje następujący schemat obliczeniowy:
x
y
v
1
=h*y’
k=h
2
/2*
f(x,y, v
1
/h)
poprawka i uwagi
x
0
y
0
v
10
k
1
x
0
+h/2
y
0
+ v
10
/2+k
1
/4 v
10
+ k
1
k
2
k=( k
1
+ k
2
+ k
3
)/3
x
0
+h/2
y
0
+ v
10
/2+k
1
/4 v
10
+ k
2
k
3
x
0
+h
y
0
+ v
10
+k
3
v
10
+ 2*k
3
k
4
k’=
(k
1
+2*k
2
+2*k
3
+k
4
)/3
x
1
=x
0
+h
y
1
=y
0
+ v
10
+k
v
11
= v
10
+k’
przyjąć w następnym kroku
I.15 Ćwiczenie 8
Kolejne ćwiczenie będzie poświę-
cone rozwiązaniu równania różniczko-
wego stopnia drugiego zgodnie ze
schematem podanym powyżej. Wyniki
obliczeń będą przedstawiane w postaci
liczbowej i graficznej. W tym celu
trzeba przygotować formę jak na ry-
sunku 30. Forma to składa się z dwóch
stron wybieranych za pomocą zakła-
dek. Podział na strony możliwy jest
dzięki komponentowi pagecontrol,
który dostępny jest w zestawie Win32.
Po wprowadzeniu na formę komponent
ten przybiera postać pustego prostoką-
ta. Stanowi on bazę dla umieszczanych
na nim stron. Celem wprowadzenia
nowej strony należy przywołać pod-
ręczne menu przez naciśnięcie prawego
Rysunek 29 Początkowa faza
przygotowania formy zawierającej więcej
niż jedną stronę
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 54
przycisku myszki nad piktogramem komponentu i wybrać pozycję newpage. Sytu-
acja jaka powstanie na ekranie pokazana jest na rysunku 29. Na obiekcie pagecon-
trol utworzony został nowy o nazwie tabsheet1, właśnie na nim należy umieścić
wszystko to co ma się pokazywać na danej stronie. Strona ta dostępna będzie auto-
matycznie w momencie wybrania utworzonej zakładki. W momencie tworzenia
strony etykieta odpowiada nazwie nowego obiektu, dostępnego na liście inspektora,
w tym przypadku tabsheet1. Tytuł ten kryje się pod właściwością caption, którą
należy zmienić tak aby oddawała zawartość strony. Kolejne strony dokładamy w
miarę potrzeb korzystając z podręcznego menu i wybierając opcję newpage.
Aby zbudować formę złożoną z wielu stron należy:
√
na formę przenieść komponent pagecontrol,
√
nad piktogramem tego komponentu kliknąć prawym
przyciskiem myszki, a z ukazującego się podręcznego
menu wybrać pozycję New Page, operację powtórzyć
tyle razy ile potrzeba stron. W ten sposób za każdym
razem na komponencie pagecontrol umieszczane są
nowe komponenty klasy Ttabsheet
√
wszystkie komponenty, które w dalszej kolejności będą
umieszczane na stronach (na komponentach klasy
Ttabsheet) będą widoczne tylko przy uaktywnieniu
odpowiedniej strony.
√
wszystkie komponenty, które umieszczono bezpośred-
nio na formie będą widoczne zawsze, niezależnie od
prezentowanej strony.
W proponowanym ćwiczeniu będą wykorzystywane dwie strony (patrz rysunek 30).
Jedna opisana jako równanie różniczkowe druga wykres. Na stronie z etykietą rów-
nanie różniczkowe należy umieścić etykiety i opisywane przez nie pola edycyjne
służące do jednoznacznego zdefiniowania warunków początkowych i parametrów
wykorzystywanych podczas rozwiązania. Dodatkowo, w celu numerycznej prezenta-
cji wyników obliczeń, umieszczono na omawianej stronie kratkę. Zawierać ona bę-
dzie skalkulowane wartości zmiennych x, y i pierwszej pochodnej y’. Dane te zosta-
ną wykorzystane do sporządzenia wykresu.
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 55
Komponent o nazwie
Chart, dostępny w zesta-
wie komponentów Addi-
tional umieszczono na
stronie z zakładką wykres.
Komponent ułatwiający
tworzenie wykresów Chart
jest bardzo rozbudowany i
daje programiście duże
możliwości prezentacji
swoich wyników. Z punktu
widzenia tego ćwiczenia
istotnym jest opracowanie
właściwości komponentu
Chart o nazwie serieslist.
Formę służącą do dodawa-
nia, usuwania i formato-
wania prezentowanych na
wykresie serii wyników
pokazano na rysunku 31.
Najważniejszym jest aby
dodać do wykresu jedną
serię wyników. Precyzyj-
nie rzecz ujmując musimy
utworzyć obiekt, któremu
po rozwiązaniu równania
przekazane zostaną wyniki
do wykreślenia. Bez tego
cała aplikacja nie będzie
funkcjonowała poprawnie.
Poniżej pokazano kod
procedury całkowania
równania różniczkowego,
którego postać zawarta jest
w funkcji calkowana. Re-
alizacja procedury rozpo-
czyna się od odczytania
parametrów umieszczo-
nych w polach edycyjnych
i definiujących parametry
procesu iteracyjnego. Ko-
lejny zestaw instrukcji
rozwiązuje równanie
zgodnie z przedstawionym
wyżej schematem. Wyniki obliczeń umieszczane są w tablicy wyniki zadeklarowa-
nej w części interfejsu modułu. Zadeklarowane tam zmienne mogą być używane
Rysunek 30 Wygląd formy do ćwiczenia 8
Rysunek 31 Wygląd formy służącej opracowaniu
właściwości SeriesList komponentu klasy TChart
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 56
przez inne procedury, w tym przypadku będzie z nich korzystał podprogram odpo-
wiedzialny za wykres wyników. Podobnie należy zadeklarować zmienną zakres
zawierająca liczbę punktów rozwiązania.
procedure TForm1.BitBtn1Click(Sender: TObject);
var
i
: integer;
yprim,x0,y0,v10,krok,krok2,k,k1,k2,k3,k4,kp : single;
function calkowana( a,b,c : single): single;
begin
result:=b-2*a/b+c
end;
begin
zakres:=strtoint(edit4.text);
stringgrid1.rowcount:=zakres+1;
krok:=strtofloat(edit5.text);
yprim:=strtofloat(edit3.text);
y0:=strtofloat(edit2.text);
wyniki[1,1]:=strtofloat(edit1.text);
wyniki[1,2]:=y0;
wyniki[1,3]:=yprim;
stringgrid1.cells[0,1]:=
floattostrf(wyniki[1,1],fffixed,6,2);
stringgrid1.cells[1,1]:=
floattostrf(wyniki[1,2],fffixed,6,2);
stringgrid1.cells[2,1]:=
floattostrf(wyniki[1,3],fffixed,6,2);
for i:=2 to zakres do
wyniki[i,1]:=wyniki[i-1,1]+krok;
krok2:=krok*krok/2;
v10:=krok*yprim;
for i := 2 to zakres do
begin
x0 := wyniki[i-1,1];
k1 := krok2*calkowana(x0,y0,v10/krok);
k2 := krok2 *
calkowana(x0+krok/2,y0+v10/2+k1/4,(v10+k1)/krok);
k3 := krok2*
calkowana(x0+krok/2,y0+v10/2+k1/4,(v10+k2)/krok);
k4 := krok2*calkowana(x0+krok,y0+v10+k3,v10+2*k3/krok);
k := (k1+k2+k3)/3;
kp := (k1+2*k2+2*k3+k4)/3;
y0 := y0+v10+k;
wyniki[i,2]:=y0;
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 57
v10 := v10+kp;
wyniki[i,3]:=v10/krok;
stringgrid1.cells[0,i]:=floattostrf(x0,fffixed,6,2);
stringgrid1.cells[1,i]:=floattostrf(y0,fffixed,6,2);
stringgrid1.cells[2,i]:=
floattostrf(v10/krok,fffixed,6,2);
end;
end;
Kod procedury prezentacji wyników może być następujący:
procedure TForm1.rysujClick(Sender: TObject);
var i : integer;
begin
seriapierwsza.clear;
for i:=1 to zakres do
seriapierwsza.addxy(wyniki[i,1],wyniki[i,2],
floattostrf(wyniki[i,1],fffixed,4,1),clRed)
end;
Procedura „odpowiedzial-
na” za sporządzenie wy-
kresu jest bardzo prosta.
Pierwszą operacją jest wy-
czyszczenie serii wyników
(kolejne wywołania mo-
głyby kumulować punkty
prezentowanego wykresu).
Drugim krokiem jest prze-
kazanie wartości skalkulo-
wanych zmiennych zależ-
nych y, korzysta się przy
tym z metody o nazwie
addxy. Parametrami są:
współrzędne pobrane z
tablicy wyniki, opis poja-
wiający się na osi odcię-
tych (współrzędna x) oraz
kolor wykresu.
Ostateczny efekt realizacji
aplikacji, na stronie Wykres, pokazano na rysunku 32.
Rysunek 32 Wykres wyników obliczeń
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 58
Zestawienie metod i właściwości komponentów wykorzystanych w przykładzie:
Obiekt
Metoda /
właści-
wość
Typ wyniku lub
typ właściwości
Objaśnienie
Chart
Serieslist
Tchartserieslist lista obiektów przechowujących parametry
poszczególnych serii wyników pokazywanych
na wykresie
Series
addxy
całkowity
dodanie punktu do wykresu
Series
clear
wyczyszczenie serii prezentowanej na wykresie
PageControl
align
Talign
wyrównanie piktogramu komponentu
TabSheet
caption
string
zawiera napis na zakładce strony
Zestawienie zdarzeń wykorzystanych w przykładzie:
Obiekt
Zdarzenie
Objaśnienie
przyciski
onClick
zainicjowanie obliczeń
forma
onCreate
utworzenie opisów gridu
Rekordy i pliki
Rekord jest strukturą złożoną z pewnej liczby elementów składowych. Każdy z
elementów, zwany także polem rekordu, może być praktycznie dowolnego typu.
Zarówno nazwy jak i typy pól muszą być określone w programie. Wykorzystuje się
do tego celu konstrukcję:
type
nazwa_typu_rekordowego = record
{wykaz pól}
end;
Jako przykład można przytoczyć często stosowaną konstrukcję rekordową przy ob-
słudze elementów graficznych. Zdefiniowany jest podstawowy typ opisujący poje-
dynczy punkt:
type
Tpoint = record
x,y : integer
end;
Typ opisujący współrzędne narożników wykorzystuje punkt, a deklaracja rekordu
może być następująca :
type
Tnarozniki = record
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 59
lewy_gorny,
prawy_gorny,
lewy_dolny,
prawy_dolny : Tpoint
end;
Wynika z nich, że pole jednego rekordu może być innym rekordem. Typ opisujący
załadowany ładunek może wyglądać następująco:
type
Tladunek = record
nazwa : string[20];
waga,
x, y, z : single;
end;
W takim rekordzie zapisywane są nazwa ładunku oraz jego parametry w postaci
wagi oraz współrzędnych (x, y, z) środka masy. Z kolei rekord opisujący parametry
statku pustego to na przykład:
type
Tstatekpusty = record
ciezar,
lpp,
xg,
zg: single;
end;
Dostęp do pól w rekordzie wymaga wykorzystania stosowanej już w odniesieniu do
komponentów, notacji typu:
nazwa_zmiennej_rekordowej.nazwa_pola
Notacja ta oparta jest na specyfikacji nazwy zmiennej typu rekordowego po czym
następuje kropka i nazwa pola. Na przykład:
statekpusty.lpp:=134.7;
Możliwe jest zagnieżdżenie w przypadku kiedy pole jest rekordem.
Alternatywnie dla powyższej notacji można stosować instrukcję with w postaci:
with zmienna_rekordowa do instrukcja;
Instrukcja ta otwiera dostęp do wnętrza rekordu (również obiektu). Dlatego nastę-
pujące podstawienie:
with statekpusty do lpp:=134.7;
nie powinno budzić wątpliwości, że dotyczy pola lpp w rekordzie statekpusty. W
przypadku operacji na pojedynczym polu zysk wynikający z „otwarcia” struktury
rekordu za pomocą instrukcji with jest niedostrzegalny. Inaczej się sprawa przedsta-
wia w przypadku większych struktur i całych zestawów operacji. Patrz ćwiczenie 9.
Rekordy mogą być związane z plikami. Deklaracja:
plik : file of Tladunek;
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 60
oznacza zmienną plikową o strukturze złożonej z elementów opisujących ładunki
zgodnie ze specyfikacją podaną wyżej. Plik może być używany po związaniu
zmiennej plikowej z konkretną pozycją katalogu. Do tego służy operacja assignfile:
assignfile(zmienna_plikowa,savedialog1.filename);
Kolejnym krokiem, na drodze do korzystania z pliku, jest przygotowanie go do zapi-
su lub odczytu, operacje reset(zmienna_plikowa) lub rewrite(zmienna_plikowa).
Operacja rewrite niszczy poprzednią zawartość pliku jeżeli taki istniał, tworzy nowy
jeżeli to konieczne. Natomiast reset ma zastosowanie tylko do udostępniania istnie-
jących zbiorów a jej działanie polega głównie na ustawieniu wskaźnik pliku na
pierwszym rekordzie.
Po zrealizowaniu wymienionych operacji plik jest gotowy do zapisu lub odczytu za
pomocą instrukcji write(zmienna_plikowa, ...) lub read(zmienna_plikowa, ...). Każ-
dy zapis czy odczyt przemieszcza wskaźnik danych do początku następnego rekor-
du. Wskaźnik ten można też umieszczać na żądanej pozycji za pomocą funkcji
seek(zmienna_plikowa, pozycja).
Po zakończeniu operacji na pliku powinien być on zamknięty za pomocą
closefile(zmienna_plikowa).
Aby użyć pliku do przechowywania zbioru rekordów należy:
1. zdefiniować typ rekordowy i zadeklarować odpowiednią zmienną,
2. zadeklarować zmienną plikową wykorzystującą zdefiniowany typ re-
kordowy,
3. powiązać zmienną plikową z konkretnym plikiem na dysku – opera-
cja assignfile(...)
4. przygotować plik do pracy – operacje rewrite lub reset,
5. wykonać operacje zapisu lub odczytu – write, read,
6. zamknąć plik, wywołując procedurę closefile (...)..
Tak jak w przypadku tablic, polom rekordów można przypisać wartości początkowe
wykorzystując dyrektywę const ze specyfikacją typu, na przykład:
const
statekpusty : Tstatekpusty =(ciezar : 2345;
lpp : 118.5;
xg
: 123.34;
zg
: 11.34);
Określa zbiór konkretnych parametrów. Charakterystycznym jest tu wskazywanie
nazwy pola po której następuje specyfikacja wartości poprzedzona dwukropkiem.
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 61
I.16 Ćwiczenie 9 - Obliczenie parametrów stateczności
Kolejne ćwiczenie wykorzystuje struktury rekordowe do obliczenia parametrów
obciążonego statku. Ćwiczenie wykorzystuje zbiory danych o załadowanych cięża-
rach oraz o parametrach hydrostatycznych. Liczba danych jest na tyle duża, że ce-
lowym jest wykorzystanie plików dyskowych do ich przechowywania. Pliki mogą
mieć organizację tradycyjną uwzględniającą struktury rekordów albo też przecho-
wywać dane w postaci tabel. Ta druga organizacja jest typowa dla systemów baz
danych, które dostarczają odpowiednich narzędzi do ich tworzenia, edycji i innych
operacji. W pierwszej wersji czytelnik będzie miał okazję poznać tradycyjne podej-
ście do operacji plikowych. W drugiej zaś będzie tworzył podstawowe elementy baz
danych jakimi są tabele. Użycie elementów typowych dla systemów baz danych
upraszcza korzystanie ze zbiorów informacji. Współczesne środowiska tworzenia
aplikacji dostarczają narzędzi składających się na interfejs z powszechnie wykorzy-
stywanymi systemami. Programista, nawet początkujący, powinien posiadać mini-
malny zakres umiejętności w zakresie manipulowania tabelami.
W ćwiczeniu obliczane zostaną przegłębienie, wysokość metacentryczna oraz prze-
chył obciążonego statku. Podstawowe zależności służące do obliczeń przedstawiono
poniżej.
I.16.1 Obliczenia parametrów stateczności
Na podstawie obliczonego całkowitego ciężaru statku z tablicy danych hydrosta-
tycznych można odczytać wartość zanurzenia T statku na równej stępce. Dodatkowo
można odczytać położenie środka wyporu x
f
, odciętą środka wodnicy x
s
, jednost-
kowy moment przegłębiający M
j
a także wysokość tak zwanego metacentrum z
m
.
Rozkład obciążeń oraz parametry statku pustego pozwalają na kalkulacje współ-
rzędnych środka masy kadłuba. Dla obliczonego wzdłużnego położenia środka cięż-
kości x
g
i ciężaru całkowitego D wielkość przegłębienia t wyznacza się ze wzoru:
j
f
g
M
x
x
D
t
)
(
−
⋅
=
Zanurzenie na pionie dziobowym staku o długości między pionami równej L
pp
wy-
raża się zależnością:
t
T
t L
x
L
d
pp
s
pp
= +
⋅
−
(
)
Zanurzenie na pionie rufowym obliczamy ze wzoru:
t
T
t x
L
r
s
pp
= −
⋅
Przechył obliczamy ze wzoru:
3
.
57
*
*
]
[
mg
D
My
=
°
ϕ
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 62
Przy czym My jest poprzecznym (przechylającym) momentem a mg jest wysoko-
ścią metacentryczną obliczoną jako różnica:
g
m
z
z
mg
−
=
Przykładowe dane hydrostatyczne do wykorzystania w programie można znaleść w
załączniku na końcu opracowania.
I.16.2 Ćwiczenie 9 w wersji z plikami
W ćwiczeniu wykorzystuje się formę z komponentem umożliwiającym przełączanie
stron. Komponent taki o angielskiej nazwie PageControl umożliwia tworzenie apli-
kacji złożonych z wielu stron o różnej zawartości. Poszczególne strony udostępniane
są przy pomocy zakładek.
W zbiorze wykorzystywanych komponentów występują również takie, których pik-
togram nie jest pokazywany w czasie realizacji, komponenty te dostarczają jednak
twórcy aplikacji bardzo użytecznych funkcji. W tym przykładzie komponentami
takiego typu są SaveDialog i OpenDialog, dostępne pod zakładką Dialogs na panelu
głównym środowiska. Pozwalają one na wybór pliku do zapisu lub odczytu.
Komponenty o nazwach SaveDialog i OpenDialog posiadają funkcje execute, której
realizacja wiąże się z wyświetleniem, dobrze znanego użytkownikom komputerów,
okna dialogowego wyboru plików. Za pomocą tego okna można swobodnie prze-
mieszczać się po strukturze katalogów i wybrać potrzebną pozycję lub też wskazać
katalog i nazwę nowego pliku. Funkcja ta zwraca wartość false jeżeli użytkownik
Rysunek 33 Wygląd formy do ćwiczenia 9
Niektóre komponenty są niewi-
doczne w czasie realizacji ich rola
polega na dostarczaniu potrzeb-
nych metod
Zakładki udostępniają strony
zawierające różne kompo-
nenty
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 63
zrezygnował z wyboru pliku, nacisnął przycisk cancel (anuluj). Ścieżkę dostępu
wraz z nazwą żądanego pliku zawiera właściwość filename komponentu.
Kratka opisująca ładunki powinien zawierać rekordy w postaci: nazwa, waga, x, y, z
środka masy. Zawartość kratki będzie umieszczana w pliku w celu późniejszego
wykorzystania (przycisk schowaj). Odwrotnie wykorzystując przycisk otwórz za-
wartość odpowiedniego pliku będzie można przenieść do kratki.
Na stronie opatrzonej zakładką dane hydrostatyczne znajduje się inna kratka prze-
znaczony do prezentacji parametrów statku. Parametrami są: zanurzenie, wypór, x
f
,
x
s
, z
m
, z
f
, M
j
. Przykładowy zestaw danych hydrostatycznych umieszczono na końcu
opracowania.
Poniżej pokazano fragment kodu dokonującego przepisania zawartości kratki do
pliku, którego nazwa została określona za pomocą komponentu savedialog1.
if savedialog1.execute then
begin
assignfile(plik,savedialog1.filename);
rewrite(plik);
i:=1;
while stringgrid1.cells[0,i]<>'' do
begin
ladunek.nazwa:=stringgrid1.cells[0,i];
ladunek.waga:=strtofloat(stringgrid1.cells[1,i]);
ladunek.x:=strtofloat(stringgrid1.cells[2,i]);
ladunek.y:=strtofloat(stringgrid1.cells[3,i]);
ladunek.z:=strtofloat(stringgrid1.cells[4,i]);
inc(i);
write(plik,ladunek)
end;
closefile(plik);
end;
Obramowany zestaw instrukcji można zapisać wykorzystując instrukcję with jako:
with ladunek do
begin
nazwa:=stringgrid1.cells[0,i];
waga:=strtofloat(stringgrid1.cells[1,i]);
x:=strtofloat(stringgrid1.cells[2,i]);
y:=strtofloat(stringgrid1.cells[3,i]);
z:=strtofloat(stringgrid1.cells[4,i])
end;
Dalej pokazano fragment kodu dokonującego przepisania zawartości pliku do kratki,
nazwa pliku jest określana za pomocą komponentu opendialog1. Operacje odczytu
powtarzane są aż do napotkania końca pliku (funkcja eof zwraca wartość true w
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 64
przypadku kiedy wskaźnik pliku wskazuje na znacznik końca). Powtarzanie odczytu
realizuje instrukcja iteracyjna:
while not eof(plik) do
Odczyt jednego rekordu przy pomocy operacji:
read(plik,ladunek);
powoduje automatyczne przesunięcie wskaźnika do następnego rekordu. W momen-
cie napotkania znacznika końca pliku funkcja eof(zmienna_plikowa) zwraca wartość
true i proces iteracyjny zostanie przerwany. Kompletny fragment przepisania za-
wartości kratki to:
if opendialog1.execute then
begin
assignfile(plik,opendialog1.filename);
reset(plik);
i:=1;
while not eof(plik) do
begin
read(plik,ladunek);
stringgrid1.cells[0,i]:=ladunek.nazwa;
stringgrid1.cells[1,i]:=floattostr(ladunek.waga);
stringgrid1.cells[2,i]:=floattostr(ladunek.x);
stringgrid1.cells[3,i]:=floattostr(ladunek.y);
stringgrid1.cells[4,i]:=floattostr(ladunek.z);
inc(i)
end;
closefile(plik);
end;
Podczas kreowania formy głównej (zdarzenie onCreate) należy opisać wiersze krat-
ki przedstawiającego wyniki obliczeń. Na stronie parametrów statku pustego musi-
my umieścić odpowiednie dane przechowywane w strukturze rekordowej o nazwie
statekpusty. Deklaracja odpowiedniego typu rekordowego to:
type
Tstatekpusty = record
ciezar,
lpp,
xg,zg : single;
end;
Następujący zestaw instrukcji umieszczony w procedurze związanej z tworzeniem
formy powoduje opisanie kratki zawierającej obliczane parametry oraz wypełnienie
pól edycyjnych zawierających dane statku pustego. W przytoczonych instrukcjach
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 65
wykorzystano metodę add typową dla list stringów (napisów). Kratka (ang. strin-
ggrid) jest zorganizowana w ten sposób, że zawartości poszczególnych wierszy
(również kolumn) są takimi właśnie listami. Każdy napis to zawartość komórki w
wierszu. Lista wiersza pierwszego dostępna jest poprzez rows[1], drugiego rows[2]
i tak dalej. Podobnie sprawa przedstawia się z zawartościami poszczególnych ko-
lumn (właściwość cols[numer]). Listy napisów są puste w momencie utworzenia
kratki.
wyniki_obliczen.rows[0].add('
Obliczone parametry');
wyniki_obliczen.rows[1].add('
Razem ci
ęż
ar
[t]');
wyniki_obliczen.rows[2].add('
Współrz
ę
dna X
[m]');
wyniki_obliczen.rows[3].add('
Współrz
ę
dna Y
[m]');
wyniki_obliczen.rows[4].add('
Współrz
ę
dna Z
[m]');
wyniki_obliczen.rows[5].add('
Zanurzenie dziobu [m]');
wyniki_obliczen.rows[6].add('
Zanurzenie
rufy
[m]');
wyniki_obliczen.rows[7].add('
Metacentrum
[m]');
wyniki_obliczen.rows[8].add('
Przechył
[°]');
edit1.text:=floattostrf(statekpusty.ciezar,fffixed,8,2);
edit2.text:=floattostrf(statekpusty.xg,fffixed,8,2);
edit3.text:=floattostrf(statekpusty.zg,fffixed,8,2);
edit4.text:=floattostrf(statekpusty.lpp,fffixed,8,2);
Zestawienie metod i właściwości komponentów wykorzystanych w przykładzie:
Obiekt
Metoda
Typ wyniku
lub typ wła-
ściwości
Objaśnienie
SaveDialog
Execute
boolowski
wybór pliku do zapisu, funkcja zwraca wartość
false jeżeli użytkownik zrezygnował z wyboru
SaveDialog
filename
string
ścieżka dostępu i nazwa wybranego do zapisu
pliku
OpenDialog
Execute
boolowski
wybór pliku do odczytu, funkcja zwraca wartość
false jeżeli użytkownik zrezygnował z wyboru
OpenDialog
filename
string
ścieżka dostępu i nazwa wybranego do odczytu
pliku
Stringgrid.
rows[i]
add
metoda mająca zastosowanie do wszelkiego typu
kolekcji, tu realizuje dopisanie do listy stringów
wskazywanego ciągu znakowego. Każdy wiersz
(także kolumna) w kratce jest listą stringów,
wszystkie metody tego obiektu mają tu zastoso-
wanie
Zestawienie zdarzeń wykorzystanych w przykładzie:
Obiekt
Zdarzenie
Objaśnienie
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 66
przyciski
onClick
zainicjowanie obliczeń, zapamiętanie oraz odtwo-
rzenie zawartości kratek
forma
onCreate
utworzenie opisów zawartości kratek, wiersze zero-
we na stronach ładunki i dane hydrostatyczne, ko-
lumna zerowa na stronie wyniki obliczeń i umiesz-
czenie parametrów statku pustego na stronie statek
pusty
I.16.3 Ćwiczenie 9 w wersji z tabelami
W powyższej wersji ćwiczenia programista musiał zadbać o szczegóły oprogramo-
wania związanego z zapisem i odczytaniem danych zawartych w plikach. Dane tam
przechowywane nie są dostępne w inny, prostszy sposób. Inaczej sprawa się ma w
sytuacji kiedy korzystamy z dostępnego systemu baz danych, który posiada narzę-
dzia do budowy, modyfikacji, tworzenia zapytań (kwerend) i innych ułatwiających
operowanie na zbiorach danych. Dzięki modułowi zwanemu Database Engine śro-
dowisko Delphi posiada możliwości korzystania z elementów baz danych popular-
nych systemów takich jak Access, Paradox czy innych włącznie z najbardziej za-
awansowanymi jak na przykład Oracle.
Jednym z podstawowych elementów każdej bazy danych jest tabela (ang. table). Jest
to struktura służąca do przechowywania cech charakterystycznych obiektów należą-
cych do określonej klasy. Do realizacji tego ćwiczenia potrzebne są dwie tabele, w
jednej będą przechowywane dane o ładunkach w drugiej dane hydrostatyczne. W
tym celu należy uruchomić aplikację Database Desktop stowarzyszoną ze środowi-
skiem Delphi. Wybranie opcji:
Tools|Database Desktop
uruchomi środo-
wisko zarządzania zbiorami danych.
W ramach tej aplikacji wybór
File|New|Table
umożliwi utworzenie nowej
tabeli. Sposób przechowywania i format danych zależy od systemu bazodanowego.
Z tego powodu należy wybrać odpowiednią pozycję z wyświetlonej listy, można też
zaakceptować system domyślny obowiązujący na danym komputerze. Być może, że
będzie to Paradox 7, po dokonaniu wyboru na ekranie pojawi się forma pokazana na
rysunku 34.
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 67
Na pokazanej formie zarejestrowano pierwszy krok tworzenia tabeli danych o ła-
dunkach. Użytkownik zdecydował się na pole nazwa typu napisowego o długości
maksymalnej równej 50 znaków oraz na pole numeryczne o nazwie waga. W mo-
mencie definiowania typu tego pola posłużył się listą akceptowanych możliwości
dostępną po użyciu prawego przycisku myszki nad polem Type. Lista to pokazana
jest też na rysunku 34, widać, że wybór jest znacznie szerszy jeśli zestawimy go z
podstawowym zestawem typów obowiązującym w Pascalu. Dla potrzeb ćwiczenia
należy wybrać typ numeryczny number. Po określeniu wszystkich pól należy wci-
snąć klawisz save as... i zapamiętać strukturę tabeli. W systemach baz danych od-
powiedni plik, na początku, nie zawiera żadnych danych lecz tylko informacje o
definiowanych polach rekordu danych. Właściwe dane można wpisać korzystając z
Rysunek 34 Wygląd formy aplikacji Database Desktop z otwartym oknem prezentującym zbiór danych
oraz oknem umożliwiającym definiowanie tabeli danych. Pokazano także listę typów wartości pól
Okno specyfikacji pól tabeli
danych
Lista możliwych typów war-
tości pól rekordów tabeli
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 68
opcji
File|Open|Table
, która przywoła odpowiednie okno prezentujące zgro-
madzone dane lub na początku pustą tabelę. Użycie klawisza F9 (lub wciśnięcie
przycisku edycji, na listwie narzędziowej prawa skrajna pozycja) umożliwi edycję
zawartości. Jedna z przygotowywanych tabel zawiera dane o ładunkach inna dane
hydrostatyczne. Okrojoną zawartość tej ostatniej można zobaczyć na drugim planie
rysunku 34 a okno jej edycji na rysunku 35. Dane o ładunkach obejmują: nazwę
towaru, wagę oraz współrzędne (x, y, z) jego środka masy. Wśród danych hydrosta-
tycznych należy zapamiętać kolejno: zanurzenie, wypór, x
f
, x
s
, z
m
, z
f
i m
j
(przykła-
dowy zestaw danych znajduje się w załączniku Błąd! Nie można odnaleźć źródła
odsyłacza.). Utworzone tabele należy umieścić w dostępnym, prywatnym katalogu.
Wygląd formy tworzonej aplikacji pokazano na rysunku 36. Zawiera ona między
innymi dwa komponenty związane z utworzonymi tabelami. Table1 odnosi się do
zbioru danych o ładunkach, Table2 zawiera dane hydrostatyczne. Komponenty
związane z tabelami wystarczą aby w podstawowym zakresie operować danymi. W
ramach takich operacji możliwe jest przeszukiwanie, uaktualnianie zbiorów pod
warunkiem ich oprogramowania. Środowisko Delphi zawiera też cały szereg obiek-
tów operujących na i związanych z elementami przechowywanymi w tabelach. W
języku angielskim określa się je mianem data-aware components. Jeden z takich
komponentów automatycznie wyświetlających zawartość tabeli w kratce (DBGrid)
będzie wykorzystywany w tej aplikacji. Tego typu elementy nie mogą jednak bezpo-
średnio korzystać z komponentów klasy Ttable potrzebny jest łącznik w postaci
obiektu Tdatasource. Komponent Datasource1 związany jest z tabelą pierwszą,
Datasource2 z drugą.
Na stronie z zakładką ładunki należy umieścić komponent o nazwie DBGrid, który
jest dostępny w zestawie komponentów DataControls. Właściwość Datasource tego
komponentu musi wskazywać na odpowiednie źródło danych. Źródłem danych jest
komponent o nazwie Datasource którego zadaniem jest udostępnianie zawartości
tabeli Table. Z tego powodu te niewidoczne w czasie realizacji komponenty wystę-
Rysunek 35 Główne okno środowiska Database Desktop z prezentowaną tabelą
danych hydrostatycznych
Przycisk edycji, w pozycji
wciśniętej zezwala na do-
konywanie zmian
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 69
pują parami. Na przygotowanej formie istnieją dwie pary obiektów (table, datasour-
ce), jedna związana z danymi o ładunkach druga z parametrami hydrostatycznymi.
Proszę zwrócić uwagę na następujący ciąg zależności:
1. Komponent table1 związany jest z plikiem dyskowym zawierającym dane o
ładunkach. Jego właściwość databasename wskazuje odpowiedni katalog, a ta-
blename nazwę pliku.
2. Komponent Datasource1 posiada właściwość Dataset, która należy ustawić na
(z listy rozwijanej wybrać odpowiednią pozycję) table1.
3. Z kolei DBGrid1 na stronie ładunki odwołuje się do Datasource1 poprzez swoją
właściwość o nazwie Datasource.
Komponent datasource stanowi pomost pomiędzy zbiorem danych (na przykład
tabelą) a komponentami służącymi do prezentacji wyników (na przykład DBGrid ale
także innymi). Zawiera on szereg metod i właściwości ułatwiających dostęp do od-
powiednich zbiorów danych. Zmiana stanu tabeli danych, na przykład otwarcie, jest
każdorazowo sygnalizowana, to samo dotyczy uaktualnienia zawartości.
Komponent DBGrid po określeniu właściwości dataset wymaga opracowania wła-
ściwość o nazwie columns. Podwójne kliknięcie pozycji tej właściwości w oknie
inspektora obiektów powoduje wyświetlenie formatki pokazanej na rysunku 37. Za
pomocą dostępnych przycisków dodajemy pola do kratki, podczas tej operacji na-
stępuje powiązanie nazwy pola tabeli z kolejną kolumną komponentu. Zmiana ko-
lejności, usuwanie i inne operacje są dostępne dzięki pozostałym przyciskom. Po
opracowaniu właściwości pierwszy rząd kratki zawiera etykiety odpowiadające na-
zwom pól w tabeli źródłowej. Taka tabela musi istnieć a właściwe komponenty mu-
Rysunek 36 Wygląd formy do ćwiczenia z bazami danych
Te komponenty udostęniają
dane z tabel
Ten komponent to DBGrid, po-
zwala na wyświetlanie i edycję
danych umieszczonych w tabe-
lach
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 70
szą zawierać stosowne odwołania. To co zostało zrobione dla tabeli ładunków nale-
ży powtórzyć dla danych hydrostatycznych.
Poniżej pokazano fragment kodu, w którym wykorzystuje się dane zapisane w tabe-
li. Wszystkie czynności związane z operacjami na danych muszą być realizowane
przy otwartej tabeli. W tym celu należy wykorzystać metodę open właściwego kom-
ponentu. Odpowiednim momentem aby to zrobić może być kreowanie formy lub też
odbywać się może w następstwie zdarzenia klik-
nięcia jakiegoś przycisku. W omawianym przy-
padku korzysta się z tego opatrzonego etykietą
Otwórz. Zakłada się, że jest to jedna z pierw-
szych czynności wykonana przez użytkownika.
Po kliknięciu przycisku Oblicz zapamiętywane
są ewentualne zmiany w prezentowanej kratce
dokonane przez użytkownika. Ta czynność jest
realizowana przez metodę post, jej użycie wy-
maga jednak wprowadzenia tabeli w stan, w
którym akceptuje ona dokonane zmiany jest to
stan edycji. Stąd najpierw przywołano metodę
edit. Następnie wskaźnik tabeli ustawiony na
pierwszym rekordzie
table1.first
. W pętli
while działającej aż do napotkania końca tabeli
identyfikowanego dzięki funkcji eof, odczyty-
wane są wartości poszczególnych pól z bieżące-
go rekordu. Inaczej niż miało to miejsce w przy-
padku pliku gdzie za pomocą pojedynczej opera-
cji odczytywano cały rekord. Bieżącym jest ten
rekord, przy którym znajduje się wskaźnik. Pozycja wskaźnika zmienia jest dzięki
takim metodom jak first, next, prior czy last. W poprzednim punkcie opracowania
gdzie wykorzystywano pliki powiedziano też, że każda operacja odczytu czy zapisu
automatycznie zmienia położenie wskaźnika. W przypadku tabel programista musi
wywołać odpowiednią metodę. Do następnego rekordu przechodzimy wywołując
bezparametrową metodę next. Istnieje również cały szereg sposobów pozwalających
na odczytanie wartości pól. W pokazanym przykładzie w tym celu wykorzystano
funkcję fielbyname z parametrem w postaci ciągu odpowiadającego nazwie. Funkcja
ta zwraca strukturę rekordową, której pole value zawiera dane zawarte w polu. Po
odczytaniu wartości z wszystkich pól metoda next powoduje przesunięcie wskaźnika
do następnego rekordu.
i:=1;
table1.edit;
table1.post;
table1.first;
suma
:= 0;
suma_X:= 0;
suma_Y:= 0;
suma_Z:= 0;
Rysunek 37 Wygląd okna edycji
właściwości columns obiektu
DBGrid
Zapamiętaj zmiany dokonane
przez użytkownika w kompo-
nentach skojarzonych z tabelą
rozpocznij od pierwsze-
go rekordu
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 71
while not table1.eof do
begin
ladunek.waga:=table1.fieldbyname('waga').value;
ladunek.x:=table1.fieldbyname('x').value;
ladunek.y:=table1.fieldbyname('y').value;
ladunek.z:=table1.fieldbyname('z').value;
table1.Next;
suma:=suma+ladunek.waga;
suma_X:=suma_X+ladunek.waga*ladunek.x;
suma_Y:=suma_Y+ladunek.waga*ladunek.y;
suma_Z:=suma_Z+ladunek.waga*ladunek.z;
inc(i);
end;
wyniki_obliczen.cells[1,1] :=
floattostrf(suma,fffixed,8,2);
wyniki_obliczen.cells[1,2] :=
floattostrf(suma_X/suma,fffixed,8,2);
wyniki_obliczen.cells[1,3] :=
floattostrf(suma_Y/suma,fffixed,8,2);
wyniki_obliczen.cells[1,4] :=
floattostrf(suma_Z/suma,fffixed,8,2);
Powtórzmy, aby powyższy program działał poprawnie należy już na początku reali-
zacji otworzyć użyte tabele. Można to zrobić podczas kreowania formy głównej
(zdarzenie onCreate). Wtedy też powinny być opisane wiersze kratki przedstawiają-
cego wyniki obliczeń, a na stronie parametrów statku pustego przygotowane odpo-
wiednie dane.
Procedura, związana z kliknięciem przycisku otwórz, pozwalająca na odczytanie
danych o ładunkach z innej tabeli może mieć postać pokazaną poniżej. Najpierw
prezentowane jest standardowe okno wyboru plików. Użytkownik wybiera właściwą
pozycję przemieszczając się, jeżeli to potrzebne, po strukturze katalogów. Może też
zrezygnować z wyboru naciskając klawisz anuluj. W tym przypadku funkcja execute
zwraca wartość false i kończy się realizacja procedury. Gdy jednak użytkownik
dokonał właściwego wyboru w procedurze ma miejsce zamiana dwóch właściwości
obiektu table1, a mianowicie databasename i tablename. Celem wydzielenia ścieżki
dostępu z wybranej nazwy stosuje się funkcję extractfilepath, identyfikator (nazwa)
pliku zwracany jest przez funkcję extractfilename. Zmiana powyższych właściwości
możliwa jest tylko przy zamkniętej tabeli, dlatego pierwszą wykonywaną operacją
jest wywołanie metody close. Po modyfikacji właściwości nową tabelę należy otwo-
rzyć (metoda open) W procedurze brak instrukcji związanych z przeniesieniem da-
nych z tabeli do kratki. Odpowiednie czynności wykonywane są automatycznie.
Komponenty typu DBGrid nazywane są w języku angielskim data-aware controls i
służą do zarówno do wyświetlania jak i zmian danych w skojarzonych z nimi zbio-
rach. Dzieje się to poza kontrolą programisty. Naciśnięcie przycisku otwórz powin-
no spowodować otwarcie nowego pliku zawierającego tabelę z danymi . Można to
zrobić w następujący sposób:
powtarzaj aż do napotkania
końca tabeli
przejdź do następnego rekordu
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 72
if opendialog1.execute then
begin
table1.close;
table1.databasename :=
extractfilepath(opendialog1.filename);
table1.tablename:=extractfilename(opendialog1.filename);
table1.open
end;
Po otwarciu tabeli kratka wypełni się odpowiednimi wartościami. Naciśnięcie przy-
cisku schowaj powinno spowodować zapamiętanie dokonanych zmian i w przypad-
ku danych o ładunkach może mieć niezwykle prostą postać:
begin
table1.edit;
table1.post
end;
Zestawienie metod i właściwości komponentów wykorzystanych w przykładzie:
Obiekt
Metoda
Typ zwra-
canego wy-
niku lub typ
właściwości
Objaśnienie
OpenDialog
Execute
boolowski
wybór pliku do odczytu, funkcja zwraca
wartość false jeżeli użytkownik zrezygno-
wał z wyboru
OpenDialog
filename
string
ścieżka dostępu i nazwa wybranego do od-
czytu pliku
Table
open
otwarcie tabeli
Table
edit
przygotowanie tabeli do edycji (zmian)
zawartości
Table
eof
boolowski
zwraca wartość true kiedy wskaźnik wska-
zuje znacznik końca danych, w każdym
innym przypadku zwracana jest wartość
false
Table
post
zarejestrowanie w tabeli dokonanych zmian
w komponencie skojarzonym z tabelą, tu
DBGrid
DataSource
DataSet
string
nazwa tabeli związanej z danym źródłem
danych
Table
Tablename
string
nazwa pliku dyskowego zawierającego dane
wyświetlane w tabeli
Table
Databasename string
nazwa ścieżki dostępu lub alias wskazujący
lokalizację pliku dyskowego zawierającego
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 73
dane wyświetlane w tabeli
DBGrid
DataSource
string
identyfikator źródła danych
DBGrid
Columns
Lista
stringów
wiąże kolumny kratki z polami w tabeli
Zestawienie zdarzeń wykorzystanych w przykładzie:
Obiekt
Zdarzenie
Objaśnienie
przyciski
onClick
zainicjowanie obliczeń, zapamiętanie oraz odtwo-
rzenie zawartości kratek
forma
onCreate
utworzenie opisów zawartości kratek, kolumna ze-
rowa na stronie wyniki obliczeń i umieszczenie pa-
rametrów statku pustego na stronie statek pusty,
otwarcie tabel
Aby w programie używać tabel przechowujących zbiory danych należy:
1. zdefiniować strukturę pól rekordu korzystając z aplikacji Database
Desktop. To środowisko umożliwi Ci również wpisanie potrzeb-
nych danych,
2. na formie Twojej aplikacji umieścić komponent klasy Ttable. Pa-
miętaj, że użycie komponentów bezpośrednio związanych z za-
wartością Twojej tabeli wymaga dodatkowego elementu klasy
Tdatasource,
3. określić właściwości Databasename i Tablename tak aby wskazy-
wały katalog i nazwę wcześniej utworzonej tabeli,
4. otworzyć tabelę korzystając z metody open lub zmieniając właści-
wość active komponentu na true
5. po ustawieniu się na odpowiednim rekordzie wykonać operacje za-
pisu czy odczytu poszczególnych pól korzystając, na przykład, z
metody fieldbyname(...),
6. przemieszczanie się po rekordach tabeli umożliwią Ci metody next,
prior, first, last.
7. zamknąć tabelę, wywołując metodę close.
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 74
Procedury i funkcje
Dotychczas korzystano z procedur lub funkcji, które realizowane były automatycznie w następstwie
wystąpienia określonego zdarzenia. Są to procedury szczególnego typu dlatego, że wywoływane były
przez określone zjawiska i powiązane z konkretnymi obiektami. Każdy obiekt posiada metody, wła-
śnie takim mianem określa się wykorzystywane wcześniej procedury lub funkcje. Fakt powiązania
metody z obiektem nie jest oczywisty dla początkującego programisty, tym bardziej, że może on je
wywoływać niemalże w tradycyjny sposób. Różnice da się dopiero zauważyć w momencie odwołania
do metody konkretnego obiektu. Niezależnie od metod wykorzystywanych komponentów środowisko
Delphi umożliwia tworzenie struktur programowych, na które składają się „zwykłe” procedury i funk-
cje.
Jakie są zatem różnice pomiędzy metodami i tradycyjnymi procedurami (funkcjami)?. Metody zwią-
zane są z klasami, deklaracja metody ma miejsce w ramach definicji klasy (patrz przykład poniżej).
Procedury a także funkcje, które mają być traktowane w tradycyjny sposób deklaruje się w bloku in-
terface poza definicjami klas. Podstawowa różnica związana jest zatem z miejscem deklaracji. W
związku z tym implementacja metody wymaga wskazania klasy. Wywołanie zaś metody wiąże się z
koniecznością wskazania obiektu. Istnieją przypadki kiedy jawne wskazanie nie jest potrzebne, zwią-
zane są one z kontekstem, w którym nie wątpliwości o którą instancję chodzi. Przykładem niech bę-
dzie wywołanie z „wnętrza” obiektu.
Mimo przytoczonych różnic zasadnicze kwestie dotyczące samego sposobu deklaracji i implementacji
pozostają podobne. W każdym przypadku obowiązuje następujący schemat organizacyjny dla proce-
dur i funkcji:
nagłówek procedury lub funkcji;
blok deklaracji
blok instrukcji
Nagłówek procedury wygląda następująco:
procedure nazwa_klasy.nazwa_procedury(lista_parametrów);
przy czym:
•
nazwa_klasy występuje w przypadku metod i jest identyfikatorem klasy utworzonym z nieprze-
rwanego ciągu liter, (_) i cyfr, pierwsza musi być litera
•
nazwa_procedury to identyfikator procedury utworzony z nieprzerwanego ciągu liter, (_) i cyfr,
pierwsza musi być litera
•
lista_parametrów zawiera specyfikację parametrów formalnych składającą się z grup zawierają-
cych nazwy zmiennych i ich typy. Poszczególne grupy parametrów oddziela się średnikiem.
Nagłówek funkcji powinien być zgodny ze schematem:
function nazwa_klasy.nazwa_funkcji(lista_parametrów) : typ_wyniku;
różnica w stosunku do deklaracji procedur polega na specyfikacji typu zwracanej wartości.
Przykład poprawnego nagłówka procedury i funkcji:
procedure zamiana(var x,y : integer; ciag : Tciag);
function szukanie(x : real; ciag : Tciag) : real;
Naturalnym sposobem wykorzystania procedury jest jej wywołanie, na przykład:
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 75
zamiana(ax, bx, Nazwaciagu);
W przypadku funkcji jej wywołanie może wystąpić jako element składowy wyrażenia, na przykład:
nowa:=szukanie(3,zestaw)+x;
Taka konstrukcja jest możliwa gdyż funkcja zwraca wynik, tu typu rzeczywistego. W każdym przy-
padku typ ten musi być zdefiniowany przez programistę. Do wywołania funkcji w sposób zwyczajowo
zarezerwowany dla procedury raczej nie jesteśmy przyzwyczajeni. Konstrukcję polegającą na wywo-
łaniu:
szukanie(3,zestaw);
przywykliśmy raczej uważać za dziwną. Można się domyślać, że rzeczywiście taką jest ale tylko w
tym konkretnym przypadku (patrz przekazywane parametry). Niemniej jednak ta konstrukcja jest po-
prawna i co ciekawe szeroko stosowana. Funkcja może wykonywać określone zadanie i zwrócić wy-
nik wskazujący na przebieg realizacji w tym na ewentualne niepowodzenia. Klasycznym przykładem
niech będzie konstrukcja warunkowa:
if opendialog.execute then ...;
Metoda, która jest funkcją, obiektu opendialog o nazwie execute wyświetla okno wyboru plików i
zwraca wartość boolowską true wtedy kiedy użytkownik wybrał potrzebną mu pozycję. Funkcja zwra-
ca wartość false gdy zrezygnowano z wyboru, na przykład naciskając przycisk Cancel. Właśnie jako
funkcje zaimplementowano większość odwołań systemowych. Zwracana wartość często pozwala
również na identyfikację przyczyn niepowodzenia. Prawidłowe wykorzystanie poszczególnych funkcji
wymaga sięgnięcia do dokumentacji technicznej.
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 76
Metody to procedury i funkcje zadeklarowane w ramach definicji klasy. Są one związane
z poszczególnymi obiektami. Mogą być wywołane automatycznie w następstwie powsta-
łych zdarzeń lub odbieranych wiadomości. Programista może również wywołać metodę
pod warunkiem znajomości obiektu macierzystego.
Zwykłe procedury i funkcje uaktywniane są na skutek jawnego wywoływane w kodzie
programu.
I.16.4 Typy proceduralne
Typy proceduralne umożliwiają realizację określonego zbioru operacji na procedurach. Pozwalają, na
przykład, związać z odpowiednią zmienną procedurę, która jest aktualnie potrzebna. Zadeklarowana
zmienna:
var
mojaproc :
procedure(var x,y : integer; ciag : Tciag);
umożliwia przypisanie:
mojaproc := inna_procedura;
Typ proceduralny deklaruje się na jeden z następujących sposobów:
nazwa : procedure(wykaz_parametrow);konwencja_transferu;
nazwa : function(wykaz_parametrow):typ_wyniku
konwencja_transferu;
Odpowiadają one deklaracjom kolejno procedury i funkcji. Charakterystycznym elementem jest brak
nazwy struktury pojawiającej się zaraz po słowie kluczowym procedure czy function. Konwencja
transferu, jeżeli występuje określa drogę i kolejność przekazywania parametrów. Mogą tu wystąpić
słowa kluczowe: register, pascal, cdecl, stdcall, safecall. Konwencja register i pascal zakłada przeka-
zywanie parametrów od lewego do prawego. Oznacza to, że najpierw pierwszy argument jest oblicza-
ny i przekazany, potem następny i tak dalej. Wszystkie pozostałe konwencje zakładają transfer od-
wrotny, to znaczy od lewej do prawej. Konwencja register wykorzystuje rejestry procesora do przeka-
zania parametrów wywołania. Wszystkie pozostałe umieszczają parametry na stosie. Po powrocie z
procedury są one, prawie zawsze, zdejmowane ze stosu automatycznie. Wyjątek stanowi konwencja
cdecl, tu programista w module wywołującym musi sam zadbać o prawidłową zawartość stosu. Jako
domyślną przyjmuje się konwencję register. Aplikacje korzystające z różnych modułów, w szczegól-
ności napisanych przy użyciu różnych języków programowania powinny wykorzystywać konwencję
stdcall.
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 77
Konwencja
Porządek
parametrów
zdejmowanie
ze stosu
Wykorzystanie
rejestrów CPU
register
lewy do prawego
funkcja
tak
pascal
lewy do prawego
funkcja
nie
cdecl
prawy do lewego
moduł wywołu-
jący
nie
stdcall
prawy do lewego
funkcja
nie
safecall
prawy do lewego
funkcja
nie
Sposoby transferu parametrów do wywoływanych procedur i funkcji są różne w poszcze-
gólnych środowiskach tworzenia programów. Jeżeli chcesz aby Twoja aplikacja zbudo-
wana z modułów napisanych w różnych językach programowania działała poprawnie to
musisz zadbać o jednolitą konwencję transferu parametrów.
Umieszczony w sekcji interface nagłówek procedury należy traktować jako deklarację
zmiennej proceduralnej
Dwa typy proceduralne uważa się za równoważne jeżeli posiadają:
•
identyczne konwencje transferu parametrów,
•
jednakową liczbę parametrów,
•
wszystkie typy parametrów identyczne.
Nazwy parametrów nie mają znaczenia. Przypisanie zmiennych proceduralnych jest możliwe tylko
pod warunkiem równoważności ich typów.
I.16.5 Parametry procedur i funkcji
Procedury i funkcje to samodzielne bloki uaktywniane w momencie ich wywołania. Chociaż mogą
one korzystać ze zmiennych bloku wywołującego i wszystkich w stosunku do niego nadrzędnych, to
dobra praktyka nakazuje aby tworzyć je jako struktury całkowicie niezależne. Powinny one operować
na własnych, lokalnie zadeklarowanych zmiennych, a komunikacja z modułem wywołującym powin-
na się odbywać poprzez parametry. W momencie wywołania procedury programista podaje jej nazwę i
określa parametry aktualne. Sposób przekazania parametrów zależy od ich deklaracji. Wyróżnia się
dwa podstawowe mechanizmy, a mianowicie przekazywanie przez wartość i przez adres. W przypad-
ku stosowania pierwszego sposobu parametrem aktualnym może być wyrażenie o odpowiednim typie
wyniku. W drugim przypadku parametr aktualny musi być identyfikatorem zmiennej (obiektu), ogól-
niej czegoś co posiada własny adres. Zmienna prosta, indeksowana, właściwość to przykłady elemen-
tów, którym komputer przydziela adresy.
Istnieje jeszcze wariant przekazywania parametrów przez stałą. Łączy on zalety poprzednich mechani-
zmów, wprowadza jednak dodatkowe ograniczenia. Odpowiedni parametr nie może być w procedurze
zmieniony, odwołanie się do niego nie jest poza tym niczym ograniczone.
Sposób przekazywania parametrów definiowany jest w nagłówku procedury czy funkcji ale także w
deklaracji zmiennej proceduralnej. Jeżeli przy specyfikacji grupy parametrów formalnych wykorzy-
stano słowo var to jest to równoznaczne z operowaniem na adresach. Brak tego słowa oznacza prze-
kazywanie parametrów przez wartość. Wszelkie zmiany dokonane w procedurze a dotyczące parame-
trów przekazywanych przez adres obowiązują w bloku wywołującym. To różni ten tryb od operujące-
go na wartościach. W ostatnim przypadku cokolwiek stanie się z parametrami nie zostanie „zauważo-
ne” w bloku wywołującym.
Podstawowe konstrukcje języka Pascal
Włodzimierz Filipowicz
strona 78
procedure zmiana(var x,y : integer; liczba : real);
Użycie przed specyfikacją grupy parametrów słowa const identyfikuje przekazywanie wielkości sta-
łych. Należy pamiętać, że ten mechanizm można stosować, gdy:
ü nie przekazuje się za jego pomocą informacji zwrotnej, parametr jest wyłącznie wielkością wej-
ściową,
ü nie wykorzystuje się parametru jako elementu roboczego w procedurze (funkcji).
Parametry do procedury można przekazywać przez wartość, przez wskazanie (adres) oraz
przez stałą. Sposób zależy od deklaracji parametrów formalnych. Zastosowanie słowa var
określa adres jako mechanizm odwoływania się do wartości. Zmiany dokonane w proce-
durze na parametrach wskazywanych są widoczne w bloku wywołującym. Parametrami
aktualnymi w takim przypadku mogą być elementy posiadające adres. Wyrażenia można
stosować przy przekazywaniu parametrów przez wartość. Zastosowanie słowa const
oznacza wykorzystanie stałych do przekazania parametrów wyłącznie typu wejściowego.
Grupa parametrów formalnych poprze-
dzona słowem var określa wskazanie
na element dostępny w bloku wywo-
łującym
ten parametr przeka-
zuje się przez war-
tość