2009.01_Biblioteka senseGUI czyli GUI z automatu_[Jezyki Programowania].pdf

(536 KB) Pobierz
441671224 UNPDF
Języki programowania
Biblioteka senseGUI
czyli GUI z automatu
Większość programistów zetknęła się z potrzebą stworzenia Graficznego
Interfejsu Użytkownika (ang. GUI). Zwykle do tego celu stosuje się popularne
biblioteki (np. Swing) lub dedykowane edytory, co jest dość pracochłonne. W
tym artykule chcielibyśmy omówić inne rozwiązanie, bazujące na podejściu
deklaratywnym.
Dowiesz się:
• Jakie są sposoby tworzenia GUI;
• Jak zaprojektować własną bibliotekę do auto-
matycznego tworzenia GUI;
• Do czego mogą się przydać adnotacje.
Powinieneś wiedzieć:
• Jak programować w jednym z popularnych ję-
zyków, np. Java.
Warto zauważyć, że jednym z najbardziej
znanych przykładów podejścia deklaratywne-
go, chociaż nie związanym z GUI, jest język
zapytań SQL. Użytkownik określa, co ma być
zrobione (jakie dane potrzebuje), ale nie defi-
niuje, jak to ma być wykonane. O użyteczno-
ści takiego podejścia niech świadczy fakt, iż
jedno zapytanie SQL może odpowiadać kilku-
dziesięciu (lub nawet kilkuset) liniom progra-
mu w klasycznym języku programowania (np.
Java czy C#).
Podobne rozwiązanie chcielibyśmy zastoso-
wać dla graficznych interfejsów użytkownika.
Naszym celem jest, aby mając określone dane
biznesowe (zdefiniowane w postaci klas np. ję-
zyka Java czy C#) GUI pojawiło się automatycz-
nie. I najlepiej, aby spełniało nasze oczekiwania
m.in. w zakresie:
Poziom trudności
cej ręczne modyfikacje kodu źródłowe-
go na wizualny projekt. Istnieją również
rozwiązania działające tylko jako czyste
generatory kodu. Wtedy ręczne modyfi-
kacje są tracone po ponownym wygene-
rowaniu kodu dla wizualnego projektu.
• Zastosowanie specjalnego, deklaratyw-
nego podejścia. Jego zaletą jest to, że
projektant/programista skupia się na tym
co jest do zrobienia , a nie jak to zrobić . Naj-
nowsze, komercyjne technologie podąża-
jące tym tropem to propozycja Microsoft
dotycząca języka XAML (ang. Extensible
Application Markup Language ). Poszcze-
gólne elementy GUI są definiowane w
specjalnym języku programowania, a wła-
ściwie języku opisu.
delu (danych) mają mieć GUI, a odpo-
wiednia biblioteka automatycznie je ge-
neruje w czasie działania programu. Na począ-
tek pokuśmy się o rozważenie kilku opcji, ja-
kie mamy, gdy chcemy stworzyć GUI. Ogólnie
rzecz biorąc, współcześni twórcy oprogramo-
wania, wykorzystują trzy główne podejścia do
tworzenia graficznego interfejsu użytkownika:
• funkcjonalności,
• użyteczności,
• wydajności,
• estetyki.
• Ręczne pisanie kodu źródłowego odwo-
łującego się do odpowiednich bibliotek.
W przypadku Javy może to być Swing
[3] lub SWT [4]. Programiści C# korzy-
stają z WinForms [5]. Najbardziej skom-
plikowana sytuacja jest w C++, gdzie za-
stosowanie konkretnej biblioteki jest
zdeterminowane przez dialekt języ-
ka. Istnieje również wiele niezależnych
rozwiązań, które czasami są dedykowa-
ne dla różnych platfor. Najbardziej po-
pularne to Qt [6], wxWidgets [7] oraz
GTK+ [8].
• Wykorzystanie wizualnego edytora,
który umożliwia narysowanie GUI i wy-
generowanie odpowiadającego mu ko-
du źródłowego. Jakość takich generato-
rów jest bardzo różna. Niektóre z nich
korzystają z tzw. inżynierii wahadło-
wej (ang. round-trip ) odzwierciedlają-
Czy to jest w ogóle możliwe? Czy komputer
może wygenerować coś tak skomplikowane-
go, jak interfejs użytkownika bez dostarczenia
Niestety, większość przedstawionych po-
dejść do tworzenia GUI, wymaga dość zna-
czącego zaangażowania ze strony progra-
misty. Cały czas, każdy element GUI od-
powiadający jednostce danych musi być
przetwarzany indywidualnie. Przedstawio-
ny pomysł polega na stworzeniu rozwiąza-
nia, które jest bardzo łatwe w użyciu, a za-
razem użyteczne i nie wymaga dużego na-
kładu pracy ze strony programisty. Opisy-
wana propozycja została zaimplementowa-
na w postaci biblioteki senseGUI dla języka
Java i może być wykorzystana w dowolnej
aplikacji pracującej na tej platformie. War-
to podkreślić, że stosując opisane podejście,
możliwe jest stworzenie podobnego rozwią-
zania dla dowolnego języka wspierającego
refleksję (np. C#).
Listing 1. Przykładowa klasa biznesowa
public class Person {
private String irstName ;
private String lastName ;
private Date birthDate ;
private boolean higherEducation ;
private String remarks ;
private int SSN ;
private double annualIncome ;
public int getAge () {
// [...]
}
}
44
01/2009
P rogramista określa, które elementy mo-
441671224.051.png 441671224.062.png 441671224.073.png 441671224.084.png 441671224.001.png 441671224.002.png 441671224.003.png 441671224.004.png 441671224.005.png 441671224.006.png 441671224.007.png
Biblioteka senseGUI – czyli GUI z automatu
mu dokładnych informacji? Otóż jest to wyko-
nalne do pewnego stopnia. Niestety, im efekt
końcowy ma być bliższy naszym oczekiwa-
niom, tym nasz wkład pracy będzie większy.
Wydaje się, że podstawą przy konstruowaniu
takiego automatu generującego GUI jest zna-
lezienie złotego środka pomiędzy generycz-
nością (zdolnością do pracy z różnymi dany-
mi) rozwiązania a naszymi oczekiwaniami, a
co za tym idzie nakładem pracy. Innymi sło-
wy, zaproponujemy rozwiązanie, które da się
stosować łatwo, szybko i przyjemnie , ale za ce-
nę pewnych uproszczeń, które musimy zaak-
ceptować.
Aby sprecyzować nasze oczekiwania funk-
cjonalne dotyczące GUI, spróbujmy określić,
do czego ono jest nam potrzebne. Przecięt-
ny użytkownik aplikacji komputerowej wy-
korzystuje graficzny interfejs użytkowni-
ka jako:
• dla każdej metody dodać widget, który za-
prezentuje wynik jej działania,
• dla każdego widgetu dodać odpowiednią
etykietę,
• dla każdego widgetu dodać kod, który od-
czyta wartość określonego atrybutu i wsta-
wi ją do widgetu,
• dodać przyciski kontrolne ( accept , cancel ),
• dla przycisku accept dodać kod, który od-
czyta zawartość widgetów, uaktualni od-
powiedni fragment modelu oraz ukryje
formatkę,
• dla przycisku cancel dodać kod ukrywają-
cy formatkę.
Uważamy, że w przypadku typowych,
biznesowych formularzy do wprowadzania/
edycji danych, najbardziej obiecującym roz-
wiązaniem jest podejście deklaratywne. Po-
wodem tego jest fakt, iż programista może
skupić się na tym, co chce osiągnąć, a nie
jak to zrobić. Kolejną potencjalną zaletą jest
możliwość przezroczystej pracy na różnych
platformach (przenośność). W ogólnym
przypadku, programista adnotuje kod źró-
dłowy, a dedykowana biblioteka generuje od-
powiedni kod zależny od platformy.
Założenia
proponowanego rozwiązania
Naszym celem będzie stworzenie biblioteki,
która automatycznie wygeneruje formularze
GUI na podstawie zwykłych klas języka Java.
Korzystając z tych formatek, użytkownik apli-
kacji będzie w stanie wprowadzić dane, uak-
tualnić istniejące informacje lub je po prostu
przeglądać. Przykładowo, programista chce
wykonać GUI (podobne do tego z Rysunku 1.)
dla wcześniej przedstawionej klasy Person . W
miarę możliwości, będziemy się starali, aby ca-
ły proces odbywał się bez nadmiernego zaan-
gażowania ze strony programisty.
W trakcie prac projektowych musimy roz-
wiązać trzy główne problemy:
Realizacja powyższych wymagań oznacza na-
pisanie kilkudziesięciu linii kodu (7 atrybutów
mnożone przez 5 do 10 linii na kontrolkę plus
zarządzanie rozkładem, przyciskami kontrol-
nymi, itp.), które są do siebie dość podobne.
Aby zmniejszyć nasz nakład pracy, może-
my skorzystać z edytora GUI. Jednym z nich
jest np. Jigloo GUI Builder [9] przeznaczony
dla środowiska Eclipse. Korzystając z tego roz-
wiązania projektant/programista jest w sta-
nie graficznie zaprojektować formatkę odpo-
wiednio rozmieszczając poszczególne widge-
ty. Przykład dla klasy Person (Listing 1.) jest
pokazany na Rysunku 1. Dla tego formularza,
edytor GUI wygenerował 105 linii kodu Javy.
Ta liczba nie zawiera elementów niezbędnych
do odczytu/zapisu wartości z/do modelu. Kod
taki musi być stworzony ręcznie przez progra-
mistę. Mimo wszystko, jest to spore udogod-
nienie w porównaniu z ręcznym pisaniem ca-
łego kodu. Niestety programista musi spędzić
trochę czasu rozmieszczając widgety, dodając
kod obsługujący odczyt/zapis danych i zarzą-
dzający rozłożeniem elementów.
Wróćmy jeszcze na chwilę do propozycji
Microsoft zwanej XAML (Extensible Appli-
cation Markup Language) mocno wykorzy-
stywanej w WPF (Windows Presentation Fo-
undation). Polega ona na tym, że sposób wy-
korzystania poszczególnych kontrolek nie jest
definiowany w języku programowania, jak w
klasycznym wykorzystaniu np. Swinga, ale za
pomocą specjalnego pliku. Do tego celu wyko-
rzystywany jest specjalny język opisu (właśnie
XAML) oparty o XML. Fragment takiego pli-
ku jest pokazany na listingu 2. Można się z nie-
go zorientować, że programista korzystający z
XAML do tworzenia GUI nie ma znacząco ła-
twiejszego zadania. Praktycznie rzecz biorąc,
musi wykonać każdy z powyżej opisanych kro-
ków, z ta tylko różnicą, że nie robi tego w ję-
zyku programowania, ale w specjalnym języku
znaczników. Z naszego punktu widzenia, czy-
li maksymalnego zautomatyzowania całego
procesu, to podejście nie wnosi nowej jakości
i właściwie nawet trudno nazwać je deklara-
tywnym. Sprawdza się za to znakomicie przy
generowaniu kodu XAML z dedykowanych
edytorów, np. Microsoft Expression Blend.
• Wejście dla danych w celu wypełnienia ich
pewną zawartością. W tym celu, progra-
mista stosuje pewne widgety (np. pole tek-
stowe) i łączy je z danymi (np. nazwiskiem
klienta w systemie). Gdy użytkownik wpro-
wadzi odpowiednie informacje, dedykowana
część programu, zapisuje je w modelu.
• Wyjście, celem zaprezentowania informa-
cji przechowywanych w modelu. W tym
celu, programista pisze kod odczytujący
odpowiedni fragment danych i wyświetla
go w określonym komponencie GUI.
• Jak odczytać zawartość klasy (jej strukturę)?
• Jaki widget powinien być użyty do po-
szczególnych rodzajów danych?
• Jak połączyć poszczególne elementy GUI
(widgety) z danymi?
Powyższe potrzeby mogą być zaspokojone,
korzystając z jednego ze wcześniej opisa-
nych sposobów. Ponieważ stworzony proto-
typ jest dedykowany dla Javy, w dalszej czę-
ści artykułu skupimy się na rozwiązaniach
przeznaczonych właśnie dla tego języka.
Następne paragrafy zawierają krótkie omó-
wienie implementacji potrzeb związanych
z wejściem/wyjściem korzystając z istnieją-
cych rozwiązań.
Sposoby tworzenia GUI
Najbardziej powszechnym sposobem tworzenia
GUI jest wykorzystanie bibliotek dostarczanych
razem z językiem. Większość graficznych inter-
fejsów użytkownika dla Javy jest implementowa-
na z użyciem bibliotek Swing [3] lub SWT [4].
Spójrzmy na kod znajdujący się na listingu nr 1.
Przedstawia on typową klasę biznesową jakich
wiele we współczesnych systemach.
Aby opracować dla niego odpowiednie GUI,
działające zgodnie z uprzednio określonymi po-
trzebami wejścia/wyjścia, należy wykonać po-
niższe kroki:
Rysunek 1. Okno obsługujące klasę Person
stworzone za pomocą edytora GUI
• utworzyć pustą formatkę,
• dodać odpowiedni manager rozkładu,
• dla każdego atrybutu dodać widget, który
zaprezentuje jego zawartość i umożliwi je-
go edycję,
Rysunek 2. Formatka wygenerowana przez
bibliotekę dla kodu z Listingu 2.
www.sdjournal.org
45
441671224.008.png 441671224.009.png 441671224.010.png 441671224.011.png 441671224.012.png 441671224.013.png 441671224.014.png 441671224.015.png 441671224.016.png 441671224.017.png 441671224.018.png 441671224.019.png 441671224.020.png
Języki programowania
Pierwszy problem rozwiążemy przy pomo-
cy techniki zwanej refleksją. Jest dostępna
dla popularnych języków programowania (Ja-
va, C# i częściowo C++) i umożliwia odczyta-
nie informacji o budowie klasy i tworzenie jej
obiektów. Pozostałymi kwestiami zajmiemy
się w dalszej części artykułu.
Niestety mimo naszych najlepszych chęci,
aby biblioteka obywała się bez podawania do-
datkowych parametrów, byliśmy zmuszeni
je zdefiniować. Dotyczy to choćby przypad-
ku gdy niecała zawartość klasy powinna być
edytowalna i/lub wyświetlana. Ponieważ nie
jest możliwe automatyczne szacowanie, któ-
re elementy powinny mieć swoje odpowied-
niki w GUI, byliśmy zmuszeni wprowadzić
pewne znaczniki. Umożliwiają one dostoso-
wanie generowanego interfejsu użytkowni-
ka do konkretnych potrzeb. Kwestią otwar-
tą było, w jaki sposób osiągnąć taką funkcjo-
nalność, czyli połączyć programistę, bibliote-
kę i GUI? Mieliśmy do wyboru kilka możli-
wości, włączając w to pliki konfiguracyjne
(np. XML) lub przekazywanie parametrów
w wywoływanych metodach. Ostatecznie, po
przeanalizowaniu różnych rozwiązań, zdecy-
dowaliśmy się wykorzystać adnotacje. Takie
konstrukcje istnieją dla języka Java oraz C#
i umożliwiają opisywanie klas oraz ich skła-
dowych. Staraliśmy się zaprojektować je mak-
symalnie prostymi w użyciu, ale ostatecznie
ich liczba wzrosła do 11. Na szczęście więk-
szość z nich ma domyślne wartości, których
nie trzeba modyfikować. Wprowadziliśmy
dwa podstawowe typy adnotacji: przezna-
czone dla atrybutów ( GUIGenerateAttribu-
te ) oraz metod ( GUIGenerateMethod ). Każdy
z nich ma dodatkowe parametry (z domyśl-
nymi wartościami). Poniżej znajdują się naj-
ważniejsze z nich:
Listing 2. Zastosowanie adnotacji celem wygenerowania GUI – wariant z wartościami domyślnymi
Label – opisuje etykietę znajdującą się
przy widgecie. Jeżeli jest pusta (wartość
domyślna) to zostanie wykorzystana na-
zwa atrybutu lub metody. Ten parametr
był wymagany przy wprowadzaniu spe-
cjalnego nazewnictwa (np. spacji, czy pol-
skich liter). WidgetClass – klasa widgetu,
która będzie wykorzystana do edycji oraz
wyświetlania danych. Domyślna wartość
zakłada użycie pola tekstowego (Jtext-
Box);
Tooltip – krótki tekst wyświetlany po na-
jechaniu kursorem na element;
GetMethod – metoda wykorzystywana
do odczytu wartości atrybutu. Domyślna
wartość zawiera pusty ciąg i oznacza za-
stosowanie podejścia opartego na gette-
rach oraz setterach (biblioteka wyszukuje
takie metody w ciele klasy);
SetMethod – analogicznie jak w przypad-
ku getMethod , ale dotyczy zapisu warto-
ści;
Order – liczba określająca kolejność widge-
tu na formularzu;
ReadOnly – flaga definiująca, czy dane są
dostępne w trybie tylko do odczytu (bez
możliwości modyfikacji);
ScaleWidget – określa, czy widget powi-
nien zmieniać swój rozmiar w czasie mo-
dyfikacji wielkości formularza.
public class PersonAnnotated {
@ GUIGenerateAttribute
private String irstName ;
@ GUIGenerateAttribute
private String lastName ;
@ GUIGenerateAttribute
private Date birthDate = new Date () ;
@ GUIGenerateAttribute
private boolean higherEducation ;
@ GUIGenerateAttribute
private String remarks ;
@ GUIGenerateAttribute
private int SSN ;
@ GUIGenerateAttribute
private double annualIncome ;
@ GUIGenerateMethod
public int getAge () {
// ...
}
// Standard getters/setters methods
}
Listing 3. Zastosowanie adnotacji celem wygenerowania GUI – wariant ze zmodyikowanymi
wartościami
public class PersonAnnotated {
@ GUIGenerateAttribute ( label = "First name" , order = 1 )
private String irstName ;
@ GUIGenerateAttribute ( label = "Last name" , order = 2 )
private String lastName ;
@ GUIGenerateAttribute ( label = "Birth date" , order = 3 )
private Date birthDate = new Date () ;
@ GUIGenerateAttribute ( label = "Higher education" , widgetClass = "mt.mas.GUI.CheckboxBo
olean" , order = 5 )
private boolean higherEducation ;
@ GUIGenerateAttribute ( label = "Remarks" , order = 50 ,
widgetClass = "javax.swing.JTextArea" , scaleWidget = false )
private String remarks ;
@ GUIGenerateAttribute ( order = 6 )
private int SSN ;
@ GUIGenerateAttribute ( label = "Annual income" , order = 7 )
private double annualIncome ;
@ GUIGenerateMethod ( label = "Age" , showInFields = true , order = 4 )
public int getAge () {
return 0 ;
}
// Standard getters/setters methods
}
Opisywane rozwiązanie jest w stanie pra-
cować z różnymi typami danych: liczbami,
tekstem, wartościami Tak/Nie, datami oraz
typami wyliczeniowymi. Domyślna imple-
Rysunek 3. Formatka wygenerowana przez
bibliotekę dla kodu z Listingu 3.
46
01/2009
441671224.021.png 441671224.022.png
Biblioteka senseGUI – czyli GUI z automatu
mentacja zakłada wykorzystanie pól teksto-
wych do wszystkich typów danych za wy-
jątkiem:
zmienioną kolejność elementów, czy wykorzy-
stanie dedykowanego widgetu dla informacji o
wykształceniu.
Zainteresowany Czytelnik, analizując kod
źródłowy biblioteki (do pobrania na stronie
www.sdjournal.org ) może zauważyć, iż można
z niej korzystać na dwa sposoby:
• Automatycznie wyszukiwać obiekty na
podstawie kryteriów podanych w specjal-
nej formatce.
• Zarządzać ekstensją klasy.
• wartości Tak / Nie – stosowany jest prze-
łącznik (check box),
• typu wyliczeniowego ( enum ), który jest
przetwarzany w oparciu o specjalne pole
wyboru zawierające wszystkie zdefiniowa-
ne w nim wartości (odczytane dzięki re-
fleksji).
Wykorzystanie powyżej opisanych funkcjo-
nalności jest równie proste, jak podstawo-
wych możliwości oryginalnego rozwiąza-
nia. Dzięki połączeniu bibliotek ( senseGUI
+ senseObjects ) programista ma ułatwione
tworzenie typowych, biznesowych aplikacji.
uproszczony – sprowadzający się do wy-
wołania jednej dużej metody, która zadba
o kompleksowe wygenerowanie i wyświe-
tlenie formatki dla podanego obiektu, za-
awansowany zakładający wołanie mniej-
szych metod umożliwiających dokładniej-
sze dostosowanie wyświetlanych elemen-
tów. W efekcie otrzymujemy np. instan-
cję JPanel (zamiast pełnego okna), zawiera-
jącą odpowiednie widgety, która może być
dodana do dowolnego, innego elementu
GUI. Dzięki temu można wyświetlać np.
zawartość atrybutów opisywanych za po-
mocą typów zdefiniowanych przez pro-
gramistów (a nie tylko typów prostych).
Listing nr 2. zawiera kod przykładowej klasy
Person udekorowany adnotacjami zdefinio-
wanymi w bibliotece. Warto zauważyć, że je-
dyne co musieliśmy zrobić to dopisać krótkie
adnotacje dla wybranych atrybutów (korzy-
stając z domyślnych wartości).
W rezultacie wywołania pojedynczej me-
tody z biblioteki, wygenerowana i wyświe-
tlona została formatka pokazana na Rysunku
2. Jest ona połączona z instancją klasy Person,
dzięki czemu wprowadzone zmiany są auto-
matycznie odzwierciedlane w modelu. Moż-
na zauważyć, że nie zawiera ona widgetu dla
metody obliczającej wiek. Jest to zgodne z
zaproponowanym podejściem, które zakła-
da, że przy domyślnym (bezparametrowym)
użyciu adnotacji, metody nie mają swoich
odpowiedników na formatkach.
Jak widać, takie proste udekorowanie ko-
du nie zawsze prowadzi do idealnych rezulta-
tów. Z punktu widzenia użytkownika, moż-
na mieć zastrzeżenia do nazewnictwa po-
szczególnych elementów, kolejności, w jakiej
występują, czy zastosowanych widgetów. W
takich sytuacjach warto wykorzystać odpo-
wiednie parametry adnotacji, tak jak pokaza-
no na Listingu 3.
Efekt wygenerowania formularza na podsta-
wie powyższego kodu pokazano na rysunku nr
3. Warto zwrócić uwagę na poprawne nazwy,
Podsumowanie
Przedstawiliśmy pewną propozycję dotyczącą
automatycznego generowania graficznych in-
terfejsów użytkownika dla aplikacji bizneso-
wych. Wygenerowany formularz jest oparty na
prostych adnotacjach umieszczonych w kodzie
źródłowym klas definiujących model danych.
Programista wybiera, które inwarianty powinny
mieć swoje odzwierciedlenie w GUI, a zaprezen-
towana biblioteka zajmuje się stworzeniem od-
powiedniego interfejsu użytkownika.
W kolejnej części artykułu dokładniej omó-
wimy budowę biblioteki oraz pokusimy się o
jej rozbudowę o kilka istotnych elementów do-
tyczących elementów międzynarodowości, ge-
nerowania GUI bez konkretnego modelu da-
nych oraz sprawdzania poprawności wprowa-
dzonych informacji.
Prototypowa biblioteka, którą tu opisaliśmy, jest
częścią większego frameworku ( senseFrame-
work ) i w połączeniu z nim pozwala na wiele wię-
cej niż tylko edycję i wyświetlanie stanu poszcze-
gólnych obiektów. Dzięki dodatkowemu zastoso-
waniu biblioteki senseObjects możemy:
• Zarządzać powiązaniami pomiędzy obiek-
tami. Stworzyliśmy odpowiednią funk-
cjonalność, która na podstawie adnota-
cji pozwala wyświetlać oraz zarządzać po-
wiązanymi obiektami (asocjacje). Co wię-
cej, można tworzyć kompozycje, asocjacje
kwalifikowane, czy powiązania ogranicza-
ne przez { xor } lub { subset }.Wyświetlić wie-
le obiektów korzystając z widoku tabela-
rycznego wygenerowanego na podstawie
adnotacji.
MARIUSZ TRZASKA
Mariusz Trzaska jest adiunktem w Polsko-Japoń-
skiej Wyższej Szkole Technik Komputerowych. Wy-
kłada tam przedmioty związane z bazami danych,
programowaniem, modelowaniem oraz inżynie-
rią oprogramowania. Oprócz tego bierze udział w
różnego rodzaju projektach komercyjnych oraz ba-
dawczych. Jest autorem kilkunastu artykułów pu-
blikowanych w kraju i za granicą.
Kontakt z autorem: mtrzaska@pjwstk.edu.pl
R E K L A M A
����������
������
�� Visual Studio Professional
������������������
Kontakt ���������������������������������������������������������������������������
441671224.023.png 441671224.024.png 441671224.025.png 441671224.026.png 441671224.027.png 441671224.028.png 441671224.029.png 441671224.030.png 441671224.031.png 441671224.032.png 441671224.033.png 441671224.034.png 441671224.035.png 441671224.036.png 441671224.037.png 441671224.038.png 441671224.039.png 441671224.040.png 441671224.041.png 441671224.042.png 441671224.043.png 441671224.044.png 441671224.045.png 441671224.046.png 441671224.047.png 441671224.048.png 441671224.049.png 441671224.050.png 441671224.052.png 441671224.053.png 441671224.054.png 441671224.055.png 441671224.056.png 441671224.057.png 441671224.058.png 441671224.059.png 441671224.060.png 441671224.061.png 441671224.063.png 441671224.064.png 441671224.065.png 441671224.066.png 441671224.067.png 441671224.068.png 441671224.069.png 441671224.070.png 441671224.071.png 441671224.072.png 441671224.074.png 441671224.075.png 441671224.076.png 441671224.077.png 441671224.078.png 441671224.079.png 441671224.080.png 441671224.081.png 441671224.082.png 441671224.083.png 441671224.085.png 441671224.086.png 441671224.087.png 441671224.088.png
 
Zgłoś jeśli naruszono regulamin