Perl Zaawansowane programowanie


IDZ DO
IDZ DO
PRZYKŁADOWY ROZDZIAŁ
PRZYKŁADOWY ROZDZIAŁ
Perl. Zaawansowane
SPIS TRE CI
SPIS TRE CI
programowanie
KATALOG KSIĄŻEK
KATALOG KSIĄŻEK
Autor: Sriram Srinivasan
KATALOG ONLINE
KATALOG ONLINE Tłumaczenie: Adam Podstawczyński
ISBN: 83-7197-999-1
Tytuł oryginału: Advanced Perl Programming
ZAMÓW DRUKOWANY KATALOG
ZAMÓW DRUKOWANY KATALOG
Format: B5, stron: 446
TWÓJ KOSZYK
TWÓJ KOSZYK
Umiesz programować w Perlu, lecz czujesz pewien niedosyt? Pracujesz nad większym
projektem niż zazwyczaj i jeste zagubiony? A może chciałby dodać do swojej aplikacji
DODAJ DO KOSZYKA
DODAJ DO KOSZYKA
efektowny interfejs użytkownika, bardziej zaawansowany mechanizm przechwytywania
błędów lub obsługę sieci i nie wiesz jak to zrobić?
Ta książka pomoże Ci stać się lepszym programistą bez względu na to, czy Twoja
CENNIK I INFORMACJE
CENNIK I INFORMACJE
znajomo ć Perla jest powierzchowna, czy dogłębna. Nauczysz się zaawansowanych
technik przygotowywania programów w Perlu o jako ci produkcyjnej. Poznasz metody
ZAMÓW INFORMACJE
ZAMÓW INFORMACJE
przetwarzania danych i używania obiektów, które wcze niej mogły wydawać Ci się
O NOWO CIACH
O NOWO CIACH
czarną magią. Książka przedstawia szerokie zastosowania Perla: od sieci, baz danych,
po interfejsy użytkownika. Znajdziesz w niej także opis wewnętrznych mechanizmów
ZAMÓW CENNIK
ZAMÓW CENNIK
języka umożliwiających tworzenie wydajniejszych aplikacji oraz łączenie Perla
z językiem C.
Do najważniejszych tematów poruszanych w książce należą:
CZYTELNIA
CZYTELNIA
" Praktyczne zastosowania pakietów i klas (programowanie obiektowe)
FRAGMENTY KSIĄŻEK ONLINE
FRAGMENTY KSIĄŻEK ONLINE
" Złożone struktury danych
" Trwało ć danych (np. bazy danych)
" Sieci
" Interfejsy graficzne budowane za pomocą pakietu Tk
" Interakcja z funkcjami języka C
" Osadzanie i rozszerzanie interpretera Perla
" Wybrane aspekty wewnętrznych mechanizmów Perla
W książce przystępnie wytłumaczono wszystkie zagadnienia związane z Perlem,
o których zapewne chciałby wiedzieć więcej: odwołania, przechwytywanie błędów
operatorem eval, nieblokujące operacje wej cia/wyj cia, zasadno ć stosowania
domknięć oraz dowiązania z użyciem mechanizmu tie. Jej lektura spowoduje,
Wydawnictwo Helion że poczujesz się prawdziwym hakerem  mistrzem Perla.
ul. Chopina 6
 Nieprzeciętny tekst i najbardziej zaawansowana książka o Perlu, jaką napisano. Autor
44-100 Gliwice
 specjalista  obja nia trudne koncepcje w sposób klarowny i kompletny.
tel. (32)230-98-63
Jon Orwant, redaktor The Perl Journal
e-mail: helion@helion.pl




Odwoływanie sią do istniejących zmiennych.......................................................................... 25
Korzystanie z odwołań ............................................................................................................ 30
Zagnieżdżone struktury danych............................................................................................... 35
Odpytywanie odwołania .......................................................................................................... 37
Odwołania symboliczne........................................................................................................... 38
Spojrzenie na wewnątrzną konstrukcją ................................................................................... 39
Odwołania w innych jązykach................................................................................................. 43
yródła informacji ..................................................................................................................... 44

Struktury definiowane przez użytkownika .............................................................................. 46
Przykłady: macierze................................................................................................................. 47
Wykładowcy, studenci i przedmioty ....................................................................................... 50
A teraz otwórzmy kopertą z decyzją jury& ............................................................................ 54
Upiąkszone drukowanie........................................................................................................... 56
yródła informacji ..................................................................................................................... 59

Zmienne, tablica symboli oraz zakresy w Perlu...................................................................... 62
Typeglob .................................................................................................................................. 65
Typy typeglob a odwołania ..................................................................................................... 69
Uchwyty plików i katalogów oraz formaty ............................................................................. 71


Odwołania do podprocedur...................................................................................................... 76
Korzystanie z odwołań do podprocedur .................................................................................. 77
Domkniącia.............................................................................................................................. 80
Używanie domkniąć ................................................................................................................ 83
Porównanie z innymi jązykami ............................................................................................... 88
yródła informacji ..................................................................................................................... 89

Wywołanie z łańcuchem: ewaluacja wyrażeń ......................................................................... 92
Wywołanie z blokiem: obsługa wyjątków............................................................................... 94
Uwaga na cudzysłów ............................................................................................................... 96
Ewaluacja wyrażeń za pomocą eval ........................................................................................ 97
Zwiąkszanie wydajności za pomocą eval................................................................................ 99
Odliczanie czasu za pomocą eval .......................................................................................... 104
Eval w innych jązykach ......................................................................................................... 105
yródła informacji ................................................................................................................... 107

Podstawowy pakiet ................................................................................................................ 109
Pakiety i pliki......................................................................................................................... 111
Inicjalizacja i destrukcja pakietów ........................................................................................ 113
Prywatność............................................................................................................................. 114
Importowanie symboli ........................................................................................................... 115
Zagnieżdżanie pakietów ........................................................................................................ 118
Automatyczne ładowanie....................................................................................................... 119
Dostąp do tablicy symboli ..................................................................................................... 120
Porównania z innymi jązykami ............................................................................................. 121

OO: wprowadzenie ................................................................................................................ 125
Obiekty w Perlu..................................................................................................................... 127
UNIVERSAL......................................................................................................................... 140
Powtórka z konwencji............................................................................................................ 141
Porównanie z innymi jązykami obiektowymi ....................................................................... 145
yródła informacji ................................................................................................................... 147


Wydajne zapisywanie atrybutów........................................................................................... 149
Delegacja ............................................................................................................................... 160
O dziedziczeniu..................................................................................................................... 161
yródła informacji ................................................................................................................... 163

Dowiązywanie skalarów........................................................................................................ 166
Dowiązywanie tablic ............................................................................................................. 169
Dowiązywanie tablic asocjacyjnych...................................................................................... 171
Dowiązywanie uchwytów plików ......................................................................................... 172
Przykład: monitorowanie zmiennych .................................................................................... 173
Porównania z innymi jązykami ............................................................................................. 177

Aspekty trwałości .................................................................................................................. 179
Dane strumieniowe ................................................................................................................ 182
Dane ukierunkowane na rekordy........................................................................................... 185
Relacyjne bazy danych .......................................................................................................... 187
yródła informacji ................................................................................................................... 193

Adaptor: wprowadzenie......................................................................................................... 197
Uwagi na temat architektury.................................................................................................. 200
Implementacja........................................................................................................................ 206
yródła informacji ................................................................................................................... 214

Wstąp do sieci........................................................................................................................ 215
Interfejs gniazd i IO::Socket.................................................................................................. 217
Obsługa wielu klientów ......................................................................................................... 219
Prawdziwe serwery................................................................................................................ 225
Obiekty wejścia-wyjścia i uchwyty plików........................................................................... 226
Gotowe moduły klientów ...................................................................................................... 227
yródła informacji ................................................................................................................... 229


Msg: zestaw narządzi do przesyłania komunikatów ............................................................. 231
Wywoływanie zdalnych procedur (RPC) .............................................................................. 243
yródła informacji ................................................................................................................... 249

Wprowadzenie do graficznych interfejsów użytkownika, Tk oraz modułu Perl/Tk............. 252
Pierwszy program w Perl/Tk ................................................................................................. 253
Przegląd widgetów................................................................................................................. 256
Zarządzanie geometrią........................................................................................................... 273
Liczniki czasu........................................................................................................................ 276
Dowiązywanie zdarzeń.......................................................................................................... 277
Pątle zdarzeń.......................................................................................................................... 279
yródła informacji ................................................................................................................... 280

Wprowadzenie do gry Tetris ................................................................................................. 282
Konstrukcja............................................................................................................................ 282
Implementacja........................................................................................................................ 284


man i perlman ........................................................................................................................ 292
Implementacja........................................................................................................................ 293
yródła informacji ................................................................................................................... 299

O generowaniu kodu.............................................................................................................. 301
Przykład działania struktury Jeeves....................................................................................... 304
Przegląd architektury Jeeves.................................................................................................. 308
Implementacja Jeeves ............................................................................................................ 311
Przykładowy analizator składniowy specyfikacji.................................................................. 318
yródła informacji ................................................................................................................... 319

Pisanie rozszerzenia: omówienie........................................................................................... 322
Przykład: fraktale w Perlu..................................................................................................... 325

Cechy oprogramowania SWIG.............................................................................................. 328
Cechy oprogramowania XS................................................................................................... 331
Różne stopnie swobody ......................................................................................................... 335
Dygresja: fraktale................................................................................................................... 335
yródła informacji ................................................................................................................... 339

Po co osadzać?....................................................................................................................... 341
Ogólne informacje o osadzaniu............................................................................................. 343
Przykłady ............................................................................................................................... 344
Dodawanie rozszerzeń ........................................................................................................... 348
yródła informacji ................................................................................................................... 349

Czytanie kodu zródłowego .................................................................................................... 352
Architektura ........................................................................................................................... 353
Typy wartości Perla ............................................................................................................... 360
Stosy i protokół przekazywania komunikatów...................................................................... 382
Soczyste rozszerzenia ............................................................................................................ 389
Aatwy interfejs do osadzania ................................................................................................. 399
Spojrzenie na przyszłość........................................................................................................ 401
yródła informacji ................................................................................................................... 402

Button (przycisk) ................................................................................................................... 405
Canvas (pole graficzne) ......................................................................................................... 405
Entry (wprowadzanie tekstu)................................................................................................. 410
Listbox (Lista wyboru) .......................................................................................................... 412
Menu...................................................................................................................................... 414
Paski przewijania i przewijanie ............................................................................................. 415
Scale (skala)........................................................................................................................... 417
HList  lista hierarchiczna................................................................................................... 417

Odwołania.............................................................................................................................. 419
Zagnieżdżone struktury danych............................................................................................. 420
Domkniącia............................................................................................................................ 421

Moduły................................................................................................................................... 421
Obiekty................................................................................................................................... 422
Operacje dynamiczne............................................................................................................. 423
Obsługa wyjątków ................................................................................................................. 424
Metainformacje...................................................................................................................... 424
Typy typeglob........................................................................................................................ 424
Uchwyty plików, formaty...................................................................................................... 425

Interfejsy użytkownika
oparte na Tk
W tym rozdziale nauczymy się budować graficzne interfejsy użytkownika (Graphical User
Interface  GUI) za pomocą jednego z najbardziej funkcjonalnych i najlepiej prezentują-
cych się pakietów narzędzi (toolkit): pakietu Tk [1]. Zaczniemy od zwięzlego przeglądu
większości widgetów Tk oraz niektórych rozszerzeń. Dowiemy się także czegoś o zarzą-
dzaniu geometrią (jak ukladać widgety na formularzu). Następnie pokrótce przeanalizu-
jemy obslugę liczników czasu w Perlu  będziemy z nich intensywnie korzystać w roz-
dziale 15., Przykład graficznego interfejsu użytkownika: Tetris. Pózniej omówimy dowiązania
zdarzeń, za pomocą których będziemy odwzorowywali dowolne kombinacje zdarzeń
związanych z myszą i klawiaturą na wywolania funkcji. Wreszcie powiemy o problemach
związanych z pętlą obslugi zdarzeń, podobnych do tych, z jakimi mieliśmy do czynienia
w rozdziale 12., Rozwiązania sieciowe oparte na gniazdach.
W celu uproszczenia opisu w tym rozdziale przedstawimy male i odizolowane porcje
kodu, ilustrujące sposób użycia poszczególnych widgetów oraz funkcji Tk. Dopiero
w następnych dwóch rozdzialach polączymy te narzędzia i rozwiążemy za ich pomocą
praktyczne problemy.
Skoro jesteśmy już przy temacie interfejsów użytkownika, warto aby Czytelnik (oraz użyt-
kownicy korzystający z jego lub jej programów) zapoznali się ze świetną, prezentującą
zupelnie nowy punkt widzenia książką Alana Coopera1 About Face: The Essentials of User
Interface Design [3].
1
Zwaneg>  >jcem języka Visual Basic .
252 Rozdział 14. Interfejsy użytkownika oparte na Tk
Wprowadzenie do graficznych interfejsów
użytkownika, Tk oraz modułu Perl/Tk
Na poziomie najbardziej podstawowym wszystkie platformy okienkowe (Apple Macin-
tosh, X Window oraz Microsoft Windows) są bardzo proste. Udostępniany przez nie ni-
skopoziomowy interfejs programowania sluży do tworzenia i zarządzania oknami, zgla-
szania interesujących zdarzeń (np. związanych z myszą i klawiaturą) oraz do rysowania
elementów graficznych, takich jak: linie, okręgi i mapy bitowe. Problem polega na tym,
że narysowanie nawet prostego formularza wymaga napisania znacznych ilości kodu
i odczytania tysięcy (doslownie) stron dokumentacji.
Często wykorzystywane fragmenty kodu interfejsów graficznych ewoluowaly do postaci
widgetów (w środowisku Microsoft Windows określanych mianem  kontrolek ); przykla-
dy widgetów to: przyciski, paski przewijania i listy. Budowanie interfejsu graficznego
sprowadza się teraz do uruchomienia interaktywnego  projektanta formularzy i prze-
ciągnięcia w odpowiednie miejsce takich gotowych do użycia elementów. Programowa-
nie obiektowe jest teraz o wiele latwiejsze!
Okazuje się, że widgety i języki skryptowe doskonale ze sobą wspólpracują. Widgety mają
proste interfejsy, zaś interfejsy graficzne oparte na formularzach nie muszą być maksy-
malnie wydajne. Cechy te sprawiają, że skrypty świetnie spisują się przy tworzeniu in-
terfejsów GU. Jeśli dodamy do tego fakt, że interfejsy graficzne muszą umożliwiać ela-
styczne konfigurowanie (ponieważ to wlaśnie z tą częścią aplikacji ma do czynienia
użytkownik  a w ogóle interfejs graficzny to najczęściej po prostu jest aplikacja), latwo
zrozumieć, skąd wzięla się ogromna popularność takich narzędzi, jak: Visual Basic, Po-
werBuilder czy Hypercard.
W systemach uniksowych rolę interfejsu okienkowego pelni X Window. Na jego bazie
zbudowano różne pakiety narzędzi: Athena, nterViews, Motif i Tk. Jeśli chodzi o profe-
sjonalny wygląd, latwość użytkowania i dostępną dokumentację, Tk nie ma sobie rów-
nych. No i na dodatek jest darmowy!
W przeciwieństwie do innych pakietów narzędzi, Tk zostal opracowany specjalnie pod
kątem języka skryptowego Tcl.2 Można wręcz zastanawiać się, czy Tk nie jest glówną
przyczyną popularności Tcl. Wiele osób nie przepada za językiem skryptowym Tcl, ale
uwielbia Tk  i stara się dostosowywać Tk do swojego ulubionego języka skryptowego,
czy to Scheme, Pythona, Guile a, czy oczywiście Perla. Pierwszą próbę polączenia Perla
z Tk podjąl Malcolm Beattie, który wewnętrznie wykorzystal interpreter Tcl do uzyska-
nia dostępu do biblioteki Tk.
Nick ng-Simmons podjąl się zadania ambitniejszego: oczyścil Tk z osadzonego kodu Tcl
i nadal mu ogólną, przenośną warstwę, ulatwiającą dodawanie Tk do innych języków
skryptowych; rozwiązanie to określa się nazwą pTk (portable Tk  przenośny Tk). Następnie
2
Zarówn> Tcl, jak i Tk z>staly >prac>wane przez dr. J>hna źusterh>uta, wówczas prac>wnika Uniwersytetu
Kalif>rnijskieg> w Berkeley, a >becnie firmy Sun Micr>systems. Patrz: http://www.sunlabs.com/research/Tcl.
Pierwszy program w Perl/Tk 253
do tego mechanizmu dodal  opakowanie dla Perla5 ( opakowania dla innych języ-
ków mają zostać dolączone w przyszlości). To polączenie pTk i modulu odpakowujące-
go Perla Tk.pm określa się wspólną nazwą Perl/Tk i to wlaśnie ono jest przedmiotem ni-
niejszego rozdzialu.
W tym samym czasie dr Ousterhout wraz ze swoim zespolem w firmie Sun przeniósl Tcl
i Tk na platformy Windows i Mac; wkrótce to samo zrobiono z polączeniem Perl/Tk. nne
polączenia umożliwiające realizację interfejsu graficznego to oczywiście Tcl/Tk oraz Py-
thon/Tk (ten ostatni nie jest oparty na pTk). Microsoft przenosi swoje rozwiązania ActiveX
(dawniej OLE) i VBA (Visual Basic for Applications) do środowiska uniksowego, mogą
więc one wkrótce stanowić liczącą się konkurencję dla Tk. Samemu pakietowi narzędzi
VB daleko jest do funkcjonalności Perla w polączeniu z Tk, za to środowisko programi-
styczne i obsluga ze strony firm niezależnych są dużo lepsze. Żyjemy w ciekawych czasach!
Do Tk dodano szereg nowych, profesjonalnie prezentujących się widgetów  udostęp-
niono je w postaci biblioteki rozszerzeń Tix. Autorem biblioteki jest oi Kim Lam. Roz-
szerzenia obejmują dymki (do wyświetlania tekstu pomocy), notatniki i siatki, takie jak
te stosowane w arkuszach kalkulacyjnych. Dystrybucja Perl/Tk obejmuje również me-
chanizmy dostępu z Perla do tych nowych widgetów.
Pierwszy program w Perl/Tk
Wszystkie interfejsy użytkownika budowane z użyciem Perl/Tk budowane są wedlug
takiej samej, ogólnej struktury:
1. Tworzymy okno glówne main (określane także oknem najwyższego poziomu
 top-level).
2. Tworzymy egzemplarze jednego lub większej liczby widgetów. Konfigurujemy je
i umieszczamy wewnątrz okna glównego. Widget jest po prostu zbiorem danych
i metod, które w efekcie dają pewien element graficzny interfejsu  np. przycisk
lub listę  i powodują, że zachowuje się on w określony sposób po kliknięciu lub
wykonaniu na nim innej czynności.
3. Uruchamiamy pętlę obslugi zdarzeń. Od tej pory o dzialaniu programu decydują
czynności wykonywane przez użytkownika (zdarzenia).
W listingu 14.1 przedstawiono wszystkie te kroki w kolejności takiej, jak opisana powy-
żej. W wyniku wykonania kodu wyświetlany jest prosty interfejs graficzny3, taki jak na
rysunku 14.1.
Listing 14.1. Prosty kod realizujący interfejs graficzny
use Tk; # Dołączamy moduł
# -------------------------------------------------------
# Tworzymy okno główne (main)
3
Nie ma w nim nic interaktywneg>  graficznie też jest raczej ub>gi.
254 Rozdział 14. Interfejsy użytkownika oparte na Tk
# -------------------------------------------------------
$glowne = MainWindow->new();
$glowne->title ("Proste");
# -------------------------------------------------------
# Tworzymy egzemplarze widgetów i konfigurujemy je
# -------------------------------------------------------
$l = $glowne->Label(text => 'witaj', # etykieta
anchor => 'n', # tekst zakotwiczamy
# "na północy" (north)
relief => 'groove', # styl ramki
width => 10, height => 3); # szerokie na 10 znaków,
# wysokie na 3
$l->pack(); # Domyślne miejsce w oknie głównym
# -------------------------------------------------------
# Uruchamiamy nieskończoną pętlę służącą do oddelegowywania zdarzeń.
# -------------------------------------------------------
MainLoop();
Rysunek 14.1. Nasz pierwszy ekran Perl/Tk
Ten przyklad ilustruje wiele ważnych koncepcji zastosowanych w Tk (a także tych zwią-
zanych z interfejsami graficznymi w ogóle).
Okno glówne jest elementem najbardziej zewnętrznym; otaczają je tylko uchwyty zmia-
ny wymiarów okna, menu systemowe oraz przyciski minimalizacji, maksymalizacji
i zamknięcia okna (określane również mianem dekoracji). Aplikacja może wykorzysty-
wać dowolną liczbę okien glównych.
Od okna glównego wymagamy następnie wyświetlenia widgetu etykiety o określonych
wlaściwościach. Jeśli wlaściwości widgetu chcemy zmienić, wywolujemy na nim metodę
configure:
$l->configure (text => 'foobar', foreground => 'red');
Niektóre widgety, np. Frame czy Notebook, same mogą zawierać inne widgety, a więc
w hierarchii widgetów dozwolone jest dowolne zagnieżdżanie. Na szczycie hierarchii
jest zawsze okno glówne.
Pierwszy program w Perl/Tk 255
Metoda pack jest następnie wywolywana w celu przeprowadzenia zarządzania geometrią,
tj. przydzielenia pozycji, szerokości i wysokości widgetu. Ta funkcja jest po prostu odde-
legowana do pojemnika widgetu, tj. glównego okna, który oblicza jakie  dzialki ekranu
przydzielić poszczególnym widgetom podrzędnym. To tak jakby napisać  skarpetki->
pakuj i pozwolić walizce samej określić, gdzie skarpetki mają zostać umieszczone i ile
można ich tam upchać.
 Pakowanie to tylko jeden ze schematów zarządzania geometrią, slużących do lokowa-
nia widgetów. Tk udostępnia menedżer geometrii grid oraz mechanizm placer; oba te ele-
menty omówimy w punkcie  Zarządzanie geometrią w dalszej części rozdzialu.
Możliwe jest utworzenie i upakowanie widgetu za jednym zamachem:
$l = $glowne->Label (text => 'Ojej')->pack();
W większości przypadków nie ma nawet potrzeby przechwycenia wartości zwracanej
z tego wyrażenia, chyba że planujemy na takim widgecie wykonywać potem metody.
Typowe podejście polega na określeniu wszystkich parametrów w chwili tworzenia wid-
getów, a potem wywolaniu funkcji MainLoop. Tak będziemy postępować bardzo często
w przykladach zamieszczonych w niniejszej książce.
Koncepcję pętli zdarzeń omówiliśmy już w rozdziale 12., ale powiemy więcej na ten te-
mat w punkcie  Pętle zdarzeń w dalszej części rozdzialu. Tymczasem wystarczy widzieć,
że MainLoop zajmuje się  oddelegowywaniem zdarzeń i kończy dzialanie dopiero wte-
dy, gdy użytkownik zamknie okno przez dwukrotne kliknięcie menu systemowego. Funk-
cja ta musi zostać wywolana  inaczej utworzony formularz nigdy nie pojawi się na
ekranie (dla widgetów trzeba zaś, z tego samego powodu, wywolywać pack).
 to tyle! Teraz wystarczy wiedzieć, jakie widgety mamy do dyspozycji, jakie wlaściwo-
ści one obslugują i jak je lączyć. Spójrzmy więc.
Formularze interfejsów graficznych: sposób łatwiejszy
Po co pisać kod tworzący statyczne ekrany, kiedy ekrany takie można po prostu nary-
sować? Stephen Uhler z zespolu Tcl/Tk w Sun Microsystems napisal graficzny kreator
pracujący w trybie WYSWG o nazwie SpecTcl (wym. jak angielskie slowo spectactle),
obslugujący różne języki. Narzędzie dostosowano do interfejsów Perl/Tk, Java/Tk i Py-
thon/ Tk  odpowiednie odmiany noszą nazwy SpecPerl, SpecJava i SpecPython. Prze-
niesienie na interfejs Perl/Tk zrealizowal Mark Kvale i program można pobrać z jego
strony prywatnej:4 http://www.keck.ucsf.edu/~kvale/specPerl/.
Za pomocą oprogramowania SpecPerl można rozmieszczać widgety w sposób wizualny,
ustawiać ich wlaściwości w odpowiednich formularzach i wybierać kolory oraz czcionki
z dostępnych palet  wszystko to jest bardzo wygodne.
4
Sun r>zp>cząl sprzedawanie SpecTcl, a więc SpecPerl jest >party na starszym (i darm>wym) k>dzie SpecTcl.
256 Rozdział 14. Interfejsy użytkownika oparte na Tk
Jednak w tym rozdziale (i następnych dwóch) kod interfejsu graficznego będziemy pisali
ręcznie, a nie za pomocą opisanego wyżej oprogramowania. Jest ku temu kilka powodów.
Po pierwsze, nie będziemy budowali bardzo wyszukanych formularzy. Po drugie, w więk-
szości przypadków koncentrujemy się na dynamicznych aspektach Tk, a kreator gra-
ficzny umożliwia tylko budowanie statycznych formularzy. Po trzecie, kiedy już Czytel-
nik zrozumie niniejszy rozdzial, będzie także rozumial kod generowany przez SpecPerl.
Przegląd widgetów
W tej części opiszemy klasy najciekawszych widgetów zaimplementowanych w Tk i Tix
oraz powiemy o ich najczęściej stosowanych opcjach konfiguracyjnych i metodach. W celu
zachowania porządku i szybszego wyszukiwania w przyszlości (gdy Czytelnik będzie
już wiedzial, czego szukać) ten szeroki zestaw wlaściwości i metod opisano w oddziel-
nym dodatku A, Spis widgetów Tk. Należy pamiętać, że ten rozdzial  choć obszerny 
traktuje tylko o podzbiorze (choć znacznym) wszystkich widgetów Tk. Nie są opisane
wszystkie widgety dostęcne w Tk i Tix. Do oprogramowania Perl/Tk dolączana jest ory-
ginalna, przejrzyście napisana i kompleksowa dokumentacja interfejsu Tk.
Właściwości widgetów
Aby oswoić się z wlaściwościami, które można skonfigurować w przypadku wszystkich
widgetów, warto zerknąć na tabelę A.1. Większość tych wlaściwości ma postać zwyklych
lańcuchów tekstowych lub liczb; zanim przejdziemy do opisywania wlaściwych widge-
tów, warto jednak bliżej przyjrzeć się trzem wlaściwościom, związanym z czcionkami,
grafiką i kolorami.
Czcionki
Czcionki określa się w formacie XLFD (X Logical Font Description), który sklada się z 14 pól
rozdzielonych lącznikami, tak jak to przedstawiono na rysunku 14.2.
Rysunek 14.2. Pola definicji czcionki
Na szczęście nie musimy pamiętać wszystkich tych pól. Każde z nich można zastąpić zna-
kiem wieloznacznym  * lub  ? ; trzeba jednak pamiętać, aby lączników bylo tyle, ile
trzeba. W systemie X Window dostępne są dwa narzędzia  graficzne fontsel i wsadowe
xlsfonts  wyświetlające wszystkie dostępne w systemie kombinacje tych pól; za pomocą
tych narzędzi można po prostu wybrać wymaganą czcionkę. Przede wszystkim należy
Przegląd widgetów 257
określić producenta, rodzinę, ciężar, pochylenie i liczbę punktów; pozostale pola można
zignorować (aby uzyskać polskie  ogonki trzeba również wybrać odpowiednie kodowa-
nie, iso8859-2  przyp. tłum.). Punkty określa się jako liczbę dziesiątych części punktu,
a więc 120 oznacza czcionkę 12-punktową. Pochylenie można określić jako  i (czcionka po-
chyla) lub  r (zwykla). Czcionkę widgetu ustawiamy za pomocą odpowiedniej wlaściwości:
$etykieta->configure (
font => '-adobe-helvetica-medium-r-normal--8-80-75-75-p-46-*-1');
W systemach Windows i Mac wartości fontów można określać albo w formacie XLFD,
albo w prostszy,  windowsowy sposób: Helvetica 24 bold. Pierwszy format będzie
caly czas obslugiwany na wszystkich platformach.
Grafika
Niektóre widgety, np. przyciski lub etykiety, mogą zawierać dwukolorowe mapy bitowe
lub wielokolorowe mapy pikseli. Ponieważ ta sama mapa bitowa lub obraz mogą slużyć
do dekorowania wielu widgetów, Tk traktuje je jako obiekty, które można wyświetlić
w wielu miejscach. To znaczy, obiekt obrazu zawiera dane, a widgety wiedzą, jak wyświe-
tlać te dane we wlasnej przestrzeni. A więc wyświetlenie mapy bitowej lub mapy pikseli
wymaga wykonania dwóch kroków: utworzenia obiektu obrazu przez podanie nazwy
pliku obrazu oraz skonfigurowania wlaściwości  bitmap lub  pixmap widgetu przez
podanie nazwy tego obiektu.
W zależności od typu posiadanego pliku obrazu, utworzenie obiektu wymaga zastoso-
wania odpowiedniej funkcji:
# Tylko format XBM (X Bitmap)
$obraz = $etykieta->Bitmap(file => 'twarz.xbm');
# Tylko format XPM (X Pixmap)
$obraz = $etykieta->Pixmap(file => 'buzia.xpm');
# Konstruktor Photo wymagany jest przy formatach GIF lub PPM (Portable pixmap)
$obraz = $etykieta->Photo(file => 'grymas.gif');
Teraz zmiana podkladu graficznego etykiety jest już prosta:
$etykieta->configure (image => $obraz);
Jeśli plik graficzny jest mapą bitową, używamy opcji  bitmap , a jeśli plikiem XPM lub
GF  wlaściwości  image . W przypadku map bitowych o tym, jakie kolory zostaną
zastosowane, decydują opcje foreground i background; w przypadku innych obra-
zów kolory opisane są w samym pliku graficznym.
Kolory
Kolory można określać za pomocą nazw symbolicznych, np.  red lub  yellow . W ka-
talogu bibliotek systemu X znajduje się plik o nazwie rgb.txt, w którym wymienione są
wszystkie dostępne nazwy symboliczne. Kolory można również podawać za pomocą
258 Rozdział 14. Interfejsy użytkownika oparte na Tk
wartości RGB w postaci #RGB, #RRGGBB, #RRRGGGBBB lub #RRRRGGGGBBBB, gdzie
R, G i B oznaczają jedną cyfrę szesnastkową, określającą odpowiednio natężenie koloru
czerwonego, zielonego i niebieskiego.
To tyle krótkiego omówienia. Spójrzmy teraz na same widgety Tk i Tix.
Etykiety i przyciski
W tabeli A.1 zawarto większość informacji koniecznych do korzystania ze standardowych
wlaściwości etykiet. Już po samych nazwach tych wlaściwości latwo rozpoznać, do czego
slużą, więc nie będziemy im tutaj poświęcać więcej miejsca.
Przyciski to po prostu etykiety z jedną dodatkową wlaściwością: opcją command, która
umożliwia przypisanie funkcji zdarzeniu polegającemu na kliknięciu przycisku. W poniż-
szym przykladzie pokazano procedurę zmien_napis, która zmienia etykietę widgetu:
use Tk;
$glowne = MainWindow->new();
$przycisk = $glowne->Button(text => 'Start',
command => \&zmien_napis);
$przycisk->pack();
MainLoop();
sub zmien_napis { # Tworzymy podprocedurę
$przycisk->cget('text') eq "Start" ?
$przycisk->configure(text => 'Stop') :
$przycisk->configure(text => 'Start');
}
Metoda cget pobiera wartość wlaściwości dającej się konfigurować.
Zamiast funkcji można użyć domknięcia, tak jak to pokazano poniżej (pominięto resztę
szablonowego kodu):
$przycisk = $glowne->Button(
text => 'Start',
command => sub {
$przycisk->cget('text') eq "Start" ?
$przycisk->configure(text => 'Stop') :
$przycisk->configure(text => 'Start')
}
);
Trzeci sposób wykorzystania wlaściwości command polega na przekazaniu jej anonimo-
wej tablicy, której pierwszym elementem będzie procedura; inne elementy tablicy prze-
kazywane są do tej procedury w trakcie jej wykonywania:
$przycisk->configure (command => [\&zmien_napis, "nowy napis"]);
Takie podejście zastosujemy w dalszej części rozdzialu do realizacji przewijania definio-
wanego z poziomu aplikacji.
Przegląd widgetów 259
Przełączniki i pola wyboru
Przelącznik (radio button) jest widgetem obejmującym lańcuch tekstowy, mapę bitową lub
obraz oraz element graficzny w ksztalcie rombu, nazywany  guzikiem (indicator)  patrz
rysunek 14.3. Podobnie jak przyciski, przelączniki obslugują opcję  command . Jednak
w przeciwieństwie do przycisków przelączniki zazwyczaj stosowane są grupami  użyt-
kownik może wybrać jedną spośród kilku możliwości. Dlatego w przelącznikach dostępne
są dwie wlaściwości  variable (zmienna) i value (wartość)  slużące do synchro-
nizacji z innymi przelącznikami grupy; chodzi o to, żeby zawsze mógl być wlączony tyl-
ko jeden guzik. Kiedy klikniemy dany przelącznik, guzik jest wlączany, a odpowiednia
wartość zmiennej ustawiana jest na jego wlasną wartość. Jeśli wartość tej zmiennej zo-
stanie zmodyfikowana, przelącznik sprawdza, czy jest ona identyczna z jego wlaściwo-
ścią value; jeśli jest  widget wlącza wlasny guzik. Jak latwo się domyślić, do wewnętrz-
nego monitorowania zmian wartości wykorzystano mechanizm tie.
Rysunek 14.3. Przykład przełącznika
W poniższym przykladzie tworzymy grupę przelączników. Rolę zmiennej synchronizu-
jącej odgrywa $napoj.
$napoj = "kawa"; # Wartość początkowa
$kawa = $glowne->Radiobutton (
variable => \$napoj,
text => 'Kawa',
value => 'kawa');
$herbata = $glowne->Radiobutton (
variable => \$napoj,
text => 'Herbata',
value => 'herbata');
$mleko = $glowne->Radiobutton (
variable => \$napoj,
text => 'Mleko',
value => 'mleko');
# Układamy przełączniki na ekranie
$kawa->pack (side => 'left');
$herbata->pack (side => 'left');
$mleko->pack (side => 'left');
Ponieważ przelączniki mają różne wartości, ale korzystają z tej samej zmiennej, mamy
zagwarantowane, że w danej chwili wlączony jest tylko jeden guzik.
Więcej wlaściwości i metod związanych z przelącznikami można znalezć w tabeli A.3.
260 Rozdział 14. Interfejsy użytkownika oparte na Tk
Pole wyboru (checkbox) jest bardzo podobne do przelącznika. Kwadratowy guzik po-
wiązany jest z odpowiednią wartością zmiennej. W przeciwieństwie do przelącznika,
zmiana wartości nie musi powodować zmiany wartości innego pola wyboru (choć i ta-
kie zachowanie można w latwy sposób osiągnąć). Pola wyboru stosuje się tam, gdzie
użytkownik ma mieć możliwość wybrania wielu spośród różnych dostępnych opcji.
Pole graficzne
Widget pola graficznego (canvas) umożliwia wyświetlanie ustrukturyzowanych elemen-
tów graficznych. Udostępnia metody do przetwarzania elementów graficznych, takich jak
okręgi, prostokąty, luki, odcinki, mapy bitowe, elementy skladające się z wielu odcin-
ków i tekst. Umożliwia nawet osadzanie innych widgetów i traktowanie ich jak zwykle
elementy pola graficznego.
W przeciwieństwie do pól graficznych w pakiecie Abstract Windowing Toolkit Javy
(a także w zasadzie w każdym innym pakiecie narzędzi GU jaki znam), pola graficzne
Tk są same w sobie obiektami: obslugują wlaściwości podobnie jak inne widgety i ze-
zwalają na stosowanie tych wlaściwości albo do poszczególnych elementów graficznych,
albo do calych nazwanych grup tych elementów. Można im również przypisać funkcje
(tj. tak jakby powiedzieć  jeśli wskaznik myszy znajdzie się nad tym okręgiem, wywolaj
procedurę foo ).
Elementy pola graficznego tym różnią się od widgetów, że każdy widget uzyskuje od
serwera X wlasne okno; nie jest tak w przypadku elementów pola graficznego. Ponadto
w przeciwieństwie do widgetów elementy pola graficznego nie są objęte funkcjami za-
rządzania geometrią (nie można zmienić ich wielkości za pomocą pojemnika). Pozostaje
dla mnie niejasne, dlaczego w Tk zdecydowano się nie ukrywać tej różnicy przed użyt-
kownikiem. W pakiecie nterViews (biblioteka X Window oparta na C++, pózniej dostęp-
na pod nazwą  Fresco ) na przyklad widgety i ustrukturyzowane elementy graficzne dzie-
dziczą po ogólnym obiekcie graficznym  glifie. Takie podejście wydaje się bardziej
przejrzyste. Z drugiej strony wdzięczny jestem, że tak precyzyjna implementacja ustruk-
turyzowanych elementów graficznych jest dostępna za darmo, wraz ze świetną doku-
mentacją  a więc moja uwaga w szerszym kontekście ma naprawdę male znaczenie.
Aby narysować odcinek w widgecie pola graficznego, wywolujemy metodę Canvas:
:create:
$glowne = MainWindow->new();
# najpierw tworzymy widget pola graficznego
$pole = $glowne->Canvas(width => 200, height => 100)->pack();
# w tym polu rysujemy odcinek
$id = $pole->create ('line',
10, 10, 100, 100, # od x0,y0 do x1, y1
fill => 'red'); # kolor wypełnienia obiektu
Przegląd widgetów 261
Pierwszym parametrem polecenia create jest typ elementu pola graficznego; pozostale
parametry zależą od pierwszego. create zwraca identyfikator, którego można potem
użyć do odwolania się do danego obiektu. Na przyklad możemy uaktualnić wspólrzęd-
ne obiektu metodą coords:
$pole->coords ($id, 10, 100);
Wszystkie wspólrzędne w Tk są liczone od lewego górnego roku. Wspólrzędna x rośnie
od lewej do prawej, a y  od góry w dól.
Można przesunąć obiekt względem jego bieżącej pozycji. Sluży do tego metoda move:
$pole->move ($id, 15, 23); # 15 i 23 to przesunięcia x i y
Elementy pola graficznego można konfigurować metodą itemconfigure; w tabeli A.5
wymieniono wlaściwości i metody dla elementów każdego typu oraz widgetu pola gra-
ficznego jako calości.
Jedną z najwygodniejszych funkcji obslugiwanych przez pole tekstowe jest możliwość
oznaczania jednego lub wielu elementów lańcuchem-identyfikatorem. Obiekt można
oznaczać dowolną liczbą takich etykiet. Slowo all w identyfikatorze oznacza wszystkie
obiekty pola graficznego. Można oznaczyć obiekt w trakcie tworzenia lub za pomocą me-
tody addtag. dentyfikator current oznacza element, nad którym wlaśnie znajduje się
wskaznik myszy. Wszystkie metody pola graficznego, które przyjmują jako argument
identyfikator elementu, mogą także zamiast niego obslużyć lańcuch. Na przyklad aby
przenieść wszystkie obiekty z etykietą  grupa o 10 pikseli w prawo, napiszemy tak:
$pole->move('grupa', 10, 0); # przesunięcie x = 10, przesunięcie y = 0
Z wlaściwości tej będziemy intensywnie korzystać w rozdziale 15.
W listingu 14.2 przedstawiono program rysujący zestaw okręgów, których środki umiesz-
czone są wzdluż spirali Archimedesa. Wzór na spiralę Archimedesa to r = aŚ, gdzie r,
tzn. promień (oznaczany na rysunku odcinkami), proporcjonalnie zależy od kąta Ś. Dodat-
kowy efekt wizualny osiągnięto przez uzależnienie również promieni okręgów od tego kąta.
Listing 14.2. Rysowanie spirali Archimedesa
use Tk;
$glowne = MainWindow->new();
$pole = $glowne->Canvas(width => 300, height => 245)->pack();
# Rysuje koła wzdłuż spirali Archimedesa
# Śródki tych kół umieszczane są wzdłuż spirali
# (promień spirali = stała * theta)
$oryg_x = 110; $oryg_y = 70; # miejsce początkowe
$PI = 3.1415926535;
$promien_okregu = 5; # promień pierwszego okręgu
$promien_spirali = 0;
262 Rozdział 14. Interfejsy użytkownika oparte na Tk
for ($kat = 0; $kat <= 180;
$promien_spirali += 7, $promien_okregu += 3, $kat += 10)
{
# przesunięcie współrzędnych spirali: r.cos( [theta] ) i r.sin( [theta] )
# sin() i cos() wolą kąty w radianach (stopnie* [pi] /90)
$spir_x = $oryg_x + $promien_spirali * cos ($kat * $PI / 90);
$spir_y = $oryg_y - $promien_spirali * sin ($kat * $PI / 90);
# spir_x oraz spir_y są współrzędnymi środka nowego koła
# Canvas::create wymaga podania górnego lewego i prawego dolnego rogu
$pole->create ('oval',
$spir_x - $promien_okregu,
$spir_y - $promien_okregu,
$spir_x + $promien_okregu,
$spir_y + $promien_okregu,
-fill => 'yellow');
$pole->create ('line',
$oryg_x, $oryg_y,
$spir_x, $spir_y,
fill => 'slategray');
}
MainLoop();
Rysunek 14.4. Kompozycja z ustrukturyzowanych elementów narysowana w polu graficznym
Tekst i wprowadzanie danych
Widget tekstowy (text) wyświetla jeden lub wiele wierszy tekstu oraz umożliwia jego
edycję (domyślne skróty klawiszowe są takie jak w Emacsie  męczarnia dla tych, któ-
rzy wciąż korzystają z vi...). Widget ten jest na tyle funkcjonalny, że może slużyć do wy-
świetlania stron WWW podobnie jak w przeglądarce; zresztą realizowano już takie pro-
jekty  dystrybucja Perl/Tk zawiera implementację przeglądarki internetowej tkweb,
a Guido van Rossum, autor Pythona, napisal za pomocą tego języka i Tk przeglądarkę
internetową Grail, potrafiącą wykonywać aplety Pythona.
W tym punkcie przyjrzymy się pokrótce możliwościom widgetu tekstowego, zaś szcze-
gólowo omówimy go  i na jego bazie zbudujemy aplikację  w rozdziale 16., Przykład
graficznego interfejsu użytkownika: Przeglądarka podręczników man.
Przegląd widgetów 263
Wstawianie tekstu na pozycjach bezwzględnych
Kiedy chcemy programowo wstawić tekst w pewnym określonym miejscu lub w pew-
nym zakresie miejsc, musimy określić przynajmniej jeden indeks. ndeks to lańcuch, np.
 2.5 , oznaczający wiersz 2., kolumnę 6. (numery wierszy liczone są od 1, a kolumn od 0).
Poniższy kod tworzy widget tekstowy i wstawia lańcuch na wybranej pozycji:
$t = $glowne->Text(width => 80, height => 10)->pack();
$t->insert('2.5', 'Próbka');
Wstawianie tekstu na pozycjach logicznych
Widget tekstowy obsluguje koncepcję znacznika (mark)  nazwy jednej pozycji w widge-
cie tekstowym, nadanej przez użytkownika. Pozycja ta liczona jest w odstępach między
dwoma znakami, a nie definiowana jako para wiersz-kolumna. Ulatwia to wstawianie
znaku na pozycji znacznika. Zaznaczona w ten sposób pozycja jest obiektem logicznym;
nie zmienia się po wprowadzeniu lub usunięciu tekstu. Widget obsluguje wbudowane
znaczniki, np. insert (miejsce kursora tekstowego), current (znak najbliższy wskaz-
nikowi myszy), wordend (koniec slowa, nad którym znajduje się wskaznik myszy), end
(koniec wiersza zawierającego kursor tekstowy) itd. Znaczniki można wstawiać zamiast
opisanych wcześniej numerów wierszy i kolumn:
$t->insert("end", "Próbka"); # Insert text at end
Szczególową listę określników indeksów można znalezć w tekście znajdującym się przed
tabelą A.6. W przykladach zamieszczonych w poniższych punktach tworzymy widget
tekstowy i wstawiamy lańcuchy w różnych miejscach, stosując różne typy identyfikatorów.
Wstawianie z indeksowaniem względnym
ndeksy mogą być także definiowane względem indeksu podstawowego. Na przyklad:
$t->insert('insert +5,
'Próbka'); # 5 znaków za pozycją kursora tekstowego
$t->insert('insert linestart', 'Próbka'); # przejdz do pozycji kursora
# tekstowego a następnie do początku
# wiersza
Zmiana właściwości zakresów tekstu za pomocą znaczników
Opisywany widget obsluguje koncepcję znaczników lub znacznikowanych stylów  lań-
cuchów definiowanych przez użytkownika i odpowiadających listom wlaściwości tekstu
(czcionka, kolor, wzór punktowy itp.). Spójrzmy:
$tekst->tagConfigure('foo',
foreground => 'yellow', background => 'red');
Teraz lańcuch  foo można zastosować do jednego lub więcej zakresów tekstu w tym
widgecie, np.:
$tekst->tagAdd('foo', '3.5', '3.7');
264 Rozdział 14. Interfejsy użytkownika oparte na Tk
Spowoduje to podświetlenie fragmentu tekstu w wierszu 3., indeksy od 5. do 7. ndeksy
określające zakres mogą być także bezwzględnymi lub względnymi pozycjami znaczni-
ka. Na przyklad poniższy fragment kodu zmienia wlaściwości wiersza, w którym znaj-
duje się kursor tekstowy:
$text->tagAdd('foo', 'insert linestart', 'insert lineend');
Można określać wiele znaczników opisujących zachodzące na siebie porcje tekstu i prze-
ciwnie, jeden znacznik można zastosować dla wielu zakresów. Wszystkie widgety tek-
stowe obslugują specjalny znacznik sel, który odzwierciedla bieżący zakres zaznaczo-
nego tekstu. Można wstawić nowy tekst i określić dla niego formatowanie  wystarczy
dolączyć jako trzeci parametr insert nazwę znacznika:
$t->insert('Próbka', '3.5','foo');
Widget tekstowy umożliwia osadzanie dowolnego innego widgetu i traktowanie go jak
jednego znaku  możemy więc umieścić w tekście przyciski i listy, które będą  poru-
szaly się wraz z calym tekstem przy wprowadzaniu kolejnych znaków.
Widget wprowadzania danych
Do wprowadzania pojedynczych wierszy tekstu w Perl/Tk sluży widget Entery, który
nie obsluguje znaczników ani osadzonych okien; jest to po prostu taka  lżejsza wersja
widgetu Text.
Perl/Tk (nie oryginalny Tk) udostępnia również widget TextUndo, stanowiący podklasę
widgetu Text. Mechanizm ten pozwala na wycofanie nieograniczonej liczby zmian
w tekście (ale nie można automatycznie wprowadzić ponownie tych zmian  nie można
 wycofać cofania ) oraz obsluguje metody pobierania i zapisywania tekstu z i do plików.
Widget ten nie jest obecny w oryginalnej dystrybucji Tcl/Tk.
Widget tekstowy i dowiązania
Widget tekstowy obsluguje metody TIEHANDLE oraz print, co umożliwia wykorzysta-
nie go jako modulu symulującego uchwyty plików. Oto jak można wykorzystać ten me-
chanizm do przekierowania operacji na uchwycie pliku do widgetu tekstowego:
use Tk;
my $mw = MainWindow->new; # Tworzymy główne okno
my $t = $mw->Scrolled('Text'); # Tworzymy przewijane okno tekstowe
$t->pack(-expand => 1,
-fill => 'both'); # Konfigurujemy je
tie (*TEXT, 'Tk::Text',$t); # dowiązujemy uchwyt pliku do widgetu
print TEXT "Hej tam\n"; # To zostanie wyświetlone w widgecie
Lista
Widget listy (list) sluży do wyświetlania listy lańcuchów, wiersz po wierszu (rysunek 14.5).
Wszystkie lańcuchy wyświetlane są z takimi samymi wlaściwościami. Jeśli chcielibyśmy
użyć różnych czcionek i kolorów, możemy napisać prostą metodę  opakowującą ten
widget i tak zasymulować taką urozmaiconą listę.
Przegląd widgetów 265
Rysunek 14.5. Lista z nazwami win
Domyślnie zdarzenia w liście polegają na wybieraniu i anulowaniu wyboru elementów.
Jeśli wymagamy dodatkowych funkcji  na przyklad specjalnej obslugi dwukrotnego
kliknięcia myszą  musimy sami dowiązać to zdarzenie do wybranej funkcji. Tryby wy-
bierania elementów to: single, browse (domyślny), multiple oraz extended. W trybie sin-
gle lub browse można w danej chwili wybrać tylko jeden element. W trybie browse można
również przeciągnąć wybór, przytrzymując przycisk 1. W trybie multiple można wybrać
dowolną liczbę elementów; wybieranie i anulowanie wyboru nie wplywa na fakt wy-
brania innych elementów. W trybie extended możliwe jest wybieranie wielu elementów
przez klikanie i przeciąganie; jednak pojedyncze kliknięcie, przed wybraniem elementu
bieżącego, powoduje anulowanie poprzedniego wyboru.
Podobnie jak w widgecie tekstowym, można tutaj w różny sposób określać pozycje na
liście  nie tylko za pomocą indeksu. Na przyklad można użyć slów end oraz active
(tam, gdzie znajduje się kursor). ndeksy, wlaściwości i metody listy wymieniono w ta-
beli A.8. Poniżej, w listingu 14.3, przedstawiono kod, w wyniku którego powstala lista
na rysunku 14.5.
Listing 14.3. Lista z przypisanymi funkcjami
use Tk;
$glowne = MainWindow->new();
$lista_win = $glowne->Listbox("width" => 20, "height" => 5
)->pack();
$lista_win->insert('end', # Wstawiamy na końcu taką listę
"Napa Valley Chardonnay", "Cabernet Sauvignon",
"Wytrawne Chenin Blanc", "Merlot", "Sangiovese" );
$lista_win->bind('', \&kup_wino);
sub kup_wino {
my $wino = $lista_win->get('active');
return if (!$wino); # Zakończ, jeśli żaden element listy nie jest aktywny
print "Ach, '$wino'. Cóż za wspaniały gust!\n";
# Usuń wino z listy
$lista_win->delete('active');
}
MainLoop();
Lista nie udostępnia takiej wlaściwości jak command, a więc aby stworzyć dowiązanie
między dwukrotnym kliknięciem myszą a zdefiniowaną przez nas podprocedurą, mu-
simy korzystać z bardziej ogólnej metody bind. Więcej informacji o tej technice przed-
stawimy w punkcie  Dowiązywanie zdarzeń .
266 Rozdział 14. Interfejsy użytkownika oparte na Tk
Ramki
Widgety ramek (Frame) nie są zbyt interesujące; przydają się, gdy chcemy budować bar-
dziej zaawansowane zestawy widgetów lub tworzymy widgety kompozytowe.
Kiedy konstruujemy zlożony formularz GU, najlepiej jest podzielić ekran na części, z któ-
rych każda ma specyficzną funkcję; części te umieszczane są wlaśnie w ramkach. Ramki
takie możemy potem  na wyższym poziomie dowolnie ukladać. Więcej na ten temat
powiemy w punkcie  Zarządzanie geometrią w dalszej części rozdzialu. Widgety ra-
mek są pojemnikami, więc można zażądać od nich utworzenia innych,  podstawowych
widgetów, np. przycisków, pól tekstowych czy pasków przewijania.
Menu
Termin  menu powszechnie stosuje się do określania obiektu pojawiającego się po klik-
nięciu myszy i zawierającego zbiór widgetów etykiet lub przycisków. stnieją trzy typy
menu: menu rozwijane (pulldown menu), menu opcji (option menu) oraz menu podręczne
(popup menu).
Tk udostępnia widget MenuButton, którego kliknięcie może powodować wyświetlenie
podręcznego widgetu Menu. Widget Menu jest po prostu pojemnikiem widgetów sta-
nowiących elementy wyświetlonego menu, nie określa zaś samego ukladu tego elemen-
tu. Do rozróżnienia między koncepcją menu a widgetem Menu użyjemy różnych stylów
czcionek. Elementy zaprezentowano na rysunku 14.6.
Rysunek 14.6. Menu rozwijane i przycisk menu
Aby skonstruować menu, musimy wykonać następujące kroki:
1. Utworzyć pasek menu, w którym zostanie umieszczony widget MenuButton. Pasek
menu to po prostu widget Frame.
2. Utworzyć przynajmniej jeden widget MenuButton i umieścić go w pasku menu.
3. Zażądać od widgetów MenuButton utworzenia i zarządzania widgetami-wpisami menu.
Przegląd widgetów 267
Wlaściwości i interfejs programowania widgetów MenuButton i Menu opisano w tabe-
lach odpowiednio A.9 i A.10. W listingu 14.4 pokazano sposób zaprogramowania menu
widocznego na rysunku 14.6.5
Listing 14.4. Menu rozwijane  Szukaj
use Tk;
$glowne = MainWindow->new();
# Widget Frame pełni rolę pojemnika przycisków menu
$pasek_menu = $glowne->Frame()->pack(side => 'top');
# Przycisk menu "szukaj"
$przycisk_szukaj = $pasek_menu->Menubutton(text => 'Szukaj',
relief => 'raised',
borderwidth => 2,
)->pack(side => 'left',
padx => 2
);
# Przycisk menu "znajdz"
$przycisk_szukaj->command(-label => 'Znajdz',
accelerator => 'Meta+Z',
underline => 0,
command => sub {print "znajdz\n"}
);
# Przycisk menu "znajdz ponownie"
$przycisk_szukaj->command(-label => 'Znajdz ponownie',
accelerator => 'Meta+P',
underline => 7,
command => sub {print "znajdz ponownie\n"}
);
$przycisk_szukaj->separator();
$typ_dopasowania = 'regexp'; # Domyślny typ wyszukiwania: wg wyrażenia
# regularnego
$wielk_lit = 1; # Domyślnie ignorujemy wielkość liter
# (włączamy pole wyboru)
# Dopasowanie wg wyrażenia regularnego
$przycisk_szukaj->radiobutton(-label => 'Wyrażenie regularne',
value => 'regexp',
variable => \$typ_dopasowania);
# Dopasowanie dokładnie podanego łańcucha
$przycisk_szukaj->radiobutton(-label => 'Dokładne dopasowanie',
value => 'exact',
variable => \$typ_dopasowania);
$przycisk_szukaj->separator();
# Ignorujemy wielkość liter
$przycisk_szukaj->checkbutton(-label => 'Ignorować wielkość liter?',
variable => \$wielk_lit);
MainLoop();
5
W celu uzyskania p>prawnie wyświetlanych p>lskich  >g>nków użylem dla p>szczególnych widgetów
d>datk>wej wlaściw>ści, font => '-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-iso8859-2'
 przyp. tłum.
268 Rozdział 14. Interfejsy użytkownika oparte na Tk
W przykladzie na widgecie MenuButton ($przycisk_szukaj) wywolujemy takie me-
tody, jak: command, separator, checkbutton i cascade. Co ciekawe, są to akurat
metody interfejsu widgetu Menu, a nie MenuButton (patrz tabele A.9 i A.10). Ze wzglę-
du na wygodę programisty, polecenia te obsluguje widget Perl/Tk MenuButton  po
prostu  po cichu oddelegowuje je do powiązanego widgetu Menu.
Zazwyczaj wpisy menu ukladane są w tej kolejności, w jakiej zostaly utworzone, ale moż-
na jawnie określić ich pozycję za pomocą metody add. Skladnia indeksowania jest po-
dobna do tej znanej z widgetu listy i zostala opisana w dodatku A. Za pomocą tej meto-
dy będziemy tworzyli dynamiczne menu w rozdziale 16.
Paski przewijania i samo przewijanie
Choć paski przewijania (scrollbar) są  pelnoprawnymi widgetami, rzadko używane są
same  zawsze sterują skojarzonymi z nimi innymi widgetami. Ze względu na to ścisle
powiązanie Perl/Tk udostępnia funkcję pomocniczą Scrolled, która dolącza paski prze-
wijania do wybranego widgetu  nie musimy więc jawnie tworzyć, określać wielkości
i  pakować takich pasków. Poniżej pokazano, jak tworzyć przewijaną listę:
$przewijana_lista = $glowne->Scrolled('Listbox', opcje listy,
scrollbars => 'se');
Wewnętrznie tworzony jest widget Frame, paski przewijania w poziomie i pionie (o ile
są konieczne) oraz lista; następnie wszystko to jest  pakowane , a widget Frame (po-
jemnik) otrzymuje zwrócone odwolanie. Czyż nie pięknie? Perl/Tk upraszcza sprawę
jeszcze bardziej: dla najbardziej typowych obiektów  przewijanych list i pól tekstowych
 udostępnia metody pomocnicze ScrlListBox oraz ScrlText:
$przewijana_lista = $glowne->ScrlListBox(opcje listy);
Wlaściwie tyle trzeba wiedzieć o przewijaniu  Czytelnik może teraz przejść od razu do
punktu  Skala bez utraty ciąglości wywodu.
Niestandardowe przewijanie
Czasem jednak chcemy mieć pelniejszą kontrolę nad przewijaniem. Na przyklad tworzy-
my trzy listy i chcemy zsynchronizować ich przewijanie. Musimy zrobić tak, aby pasek
przewijania informowal wszystkie trzy widgety o fakcie przesunięcia. Jest z tym pewien
drobny problem: mechanizm ten powinien dzialać również w przeciwną stronę, tj. wid-
gety także powinny informować, że zostaly przewinięte z innych przyczyn. Na przyklad
klikamy jedną listę i przeciągamy wskaznik myszy  lista zostanie przewinięta; trzeba
więc zapewnić, aby pasek przewijania oraz dwie pozostale listy zachowaly się podobnie.
nnymi slowy, elementem sterującym nie zawsze jest tutaj pasek przewijania  jest to
raczej relacja obustronna.
Jak widać w tabeli A.11, nie istnieje żadna jawna wlaściwość dowiązująca pasek przewija-
nia do widgetu, ale istnieje wlaściwość command umożliwiająca powiadomienie pewnej
funkcji o fakcie przesunięcia suwaka. Ponadto wszystkie widgety umożliwiające przewijanie
Przegląd widgetów 269
(listy, pola tekstowe, ramki i pola graficzne) obslugują dwie metody xview i yview (ta-
bela A.12), które informują przewijany widget, która część jego treści ma zostać wyświe-
tlona w oknie. A więc żeby pasek przewijania powodowal przewinięcie widgetu, skon-
figurujemy wlaściwość command takiego paska w następujący sposób:
$pasek->configure (command => [N$widget]);
Pasek przewijania automatycznie wywoluje określoną metodę (xview lub yview) na
widgecie. Skąd widget wie, o ile ma zostać przewinięty? Otóż, o czym jeszcze nie wspomi-
naliśmy, pasek dolącza do wywolania yview argumenty  wewnętrznie komunikat wy-
slany z paska przewijania do widgetu wygląda więc mniej więcej tak:
$widget->yview('moveto', 30);
Takie polecenie powoduje, że widget tak przemieszcza swoją zawartość, że górny wiersz
lub piksel ustawiony jest na 30 procentach wysokości.
A teraz spójrzmy, jak ten mechanizm dziala w drugą stronę, gdy to widget informuje
pasek przewijania.
Wszystkie przewijane widgety obslugują metody xscrollcommand oraz yscroll-
command, które powinniśmy skonfigurować tak, aby wywolywaly metodę set paska
przewijania:
$lista->configure ('yscrollcommand', [$pasek]);
Tę symbiotyczną relację przedstawiono na rysunku 14.7. Szczególowe informacje o wy-
mienionych wyżej poleceniach i wlaściwościach można znalezć w tabelach A.11 i A.12.
Rysunek 14.7. Interakcja między paskiem przewijania i skojarzonym z nim widgetem (listą)
W listingu 14.5 pokazano, jak wspólpracują ze sobą poszczególne polecenia konfigura-
cyjne w polączeniu z jedną listą.
Listing 14.5. Konfigurowanie paska przewijania i listy, tak aby wzajemnie powodowały przewijanie
use Tk;
$glowne = MainWindow->new();
$lista_aut = $glowne->Listbox("width" => 15, "height" => 4,
)->pack(side => 'left',
padx => 10);
270 Rozdział 14. Interfejsy użytkownika oparte na Tk
$lista_aut->insert('end', # Dopisujemy następujące elementy na końcu listy
"Acura", "BMW", "Ferrari", "Lotus", "Maserati",
"Lamborghini", "Chevrolet"
);
# Rysujemy pasek przewijania i informujemy go o liście
$pasek = $glowne->Scrollbar(orient => 'vertical',
width => 10,
command => ['yview', $lista_aut]
)->pack(side => 'left',
fill => 'y',
padx => 10);
# Informujemy też listę o pasku
$lista_aut->configure(yscrollcommand => ['set', $pasek]);
MainLoop();
Skala
Widget skali (scale) przypomina termometr. Wzdluż poziomego lub pionowego  koryt-
ka wyświetlane są odpowiednie oznaczenia; wewnątrz korytka znajduje się suwak,
którym można poruszać programowo lub ręcznie (za pomocą myszy lub klawiatury).
Wlaściwości i metody skali wymieniono w tabeli A.13.
Na rysunku 14.8 przedstawiono dwie skale z podzialkami Celsjusza i Fahrenheita (ba-
zowa jest skala Celsjusza od 0 do 100 stopni). Skale są skoordynowane  przesunięcie
jednego suwaka wplywa na drugi suwak.
Rysunek 14.8. Skoordynowane skale z podziałkami Celsjusza i Fahrenheita
W listingu 14.6 pokazano, jak można zaimplementować taki program.
Listing 14.6. Konwersja stopni Celsjusza na Fahrenheita z użyciem widgetów skali
use Tk;
# Przelicznik stopni Celsjusza na Fahrenheita z wykorzystaniem skal
$glowne = MainWindow->new();
$wart_celsjusza = 50;
oblicz_fahrenheita();
Przegląd widgetów 271
#---------------------- Skala CELSJUSZA------------------------------
$glowne->Scale(orient => 'horizontal',
from => 0, # Od 0 stopni C
to => 100, # do 100 stopni C
tickinterval => 10,
label => 'Celsius',
font => '-adobe-helvetica-medium-r-normal'
. '--10-100-75-75-p-56-iso8859-1',
length => 300, # w pikselach
variable => \$wart_celsjusza, # zmienna globalna
command => \&oblicz_fahrenheita # zmieniamy Fahr.
)->pack(side => 'top',
fill => 'x');
#---------------------- Skala FAHRENHEITA----------------------------
$glowne->Scale(orient => 'horizontal',
from => 32, # Od 32 stopni F
to => 212, # do 212 stopni F
tickinterval => 20, # co 20 stopni
label => 'Fahrenheit',
font => '-adobe-helvetica-medium-r-normal'
. '--10-100-75-75-p-56-iso8859-1',
length => 300, # w pikselach
variable => \$wart_fahrenheita, # zmienna globalna
command => \&oblicz_celsjusza # zmieniamy Cels.
)->pack(side => 'top',
fill => 'x',
pady => '5');
sub oblicz_celsjusza {
# Suwak skali Celsjusza automatycznie się porusza po zmianie
# zmiennej $wart_celsjusza
$wart_celsjusza = ($wart_fahrenheita - 32)*5/9;
}
sub oblicz_fahrenheita {
$wart_fahrenheita = ($wart_celsjusza * 9 / 5) + 32;
}
MainLoop();
W przykladzie tym, w wyniku przesunięcia suwaka skali Celsjusza, wywolywana jest
funkcja oblicz_fahrenheita(). Funkcja ta zmienia wartość $wart_fahrenheita,
skojarzoną ze skalą Fahrenheita. Jak widać, obsluga skali zazwyczaj wymaga tylko uży-
cia wlaściwości command i variable. Nie trzeba jawnie wywolywać metody set().
Lista hierarchiczna
Dane hierarchiczne, np. struktury systemu plików lub wykresy obrazujące organizację,
można przedstawić za pomocą widgetu listy hierarchicznej HList. Każdy wpis jest wci-
nany o jeden poziom względem wpisu nadrzędnego. Opcjonalnie HList może rysować
rozgalęzienia; z wpisami można również skojarzyć ikony oraz inne widgety. Wpis identy-
fikowany jest nie przez indeks (jak w liście), lecz przez  ścieżkę wpisu przypominającą
ścieżkę do pliku (rolę separatora pelni wybrany przez użytkownika znak). Niektóre in-
teresujące wlaściwości i metody widgetu HList wymieniono w tabeli A.14.
272 Rozdział 14. Interfejsy użytkownika oparte na Tk
W listingu 14.7 budujemy opartą na widgecie HList przeglądarkę systemu plików. Dwu-
krotne kliknięcie katalogu powoduje rozwinięcie lub zwinięcie jego zawartości oraz od-
powiednią zmianę ikony.
W listingu 14.7 przedstawiono jeden ze sposobów uzyskania wyniku zilustrowanego na
rysunku 14.9. Warto zwrócić szczególną uwagę na kod otwierający i ustawiający mapy
bitowe, a także na część zmieniającą ksztalt kursora po wykonaniu zadania.
Listing 14.7. Przeglądarka systemu plików oparta na widgecie HList
use Tk;
require Tk::HList;
$glowne = MainWindow->new();
$h = $glowne->Scrolled('HList',
'-drawbranch' => 1,
'-separator' => '/',
'-indent' => 15,
'-command' => \&pokaz_ukryj_katalog,
)->pack('-fill' => 'both',
'-expand' => 'y');
$ikony{"otwarty"} = $glowne->Bitmap(-file => './otwarty_folder.xbm');
$ikony{"zamkniety"} = $glowne->Bitmap(-file => './folder.xbm');
pokaz_ukryj_katalog("/");
MainLoop();
#-----------------------------------------------------------------------
sub pokaz_ukryj_katalog { # Wywoływana po dwukrotnym kliknięciu wpisu
my $sciezka = $_[0];
return if (! -d $sciezka); # To nie katalog
if ($h->info('exists', $sciezka)) {
# Przełączamy status katalogu.
# Poznajemy, czy jest otwarty, po sprawdzeniu następnego wpisu:
# otwarty jest wtedy, gdy jest to podłańcuch bieżącej ścieżki
$nast_wpis = $h->info('next', $sciezka);
if (!$nast_wpis || (index ($nast_wpis, "$sciezka/") == -1)) {
# Nie jest otwarty. Otwieramy.
$h->entryconfigure($sciezka, '-image' => $ikony{"otwarty"});
dodaj_zaw_katalogu($sciezka);
} else {
# Jest otwarty. Zamykamy, zmieniamy ikonę i usuwamy podrzędny węzeł.
$h->entryconfigure($sciezka, '-image' => $ikony{"zamkniety"});
$h->delete('offsprings', $sciezka);
}
} else {
die "'$sciezka' nie jest katalogiem\n" if (! -d $sciezka);
$h->add($sciezka, '-itemtype' => 'imagetext',
'-image' => $ikony{"otwarty"}, '-text' => $sciezka);
dodaj_zaw_katalogu($sciezka);
}
}
sub dodaj_zaw_katalogu {
my $sciezka = $_[0];
my $starykursor = $glowne->cget('-cursor');
$glowne->configure('-cursor' => 'watch');
$glowne->update();
my @pliki = <$sciezka/*>;
Zarządzanie geometrią 273
foreach $plik (@pliki) {
$plik =~ s|//|/|g;
($tekst = $plik) =~ s|^.*/||g;
if (-d $plik) {
$h->add($plik, '-itemtype' => 'imagetext',
'-image' => $ikony{"zamkniety"}, '-text' => $tekst);
} else {
$h->add($plik, -itemtype => 'text',
'-text' => $tekst);
}
}
$glowne->configure('-cursor' => $starykursor);
}
Rysunek 14.9. Widget HList w przeglądarce systemu plików
Na tym kończy się nasza przechadzka po galerii widgetów Tk i Tix. Opis innych wid-
getów można znalezć w dokumentacji Tk; w katalogu contrib dystrybucji Tk można na-
tomiast znalezć widgety dostarczone przez niezależnych programistów. Spójrzmy teraz
na pozostale mechanizmy udostępniane przez pakiet Tk: zarządzanie geometrią, liczniki
czasu oraz dowiązania i pętle zdarzeń.
Zarządzanie geometrią
Zobaczyliśmy już, do czego sluży metoda pack. Chodzi o koncepcję określaną mianem
 zarządzania geometrią  ukladania widgetów na ekranie i określania sposobu ich za-
chowania, gdy wymiary ekranu ulegną zmianie. Tk obsluguje trzy typy menedżerów
geometrii: placer, packer oraz grid. Najprostszy jest menedżer placer; podobnie jak w przy-
padku widgetu Bulletin Board pakietu Motif albo w przypadku regul zarządzania geo-
metrią Visual Basic, wystarczy określić tutaj wspólrzędne x i y każdego widgetu. Więcej
informacji o menedżerze placer można znalezć w dokumentacji Tk.
Packer
Packer, podobnie jak widget Form pakietu Motif, jest dobrym menedżerem geometrii,
obslugującym koncepcję dostępnego (pozostalego) pola, na którym widget można umieścić.
Packer nie jest obiektem; to po prostu algorytm zaimplementowany za pomocą metody
pack(). nnymi slowy, wywolanie $widget->pack() oznacza, że polecamy widge-
towi  upakować się w najbliższym dostępnym miejscu wewnątrz widgetu nadrzędnego.
274 Rozdział 14. Interfejsy użytkownika oparte na Tk
Kiedy pakujemy walizkę, zazwyczaj zaczynamy od jednego końca i każdy następny
przedmiot umieszczamy w najbliższym wolnym miejscu. Dokladnie tak dziala packer;
jest jednak jedna ważna różnica. Kiedy już widget dolączony jest do krawędzi widgetu
nadrzędnego, cala ta krawędz jest  odcinana z pozostalej dostępnej przestrzeni. Algo-
rytm ten zilustrowano na rysunku 14.10.
Rysunek 14.10. Algorytm  pakowania
Gdyby w zilustrowanym wyżej przykladzie parametrowi side przekazano wartość top
lub bottom, wysokość odciętej  dzialki zależna bylaby od wysokości etykiety.
Funkcja pack sluży do wykonywania trzech czynności:
" Określania kolejności pakowania widgetów.
Kolejność wywolania pack wplywa na kolejność umieszczania widgetów w oknie;
to zaś wplywa na ilość miejsca dostępnego dla widgetu. Kiedy wymiary pojemnika
ulegają zmianie, algorytm pakowania wykonywany jest ponownie, przy zastosowaniu
tej samej kolejności.
" Określania, jak wypelniana jest dostępna  dzialka .
Tym ustawieniem steruje wartość fill: x (rozciągnij widget wzdluż osi x do pelnej
szerokości  dzialki ); y (rozciągnij widget wzdluż osi y do pelnej wysokości  dzialki );
both (wzdluż obu osi); none (nie rozciągaj). Opcje ipadx i ipady powodują, że
wokól widgetu pozostawiane jest trochę miejsca (konieczne jest wtedy przydzielenie
odpowiednio większej  dzialki ). Opcja anchor określa krawędz lub róg  dzialki ,
do którego przylega widget. Domyślnie opcja ta ma wartość center.
Zarządzanie geometrią 275
" Określania, co ma się stać z pozostalym miejscem widgetu nadrzędnego po wstawieniu
wszystkich wymaganych widgetów potomnych.
Sluży do tego parametr expand ( rozciągnij ). Zazwyczaj ostatni wstawiany widget
zajmuje cale pozostale miejsce   wypelnia je w calości. Jeśli jednak inne widgety
byly pakowane z wartością expand ustawioną na y (tak), wtedy dodatkowe miejsce
w poziomie dzielone jest równo między wszystkie widgety z taką wartością  ale te,
których wartość side jest równa left bądz right. Podobnie dodatkowe miejsce
w pionie dzielone jest między wszystkimi widgetami z wartościami top lub bottom.
Algorytm pakowania nigdy nie dopuszcza do nakladania się widgetów.
A jak umieścić trzy widgety po lewej stronie (tak jak na rysunku 14.11), jeśli pierwszy
ma zająć calą  pionową dzialkę ?
Rysunek 14.11. Trzy widgety  upakowane po lewej stronie
Jedynym sposobem rozwiązania tego problemu jest utworzenie widgetu ramki i umiesz-
czenie go po lewej stronie okna glównego. Ponieważ ramka jest pojemnikiem innych wid-
getów, te trzy widgety można umieścić wewnątrz ramki  można to uzyskać, pisząc
następujący kod:
$ramka->pack(-side => 'left', -fill => 'y', -expand => 'y');
# Teraz tworzymy przyciski p1, p2 i p3 jako elementy potomne ramki
# i pakujemy je od góry do dołu
$b1 = $ramka->Button (-text => 'Hej ')->pack();
$b2 = $ramka->Button (-text => 'cześć')->pack();
$b3 = $ramka->Button (-text => 'wam ')->pack();
pack domyślnie umieszcza widgety, zaczynając od góry.
Alternatywnym sposobem  i być może prostszym  jest skorzystanie z menedżera
geometrii grid.
Grid
Metoda grid informuje widget, że ma skorzystać z uslug menedżera geometrii grid (siat-
ka). Wszystkie widgety potomne danego widgetu macierzystego muszą używać tego
samego menedżera geometrii; jednak dla dowolnej kombinacji widgetów macierzystego
i potomnych oraz dla widgetów osadzonych wewnątrz tych potomnych możemy użyć
dowolnego menedżera geometrii.
276 Rozdział 14. Interfejsy użytkownika oparte na Tk
Menedżer geometrii grid umożliwia ukladanie widgetów w wierszach i kolumnach, po-
dobnie jak to się robi z użyciem znaczników HTML-a do tworzenia tabel. Szerokość ko-
lumny zależy od najszerszego widgetu w niej umieszczonego, a wysokość  od najwyż-
szego. Siatkę taką uzyskujemy w następujący sposób:
$przycisk->grid (row =>0, column =>0);
To polecenie umieszcza przycisk w górnym lewym rogu.
Podobnie jak w przypadku tabel HTML, widget może zajmować dowolną liczbę wierszy
i kolumn  slużą do tego opcje rowspan i columnspan. Widget wciąż jednak należy
do wiersza i kolumny określonych opcjami row i column:
$przycisk->grid(row => 1, col => 2,
columnspan => 2, sticky => 'ns');
Utworzony powyżej przycisk zajmuje dwie kolumny, ale nie wykorzystuje calego dostęp-
nego miejsca. Opcja sticky powoduje, że widget  przylepiony jest do ścianek pólnoc-
nej i poludniowej swojej komórki. Gdyby wartość tej opcji wynosila nsew (north  pól-
noc; south  poludnie; east  wschód; west  zachód), widget zostalby rozciągnięty na
calą komórkę. Domyślnie widget umieszczany jest w centrum  dzialki i zajmuje tylko
tyle miejsca, ile musi zajmować. Podobnie jak packer, menedżer grid także obsluguje
opcje ipadx oraz ipady.
Liczniki czasu
Tk udostępnia niezajmujące wiele zasobów liczniki czasu: procedura może zostać wywo-
lana po uplynięciu czasu określonego w milisekundach. Jednorazowe liczniki tworzymy
metodą after  można ją zastosować w dowolnym widgecie; liczniki powtórzeniowe
uzyskuje się metodą repeat. W poniższym przykladzie przycisk zmienia etykietę po
wciśnięciu, a po 300 milisekundach etykieta powraca do poprzedniego stanu.
$przycisk->configure (text => 'A kuku.',
command => \&zmien_nazwe);
sub zmien_nazwe {
my ($stara_nazwa) = $przycisk->cget('text');
$przycisk->configure (text => '...ojej');
$przycisk->after (300,
sub {$przycisk->configure(text => $stara_nazwa)});
}
Z metody after będziemy intensywnie korzystali w rozdziale 15. do tworzenia animacji.
Obie metody, after i repeat, zwracają obiekty w postaci liczników czasu. Ponieważ
mechanizmy te są dość wydajne i malo wymagające (w przeciwieństwie do funkcji
alarm() i sygnalu SIGALRM), możemy tworzyć wiele liczników bez obawy o szybkość
dzialania programu. Aby anulować licznik, używamy metody cancel:
$licznik = $przycisk->after(100, sub {print "foo"});
$licznik->cancel();
Dowiązywanie zdarzeń 277
Mechanizm liczników w Tk, w przeciwieństwie do SIGALRM, nie powoduje wywlaszcze-
nia; aby bylo możliwe sprawdzenie, czy licznik skończyl odliczanie, kontrola musi zostać
przekazana do pętli zdarzeń. Dlugo dzialająca podprocedura może więc opóznić odliczanie.
Dowiązywanie zdarzeń
Dowiązanie zdarzenia jest skojarzeniem pewnej funkcji z dowolnym typem zdarzenia.
Przyklady już widzieliśmy  na przyklad wlaściwość command widgetu przycisku po-
woduje, że kliknięcie przycisku myszy wywoluje procedurę zdefiniowaną przez użytkow-
nika. Polecenie bind() zapewnia bardziej ogólny (a więc niskopoziomowy) dostęp do
tych najbardziej podstawowych zdarzeń, takich jak wciśnięcie lub zwolnienie klawisza na
klawiaturze czy przycisku myszy (kliknięcie myszą sklada się z wciśnięcia i puszczenia
przycisku myszy  jak widać, zajmujemy się tutaj naprawdę niskopoziomowymi zda-
rzeniami). nne  interesujące zdarzenia to ruchy myszą, wejście wskaznika myszy w ob-
szar okna lub wyjście z niego, przeniesienie lub zmiana wielkości okna. Same widgety
do dzialania i tak wykorzystują metodę bind(), ale użytkownik może określić wlasne
dowiązania. Dowiązana procedura wykonywana jest wtedy, gdy śledzone zdarzenie ma
miejsce wewnątrz widgetu lub jest z nim związane (np. zmiana wielkości okna).
Skladnia funkcji bind jest następująca:
$widget->bind(sekwencja zdarzeń, podprocedura);
Sekwencja zdarzeń to lańcuch zawierający nazwy podstawowych zdarzeń; każde pod-
stawowe zdarzenie umieszczane jest w nawiasach kątowych. Poniżej przedstawiono
przyklady sekwencji zdarzeń:
"" # Wciśnięto klawisz "a" (nie wciśnięto klawisza
# Control/shift/meta
" # Wciśnięto Control i a
" " # Sekwencja dwóch zdarzeń
"" # Wciśnięto przycisk 1 myszy
"" # Poruszono myszą przy wciśniętym przycisku 1
Jedno zdarzenie (to umieszczane w nawiasach kątowych) ma następującą skladnię:
""
Przyklady modyfikatorów to: Control, Meta, Alt, Shift, Button1 (lub B1), Button2,
Double (dwukrotne kliknięcie) i Triple (kliknięcie potrójne). Modyfikator Any ozna-
cza wszystkie możliwe modyfikatory (a także brak modyfikatora).
Typ zdarzenia to KeyPress (wciśnięcie klawisza), KeyRelease (puszczenie klawisza),
ButtonPress lub Button (wciśnięcie przycisku myszy), ButtonRelease (puszczenie
przycisku myszy), Enter (wejście w obszar okna), Leave (wyjście z obszaru okna) oraz
Motion (ruch).
Przy określaniu zdarzeń związanych z klawiaturą składnik jest lańcuchem określającym
konkretny klawisz. W przypadku znaków ASC jest to po prostu sam znak; inne sym-
bole to: Enter, Right, Pickup, Delete, BackSpace, Escape, Help, F1 itd.
278 Rozdział 14. Interfejsy użytkownika oparte na Tk
Najczęściej obslugiwane zdarzenia to wciśnięcia klawiszy i kliknięcia myszą. Dlatego
w Tk wprowadzono skrótowy zapis: zamiast pisać , wystarczy
; a za-
miast , wystarczy <1>.
Widgety pól tekstowego i graficznego umożliwiają precyzyjniejsze określanie zdarzeń.
Oprócz zdarzeń związanych z samym widgetem, można tutaj dowiązać procedury do
funkcji związanych ze znacznikami. Jako pierwszy argument bind można podać nazwę
znacznika, a jako drugi i trzeci  odpowiednio sekwencję zdarzeń i nazwę podprocedury:
$tekst->bind('odsylacz', '<1>', \&otworz_strone);
Po wprowadzeniu takiego kodu każda porcja tekstu oznaczona jako  odsylacz będzie
reagowala na kliknięcie przyciskiem myszy przez wywolanie podprocedury otworz_
strone.
Dowiązywanie wielu podprocedur
Do tego samego zdarzenia można dowiązać kilka podprocedur. Na przyklad wciśnięcie
przycisku myszy może być interpretowane zarówno jako , jak i jako -Button1>. Jeśli dla danego widgetu (lub znacznika) zachodzi konflikt zdarzeń, wy-
wolywana jest procedura określona dla zdarzenia bardziej specyficznego. Tutaj bardziej
specyficznym zdarzeniem jest (zdarzenie to jest dokladniej opisa-
ne niż ).
Oprócz dopasowania najbardziej specyficznego dowiązania na poziomie widgetu, Tk
podobnie postępuje na poziomie klasy (np. klasy  wszystkie przyciski), potem na po-
ziomie glównym widgetu, wreszcie na poziomie o nazwie  all (wszystkie). Wykony-
wane są dowiązania wszystkich czterech kategorii. Tę kolejność można zmienić metodą
bindtags(), jednak nie zalecam takiego postępowania.
Choć Tk umożliwia zmianę domyślnych dowiązań widgetów, zalecam pozostawienie ich
niezmienionych. Użytkownicy przyzwyczajają się do pewnego sposobu dzialania progra-
mów. Na przyklad dwukrotne kliknięcie wewnątrz widgetu pola tekstowego zazwyczaj
powoduje zaznaczenie slowa znajdującego się pod wskaznikiem myszy; użytkownik mógl-
by odczuć zmniejszenie wygody korzystania z programu, gdybyśmy dowiązanie to zmie-
nili. Z drugiej strony istnieje wiele miejsc, w których można  i powinno się  dodawać
wlasne dowiązania. Najczęściej robi się to w polach tekstowym i graficznym, o czym
przekonamy się w następnych dwóch rozdzialach.
Składniki
Wiemy już, jak precyzyjnie określić zdarzenie. Są jednak sytuacje, w których musimy
postąpić dokladnie odwrotnie: chcemy zdefiniować zdarzenie możliwie ogólnie, np. jako
(wciśnięcie dowolnego klawisza). Przecież nie będziemy określali
oddzielnych dowiązań dla każdego klawisza. Jednak kiedy już klawisz zostanie wciśnięty,
być może przydaloby się, aby podprocedura uzyskiwala informację o tym, który to byl
klawisz. Tu wlaśnie dochodzą do glosu skladniki zdarzenia.
Pętle zdarzeń 279
Każde zdarzenie zawiera informację o skladnikach; do uzyskania tych informacji sluży
funkcja Ev(). Parametrem tej funkcji jest jeden znak określający tę część rekordu zda-
rzenia, jaka nas interesuje. Ev('k') określa kod znaku, Ev('x') i Ev('y')  wspól-
rzędne x i y wskaznika myszy, a Ev('t')  czas zdarzenia. Funkcja Ev obsluguje po-
nad 30 takich parametrów. Poniżej pokazano, jak korzystać z tego mechanizmu:
$etykieta->bind("" => [\&porusz, Ev('k')]);
sub porusz {
my $klawisz = shift;
if ($klawisz eq 'k') {
porusz_w_lewo();
} elsif ($klawisz eq 'l') {
porusz_w_prawo();
}
}
W tym przykladzie funkcja bind jest tak uruchomiona, że rejestruje zdarzenia związane
z wciskaniem klawiszy; do określonej przez użytkownika podprocedury przesylane są
przy każdym wywolaniu kody klawiszy.
Pętle zdarzeń
MainLoop uruchamia pętlę zdarzeń. Przechwytuje ona zdarzenia związane z systemem
okien i przekazuje je odpowiednim widgetom. Kiedy w odpowiedzi na zdarzenie wywo-
lywana jest pewna procedura, oczekuje się, że jej wykonywanie zostanie zakończone
możliwie szybko; w przeciwnym razie wstrzymuje ona wszystkie kolejne zdarzenia, które
zaszly od chwili jej wywolania.
Jeśli konieczne jest wykonanie dlugiej czynności, intensywnie korzystającej z procesora,
programista powinien podzielić ją na mniejsze fragmenty i ustawić licznik, który będzie
to zadanie wykonywal w regularnych odstępach czasu. Dzięki temu pętla zdarzeń ma
szansę obslużyć oczekujące zdarzenia. Takie dzielenie czasu procesora określa się mia-
nem  wielozadaniowości równoleglej . Wczesne wersje Microsoft Windows (do wersji 3.1)
wymagaly, aby aplikacje zachowywaly się wlaśnie w taki, poprawny sposób. W prze-
ciwnym razie  zamrażaly one caly system operacyjny.
W zadaniach takich jak generacja grafiki trójwymiarowej czy animacje  czyli intensyw-
nie korzystających zarówno z procesora, jak i z interfejsu graficznego  można użyć
metody $widget->update, która powoduje przetworzenie wszystkich zdarzeń. Kończy
ona dzialanie dopiero wtedy, gdy wszystkie oczekujące w kolejce komunikaty (w tym
żądania odświeżenia ekranu) zostaną przetworzone.
W środowiskach ukierunkowanych na zdarzenia nie powinno się stosować blokujących
funkcji systemowych, o czym powiedzieliśmy już w rozdziale 12. Najbardziej znane
funkcje tego typu to read i write, szczególnie jeśli wykonywane są na potokach
i gniazdach. Na przyklad operator <> blokuje wykonywanie programu dopóty, dopóki
nie uzyska kolejnego wiersza tekstu. Zamiast bezpośrednio wykonywać operację wej-
ścia-wyjścia, trzeba spowodować, aby Tk poinformowal nas, że taką operację można już
280 Rozdział 14. Interfejsy użytkownika oparte na Tk
przeprowadzić  wiemy, że wtedy nie zablokuje ona programu. Tk udostępnia proce-
durę fileevent, która powiadamia procedurę, kiedy deskryptor pliku jest gotowy do
odczytu lub zapisu. Korzystamy z niej w następujący sposób:
open (F, "/tmp/foo");
$przycisk->fileevent(F, "readable", \&odczytaj_plik);
sub odczytaj_plik {
if (eof(F)) {
$przycisk->fileevent(F, "readable", undef); # anuluj dowiązanie
return ;
}
if (sysread (F, $buf, 1024)) {
$tekst->insert('end', $buf); # dopisz odczytane dane
} else {
# sysread zwrócił undef. Problem z plikiem.
$tekst->insert('end', "Błąd !!!";
$przycisk->fileevent(F, "readable", undef); # anuluj dowiązanie
}
}
Kiedy wywolana jest taka procedura, Tk (który w Uniksie wykorzystuje wewnętrznie
funkcję select) gwarantuje, że do odczytu lub zapisu jest gotowy najwyżej jeden znak.
Nie gwarantuje się gotowości większej ilości danych  dalszy odczyt lub zapis spowo-
duje lub nie spowoduje blokowania. Funkcja wywolywana jest również w przypadku
napotkania znaku końca pliku lub wystąpienia blędu, trzeba więc sprawdzać, czy nie za-
szla któraś z tych okoliczności. W przeciwnym razie procedura wykonywana jest zaraz
po tym, jak zwrócila dane  czyli tworzona jest nieskończona pętla. Jak powiedzieliśmy
przy okazji poruszania tematyki sieciowej, najlepiej stosować nieblokujące funkcje wej-
ścia-wyjścia, o ile tylko są obslugiwane przez system.
W tym rozdziale omówiliśmy widgety, pętle zdarzeń, liczniki czasu i dowiązania zda-
rzeń. W następnych dwóch polączymy opisane koncepcje i zastosujemy je do rozwiąza-
nia praktycznych problemów. Zobaczymy również, jak w naprawdę ciekawy sposób
można wykorzystać dwa przydatne widgety Tk: pola graficzne i tekstowe.
yródła informacji
1. Biblioteki i teksty o Tcl/Tk dostępne pod adresami http://www.sunlabs.com/research
/Tcl (strony firmy Sun) i http://www.neosoft.com (wszystko o Tcl/Tk).
2. Dokumentacja Perl/Tk.
3. About Face: The Principles of User-Interface Design. Alan Cooper. DG Books
Worldwide, 1995.
Książka forsująca nowy sposób postrzegania tematyki projektowania interfejsów
graficznych.
4. Bringing Design to Software. Terry Winograd. Addison-Wesley, 1996.
Specjaliści różnych dziedzin piszą o projektowaniu dobrych programów,
szczególnie interfejsów użytkownika.


Wyszukiwarka

Podobne podstrony:
Perl Zaawansowane programowanie Wydanie II perlz2
PHP Zaawansowane programowanie Vademecum profesjonalisty
Zaawansowane programowanie w T SQL
JavaScript Zaawansowane programowanie
C Zaawansowane programowanie zaprcp
Zaawansowane programowanie w PHP
mod perl Podrecznik programisty modpkp
Linux Apache MySQL i PHP Zaawansowane programowanie lapzap

więcej podobnych podstron