2 / 2 0 0 5
Wszelkie prawa zastrzeżone. Rozpowszechnianie artykułu bez zgody Software Wydawnictwo Sp. z o.o. zabronione.
Software Wydawnictwo Sp. z o.o., ul. Lewartowskiego 6, 00-190 Warszawa, POLSKA.
redakcja@phpsolmag.org
Projekty
PHP Solutions Nr 2/2005
www.phpsolmag.org
2
Projekty
PHP-GTK
PHP Solutions Nr 2/2005
www.phpsolmag.org
3
C
zęste uruchamianie wielu kom-
puterów jest bardzo męczące,
szczególnie, gdy są one rozsia-
ne po wielu pomieszczeniach w budynku.
Można jednak ułatwić tę czynność – słu-
żą do tego aplikacje typu Wake-On-Lan
(WOL), które budzą wszystkie lub wybra-
ne komputery znajdujące się w sieci lo-
kalnej na podstawie adresów MAC ich
kart sieciowych.
Wzięliśmy pod lupę jeden z takich
programów. Jego instalację opisaliśmy
w Ramce Instalacja Wake-On-Lan. Jest
to prosta aplikacja działająca w trybie tek-
stowym – aby ją uruchomić, trzeba w linii
poleceń wpisać
wakeonlan <adres _ MAC>
,
np.
wakeonlan 3F:34:00:AC:90:A0
i zdalny
komputer zostanie uruchomiony. Już to
jest powodem do radości – o niebo lepiej
przecież wprowadzić serię takich instruk-
cji, niż biegać po całym budynku! Dlacze-
go by jednak nie ułatwić sobie życia auto-
matyzując ten proces? Zrobimy to, wypo-
sażając to pożyteczne narzędzie w inter-
Za pomocą biblioteki PHP-GTK możemy tworzyć
profesjonalne aplikacje okienkowe przy użyciu
PHP. Wystarczy przećwiczyć ją na paru prostych
przykładach i zacząć pisać programy, do których
dawniej potrzebowalibyśmy znajomości C++ czy
Javy.
fejs graficzny (frontend), do napisania któ-
rego użyjemy PHP z biblioteką PHP-GTK.
Jest ona darmowa i dostępna na naszej
płycie oraz w Sieci (jej instalację opisali-
śmy w Ramce Instalacja PHP-GTK).
Dla wielu programistów PHP jest to
nowość – przyzwyczaili się bowiem, że
PHP to wyłącznie wygodny język skryp-
towy umożliwiający tworzenie zaawanso-
wanych stron WWW lub aplikacji interne-
towych. Niewielu natomiast zdaje sobie
sprawę z faktu, że po zainstalowaniu
Tworzymy frontend
do Wake-On-Lan w PHP-GTK
Jacek Niewęgłowski
W SIECI
NA CD
Na CD zamieściliśmy kody
źródłowe gotowego interfejsu,
aplikację wakeonlan oraz roz-
szerzenie PHP-GTK pod Win-
dows i Linuksa.
1. http://gtk.php.net
2. http://gtk.php.net/manual/en/
reference.php
3. http://oldgsd.lsd.di.uminho.pt./
jpo/software/wakeonlan/
4. http://www.gtk.org
Co należy wiedzieć...
Czytelnik powinien mieć podstawową
znajomość składni języka PHP. Przydat-
na też będzie wiedza z zakresu podstaw
programowania obiektowego, w tym po-
jęć klasa i obiekt.
Co obiecujemy...
Programista dowie się, jak stworzyć
graficzny interfejs do programu Wake-
On-Lan i zdobędzie wystarczającą wie-
dzę, aby samodzielnie tworzyć aplikacje
z wykorzystaniem PHP-GTK.
Projekty
PHP Solutions Nr 2/2005
www.phpsolmag.org
2
Projekty
PHP-GTK
PHP Solutions Nr 2/2005
www.phpsolmag.org
3
PHP-GTK parser PHP może również
stać się potężnym narzędziem do budo-
wania w pełni funkcjonalnych, samodziel-
nych aplikacji okienkowych działających
zarówno pod kontrolą systemu Linux jak
i Windows.
Okno na dzień dobry
Tworzenie interfejsu graficznego (w skró-
cie GUI) przy użyciu PHP-GTK nie jest
zbyt trudne (co wynika m.in. z przejrzy-
stości biblioteki GTK). Czas jego wdroże-
nia zależy przede wszystkim od tego, jak
dobrze zaprojektujemy go przed przystą-
pieniem do programowania.
Najprostszy przykład zastosowania
PHP-GTK znajduje się na Listingu 1 – te
kilka linijek kodu to wszystko, czego po-
trzebujemy, aby wygenerować okienko
i ustawić jego podstawowe właściwości.
Efekt działania tego skryptu przedstawia
Rysunek 1.
Koncepcja
Nasz interfejs ma być funkcjonalny i pro-
sty w obsłudze. Aplikacja zewnętrzna Wa-
ke-On-Lan ma być uruchamiana jednym
kliknięciem. Chcemy móc wpisywać adre-
sy MAC w okienku i gromadzić listę kilku
do kilkunastu ostatnio odwiedzanych, aby
następnie je wybierać. Lista powinna być
automatycznie ładowana przy uruchamia-
niu frontendu i zapisywana przy jego za-
mykaniu.
Musimy wziąć pod uwagę to, iż apli-
kacja Wake-On-Lan przesyła budzą-
ce zdalny komputer „magiczne pakiety”
przy pomocy protokołu UDP (ang. User
Datagram Protocol) – nie mamy więc
żadnej gwarancji, iż zostaną dostarczo-
ne do adresata. Obowiązek zmniejsze-
nia ilości błędów spada więc na nas –
dlatego powinniśmy zaimplementować
sprawdzanie danych po stronie naszej
aplikacji, która automatycznie będzie in-
formować o błędach dotyczących forma-
tu adresu.
Podczas pisania naszego programu
będziemy dążyli do uzyskania efektu ta-
kiego jak na Rysunku 2. Cały kod two-
rzący interfejs umieścimy w jednym pliku
o nazwie wolgui.php.
Tworzymy interfejs
Każda aplikacja PHP-GTK musi zawie-
rać na końcu pliku z kodem instrukcję
gtk::main()
, która rozpoczyna główną
pętlę programu. Aby poprawnie zakoń-
czyć działanie aplikacji, trzeba wywołać
instrukcję
gtk::quit()
. Okienko jest pod-
stawowym obiektem zawierającym inne
elementy interfejsu (widgety, ang. wid-
gets), zwanym kontenerem (ang. con-
tainer).
Na Listingu 1 mamy już zalążek ko-
du tworzącego okienko – musimy go
tylko rozwinąć. Na początek usuwa-
my (albo blokujemy znacznikiem ko-
mentarza) instrukcję
$window->show _
all()
, którą należy umieścić po usta-
wieniu wszystkich elementów fronten-
du. Inicjujemy okienko, nadajemy mu
tytuł i ustawiamy jego parametry: poło-
żenie (zawsze na środku ekranu), sze-
rokość (stała), wysokość (automatycz-
na, zależna od zawartości okna), sze-
rokość ramki (margines wewnętrzny
między elementami okna a jego kra-
wędzią), możliwości skalowania (wyłą-
czamy całkowicie), a następnie decydu-
jemy, że zamknięcie okienka oznacza
przerwanie programu. Efekty naszych
dotychczasowych prac można obejrzeć
na Listingu 3.
Wypadałoby powiedzieć parę słów
o zamykaniu okna – skąd program ma
wiedzieć, że gdy to robimy, każemy mu
przerwać swoje działanie? Dzieje się to
dzięki połączeniu (ang. connecting) wy-
syłanego przez wydarzenie (ang. event)
zamknięcia okna sygnału (ang. signal)
destroy
z funkcją
shutdown()
(napisze-
my ją później), która wywołując
gtk::
quit()
przerywa działanie aplikacji. Sy-
gnały wysyła podczas interakcji z użyt-
kownikiem każdy element interfejsu wy-
stępujący w PHP-GTK, a kojarzone z ni-
mi funkcje (które są zwykłymi funkcjami
Rysunek 1.
Pierwsze okienko w PHP-
GTK
Listing 1.
Tworzenie okienka
w PHP-GTK
$window
= &
new
GtkWindow
()
;
$window
-
>
set_title
(
"Wake-On-Lan"
)
;
$window
-
>
set_usize
(
300, -1
)
;
$window
-
>
show_all
()
;
Czym jest adres MAC?
Adres MAC (ang. Media Access Control
address) to 48-bitowy, sprzętowy adres
karty sieciowej nadawany przez produ-
centa. Jest unikalny w skali światowej
(teoretycznie – niektóre nowsze karty
dają możliwość jego zmiany). Zapisu-
jemy go w postaci sześciu liczb 8-bito-
wych w systemie szesnastkowym (hek-
sadecymalnym), rozdzielonych znakami
dwukropka lub myślnikami – przykład:
A0:04:FC:0C:00:26.
Wymagania Wake-On-Lan
Na komputerze, z którego będziemy uruchamiać inne maszyny potrzebujemy jedynie za-
instalowanej aplikacji WOL. Jednak, aby cała operacja przebiegła pomyślnie, budzony
komputer musi spełniać pewne warunki sprzętowe. Oto one:
• płyta główna ATX z 3-pinowym złączem WOL,
• opcja LAN Wakeup włączona w BIOS-ie,
• karta sieciowa wspierająca WOL, prawidłowo podłączona do płyty głównej,
• zasilacz zgodny ze specyfikacją ATX 2.01.
Instalacja Wake-On-Lan
Istnieje kilka różnych aplikacji typu WOL. My skorzystamy z programu napisanego
w PERL-u przez José Pedro Oliveira, dostępnego pod adresem http://gsd.di.uminho.pt/
jpo/software/wakeonlan/downloads/wakeonlan-0.40.tar.gz
Aplikacja działa pod kontrolą systemu Linux, a jej instalacja sprowadza się do kilku
prostych kroków – ściągnij archiwum, przejdź do katalogu, w którym zostało zapisane
i wykonaj następujące polecenia:
tar -zxvf wakeonlan-0.40.tar.gz
cd wakeonlan-0.40
perl Makefile.PL
make
make install
Po wykonaniu tych czynności dostępne będzie polecenie
wakeonlan
. Sposób jego użycia
to:
wakeonlan <adres karty sieciowej komputera do obudzenia>
Przykład:
wakeonlan 00:09:7B:89:48:71
PHP-GTK
Projekty
PHP Solutions Nr 2/2005
www.phpsolmag.org
4
PHP) określamy angielskim terminem
callbacks.
Przejdźmy teraz do tworzenia funk-
cji
shutdown()
. Wiemy już częściowo, co
ma ona robić – przerywać działanie pro-
gramu. Fajnie by było, gdyby zostawiała
po tym ślad w konsoli systemowej, z któ-
rej użytkownik wywoła skrypt – łatwiej
będzie stwierdzić, że program został za-
mknięty prawidłowo. Pamiętajmy, że nie
pracujemy w trybie CGI i naszym stan-
dardowym wyjściem jest nie ekran prze-
glądarki, tylko konsola – do niej więc funk-
cje takie jak
print()
czy
echo
będą kiero-
wać dane. Kod funkcji
shutdown()
prezen-
tujemy na Listingu 4.
Elementy interfejsu
Utworzenie i ustawienie okienka to jed-
nak dopiero początek. Oto lista elemen-
tów, które dopiero musimy zaimplemento-
wać w naszym frontendzie:
• etykieta Adres MAC, opisująca pole
tekstowe do wprowadzania adresu,
• pole tekstowe, w które użytkownik bę-
dzie mógł wpisać adres MAC budzo-
nego komputera,
• lista przechowującą ostatnio wprowa-
dzone adresy (historia),
• przycisk Obudź, wywołujący ze-
wnętrzną aplikację,
• przycisk Zamknij, kończący działanie
aplikacji,
• przycisk Wyczyść listę, pozwalający wy-
czyścić historię wpisywanych adresów.
Zaczniemy od utworzenia tych elemen-
tów, a dopiero później rozmieścimy je
w obszarze naszego okna. Każdy z wid-
getów jest zdefiniowany w odrębnej kla-
sie – tworzymy więc jego instancję czy-
li obiekt. Wszelkie parametry przekazu-
jemy albo przez jego konstruktor, albo
później, przy użyciu innych metod obiek-
tu. Na pierwszy ogień pójdzie etykieta
Adres Mac, zdefiniowana w klasie
Gtk-
Label
. Tworzymy jej instancję
$label
,
przekazując jednocześnie napis, który
ma się na niej ukazać, a następnie usta-
wiamy dodatkowy parametr – wyrówna-
nie tekstu.
Następnie utworzymy pole tekstowe
– obiekt klasy
GtkEntry
, który nazwiemy
$macaddress
. Pamiętajmy, aby umożliwić
użytkownikowi wpisywanie tekstu do nie-
go. Efekt prac widać na Listingu 5.
Zrobiliśmy już najprostsze elemen-
ty, czas na coś trudniejszego. Zajmij-
my się więc historią ostatnio wpisywa-
nych adresów, która będzie listą zawie-
rającą tylko jedną kolumnę o nagłówku
Ostatnio wywołane i umożliwiającą wy-
branie tylko jednego elementu pojedyn-
czym kliknięciem. Ma także dostosowy-
wać swoją szerokość do zawartości. Ża-
den problem, PHP-GTK dysponuje kla-
są
GtkCList
. Tworzymy jej obiekt nazy-
wając go
$clist
. Ustawiamy jej parame-
try dotyczące kolumn i wymiarów. Defi-
Rysunek 2.
Wygląd interfejsu
Listing 2.
Ładowanie rozszerzenia
PHP-GTK
if
(
!extension_loaded
(
'gtk'
)){
dl
(
'php_gtk.'
.PHP_SHLIB_SUFFIX
)
;
}
Wymagania PHP-GTK
Do poprawnego działania rozszerzenie PHP-GTK potrzebuje zainstalowanego PHP
4.3.x oraz biblioteki Gtk+ 1.2.6 lub nowszej (ale nie z serii Gtk+ 2.x). Aktualnie rozszerze-
nie w żadnym stopniu nie współpracuje z PHP5. W przyszłości autorzy planują dodanie
obsługi PHP5 i biblioteki Gtk+ z serii 2.x.
Instalacja PHP-GTK
Pod Linuksem zwykle mamy w systemie bibliotekę Gtk+, a jeśli nie, to znajduje się ona
wśród pakietów w dystrybucji. Można też ją ściągnąć ze stron GTK: http://www.gtk.org
i zainstalować. Następnie pobieramy archiwum PHP-GTK – z płyty załączonej do nume-
ru lub z witryny projektu PHP-GTK: http://gtk.php.net/, sekcja download. Potem wchodzi-
my do katalogu, w którym umieściliśmy ten plik i wydajemy następujące polecenia:
tar -zxvf php-gtk-1.0.1.tar.gz
cd php-gtk-1.0.1
./buildconfig
./configure –enable-php-gtk –disable-libglade
make
make install
Pod Windows sprawa jest jeszcze prostsza – wystarczy ściągnąć z Sieci lub przegrać
z naszej płyty archiwum PHP-GTK zawierające komplet skompilowanych pakietów, któ-
rych potrzebujemy: rozszerzenie PHP-GTK, bibliotekę GTK 1.2.x i parser PHP 4, a na-
stępnie rozpakować archiwum i postępować zgodnie z instrukcją umieszczoną w pliku
Readme.
Listing 3.
Zmodyfikowany kod tworzący okienko
// zainicjowanie okienka
$window
= &
new
GtkWindow
()
;
// nadanie mu tytułu
$window
-
>
set_title
(
"Wake-On-Lan"
)
;
// ustalenie szerokości ramki okienka
$window
-
>
set_border_width
(
10
)
;
// wyłączenie możliwości skalowania okna: zmniejszania, zwiększania
// i automatycznej minimalizacji do rozmiaru, przy którym widoczne są
// wszystkie jego elementy (każdy parametr może być true albo false)
$window
-
>
set_policy
(
false, false, false
)
;
// ustawienie szerokości okienka na 300 pikseli i wysokości na automatyczną
$window
-
>
set_usize
(
300, -1
)
;
// ustawienie okienka na środku ekranu (bez względu na rozdzielczość)
$window
-
>
set_position
(
GTK_WIN_POS_CENTER
)
;
// skojarzenie akcji zamknięcia okienka z wywołaniem funkcji
// shutdown(), która uruchamia instrukcję gtk::quit()
$window
-
>
connect
(
"destroy"
,
"shutdown"
)
;
Projekty
PHP-GTK
PHP Solutions Nr 2/2005
www.phpsolmag.org
5
niujemy zdarzenia (było już o tej techni-
ce) na wypadek wybrania wiersza oraz
usunięcia jego zaznaczenia. Oba zda-
rzenia obsłuży callback
add _ to _ en-
trybox()
, wpisujący wybraną z listy po-
zycję do pola adresowego. To, co zrobi-
liśmy zawierają Listingi 6 i 7.
Z obiektów widocznych pozostaną
nam jeszcze trzy przyciski – Obudź, Za-
mknij, Wyczyść listę. Każdy z nich sta-
nowi obiekt
GtkButton
. Do dwóch z nich
będziemy chcieli dodać skróty klawiszo-
we – w tym celu utworzymy obiekt kla-
sy
GtkAccelGroup
, który będzie je prze-
chowywał:
$keys = &new GtkAccelGroup();
Zaczniemy od przycisku Zamknij. Two-
rzymy go, ustawiając jednocześnie na-
pis, który ma się na nim znaleźć. Następ-
nie ustalamy, że po naciśnięciu (emituje
wtedy sygnał
clicked
) przycisk ma wy-
woływać przerywającą działanie progra-
mu funkcję
shutdown()
. Chcemy też, aby
użytkownik mógł zrobić to samo wciska-
jąc klawisz [Escape] – nic trudnego, moż-
na skojarzyć z przyciskiem skrót klawi-
szowy, używając metody
add _ accelera-
tor()
(Listing 8).
Przejdźmy teraz do przycisku Wy-
czyść listę. Tworzymy obiekt typu
Gtk-
Button
, nazywając go
$btnClear
i łączy-
my jego wciśnięcie z wywołaniem funk-
cji
clearlist()
, która usuwa historię wpi-
sywanych adresów – zarówno z obiek-
tu listy
$clist
, jak i zmiennej tablicowej
$macs
. Wynik naszych działań przedsta-
wiamy na Listingu 9.
Zdefiniujemy teraz przycisk Obudź,
który będzie wywoływał aplikację ze-
wnętrzną Wake-On-Lan. Nazwiemy go
$btnWake
(Listing 10). Ponieważ czyn-
ność przez niego powodowana jest naj-
ważniejsza w całej aplikacji, dobrze by
było, gdyby użytkownik mógł ją wywołać
klawiszem. A nawet jednym z dwóch kla-
wiszy – niech będą to [Enter] – na klawia-
turze głównej i numerycznej. Wiemy już,
jak przypisać skrót klawiszowy: wystarczy
wywołać funkcję
add _ accelerator()
. Tu
zrobimy to dwukrotnie.
Pamiętajmy, że uruchomienie tej
aplikacji to ostatnia czynność – najpierw
trzeba odczytać adres z pola adreso-
wego (
$macaddress
) i (przy pomocy wy-
rażeń regularnych) sprawdzić jego po-
prawność. Jeśli został wprowadzony
prawidłowo, nasz interfejs wywoła apli-
kację, o której mowa. Jeśli nie – oczom
użytkownika ukaże się okienko informu-
jące go o błędzie, a okno aplikacji zosta-
nie schowane. Jak to wykonamy? Jak
już wiemy, wciśnięcie przycisku gene-
ruje sygnał
clicked
. Połączymy go więc
z funkcją
checkvalidity()
, którą dopiero
utworzymy. Zawrzemy w niej wszystkie
powyższe czynności sprawdzające (Li-
sting 11). Należy nadmienić, że tym ra-
Pierwszy test
Przed skorzystaniem z funkcji i metod tego rozszerzenia powinniśmy zadbać o jeszcze
jeden mały drobiazg. Aby mieć pewność, że rozszerzenie zostało załadowane, napisze-
my kilka instrukcji, które to sprawdzą oraz w razie potrzeby załadują rozszerzenie.
Do sprawdzenia, czy rozszerzenie jest dostępne wykorzystajmy funkcję
exten-
sion _ loaded()
, podając jej jako parametr ciąg
gtk
. Funkcja ta zwróci wartość logiczną
TRUE
lub
FALSE
. Gdy okaże się, że rozszerzenia brakuje, użyjemy funkcji
dl()
, aby je za-
ładować. W systemach uniksowych rozszerzenie pliku z wykorzystywaną przez nas bi-
blioteką to .so, natomiast w Windows jest to .dll. Aby uniezależnić się od systemu ope-
racyjnego, możemy w funkcji
dl(),
zamiast wpisywania rozszerzenia pliku, użyć stałej
PHP _ SHLIB _ SUFFIX
, która przechowuje odpowiednie rozszerzenie. Kod ładujący roz-
szerzenie powinien znaleźć się na samym początku pliku (Listing 2).
Listing 7.
Kod funkcji add_to_entrybox()
function
add_to_entrybox
(
$clist
){
global
$macaddress
;
// metoda set_text() wpisuje treść do pola tekstowego, a get_text() –
// pobiera tekst wybranej z listy $clist pozycji o numerze
// przechowywanym przez atrybut tej listy – tablicę selection (lista
// wszystkich wybranych pozycji) pod indeksem 0
$macaddress
-
>
set_text
(
@$clist
-
>
get_text
(
$clist
-
>
selection
[
0
]
, 0
))
;
}
Listing 6.
Utworzenie listy przechowującej adresy
// w konstruktorze określamy: liczbę kolumn listy
(
parametr 1.
)
// i tablicę zawierającą nagłówki kolejnych kolumn (parametr 2.)
$clist
= &
new
GtkCList
(
1,
array
(
"Ostatnio wywołane:"
))
;
// ustawienie wybierania tylko jednej pozycji na liście
$clist
-
>
set_selection_mode
(
GTK_SELECTION_BROWSE
)
;
// szerokość automatyczna, wg rozmiaru okna (-1), wysokość 200 pikseli
$clist
-
>
set_usize
(
-1, 200
)
;
// zaznaczenie elementu na liście wysyła sygnał select_row
// i wywołuje funkcję add_to_entrybox()
$clist
-
>
connect
(
"select-row"
,
"add_to_entrybox"
)
;
// usunięcie zaznaczenia elementu wysyła sygnał unselect_row
// i wywołuje funkcję add_to_entrybox()
$clist
-
>
connect
(
"unselect-row"
,
"add_to_entrybox"
)
;
Listing 5.
Tworzenie etykiety – obiektu klasy GtkLabel i pola tekstowego
(GtkEntry)
// utworzenie obiektu etykiety i przekazanie napisu przez konstruktor
$label
= &
new
GtkLabel
(
"Adresy MAC:"
)
;
// dokładne wyrównanie tekstu
$label
-
>
set_justify
(
GTK_JUSTIFY_FILL
)
;
// utworzenie obiektu pola tekstowego
$macaddress
= &
new
GtkEntry
()
;
// ustawienie możliwości pisania tekstu (true – można, false – nie)
$macaddress
-
>
set_editable
(
TRUE
)
;
Listing 4.
Kod funkcji shutdown()
function
shutdown
(
){
(
"Zamykanie programu.
\n
"
)
;
gtk::main_quit
()
;
}
PHP-GTK
Projekty
PHP Solutions Nr 2/2005
www.phpsolmag.org
6
zem nie tylko powiązaliśmy sygnał z cal-
lbackiem, ale również przekazaliśmy do
niego odniesienie do obiektu
$macad-
dress
.
Natomiast będąc przy funkcji
check-
validity()
, zwróćmy uwagę na trzy rze-
czy. Pierwszą i najważniejszą jest wy-
wołanie uruchamiającej aplikację ze-
wnętrzną Wake-On-Lan funkcji
wake()
w przypadku, gdy adres jest poprawny.
Druga dotyczy użycia będącej kontene-
rem i obiektem niewidocznym dla użyt-
kownika tabeli porządkującej elementy
(obiekt klasy
GtkTable
). Po jej utworze-
niu i przypisaniu do okienka dodajemy
do niej elementy, które mają się w oknie
ukazać, określając położenie każdego
z nich (szczegóły w Ramce Rozmiesz-
czenie elementów w tabeli). Jest to cał-
kiem wygodny sposób organizowania
widgetów. Trzecią sprawą, której należy
poświęcić trochę uwagi jest wywołanie
przedstawionej na tym samym Listingu
funkcji
warnclick()
. Zamyka ona dowolne
okienko przekazane jej przez parametr –
w tym przypadku, jest to okno z komuni-
katem o błędzie.
Można też zrobić, aby przycisk
Obudź był niedostępny (i nie dało się na
niego kliknąć), gdy pole adresowe jest
puste. Tylko po co? Czy nie wystarczy
potraktowanie pustego pola
$macaddress
jako błędnie wprowadzonego MAC-a?
Byłoby to poprawne, ale znacznie mniej
eleganckie. Jak to wykonać? Program
musi w jakiś sposób sprawdzać, że po-
le
$macaddress
nie jest puste i modyfiko-
wać właściwości przycisku Obudź. Zmia-
ny w polu tekstowym generują sygnał
changed
– trzeba go więc połączyć z od-
powiednią funkcją, która będzie włączać
lub wyłączać przycisk
$btnWake
i gotowe!
Wymaga to drobnej modyfikacji we frag-
mencie kodu tworzącego pole adresowe
$macaddress
(dodanie połączenia sygna-
łu) i utworzenia funkcji
check _ length()
.
Wdrożenie tego pomysłu widać na Li-
stingu 12.
Utworzymy teraz funkcję
wake()
. Jej
kod widnieje na Listingu 13. Pisaliśmy
już, iż spełnia ona najważniejszą w ca-
łym programie rolę, którą jest wywołanie
aplikacji Wake-On-Lan. Wspomnijmy, iż
używa ona do tego wbudowanej do PHP
funkcji
system()
. Przedtem jednak prze-
Rozmieszczenie elementów w tabeli
Ostatnie cztery parametry metody
attach()
obiektu klasy
GtkTable
określają pozycję,
w której ma być umieszczony element. Pierwsze dwa z nich określają pozycje w pozio-
mie, kolejne dwa – w pionie. Każda para określa zasięg “od-do” między krawędziami ko-
lumn i wierszy (po 2 krawędzie na każdy wiersz i tyle samo na każdą kolumnę).
Przykład z naszej aplikacji: chcemy umieścić element
$label
w jednej komórce znaj-
dującej się w pierwszym wierszu tabeli o pięciu wierszach i trzech kolumnach.
$table->attach($label, 0, 1, 0, 1);
Można to opisać w następujący sposób: “element
$label
umieścimy w tabeli
$table
mię-
dzy 0. i 1. krawędzią w poziomie i między 0. i 1. krawędzią w pionie”. Okno, którego frag-
mentem jest nasz przykład przedstawiamy na Rysunku 3. Jest to układ widgetów naszego
interfejsu z Rysunku 2, na który dodatkowo nanosimy siatkę tabeli. Widoczne tam wąskie
odstępy między kolumnami i wersami są traktowane tak samo, jak inne kolumny i wersy.
Rysunek 3.
Numeracja krawędzi tabeli
Listing 10.
Kod tworzący przycisk „Obudź”
// stworzenie nowego przycisku Obudź, wywołującego aplikację Wake-On-Lan
$btnWake
= &
new
GtkButton
(
"Obudź"
)
;
// przypisanie 1. skrótu klawiszowego (Enter na klawiaturze głównej)
$btnWake
-
>
add_accelerator
(
"clicked"
,
$keys
,GDK_KEY_Return,GDK_MOD2_MASK,0
)
;
// przypisanie 2. skrótu (Enter na klawiaturze numerycznej)
$btnWake
-
>
add_accelerator
(
"clicked"
,
$keys
,GDK_KEY_KP_Enter,GDK_MOD2_MASK,0
)
;
// połączenie wciśnięcia przycisku z funkcją checkvalidity() i przekazanie
// do niej odniesienia do pola $macaddress
$btnWake
-
>
connect
(
"clicked"
,
"checkvalidity"
,
$macaddress
)
;
$btnWake
-
>
set_sensitive
(
FALSE
)
;
Listing 9.
Kod tworzący przycisk czyszczący listę oraz funkcje clearlist()
// utworzenie przycisku
$btnClear
i nadanie mu etykiety Wyczyść
list
ę
$btnClear
= &
new
GtkButton
(
"Wyczyść listę"
)
;
// połączenie wciśnięcia $btnClear z funkcją clearlist()
$btnClear
-
>
connect
(
"clicked"
,
"clearlist"
)
;
function
clearlist
(
){
global
$clist
,
$macs
;
$clist
-
>
clear
()
;
$macs
=
array
()
;
}
Listing 8.
Przyciski
// utworzenie przycisku
$btnAbort
z etykietą Zamknij
$btnAbort
= &
new
GtkButton
(
"Zamknij"
)
;
// Przypisanie skrótu klawiszowego do przycisku $btnAbort (Zamknij). Parametry
// to po kolei: sygnał emitowany przez przycisk po naciśnięciu, nazwa obiektu
// klasy GtkAccelGroup, kod klawisza (kody są w plikach źródłowych PHP-GTK:
// ext/gtk+/php_gdk.c), maska klawiszy-modyfikatorów (alt,shift,ctrl – tu
// żadnego nie używamy). Ostatni parametr (wartość 0) nie jest używany.
$btnAbort
-
>
add_accelerator
(
"clicked"
,
$keys
,GDK_KEY_Escape,GDK_MOD2_MASK,0
)
;
// skojarzenie przycisku z funkcją shutdown()
$btnAbort
-
>
connect
(
"clicked"
,
"shutdown"
)
;
Projekty
PHP-GTK
PHP Solutions Nr 2/2005
www.phpsolmag.org
7
wana w pliku, gdy tylko wyłączamy nasz
interfejs i automatycznie z niego łado-
wana, gdy go znów uruchamiamy. Stwo-
rzymy więc w tym celu funkcje:
write _
list()
, która będzie zapisywać dane
i
loadlist()
, która będzie je odczytywać.
Pierwszą rzeczą, która przychodzi na
myśl, jest wybór metody zapisu i odczy-
tu. Nasza lista adresów (zmienna
$macs
)
jest tablicą, więc ma swoją strukturę. Aby
ją bezproblemowo zachować, a potem
odtworzyć, użyjemy funkcji
serialize()
i
unserialize()
. Poddają one dane seria-
lizacji i deserializacji.
Listing 13.
Kod funkcji wake()
function
wake
(
$address
){
// zamiana liter w adresie na
// wielkie i dodanie znaku końca
// linii (CR), niezbędnego do
// uruchomienia aplikacji WOL
$address
=
strtoupper
(
$address
)
.
"
\n
"
;
// dodanie adresu do historii
add_to_list
(
$address
)
;
// wywołanie aplikacji WOL
system
(
"wakeonlan "
.
$address
)
;
}
Listing 12.
Kod funkcji check_
length() i połączenia sygnału
changed
function
check_length
(
$inputfield
){
global
$btnWake
;
// sprawdzanie, czy w polu
// tekstowym ($inputfield) jest
// tekst (długość >0). Pobieramy
// go metodą get_text()
if
(
strlen
(
$inputfield
-
>
get_text
())
>
0
){
// jeśli tak, ustawiamy
// przycisk Obudź na aktywny
$btnWake
-
>
set_sensitive
(
TRUE
)
;
}
else
{
// jeśli nie, ustawiamy
// Obudź na nieaktywny
$btnWake
-
>
set_sensitive
(
FALSE
)
;
}
}
// połączenie dokonywania zmian
// w polu $macaddress z wywołaniem
// funkcji check_length
$macaddress
-
>
connect
(
"changed"
,
"check_length"
)
;
Listing 11.
Kody funkcji checkvalidity() i warnclick()
function
checkvalidity
(
$button
,
$entry
){
global
$window
;
// odczytanie adresu MAC z pola adresowego
$address
=
$entry
-
>
get_text
()
;
// zamiana myślników w adresie na dwukropki
$address
=
str_replace
(
"-"
,
":"
,
$address
)
;
// sprawdzenie poprawności adresu przy użyciu wyrażeń regularnych
if
(
preg_match
(
"/^[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]
{2}:[0-9a-f]{2}:[0-9a-f]{2}
$
\b
/i"
,
trim
(
$address
))){
// jeśli jest poprawny, wywołujemy funkcję wake() (uruchamia aplikację WOL)
wake
(
trim
(
$address
))
;
}
else
{
// jeżeli MAC jest niepoprawny, to:
// ukrywamy okienka aplikacji
$window
-
>
hide
()
;
// tworzymy okienko $warnwindow, w którym będzie ostrzeżenie
$warnwindow
= &
new
GtkWindow
()
;
// ustawiamy tytuł okienka
$warnwindow
-
>
set_title
(
"WOL: Błąd"
)
;
// ustawiamy margines okienka
$warnwindow
-
>
set_border_width
(
10
)
;
// ustawiamy skalowalność okienka
$warnwindow
-
>
set_policy
(
true, false, false
)
;
// ustawiamy pozycję okienka (pośrodku ekranu)
$warnwindow
-
>
set_position
(
GTK_WIN_POS_CENTER
)
;
// ustawiamy rozmiar okienka (szerokość automatyczna, wysokość 80)
$warnwindow
-
>
set_usize
(
-1, 80
)
;
// tworzymy tabelę porządkującą i ustalamy liczbę wersów (2) i kolumn (3)
$tab2
= &
new
GtkTable
(
2, 3
)
;
// tworzymy etykietę zawierającą komunikat o błędzie
$errormsg
= &
new
GtkLabel
(
"Format wprowadzanego adresu MAC jest
nieprawidłowy !"
)
;
// włączamy zawijanie tekstu (ang. wrapping) w tej etykiecie
$errormsg
-
>
set_line_wrap
(
true
)
;
// definiujemy przycisk OK i łączymy go z funkcją warnclick() zamykającą
// okienko komunikatu i przywracającą okno aplikacji
$btnok
= &
new
GtkButton
(
"OK"
)
;
$btnok
-
>
connect
(
"clicked"
,
"warnclick"
, &
$warnwindow
)
;
// przypisanie tabeli porządkującej do obiektu okienka
$warnwindow
-
>
add
(
$tab2
)
;
// wstawienie etykiety i przycisku do tabeli porządkującej
$tab2
-
>
attach
(
$errormsg
, 0, 5, 0, 1
)
;
$tab2
-
>
attach
(
$btnok
, 2, 3, 2, 3
)
;
// pokazanie okienka i wszystkich jego elementów
$warnwindow
-
>
show_all
()
;
}
}
// funkcja zamykająca dowolne okienko i przywracającą okno główne
function
warnclick
(
$button
,
$win
){
global
$window
;
// zamknięcie (zniszczenie) okienka przekazanego przez parametr
$win
-
>
destroy
()
;
// ukazanie okna głównego aplikacji
$window
-
>
show
()
;
}
prowadza jeszcze dwie czynności – za-
mienia wszystkie litery w adresie na wiel-
kie (ze względów estetycznych) i dodaje
adres do historii (stąd się ona bierze) przy
użyciu funkcji
add _ to _ list()
. Funkcja
dodaje adres zawsze na początku listy
(jako najnowszy). Najpierw sprawdza, czy
dodawany MAC istnieje już w historii i do-
pisuje go tylko, jeśli nie. Pamiętajmy też,
że nie możemy przekroczyć maksymalnej
liczby obiektów na liście (10) – gdyby mia-
ło to nastąpić, to najstarszy adres zosta-
nie usunięty. Kod
add _ to _ list()
przed-
stawiliśmy na Listingu 14.
Jak już wcześniej wspomnieliśmy,
chcemy, aby historia adresów była zapisy-
PHP-GTK
Projekty
PHP Solutions Nr 2/2005
www.phpsolmag.org
8
Pierwszy z tych procesów przetwa-
rza całą tablicę (dane i ich strukturę)
na formę, którą można zapisać w pli-
ku, a drugi jest jego odwrotnością. Wy-
korzystamy więc
serialize()
w funkcji
write _ list()
i
unserialize()
w
load-
list()
. Obie funkcje zostały przedsta-
wione na Listingu 15.
Teraz pora na implementację auto-
matycznego ładowania listy przy uru-
Listing 14.
Kod funkcji add_to_list()
function
add_to_list
(
$address
){
global
$macs
,
$clist
;
// oczyszczenie adresu ze znaków takich jak spacje i entery
$address
=
trim
(
$address
)
;
// zmienna określająca, czy adres znajduje się na liście (TRUE)
// czy nie (FALSE). Tu ustawiamy ją domyślnie na FALSE
$exists
= FALSE;
// tablica tymczasowa, do której przenosimy adresy, jeżeli lista
// jest już pełna i trzeba usunąć najstarszy element
$temp
=
array
()
;
// pętla sprawdzająca, czy dodawany adres jest już na liście
foreach
(
$macs
AS
$addr
){
// jeśli adres został znaleziony, to ustawiamy $exists na TRUE
if
(
$addr
===
$address
){
$exists
= TRUE;
}
}
// po zakończeniu pętli: jeśli funkcja nie znalazła dodawanego
// adresu na liście, to:
if
(
!
$exists
){
// jeżeli historia ma maksymalną zdefiniowaną liczbę linii (10)
if
(
count
(
$macs
)
== MAX_LINES
){
// to tworzymy tablicę tymczasową $temp i w pętli foreach
$temp
=
array
()
;
// przenosimy do niej wszystkie adresy oprócz najstarszego
// ze zmiennej $macs
foreach
(
$macs
AS
$key
=
>
$value
){
if
(
$key
<
(
MAX_LINES
)
&&
(
$key
>
0
)){
$temp
[
$key
-1
]
=
$value
;
}
}
// po czym dodajemy do niej ($temp) nasz nowy adres ($address)
$temp
[]
=
$address
;
// i przenosimy jej zawartość z powrotem do tablicy $macs
$macs
=
$temp
;
// jeżeli historia jest krótsza niż 10 pozycji
}
else
{
// to zwyczajnie dopisujemy do niej nowy adres
$macs
[]
=
$address
;
}
// sortujemy tablicę $macs
ksort
(
$macs
)
;
// czyścimy zawartość listy $clist
$clist
-
>
clear
()
;
// usuwamy zmienną $temp
unset
(
$temp
)
;
// przenosimy zawartość tablicy $macs do listy $clist
foreach
(
$macs
AS
$addr
){
$clist
-
>
prepend
(
array
(
$addr
))
;
}
}
}
chamianiu programu i jej zapisywa-
nia przy jego zamykaniu. W pierw-
szym przypadku, wstawimy odwołanie
do funkcji
loadlist()
w części głównej
programu, tuż przed instrukcją
gtk::
main()
. W drugim umieścimy wywoła-
nie
write _ list()
wewnątrz napisa-
nej wcześniej funkcji
shutdown()
, któ-
rą lekko zmienimy, co widać na Listin-
gu 16.
Układanka
Najwyższy czas na rozmieszczenie
wszystkich elementów w obrębie okien-
ka aplikacji. Musimy tylko zadbać o jeden
drobiazg – nasza lista
$clist
nie jest, nie-
stety, z definicji listą przewijalną. Dostęp-
ne na niej są tylko te elementy, które są
widoczne na ekranie – reszty nie można
nijak z niej wybrać.
Istnieje jednak proste rozwiązanie te-
go problemu – biblioteka PHP-GTK dys-
ponuje klasą
GtkScrolledWindow
, która
definiuje okienko przewijalne ze znanymi
wszystkim paskami. Umieszczenie naszej
listy w tym okienku (które zostanie po-
traktowane jako kontener) umożliwi nam
więc jej przewijanie. Stworzymy obiekt
o nazwie
$scwindow
i ustalimy, że okienko
ma być przewijalne tylko w pionie, a pa-
sek przewijania – pojawiać się tylko wte-
dy, gdy jest potrzebny (czyli, gdy jest wy-
starczająco dużo pozycji). Rozwiązanie to
przedstawiliśmy na Listingu 17.
Przejdziemy teraz do finalizacji na-
szej układanki – ułożymy widgety
w oknie głównym aplikacji. Podobnie,
jak w przypadku okienka informujące-
go o błędzie, także tutaj użyjemy do te-
go tabeli
GtkTable
. Nadamy jej 4 wersy
i 2 kolumny, przypiszemy do nich widge-
ty, po czym dodamy skróty klawiszowe
oraz instrukcję, o której wspominaliśmy
na samym początku –
$window->show _
all()
, która wyświetla na ekranie okien-
ko i wszystkie jego elementy. Widać to
na Listingu 18.
Tak oto, krok po kroku, skompleto-
waliśmy cały interfejs programu Wake-
On-Lan.
Uruchomienie aplikacji
Spróbujmy teraz uruchomić naszą apli-
kację pod systemem Linux. Jak już wspo-
mnieliśmy, wystarczy w konsoli systemo-
wej wpisać
php wolgui.php
(przy założe-
niu, że ścieżka do interpretera PHP jest
zdefiniowana). Powinno ukazać się okien-
ko podobne do tego z Rysunku 1. Takie
uruchamianie programu nie należy jed-
nak do najwygodniejszych – usprawnij-
my więc je trochę. Wystarczy, że w pierw-
szej linijce skryptu (jeszcze przed znacz-
nikiem
<?php
) umieścimy odwołanie do
parsera PHP, jak w przykładzie:
#!/usr/
local/bin/php -q
Oczywiście, podana ścieżka dostępu
jest zależna od położenia parsera PHP
na dysku i może być inna – sprawdzimy
ją wydając polecenie
whereis php
(jeśli
Projekty
PHP-GTK
PHP Solutions Nr 2/2005
www.phpsolmag.org
9
nic nie zwraca, to znaczy, że PHP nie
jest w ogóle zainstalowany!). Następnie,
komendą
chmod oug+x wolgui.php
zmie-
nimy uprawnienia dostępu do pliku wol-
gui.php. Od tej pory, aby uruchomić apli-
kację, wystarczy w wierszu poleceń wpi-
sać jej nazwę.
Co dalej z PHP-GTK
Zaprojektowanie i wykonanie interfejsu
opisanego w tym artykule dość dokład-
nie pokazuje ideę programowania z wy-
korzystaniem PHP-GTK. Oczywiście,
możliwości tego rozszerzenia są du-
żo większe i nie sposób je tu wszystkie
opisać. Na szczęście, na stronie pro-
jektu, pod adresem http://gtk.php.net/
manual/en/reference.php
dostępna
jest znakomita dokumentacja (niestety
nie w języku polskim). Opisano w niej
dokładnie każdy dostępny widget –
wszystkie jego cechy, metody i emito-
wane sygnały. Zachęcamy więc do lek-
tury tego podręcznika oraz nauki PHP-
GTK i jego wdrażania w swoich projek-
tach. n
Listing 16.
Zmieniony kod funkcji shutdown()
function
shutdown
(
){
(
"Zamykanie programu.
\n
"
)
;
// serializacja listy i zapis do pliku
write_list
()
;
// przerwanie pętli głównej programu
gtk::main_quit
()
;
Listing 15.
Kody funkcji write_list() i loadlist()
function
write_list
(
){
global
$macs
;
// otwarcie pliku list.dat do zapisu (w)
$fp
=
fopen
(
"list.dat"
,
"w"
)
;
// serializacja (serialize()) i zapisanie (fputs()) danych w pliku
fputs
(
$fp
, serialize
(
$macs
))
;
// zamknięcie pliku
fclose
(
$fp
)
;
}
function
loadlist
(
){
global
$clist
,
$macs
;
// sprawdzenie, czy istnieje plik list.dat
if
(
file_exists
(
"list.dat"
)){
// jeśli tak, to czy da się go otworzyć do odczytu
if
(
$fp
=
fopen
(
"list.dat"
,
"r"
)){
// gdy jest otwarty, to odczytujemy go w pętli while linia po linii
while
(
!
feof
(
$fp
)){
$fline
.=
fgets
(
$fp
, 1024
)
;
}
// po czym dane są deserializowane, czyli zamieniane na zwykłą
// tablicę $macs
$macs
= unserialize
(
trim
(
$fline
))
;
}
// zamknięcie pliku
fclose
(
$fp
)
;
}
// wypełnienie listy $clist wartościami z $macs
foreach
(
$macs
AS
$value
){
$clist
-
>
prepend
(
array
(
$value
))
;
}
}
Listing 18.
Kod tworzący tabelę
i umieszczający w niej widgety
// utworzenie obiektu tabeli
$table
$table
= &
new
GtkTable
(
4, 2
)
;
// odstępy między rzędami
// (wersami) tabeli
$table
-
>
set_row_spacings
(
5
)
;
// odstępy między kolumnami tabeli
$table
-
>
set_col_spacings
(
5
)
;
// dodanie skrótów klawiszowych
$window
-
>
add_accel_group
(
$keys
)
;
// umieszczenie tabeli w oknie
$window
-
>
add
(
$table
)
;
// ustawienie w tabeli widgetów
$table
-
>
attach
(
$label
,
0, 1, 0, 1
)
;
$table
-
>
attach
(
$macaddress
,
2, 3, 0, 1
)
;
$table
-
>
attach
(
$scwindow
,
0, 3, 4, 5
)
;
$table
-
>
attach
(
$btnClear
,
0, 1, 6, 7
)
;
$table
-
>
attach
(
$btnAbort
,
0, 1, 8, 9
)
;
$table
-
>
attach
(
$btnWake
,
2, 3, 8, 9
)
;
// wyświetlenie na ekranie okienka
// i jego wszystkich elementów
$window
-
>
show_all
()
;
Listing 17.
Kod tworzący okno klasy
GtkScrolledWindow
// utworzenie okienka przewijalnego
$scwindow
=&
new
GtkScrolledWindow
()
;
// przewijalność okna: pozioma
// (wyłączona) i pionowa (automa-
// tyczna, włączana zależnie od
// rozmiaru zawartości)
$scwindow
-
>
set_policy
(
GTK_POLICY_NEVER,
GTK_POLICY_AUTOMATIC
)
;
// dodanie do okienka listy $clist
$scwindow
-
>
add
(
$clist
)
;