MateriaT1l y eCG IT Documentation
Transkrypt
MateriaT1l y eCG IT Documentation
Materiały eCG IT Documentation Wydanie 1 Robert Bednarz July 18 2015 Spis treści 1 Narz˛edzia 1.1 Geany . . . . . . . . . . . . 1.2 Kompilator C/C++ . . . . . 1.3 Biblioteka Qt . . . . . . . . 1.4 Interpreter Pythona . . . . . 1.5 Git . . . . . . . . . . . . . 1.6 ReStructuredText i Sphinx . 1.7 Serwer deweloperski WWW 1.8 Materiały . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 4 4 7 17 17 20 24 27 Linux Live USB 2.1 W Windows . . . . . . 2.2 W Linuksie . . . . . . . 2.3 Pierwsze uruchomienie . 2.4 Obsługa LxPupTahr . . 2.5 Materiały dodatkowe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 29 30 32 33 47 3 Biblioteka Qt 3.1 Adresy (Qt5) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Zadania (Qt5) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3 Metryka . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 79 91 93 4 Technologie WWW 95 4.1 GetSimple CMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 4.2 Materiały . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 5 Python 5.1 Toto Lotek – Python od podstaw . . 5.2 Python kreśli . . . . . . . . . . . . 5.3 Czat (wersja rozszerzona) . . . . . 5.4 Pythonizmy . . . . . . . . . . . . . 5.5 Słownik Pythona . . . . . . . . . . 5.6 Słownik systemowy . . . . . . . . 5.7 Propozycja scenariuszy warsztatów 5.8 Metryka . . . . . . . . . . . . . . . 2 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 105 121 124 157 162 163 164 164 Gra robotów 165 6.1 Zasady i zaczynamy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 6.2 RG – klocki 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 i 6.3 6.4 6.5 6.6 6.7 RG – klocki 2A . . . RG – klocki 2B . . . RG – klocki 3B . . . RG – dokumentacja Słownik Pythona . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 176 181 186 200 7 Galerie 203 7.1 GetSimple CMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 7.2 Metryka . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 8 Indeks ii 205 Materiały eCG IT Documentation, Wydanie 1 Materiały zawarte w tym repozytorium zawieraja˛ ćwiczenia, poradniki oraz dokumentacj˛e zwiazan ˛ a˛ z zagadnieniami technologii informacyjnej i informatyki. Spis treści: Spis treści 1 Materiały eCG IT Documentation, Wydanie 1 2 Spis treści ROZDZIAŁ 1 Narzedzia ˛ Poniżej przedstawiamy zestaw przykładowych narz˛edzi do programowania, tworzenia skryptów i dokumentacji, stron WWW itp. zadań. Spis treści • Narz˛edzia – Geany * Instalacja – Kompilator C/C++ – Biblioteka Qt * System i środowisko IDE * Qt5 w systemie Windows – Interpreter Pythona – Git * Konto * Klient Gita * Lokalne repozytorium * Podstawy Gita – ReStructuredText i Sphinx * Instalacja Sphinksa * Tworzenie dokumentacji * Podstawy reST · Nagłówki · Dyrektywy · Listy · Linki * Budowanie dokumentacji * Dokumentacja online – Serwer deweloperski WWW * Linux * Serwer2Go w Windows – Materiały * Słownik * Metryka Informacja: Przykłady poleceń wydawanych w terminalu b˛edziemy podawali dla systemu Debian i pochodnych (np. (X)Ubuntu, Mint) oraz menedżera pakietów apt-get, poprzedzamy je symbolami ~$; a także dla system Arch Linux i pochodnych (np. Bridge, Manjaro) korzystajacych ˛ z menedżera pacman, poprzedzamy je symbolami ~#. 3 Materiały eCG IT Documentation, Wydanie 1 Instalacja pakietów wymaga uprawnień roota. W wielu systemach używa si˛e polecenia sudo, które pozwala zwykłemu użytkownikowi uruchamiać programy z podwyższonymi uprawnieniami. Jeżeli Twój system nie ma skonfigurowanego domyślnie polecenia sudo (np. Debian lub Arch Linux), musisz zalogować si˛e na konto roota, np. wpisujac ˛ w terminalu su root. 1.1 Geany Geany to proste i lekkie środowisko IDE dost˛epne na licencji GNU General Public Licence. Geany oferuje kolorowanie składni dla najpopularniejszych j˛ezyków, m.in. C, C++, C#, Java, PHP, HTML, Python, Perl i Pascal, wsparcie dla kodowania w ponad 50 standardach, dopełnianie poleceń, mechanizmy automatycznego zamykanie tagów dla HTMLXML, auto-wci˛eć, pracy na kartach i wiele, wiele wi˛ecej. Podczas pisania kodu przydatny okazuje si˛e brudnopis, pozwalajacy ˛ tworzyć dowolne notatki, a także możliwość kompilacji plików źródłowych bezpośrednio z poziomu programu. 1.1.1 Instalacja W systemie Linux korzystamy z dedykowanego menedżera pakietów, np. w Xubuntu (i innych debianopochodnych) lub Archu wystarczy wpisać w terminalu: ~$ sudo apt-get install geany geany-plugins ~# pacman -S geany geany-plugins W MS Windows ściagamy ˛ i instalujemy pełna˛ wersj˛e binarna˛ Geany przeznaczona˛ dla tych systemów. Pełna oznacza tutaj, ze zawiera biblioteki GTK wykorzystywane przez program. Podczas standardowej instalacji można zmienić katalog docelowy, np. na C:\Geany. Zanim rozpoczniemy prac˛e w edytorze, warto dostosować kilka ustawień. W menu Narz˛edzia/Menedżer wtyczek zaznaczamy pozycj˛e “Addons” (dost˛epna po zainstalowaniu wtyczek), a nast˛epnie “Przegladarka ˛ plików”. Zanim wyjdziemy z okna naciskamy przycisk “Preferencje” i na zakładce “Przegladarka ˛ plików” zaznaczamy opcj˛e “Poda˛żanie za ścieżka˛ do bieżacego ˛ pliku”. Dzi˛eki temu w panelu bocznym w zakładce “Pliki” zobaczymy list˛e katalogów i plików, które łatwo możemy otwierać. W menu Edycja/Preferencje CTRL+ALT+P w zakładce Edytor/Wci˛ecia jako “Typ” wci˛eć wybieramy opcj˛e “spacje”. Warto zapami˛etać i stosować kilka użytecznych skrótów. Jeśli kodujemy w C/C++: F8 – uruchamia linker, F9 – buduje plik wykonawczy. Klawisz F5 uruchamia z kolei pliki wykonywalne, skrypty Pythona lub otwiera strony HTML w przegladarce. ˛ Wci˛ecia wstawiaja˛ si˛e automatycznie lub poprzez naciśni˛ecie klawisza TAB. Jeżeli chcemy wciać ˛ od razu cały blok kodu, zaznaczamy go i również używamy TAB lub CTRL+I, zmniejszenie wci˛ecia uzyskamy naciskajac ˛ CTRL+U. Środowisko Geany nie zawiera narz˛edzi potrzebnych do kompilowania czy wykonywania programów pisanych w różnych j˛ezykach. Wymagane narz˛edzia musimy doinstalować osobno. 1.2 Kompilator C/C++ W systemie Linux wystarczy zazwyczaj wydać jedno polecenie wykorzystujace ˛ używany w danej dystrybucji menedżer pakietów, np.: ~$ sudo apt-get install gcc ~# pacman -S gcc 4 Rozdział 1. Narzedzia ˛ Materiały eCG IT Documentation, Wydanie 1 1.2. Kompilator C/C++ 5 Materiały eCG IT Documentation, Wydanie 1 6 Rozdział 1. Narzedzia ˛ Materiały eCG IT Documentation, Wydanie 1 W MS Windows instalujemy minimalistyczne środowisko deweloperskie dostarczajace ˛ wolne narz˛edzia GNU Compiler Collection, czyli MinGw. W tym celu pobieramy instalator mingw-get-setup.exe i uruchamiamy. W oknie wyboru pakietów zaznaczamy widoczne poniżej paczki, w tym przede wszystkim mingw32-gcc-g++, a nast˛epnie wybieramy polecenie Installation/Apply. Po pobraniu i rozpakowaniu wskazanych narz˛edzi oraz zakończeniu instalatora (Close) trzeba dodać do ścieżki systemowej położenie kompilatora i linkera; dzi˛eki temu b˛edzie można korzystać z tych i innych narz˛edzi bezpośrednio z Geany lub okienka konsoli tekstowej. W oknie “Uruchamianie” (WIN+R) wpisujemy polecenie wywołujace ˛ okno “Zmienne środowiskowe” – można je również uruchomić z okna właściwości komputera: Klikamy przycisk Nowa i tworzymy nowa˛ zmienna˛ użytkownika zgodnie z podanym zrzutem: Wskazówka: Powyżej przedstawiliśmy instalacj˛e narz˛edzi MinGw z konta zwykłego użytkownika. Można w razie potrzeby czynności te wykonać również z konta administratora, co pozwoli udost˛epnić narz˛edzia wszystkim użytkownikom. Podobnie ścieżk˛e do kompilatora itd. można dopisać do zmiennej systemowej PATH, dzi˛eki czemu wszyscy użytkownicy b˛eda˛ mogli wywoływać narz˛edzia bez podawania pełnej ich lokalizacji. 1.3 Biblioteka Qt Qt to zestaw bibliotek przeznaczonych dla j˛ezyka C++, QML i Java, który znakomicie ułatwia tworzenie graficznego interfejsu użytkownika. Zawiera również klasy udost˛epniajace ˛ obsług˛e m.in. multimediów , sieci czy baz danych. 1.3. Biblioteka Qt 7 Materiały eCG IT Documentation, Wydanie 1 8 Rozdział 1. Narzedzia ˛ Materiały eCG IT Documentation, Wydanie 1 1.3. Biblioteka Qt 9 Materiały eCG IT Documentation, Wydanie 1 10 Rozdział 1. Narzedzia ˛ Materiały eCG IT Documentation, Wydanie 1 1.3. Biblioteka Qt 11 Materiały eCG IT Documentation, Wydanie 1 12 Rozdział 1. Narzedzia ˛ Materiały eCG IT Documentation, Wydanie 1 1.3. Biblioteka Qt 13 Materiały eCG IT Documentation, Wydanie 1 1.3.1 System i środowisko IDE Bilioteka Qt jest przenośna z założenia, wi˛ec programować z jej wykorzystaniem można w wielu systemach i środowiskach. Proponujemy system Linux, np. dystrybucj˛e Debian (v. Jessie) lub Xubuntu 14.04. Instalacja wymaganych narz˛edzi sprowadza si˛e do wydania prostych poleceń w terminalu: ~$ sudo apt-get update ~$ sudo apt-get install qtcreator qt5-qmake qt5-default qt4-qtconfig Pierwsze polecenie zaktualizuje repoytoria, czyli wersje dost˛epnego oprogramowania; drugie zainstaluje dedykowane środowisko IDE, które pozwala projektować interfejs graficzny, bardzo ułatwia edycj˛e kodu, pozwala budować, uruchamiać i debugować różne wersje tworzonych aplikacji. 1.3.2 Qt5 w systemie Windows Instalacja jest bardzo prosta. Pobieramy Qt Online Installer for Windows i uruchamiamy. Nast˛epnie przeprowadzamy standardowa˛ instalacj˛e z domyślnymi ustawieniami, podajac ˛ w razie potrzeby hasło administratora. Wyglad ˛ i działanie aplikacji Qt Creator w systemie Linux i Windows sa˛ takie same. 14 Rozdział 1. Narzedzia ˛ Materiały eCG IT Documentation, Wydanie 1 1.3. Biblioteka Qt 15 Materiały eCG IT Documentation, Wydanie 1 16 Rozdział 1. Narzedzia ˛ Materiały eCG IT Documentation, Wydanie 1 1.4 Interpreter Pythona W systemach Linux Python jest szeroko stosowany i dost˛epny jest w ramach standardowej instalacji wi˛ekszości dystrybucji i to zarówno w wersji 2.x, jak i 3.x. Dla formalności polecenia instalacyjne to: ~$ sudo apt-get install python2 python3 ~# pacman -S python python2 Informacja: Warto zauważyć, że w dystrybucjach opartych na Debianie polecenie python jest dowiazaniem ˛ do wersji 2.x, a paczki nazywaja˛ si˛e odpowiednio python2-... lub python3-.... W Arch Linuksie i pochodnych nazwa python domyślnie wskazuje wersj˛e 3.x (!), podobnie nazwy pakietów dodatkowych. Wersja i paczki z liniii 2.x maja˛ w nazwie python2. Polecić natomiast można doinstalowanie dodatkowych narz˛edzi, w tym rozszerzonej konsoli: ~$ sudo apt-get install ipython2 ipython3 ~# pacman -S python-pip ipython python2-pip ipython2 W MS Windows najprościej zainstalować Pythona przy użyciu skryptu konsoli PowerShell dost˛epnej w wersjach Professional (oznaczona˛ niebieska˛ ikona˛ i niebieskiem tłem): (new-object System.Net.WebClient).DownloadFile("https://www.python.org/ftp/python/2.7.8/python-2.7.8. msiexec /i python-2.7.8.msi TARGETDIR=C:\Python27 [Environment]::SetEnvironmentVariable("Path", "$env:Path;C:\Python27\;C:\Python27\Scripts\", "User") (new-object System.Net.WebClient).DownloadFile("https://raw.github.com/pypa/pip/master/contrib/get-pi C:\Python27\python.exe get-pip.py virtualenv Jeżeli w naszej wersji Windows nie ma PowerShella, ściagamy ˛ interpreter Pythona i instalujemy r˛ecznie, pami˛etajac ˛ o zaznaczeniu opcji “Add Python.exe to Path”. Nast˛epnie instalujemy program pip do zarzadzania ˛ dodatkowymi bibliotekami za pomoca˛ polecenia: python -c "exec('try: from urllib2 import urlopen \nexcept: from urllib.request import urlopen');f=ur Aby uruchamiać skrypty bezpośrednio z poziomu Geany lub konsoli tekstowej bez podawania pełnej ścieżki warto ja˛ dodać do zmiennej użytkownika lub systemu o nazwie PATH, tak jak pokazano wyżej dla narz˛edzi MinGw. Na potrzeby pojedynczej sesji odpowiedni efekt osiagniemy ˛ wydajac ˛ polecenie w konsoli: set PATH=%PATH%;c:\Python27\;c:\Python27\Scripts\ 1.5 Git Git Source Code Mirror – to rozproszony system wersjonowania kodów źródłowych napisany przez Linusa Torvaldsa, twórc˛e jadra ˛ Linux. Skrót SCM bywa również rozwijany jako software configuration management, co oznacza “zarzadzanie ˛ konfiguracja˛ oprogramowania”. Gita można używać do rozwijania zarówno małych, jak i dużych projektów (np. Arch Linux, GIMP, jQuery). 1.5.1 Konto Przede wszystkim wchodzimy na stron˛e https://github.com/. Nast˛epnie wykonujemy poniższe instrukcje: • Krok 1 – założenie konta; podajemy nazw˛e użytkownika, adres email i hasło, jako typ konta wybieramy free. • Krok 2 – weryfikacja adresu e-mail 1.4. Interpreter Pythona 17 Materiały eCG IT Documentation, Wydanie 1 18 Rozdział 1. Narzedzia ˛ Materiały eCG IT Documentation, Wydanie 1 • Krok 3 – zakładamy repozytorium dla projektu, wybieramy typ Public (publiczne) oraz Initialize this repository with a README (utwórz poczatkowy ˛ plik README.md). To wszystko. Od tej pory można rozwijać projekt. 1.5.2 Klient Gita Treść projektu można dodawać do repozytorium centralnego w serwisie github.com za pomoca˛ przegladarki. ˛ Cz˛eściej jednak robi si˛e to offline, czyli pracuje si˛e na swoim repozytorium lokalnym za pomoca˛ jakiegoś programu. Ze wzgl˛edu na szybkość działania polecamy środowisko Linux. Instalacja podstawowego klienta tekstowego w systemach debianopodobnych (poza samym Debianem :-), Ubuntu, Linux Mint, PepperMint itd.) i opartych na Arch Linuksie (np. Bridge Linux) sprowadza si˛e do użycia odpowiedniego menedżera pakietów: ~$ sudo apt-get install git ~# pacman -S git W systemach Windows tego samego klienta tekstowego pobieramy ze strony http://git-scm.com/download/win i instalujemy wybierajac ˛ domyślne opcje. Po opanowaniu podstaw obsługi Gita można oczywiście zainstalować programy z graficznym interfejsem użytkownika, jednak w wi˛ekszości przypadków nie jest to konieczne. 1.5.3 Lokalne repozytorium Pierwsza˛ rzecza˛ b˛edzie najcz˛eściej sklonowanie założonego repozytorium na nasz komputer. Uruchamiamy wi˛ec terminal (wiersz poleceń czy też konsol˛e tekstowa) ˛ i wydajemy polecenie: ~$ git clone https://github.com/nazwa_użytkownika/nazwa_repozytorium.git Jak widać argumentem jest tutaj Git URL, czyli schematyczny adres repozytorium, który możemy sprawdzić na stronie github.com. Domyślnym protokołem transferu jest https, chociaż można wykorzystywać również inne, np. SSH. W rezultacie w bieżacym ˛ folderze (zazwyczaj katalogu domowym użytkownika) utworzony zostanie katalog o nazwie naszego projektu zawierajacy ˛ wszystkie dodane do tej pory materiały, np. plik README.md, oraz ukryty katalog konfiguracyjny .git, którego nie należy usuwać. Informacja: Za pomoca˛ narz˛edzia git można również utworzyć zupełnie nowy projekt. Służy do tego opcja init. Możliwe jest również sklonowanie istniejacego ˛ projektu do katalogu o narzuconej nazwie, np.: https://github.com/koduj-z-klasa/python101.git. 1.5.4 Podstawy Gita Informacja: Wszystkie poniższe polecenia wydajemy w głównym katalogu projektu. Warto poczytać polska˛ wersj˛e 1 podr˛ecznika Git SCM. Dost˛epna jest również wersja 2 podr˛ecznika Git, ale tylko w j. angielskim. Codzienna˛ prac˛e projektem warto rozpoczać ˛ od zsynchronizowania wersji lokalnej z ewentualnymi zmiananami zapisanymi na serwerze: ~$ git pull Bardzo cz˛esto b˛edziemy korzystać z polecenia: ~$ git status 1.5. Git 19 Materiały eCG IT Documentation, Wydanie 1 – które informuje nas o tym, jakie pliki zostały dodane do poczekalni, ale sa˛ nieśledzone (ang. Untracked files), jakie zostały zmienione, ale nie zostały zatwierdzone (ang. Changes not staged for commit), a jakie czekaja˛ na zatwierdzenie (ang. Changes to be committed). Komunikat On branch master informuje, że pracujemy na głównej gał˛ezi (ang. master branch) projektu. Zarówno nieśledzone, jak i niezatwierdzone pliki, które chcemy umieścić w projekcie, dodajemy poleceniem: ~$ git add ścieżka/nazwa_pliku Można używać znaków specjalnych, np. git add *.jpg. Jeżeli mamy rozbudowana˛ struktur˛e katalogów w projekcie, przydatne jest polecenie dodajace ˛ nowe zmiany hurtowo i rekursywnie: ~$ find . -name "*.rst" -exec git add {} \; Po dodaniu wszystkich nowych plików i zmian do poczekalni, trzeba je zatwierdzić: ~$ git commit -m "Opis zmian ..." Jeżeli pominiemy opcjonalny przełacznik ˛ -m otwarty zostanie edytor, w którym opisujemy dokonywane zmiany. Zatwierdzone zmiany prześlemy na serwer wydajac ˛ polecenie: ~$ git push – które poprosi nas o podanie nazwy użytkownika (adres email) i hasła, a nast˛epnie prześle informacje na serwer. Powyższy porzadek ˛ komend jest typowy dla sesji z gitem. Informacja: Wskazówka: nie należy usuwać plików/katalogów lub zmieniać ich nazw w katalogu projektu za pomoca˛ narz˛edzi systemowych, np. menedżera plików, ponieważ Git nie b˛edzie nic o tym wiedział i zasypie nas wieloma komunikatami podczas sesji. Zamiast tego używamy poniższych poleceń: ~$ git rm plik ~$ git rm -rf katalog ~$ git mv stara_nazwa nowa_nazwa 1.6 ReStructuredText i Sphinx Git dobrze nadaje si˛e do prowadzenia projektów nie tylko typowo programistycznych, ale również dokumentacyjnych i szkoleniowych, a wi˛ec zawierajacych ˛ nie tylko kod, ale przede wszystkim instrukcje, poradniki, scenariusze, itp. W katalogu naszego projektu zakładamy katalog podrz˛edny o nazwie np. docs, w którym tworzyć b˛edziemy nasza˛ dokumentacj˛e. ~/nazwa_projektu$ mkdir docs Dokumenty zapisywane b˛eda˛ w formacie reStructuredText, czyli za pomoca˛ tekstowego j˛ezyka znaczników, w plikach z rozszerzeniem .rst. Zawartość tych plików może być później przetworzona do postaci formatów docelowych, takich jak HTML, PDF czy LaTeX. Zadanie to realizowane b˛edzie przez narz˛edzie Sphinx napisane w Pythonie i służace ˛ m.in. do tworzenia dokumentacji tego j˛ezyka. 1.6.1 Instalacja Sphinksa Przede wszystkim potrzebujemy interpretera Pythona i narz˛edzia instalacji modułów dodatkowych pip – zobacz w sekcji Interpreter Pythona. Nast˛epnie wydajemy polecenia: 20 Rozdział 1. Narzedzia ˛ Materiały eCG IT Documentation, Wydanie 1 ~$ sudo apt-get install mercurial ~$ sudo pip install sphinx_rtd_theme hg+https://bitbucket.org/birkenfeld/sphinx#sphinx W Archu: ~# pacman -S mercurial ~# pip install sphinx_rtd_theme hg+https://bitbucket.org/birkenfeld/sphinx#sphinx Informacja: Instalacja klienta systemu kontroli wersji Mercurial wynika z tego, że korzysta z niego projekt Sphinx. Instalacja tematu sphinx_rtd_theme jest opcjonalna, domyślny temat wyglada ˛ tak, jak w dokumentacji Pythona. Teraz możemy przejść do konfiguracji Sphinksa, która sprowadza si˛e do wygenerowania pliku z ustawieniami o nazwie conf.py. W głównym katalogu tworzonej dokumentacji, czyli docs, wydajemy polecenie: ~$ sphinx-quickstart Na wi˛ekszość pytań kreatora odpowiadamy naciśni˛eciem Enter, przyjmujac ˛ ustawienia domyślne. Zwrócić uwag˛e należy na: • Project name: – wpisujemy nazw˛e naszego projektu; • Author name(s): – wpisujemy autora; • Project version: – podajemy wersj˛e, np. 1; • Project release: – podajemy wydanie, np. 0; • Project langiage [en]: – określamy j˛ezyk jako pl; • Please indicate... Sphinx extensions: – odpowiadajac ˛ y dołaczamy ˛ rozszerzenia, można właczyć: ˛ autodoc, doctest, pngmath i viewcode – przydaja˛ si˛e w dokumentacji Pythona. Zobacz: lista rozszerzeń Sphinksa; • Create Makefile? – odpowiadamy y, dzi˛eki czemu budowanie dokumentacji sprowadzi si˛e do wydania polecenia make html. Po skonfigurowaniu Sphinksa w katalogu docs powinny znaleźć si˛e pliki: conf.py, Makefile, make.bat i index.rst, a także katalogi _build, _static, _templates. Jeżeli chcemy używać tematu sphinx_rtd_theme na końcu pliku conf.py dopisujemy: try: import sphinx_rtd_theme html_theme = "sphinx_rtd_theme" html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] except: pass 1.6.2 Tworzenie dokumentacji Na poczatku ˛ warto dostosować plik główny, czyli index.rst. Jest on nasza˛ “strona˛ główna”, ˛ zawiera m. in. dyrektyw˛e tworzac ˛ a˛ spis treści: Welcome to Projekt ILO's documentation! ======================================= Contents: .. toctree:: :maxdepth: 2 1.6. ReStructuredText i Sphinx 21 Materiały eCG IT Documentation, Wydanie 1 Serwis eCG <http://ecg.vot.pl/> cwiczenia/index programowanie/index Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` Jak widać domyślne komunikaty sa˛ w j˛ezyku angielskim, należy wi˛ec je spolszczyć zmieniaj ˛ ac ˛ treść według uznania. Dyrektywa .. toctree:: generuje spis treści na podstawie wskazanych plików. W powyższym listingu dodano dwa przykładowe wpisy wskazujace ˛ pliki index.rst umieszczone we wskazanych podkatalogach. Sphinx odczytuje nagłówki z tych plików i umieszcza w spisie. Domyślnie sczytywane sa˛ dwa poziomy zagnieżdżenia (:maxdepth: 2). Gdybyśmy chcieli mieć numeracj˛e, dodalibyśmy opcj˛e: :numbered:. Pokazano również, jak dodawać stałe linki w spisie (Serwis eCG ...). Z sekcji indeksów (Indices ...) można usunać ˛ wszystkie spisy lub zostawić wybrane, np. genindex udost˛epnia indeks zdefiniowanych terminów i poj˛eć. Dokumenty w katalogu docs warto kategoryzować i umieszczać w osobnych katalogach. Nazwy plików moga˛ być dowolne, chociaż dobrze jest przyjać ˛ jakaś ˛ przejrzysta˛ konwencj˛e. Poszczególne pliki należy traktować jako kolejne strony w wersji HTML. 1.6.3 Podstawy reST Żeby zrozumieć proste w gruncie rzeczy zasady formatowania reST najlepiej podgladać ˛ kod gotowych stron! Wystarczy w nagłówku kliknać ˛ link View page source, skopiować, wkleić i wypełnić własna˛ treścia.˛ Zacznij od strony, która˛ czytasz... Jeżeli chcesz rozumieć, czytaj dalej. Podstawowe zasady sa˛ nast˛epujace: ˛ • Wci˛ecia sa˛ ważne! • Akapity itp. oddzielamy pustym wierszem. • *pochylenie*, **pogrubienie** • ‘‘przykład kodu‘‘ Nagłówki Kolejne poziomy nagłówków tworzymy poprzez podkreślanie ich sekwencjami znaków: Cz˛ eść 1 ############## Rozdział 2 ************** Sekcja 3 ============== Podsekcja 4 -------------- 22 Rozdział 1. Narzedzia ˛ Materiały eCG IT Documentation, Wydanie 1 Podpodsekcja 5 ^^^^^^^^^^^^^^ Akapit 6 """""""""""""" Wybór znaków nie jest narzucony, ale najlepiej trzymać si˛e jednej konwencji, np. powyższej. Dyrektywy Ogólna postać dyrektyw to: .. <nazwa>:: <argumenty> :<opcja>: <wartość> treść Użyteczne dyrektywy: • .. image:: ścieżka/plik.jpg – pozwala wstawić obrazek • .. figure:: • .. note:: – warte zauważenia • .. tip:: – wskazówka • .. warning:: – ostrzeżenie • .. highlight:: • .. literalinclude:: • .. code block:: • .. raw:: • .. include:: ścieżka/plik.jpg – pozwala wstawić obrazek z etykieta˛ cpp – formatuj kod jako cpp (lub python, bash, html itd.) ścieżka/test.cpp – wstaw kod z podanego pliku cpp – treść poniżej to kod w cpp (lub python, bash, html itd.) html – interpretuj treść poniżej jako HTML ścieżka/plik.rst – wstaw treść z innego pliku Listy Aby uzyskać list˛e wypunktowana˛ lub numerowana˛ stosujemy: * punkt * punkt drugi zawiera dwie linie 1. punkt 1 2. punkt 2 #. automatyczne numerowanie #. automatyczne numerowanie Linki • ‘Nagłówek‘_ – link do nagłówka w bieżacym ˛ dokumencie • .. _Strona Pythona: wstawienie linku http:\\www.python.org – definicja linku, ‘Strona Pythona‘_ – 1.6. ReStructuredText i Sphinx 23 Materiały eCG IT Documentation, Wydanie 1 • ‘Strona Pythona <http:\\www.python.org>‘_ – tak też można • .. _nazwa-linku: – definicja linku w dokumentacji, :ref:‘zobacz tutaj <nazwa-linku>‘ – wstawienie linku Wskazówka: Powtórzmy, najlepsza˛ metoda˛ poznania składni reST jest przegladanie ˛ źródeł gotowych plików. 1.6.4 Budowanie dokumentacji W katalogu głównym dokumentacji, czyli docs wydajemy polecenie: ~/projekt/docs$ make html W terminalu zobaczymy komunikaty, niektóre z nich b˛eda˛ informacja˛ o bł˛edach w formatowaniu, które musimy usunać. ˛ Gotowa wersja zostanie umieszczona w katalogu _build/html. Możemy go wgrać do sieci, możemy spakować i udost˛epnić – po otwarciu pliku index.html w przegladarce ˛ zobaczymy efekt naszej pracy. Od czasu do czasu, zwłaszcza po wielu zmianach położenia plików i katalogów, budowanie dokumentacji warto poprzedzić usuni˛eciem poprzedniej wersji HTML: ~/projekt/docs$ make clean 1.6.5 Dokumentacja online Projekty hostowane w serwisie GitHub łatwo podpiać ˛ do serwisu Read the Docs, który umożliwia automatyczne generowanie wersji HTML, PDF, a nawet Epub (chociaż przy wykorzystaniu stabilnej, a nie testowej wersji Sphinksa) i udost˛epnianie jej online. W tym celu trzeba założyć konto na stronie Read the Docs – Sign Up. Po zalogowaniu importujemy projekt z GitHuba (ang. Import a Project – Import from GitHub) i po potwierdzeniu domyślnych opcji możemy cieszyć wersja˛ online dost˛epna˛ pod adresem typu: http://nazwa_projektu.readthedocs.org. Wersj˛e PDF ściagniemy ˛ po zalogowaniu, wejściu na stron˛e projektu, wybraniu zakładki Downloads i linku latest PDF. 1.7 Serwer deweloperski WWW Jeżeli chcemy tworzyć i testować aplikacje sieciowe wykorzystujace ˛ bazy danych za pomoca˛ j˛ezyków skryptowych, np. PHP czy Python, potrzebujemy środowiska testowego, na które składa si˛e serwer WWW, interpreter j˛ezyka skryptowego i system bazodanowy. Zestawy takiego oprogramowania określa si˛e skrótami WAMP lub LAMP w zależności od wykorzystywanego systemu operacyjnego: W – Windows, L – Linux. Pozostałe litery rozwija si˛e najcz˛eściej jako: • A – Apache; • M – MySQL, w linuksach raczej MariaDB; • P – PHP, Perl lub Python. Wymienionego oprogramowanie to najpopularniejsze, ale nie jedyne rozwiazania. ˛ Dost˛epnych jest wiele innych, równie dobrych serwerów czy baz danych. Warto też wiedzieć, że instalacja i konfiguracja kompletu wymienionych programów nie jest zazwyczaj konieczna. Np. jeżeli tworzymy aplikacje sieciowe w Pythonie wystarcza dedykowana biblioteka (np. Flask) lub framework (np. Django), które zapewniaja˛ serwer HTTP i obsług˛e wbudowanej bazy SQLite. 24 Rozdział 1. Narzedzia ˛ Materiały eCG IT Documentation, Wydanie 1 1.7.1 Linux W systemach opartych na Debianie (Ubuntu, Linux Mint itd.) lub na Arch Linuksie można zainstalować serwer Apache2 i interpreter PHP5 za pomoca˛ dedykowanych menedżerów pakietów, czyli odpowiednio: ~$ sudo apt-get install apache2 php5 php5-gd php5-sqlite php5-curl libapache2-mod-php5 ~# pacman -S apache php php-gd php-sqlite php-curl libapache-mod-php5 Podstawowa konfiguracja sprowadza si˛e do uaktywnienia odpowiednich modułów: ~$ sudo a2enmod userdir rewrite ~$ sudo service apache2 restart ~# a2enmod userdir rewrite ~# systemctl restart httpd – i odblokowania możliwości wykopnywania skryptów w katalogach domowych użytkowników poprzez zakomentowanie nast˛epujacych ˛ linii z pliku /etc/apache2/mods-available/php5.conf (Debian) lub /etc/httpd/mods-available/php5.conf (Arch): # To re-enable PHP in user directories comment the following lines # (from <IfModule ...> to </IfModule>.) Do NOT set it to On as it # prevents .htaccess files from disabling it. #<IfModule mod_userdir.c> # <Directory /home/*/public_html> # php_admin_flag engine Off # </Directory> #</IfModule> 1.7. Serwer deweloperski WWW 25 Materiały eCG IT Documentation, Wydanie 1 Tworzone strony umieszczamy w podkatalogu public_html katalogu domowego. Wywołujemy je wpisujac ˛ w przegladarce ˛ adres: 127.0.0.1/~użytkownik – powinny zostać zwrócone pliki index.php lub index.html, o ile istnieja.˛ Jeżeli mamy kilka projektów, umieszczamy je w podkatalogach, np. public_html/projekt1 i wywołujemy: 127.0.0.1/~użytkownik/projekt1. 1.7.2 Serwer2Go w Windows W systemie Microsoftu najłatwiej skorzystać z gotowego zestawu WAMP. Proponujemy Serwer2Go, ściagamy ˛ wersj˛e exe Apache 1.3.35 + PHP 5.3.2, SQLite, czyli pierwsza˛ dost˛epna.˛ Nast˛epnie uruchamiamy i wskazujemy miejsce instalacji, proponujemy główny katalog wybranego dysku, C:, D: itp.: Po rozpakowaniu plików, wchodzimy do katalogu instalacyjnego, aby otworzyć w edytorze plik konfiguracyjny pms_config.ini. M. in. dlatego, że Internet Explorer nie najlepiej współpracuje z serwerem, we wspomnianym pliku zmieniamy ustawienia: 26 Rozdział 1. Narzedzia ˛ Materiały eCG IT Documentation, Wydanie 1 ShowTrayIcon=1 StartLocal=1 BrowserType=FIREFOX Oprogramowanie uruchamiamy za pomoca˛ pliku Server2Go, który uruchomi serwer WWW pod adresem 127.0.0.1:4001 w Firefoksie. Swoje strony umieszczamy w podkatalogu htdocs katalogu instalacyjnego. 1.8 Materiały 1. Edytor Geany 2. MinGw 3. J˛ezyk Python 4. Biblioteka Qt 5. Qt Creator 6. Strona projektu Git 7. First Steps with Sphinx 8. Wprowadzenie do składni Sphinx reST 9. Docutils 10. Składnia reST & Sphinx 1.8.1 Słownik Qt zestaw bibliotek programistycznych ułatwiajacych ˛ tworzenie aplikacji z interfejsem graficznym w j˛ezykach C++, QML i Java. środowisko IDE zintegrowane środowisko programistyczne (ang. Integrated Development Environment, IDE), składajace ˛ si˛e z jednej lub wielu aplikacji, umożliwiajace ˛ tworzenie, testowanie, budowanie i uruchamianie kodu. Qt Creator wieloplatformowe środowisko IDE dla aplikacji pisanych w j˛ezykach C++, JavaScript i QML. Zawiera m.in. debugger i edytor GUI (graficznego interfejsu użytkownika). MinGw ang. Minimalist GNU for Windows; minimalistyczne środowisko dostarczajace ˛ narz˛edzia GNU (linker, kompilator itd.) pozwalajace ˛ na kompilacj˛e natywnych plików wykonywalnych dla Windows z kodu pisanego w C/C++. GNU Compiler Collection zestaw kompilatorów do różnych j˛ezyków programowania rozwijany w ramach projektu GNU i udost˛epniany na licencji GPL oraz LGPL. Zob. hasło w Wikipedii. GPL ang. GNU General Public License – licencja wolnego i otwartego oprogramowania stworzona w 1989 roku przez Richarda Stallmana i Ebena Moglena na potrzeby Projektu GNU. Ostatnia wersja, trzecia, opublikowana została 29 czerwca 2007 r. Zob. hasło w Wikipedii. Debian jedna z najstarszych i wiad ˛ acych ˛ dystrybucji Linuksa, umożliwia elastyczna˛ konfiguracj˛e systemu i dostosowanie go do własnych potrzeb. Jak wi˛ekszość dystrybucji, umożliwia wybór wielu środowisk graficznych, np. XFCE lub Gnome. Xubuntu 14.04 odmiana jednej z najpopularniejszych dystrybucji Linuksa, Ubuntu, dostarczana z klasycznym, lekkim i konfigurowlanym środowiskiem graficznym XFCE. 1.8. Materiały 27 Materiały eCG IT Documentation, Wydanie 1 środowisko graficzne w systemach linuksowych zestaw oprogramowania tworzacy ˛ GUI, czyli graficzny interfejs użytkownika, cz˛esto zawiera domyślny wybór aplikacji przeznaczonych do wykonywania typowych zadań. Najpopularnijesze środowiska to XFCE, Gnome, KDE, LXDE, Cinnamon, Mate. serwer WWW (ang. web server) – oprogramowanie obsługujace ˛ protokół http, podstawowy protokół sieci WWW, służacy ˛ przesyłaniu dokumentów hipertekstowych. interpreter program, który analizuje kod źródłowy, a nast˛epnie go wykonuje. Interpretery sa˛ podstawowym składnikiem j˛ezyków wykorzystywanych do pisania skryptów wykonywanych po stronie klienta WWW (JavaScript) lub serwera (np. Python, PHP). system bazodanowy system zarzadzania ˛ baza˛ danych (ang. Database Management System, DBMS) – oprogramowanie służace ˛ do zarzadzania ˛ bazami danych, np. SQLite, MariaDB, MySQL, PostgreSQL. framework (ang. framework – struktura) – oprogramowanie b˛edace ˛ zestawem narz˛edzi ułatwiajacych ˛ i przyśpieszajacych ˛ tworzenie aplikacji. 1.8.2 Metryka Autor Robert Bednarz ([email protected]) Utworzony 2015-07-18 o 04:24 28 Rozdział 1. Narzedzia ˛ ROZDZIAŁ 2 Linux Live USB Scenariusze zawarte w naszym serwisie tworzone sa˛ w oparciu o system Linux, ale można je również realizować na systemach Windows. Poniżej przedstawiamy, w jaki sposób przygotować w pełni funkcjonalna˛ dystrybucj˛e Linuksa uruchamiana˛ z klucza USB. Może posłużyć nie tylko jako środowisko programistyczne, ale również jako podr˛eczny sytem przenośny lub ratunkowy. Spis treści • Linux Live USB – W Windows – W Linuksie – Pierwsze uruchomienie – Obsługa LxPupTahr ˛ z internetem * Połaczenie ˛ WWW * Przegladarka * Domyślne katalogi * Instalacja programów * Skróty klawiaturowe * Konfiguracja LXDE * Wskazówki – Materiały dodatkowe 2.1 W Windows Aby w systemie MS Windows przygotować bootowalny klucz USB z możliwościa˛ zapisu ustawień i dokumentów, wykonaj nast˛epujace ˛ czynności: 1. Pobierz program Rufus – małe, szybkie i sprawdzone (:-)) narz˛edzie do tworzenia bootowalnych kluczy USB. Narz˛edzie nie wymaga instalacji. 2. Pobierz plik kzkbox_20150716.iso, który udost˛epniamy pod adresem: Copy.com. Plik zawiera dostosowany obraz systemu LxPupTahr 15.05.01 z pulpitem LXDE. 3. Przygotuj pendrajwa o pojemności min. 2GB zawierajacego ˛ jedna,˛ główna,˛ aktywna˛ partycj˛e FAT32. Taka partycja jest domyślna na wi˛ekszości kluczy. Pami˛etaj, że zostanie ona sformatowana! A wi˛ec zarchiwizuj ewentualne dane. Podłacz ˛ nap˛ed do komputera i sprawdź, jaka˛ liter˛e przydzielił mu system. 4. Uruchom program Rufus z uprawnieniami administratora(!): • z listy “Urzadzenie” ˛ wybierz pendrajwa kierujac ˛ si˛e oznaczeniem literowym i pojemnościa; ˛ 29 Materiały eCG IT Documentation, Wydanie 1 Rys. 2.1: Pulpit LxPupTahr ze środowiskiem LXDE • zaznacz w razie potrzeby opcj˛e “Szybkie formatowanie” (domyślna) • zaznacz opcj˛e “Utwórz bootowalny dysk używajac” ˛ -> “Obraz ISO”, kliknij ikon˛e obok i wskaż ściagni˛ ˛ ety plik kzkbox_20150716.iso; • wybierz “Opcje formatowania” i zaznacz “Dodaj łatk˛e dla starych biosów”; • kliknij “Start” i po 4-5 min. powinieneś zobaczyć napis “Gotowe”. Możesz już spróbować uruchomić komputer z wykorzystaniem tak przygotowanego pendrajwa. Wystarczy, że podczas uruchamiania wciśniesz odpowiedni klawisz, najcz˛eściej F1, F2, F10, F12 lub DEL. 2.2 W Linuksie • W Ubuntu i pochodnych instalujemy program Unetbootin poleceniami: ~$ sudo apt-add-repository ppa:gezakovacs/ppa ~$ sudo apt-get update ~$ sudo apt-get install unetbootin • W Debianie Jessie 8 ściagamy ˛ pakiet unetbootin_608-1_i386.deb, a nast˛epnie w katalogu z pobranym plikiem wydajemy polecenia jako root: ~# dpkg -i unetbootin_608-1_i386.deb ~# apt-get install -f • W Arch Linuksie i pochodnych jako root wydajemy polecenia: ~# pacman -Syu ~# pacman -S unetbootin 30 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.2. W Linuksie 31 Materiały eCG IT Documentation, Wydanie 1 • Wpinamy pendrajwa o pojemności min. 4GB dla dystrybucji SRU (Xubuntu). Pendrajw powinien mieć przynajmniej jedna˛ główna˛ i aktywna˛ partycj˛e FAT32 – tak jest zazwyczaj. • Po uruchomieniu programu “Unetbootin” zaznaczamy opcj˛e “Obraz dysku”, klikamy przycisk ”...” i wskazujemy pobrany obraz. • Jeżeli wybraliśmy obraz Xubuntu, SRU lub FREE_DESKTOP, w polu “Przestrzeń używana do zachowania plików...” wpisujemy min. 512. Jeżeli wybraliśmy obraz LxPupTahr przechodzimy do nast˛epnego punktu. • Upewniamy si˛e, że w polu “Nap˛ed:” wyświetlona jest litera przydzielona właściwemu pendrajwowi i klikamy “OK”. Czekamy w zależności od wybranej dystrybucji i pr˛edkości klucza USB od 1-20 minut. 2.3 Pierwsze uruchomienie Po pierwszym uruchomieniu zatwierdź okno kreatora ustawień przyciskiem “Ok” i zamknij kreator połaczenia ˛ z internetem. Nast˛epnie: • skonfiguruj typ klawiatury, aby działały skróty klawiszowe; • zamknij system, aby zapisać ustawienia i utworzyć plik zapisu. Po ponownym uruchomieniu system gotowy b˛edzie do pracy :-) 32 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.4 Obsługa LxPupTahr System w dostosowanej wersji zawiera: • spolszczone prawie wszystkie elementy systemu; • zaktualizowane listy oprogramowania; • zaktualizowana˛ i spolszczona˛ przegladark˛ ˛ e Pale Moon <https://www.palemoon.org/>_ (otwartoźrodłówa, oparta na Firefoksie); • fonty Ubuntu oraz podstawowe z Windows; • pakiety python-pip, python-virtualenv, git oraz bibliotek˛e Pygame; • skonfigurowane środowiska programistyczne Geany IDE oraz Sublime Text 3; • możliwość łatwej instalacji środowiska PyCharm Proffesional; • skonfigurowane elementy interfejsu LXDE; • skonfigurowane skróty klawiszowe. Wszystkie pozostałe biblioteki potrzebne do realizacji Szkolenia z Pythona Kzk zainstalujemy przy użyciu narz˛edzia pip. 2.4.1 Połaczenie ˛ z internetem System LxPupTahr domyślnie wczytuje si˛e w całości do pami˛eci RAM i uruchamia środowisko graficzne LXDE z zalogowanym użytkownikiem root, czyli administratorem w systemach linuksowych. Na poczatku ˛ b˛edziesz chciał nawiazać ˛ połaczenie ˛ z internetem. Z menu “Start/Konfiguracja” uruchamiamy Internet kreator połaczenia, ˛ klikamy “Wired or wireless LAN”, w nast˛epnym oknie wybieramy narz˛edzie “Simple Network Setup”. Po jego uruchomieniu powinniśmy zobaczyć list˛e wykrytych interfejsów, z której wybieramy eth0 dla połaczenia ˛ kablowego, wlan0 dla połaczenia ˛ bezprzewodowego. W przypadku eth0 połaczenie ˛ powinno zostać skonfigurowane od razu, natomiast w przypadku wlan0 wskazujemy jeszcze odpowiednia˛ sieć, metod˛e zabezpieczeń i podajemy hasło. Jeżeli uzyskamy połaczenie, ˛ w oknie “Network Connection Wizard/Kreator Połaczenia ˛ Sieci” zobaczymy aktywne interfejsy. Sugerujemy kliknać ˛ “Cancel/Anuluj”, a w ostatnim oknie informacyjnym “Ok”. 2.4.2 Przegladarka ˛ WWW Domyślna˛ przegladark ˛ a˛ jest PaleMoon, otwartoźródłowa odmiana oparta na Firefoksie. Od czasu do czasu warto ja˛ zaktualizować. 2.4.3 Domyślne katalogi • /root/my-documents lub /root/Dokumenty • /root/my-documents/clipart lub /root/Pobrane - tu zapisywane sa˛ pliki pobierane z internetu • /root/my-documents/clipart lub /root/Obrazy • /root/my-documents/tmp lub /root/tmp - katalogi tymczasowe • /usr/share/fonts/default/TTF/ – dodatkowe czcionki TrueType, np. z MS Windows 2.4. Obsługa LxPupTahr 33 Materiały eCG IT Documentation, Wydanie 1 2.4.4 Instalacja programów Jeżeli chcemy coś doinstalować, uruchamiamy Quickpet tahr z menu “Start/Konfiguracja”. Na poczatku ˛ klikamy “tahrpup updates”, aby zaktualizować list˛e dost˛epnych aplikacji. Nast˛epnie restartujemy program i sprawdzamy, czy w poszczególnych zakładkach znajdziemy potrzebne nam narz˛edzia, np.: Firefox, Chrome, Flash, Skype i inne. Jeżeli w Quickpet tahr nie znajdziemy wymaganej aplikacji, uruchamiamy Puppy Package Manager/Puppy Manager Pakietów z menu “Start/Konfiguracja”. Aktualizujemy list˛e dost˛epnych aplikacaji: klikamy ikon˛e ustawień obok koła ratunkowego, w nast˛epnym oknie zakładk˛e “Update database/Aktualizuj baz˛e danych” i przycisk “Update now/Aktualizuj teraz”. Po uruchomieniu okna terminala naciskamy klawisze ENTER klika razy, aby potwierdzić aktualizacj˛e repozytoriów. Na koniec zamykamy okno aktualizacji przyciskiem “OK”, co zrestartuje menedżera pakietów. Po ponownym uruchomieniu PPM, wpisujemy nazw˛e szukanego pakietu w pole wyszukiwania, nast˛epnie wybieramy pakiet z wyświetlonej listy, co dodaje go do kolejki. W ten sposób możemy wyszukać i dodać kilka pakietów na raz. Na koniec zatwierdzamy instalacj˛e przyciskiem “Do it!” Wskazówka: Trzeba pami˛etać, że używamy dystrybucji okrojonej, wi˛ec nie wszystko warto instalować z repozytoriów, bo nie zawsze znajdziemy tam oprogramowanie odpowiednio dostosowane do naszej dystrybucji. LxPup oferuje jednak dwa inne sposoby doinstalowywania oprogramowania na żadanie! ˛ Pierwszy to paczki w formacie PET, dost˛epne np. na stronie pet_packages-tahr <http://distro.ibiblio.org/puppylinux/pet_packages-tahr/>. Ścia˛ gamy je, a nast˛epnie instalujemy dwukrotnie klikajac ˛ (uruchomi si˛e narz˛edzie petget). Drugim formatem stosowanym dla dużych pakietów, które używamy od czasu do czasu, jest format SFS. Spakowane w ten sposób oprogramowanie możemy dodawać “w locie” w trakcie działania systemu. Korzystamy z narz˛edzia SFS-Load w locie (Start/Konfiguracja). Wskazówka: Duże pliki SFS itp. zasoby warto przechowywać nie w katalogu domowym /root, ale w katalogu głównym startowego pendrajwa. Jest on łatwo dost˛epny podczas pracy z systemem w ścieżce /initrd/mnt/dev_save/, łatwo go również otworzyć z lewego panelu w menedżerze plików. Zazwyczaj ozna34 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.4. Obsługa LxPupTahr 35 Materiały eCG IT Documentation, Wydanie 1 czony b˛edzie sdb1. 2.4.5 Skróty klawiaturowe Uwaga: Poniższe skróty zadziałaja,˛ jeżeli ustawimy odpowiedni typ klawiatury. Procedura jest bardzo prosta. Uruchamiamy “Ustawienia Puppy” (pierwsza ikona obok przycisku Start, lub Start/Konfiguracja/Wizard Kreator), wybieramy “Mysz/Klawiatura”. W nast˛epnym oknie “Zaawansowana konfiguracja”, potwierdzamy “OK”, dalej “Model klawiatury” i na koniec zaznaczamy “pc105”. Pozostaje potwierdzenie “OK” i jeszcze klikni˛ecie przycisku “Tak” w poprzednim oknie, aby aktywować ustawienia. Oznaczenia: C – Control, A – Alt, W - Windows (SuperKey). • C+A+Left - puplpit lewy • C+A+Right - pulpit prawy • Alt + Space - menu okna • C+Esc - menu start • C+A+Del - menedżer zadań • W+f - menedżer plików (pcmanfm) • W+t - terminal (LXTerminal) • W+e - Geany IDE • W+s - Sublime Text 3 • W+p - PyCharm IDE 36 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.4. Obsługa LxPupTahr 37 Materiały eCG IT Documentation, Wydanie 1 38 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.4. Obsługa LxPupTahr 39 Materiały eCG IT Documentation, Wydanie 1 40 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.4. Obsługa LxPupTahr 41 Materiały eCG IT Documentation, Wydanie 1 42 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.4. Obsługa LxPupTahr 43 Materiały eCG IT Documentation, Wydanie 1 • W+w - przegladarka ˛ WWW (Palemoon) 2.4.6 Konfiguracja LXDE • Wyglad, ˛ Ikony, Tapeta, Panel: Start/Pulpit/Zmiana wygladu. ˛ • Ekran(y): Start/System/System/Ustawienia wyświetlania. • Czcionki: Start/Pulpit/Desktop/Manager Fontu. • Menedżer plików: Edycja/Preferencje w programie. • Ustawienia Puppy: Start/Konfiguracja/Wizard Kreator • Internet kreator połaczenia: ˛ Start/Konfiguracja • Zmiana rozmiaru pliku zapisu: Start/Akcesoria • Puppy Manager Pakietów: Start/Konfiguracja • Quickpet tahr: Start/Konfiguracja • SFS-załadowanie w locie: Start/Konfiguracja/SFS-Załadowanie w locie • QuickSetup ustawienia pierwszego uruchamiania: Start/Konfiguracja • Restart menedżera okien (RestartWM): Start/Zamknij • WM Switcher – switch windowmanagers: • Startup Control – kontrola aplikacji startowych: Start/Konfiguracja • Domyślne aplikacje: Start/Pulpit/Preferowane programy • Terminale Start/Akcesoria 44 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.4. Obsługa LxPupTahr 45 Materiały eCG IT Documentation, Wydanie 1 46 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 • Ustawienie daty i czasu: Start/Pulpit 2.4.7 Wskazówki 1. Dwukrotne klikni˛ecie – menedżer plików PcManFm domyślnie otwiera pliki i katalogi po pojedynczym klikni˛eciu. Jeżeli chcielibyśmy to zmienić, wybieramy “Edycja/Preferencje”. 2.5 Materiały dodatkowe Informacja: Win˛e za niebootujacy ˛ system z klucza USB cz˛eściej ponosi sprz˛et niż sam pendrajw czy nagrany na nim system. Niestety, dotyczy to głównie przestarzałych maszyn oraz... najnowszych. Pierwsze maja˛ niedostosowany BIOS, drugie zbyt “nowe” komponenty, do których może brakować sterowników. Problematyczne moga˛ być też laptopy z biosem UEFI, plus SecureBoot, z preinstalowanym “jedynym słusznym” systemem... Przeczytaj rozdział “Problemy”. 2.5.1 Puppy Linux LxPupTahr to wersja systemu Puppy Linux 6.0.3 tahrpup CE opartego na systemie Ubuntu Tahr 14.04 LTS. Puppy Linux to dystrybucja zaprojektowanych specjalnie do pracy w trybie live, ale z możliwościa˛ przechowywania dokonywanych zmian, takich jak dodawanie/usuwanie oprogramowanie, zmienianie ustawień, tworzenie dokumentów. LxPupTahr Zaproponowana przez nas wersja jest dostosowana do potrzeb szkoleń z Pythona. Bez problemu można jednak przygotować swoja˛ wersj˛e. Wystarczy pobrać oryginalny obraz LxPupTahr-15.05.1-pae.iso (251 MB) i wgrać go na pendrajwa. TahrPup W systemie TahrPup domyślne środowisko graficzne jest połaczeniem ˛ menedżera okien JVM z pulpitem ROX Desktop i menedżerem plików ROX-Filer. Łatwo je wypróbować. Wystarczy pobrać plik tahr-6.0.2_PAE.iso (ok. 201 MB). W wersji tej łatwo doinstalować środowisko XFCE. Plik zapisu Jeżeli omawiane systemy uruchamiany po raz pierwszy (w zasadzie wtedy, kiedy nie ma żadnego pliku zapisu), wita nas kreator konfiguracji, a system ma interfejs w j˛ezyku angielskim. Dostosowywanie systemu i tworzenie pliku zapisu omówione zostały w rozdziale Konfiguracja Można tego uniknać, ˛ jeżeli dysponujemy jakimś przygotowanym plikiem zapisu (ang. savefile). W serwisie Copy.com udost˛epniliśmy plik lxtahrsave-lxde.2fs oraz tahrsave-xfce.2fs. Pierwszy przeznaczony jest dla LxPupTahr, drugi dla TahrPup. Należy wgrać je do głównego katalogu pendrajwa. Trzeba również pobrać pakiet jre1.7.0_65_5.7.0.sfs i umieścić go w tym samym miejscu co plik zapisu. Plik zapisu zawiera konfiguracj˛e systemu, czyli: • zaktualizowane listy oprogramowania; 2.5. Materiały dodatkowe 47 Materiały eCG IT Documentation, Wydanie 1 Rys. 2.2: Pulpit LxPupTahr ze środowiskiem LXDE Rys. 2.3: Pulpit PupThar ze środowiskiem JVM/ROX 48 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 Rys. 2.4: Pulpit PupTahr ze środowiskiem XFCE • zaktualizowana˛ i spolszczona˛ domyślna˛ przegladark˛ ˛ e Pale Moon <https://www.palemoon.org/>_ (otwartoźrodłówa, oparta na Firefoksie); • fonty Droid, Ubuntu oraz podstawowe z Windows; • pakiety python-pip, python-virtualenv oraz bibliotek˛e pygame; • skonfigurowane mini środowisko programistyczne IDE – Geany. • środowisko PyCharm IDE Educational • skonfigurowane elementy interfejsu LXDE lub JVM/ROX i XFCE Uwaga: Na pendrajwie nie może znajdować si˛e żaden inny plik o nazwie rozpoczynajacej ˛ si˛e na (lx)tahrsave. Ewentualne utworzone wcześniej pliki zapisu trzeba albo skasować, albo zmienić im nazwy. ZALECAMY powi˛ekszyć rozmiar pliku zapisu do 1024 MB za pomoca˛ narz˛edzia: Zmiana rozmiaru pliku osobistego przechowywania (Start/Setup). Informacja: Nazwa pliku pupsave zawsze zaczyna si˛e “(lx)tahrsave-”, np.: lxtahrsave-lxde.2fs. Położenie jest... dowolne, tzn. można go zapisać na kluczu USB, ale równie dobrze może być zapisany na dowolnej partycji szybkiego dysku stacjonarnego. Podczas uruchamiania system potrafi odnaleźć ten plik na wszystkich dost˛epnych partycjach i załadować go! Wskazówka: Pracujac ˛ w systemie, mamy dost˛ep do głównego katalogu naszego pendrajwa (zazwyczaj oznaczonego w menedżerze plików sdb1). Możemy w nim tworzyć dowolne foldery i zapisywać pliki, np. pet i sfs, z których b˛edziemy korzystać w miar˛e potrzeb. Dzi˛eki temu unikniemy zb˛ednego rozrastania si˛e pliku zapisu. Należy uważać, aby z katalogu głównego nie usunać ˛ plików systemowych. Przechowywanie ustawień i dokumentów w pliku zapisu ma swoje zalety: 2.5. Materiały dodatkowe 49 Materiały eCG IT Documentation, Wydanie 1 • wystarczy usunać ˛ omawiany plik, a system uruchomi si˛e w wersji domyślnej; b˛edzie można skonfigurować go od podstaw; • można udost˛epniać innym pliki zapisu; wystarczy, że wgraja˛ go na pendrajwa przygotowanego zgodnie z nasza˛ instrukcja,˛ a dostana˛ skonfigurawane środowisko i programy, a nawet ewentualne dokumenty. Ostatecznie zawartość katalogu głównego pendrajwa przedstawiać powinna si˛e nast˛epujaco: ˛ Rys. 2.5: Katalog główny pendrajwa z systemem LXPupTahr Nazwa pliku zapisu (persystencji) w systemie TahrPup zaczyna si˛e zawsze od tahrsave-. Przykładowy plik udost˛epniamy udost˛epniamy w serwisie Copy.com pod nazwa˛ tahrsave-xfce.2fs. Zawiera on wst˛epna˛ konfiguracj˛e oraz nast˛epujace ˛ dostosowania: W systemie TahrPup pulpit JVM/ROX uruchomić można przy użyciu narz˛edzia WM Switcher – przełaczanie ˛ menedżerów okien. Autor Robert Bednarz ([email protected]) Utworzony 2015-07-18 o 04:24 2.5.2 Konfiguracja Jeżeli nie chcesz używać udost˛epnionych przez nas plików zapisu, możesz sam skonfigurować swój system. Poniżej wskazówki. 50 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 Ustawienia wstepne ˛ Okno QuickSetup ustawienia pierwszego uruchamiania konfigurujemy wg zrzutu i klikamy “Ok”, dalej potwierdzamy konieczność doinstalowania pakietu j˛ezykowego, a w trzecim oknie klikamy “Exit” – bo niczego jeszcze nie doinstalowaliśmy. Internet Uruchamia si˛e Internet kreator połaczenia, ˛ jeśli jesteśmy podpi˛eci do sieci kablem i dostajemy parametry przez DHCP, powinniśmy zobaczyć komunikat “Congratulations, you are connected”. W przeciwnym razie klikamy “Wired or wireless LAN”, w nast˛epnym oknie wybieramy na poczatek ˛ narz˛edzie “Simple Network Setup”. Po jego uruchomieniu powinniśmy zobaczyć list˛e wykrytych interfejsów, z której wybieramy wlan0 dla połaczenia ˛ bezprzewodowego. Nast˛epnie w razie potrzeby wskazujemy odpowiednia˛ sieć, metod˛e zabezpieczeń i podajemy hasło. Jeżeli uzyskamy połaczenie, ˛ w oknie “Network Connection Wizard” zobaczymy aktywne interfejsy. Sugerujemy kliknać ˛ “Cancel”, a w ostatnim oknie informacyjnym “Ok”. Na koniec zamykamy okno Welcome kreatora. Przegladarka ˛ WWW Domyślna˛ przegladark˛ ˛ e Pale Moon należy od czasu do czasu aktualizować. Start/Internet/Palemoon-updater, zaznaczamy “Update Pale Moon” i klikamy OK. W tym celu wybieramy Jeśli po aktualizacji przywita nas angielskoj˛ezyczny interfejs, na stronie powitalnej klikamy link “Language Packs”, pobieramy plik tłumaczeń pl.xpi i instalujemy. Na koniec wpisujemy polu adresu polecenie “about:config”, w pole wyszukiwania “useragent” i zmieniamy opcj˛e “general.useragent.locale” na “pl-PL” (o ile potrzeba). 2.5. Materiały dodatkowe 51 Materiały eCG IT Documentation, Wydanie 1 52 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.5. Materiały dodatkowe 53 Materiały eCG IT Documentation, Wydanie 1 54 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.5. Materiały dodatkowe 55 Materiały eCG IT Documentation, Wydanie 1 Dotakowe ustawienia sa˛ opcjonalne, ale wskazane. Wybieramy “Edycja/Preferencje”, aby skonfigurować stron˛e domyślna,˛ i katalog pobieranych plików, np. root/Pobrane (trzeba go utworzyć), wreszcie opcje śledzenia i historii. Te ostatnie ustawienia wpływaja˛ na rozmiar wolnego miejsca w pliku zapisu. Na koniec można zainstalować blocker reklam wybierajac ˛ odpowiedni dodatek, np. AdBlockEdge, albo korzystajac ˛ z narz˛edzia Pup-Advert-Blocker (Start/Internet/), w którym wybieramy serwis “Mvps.org” i klikamy ikon˛e koła z˛ebatego. Ściagni˛ ˛ eta lista zawierajaca ˛ adresy IP oraz nazwy serwerów reklamowych zostanie dodana do pliku /etc/hosts i przekierowana na adres lokalny, co uniemożliwi jakakolwiek ˛ z nimi komunikacj˛e ;-) Plik zapisu Podczas pierwszego zamkni˛ecia system prosi o utworzenie pliku zapisu (ang. savefile, dodatkowe informacje zob. w Puppy Linux), w którym zapisywane b˛eda˛ wprowadzane przez nas zmiany: konfiguracja, instalacja programów, utworzone dokumenty. Na poczatku ˛ może pojawić si˛e pytanie o przetłumaczenie informacji rozruchowych, wybieramy “Yes” i potwierdzamy kolejny komunikat. Gdyby pytanie to pojawiło si˛e nast˛epnym razem, wybierz “No”. W nast˛epnym oknie klikamy “Zapisz”, nast˛epnie “administrator”. Wybieramy partycj˛e oznaczajac ˛ a˛ pendrajwa: w konfiguracjach z 1 dyskiem twardym b˛edzie ona oznaczona najcz˛esciej sdb1 (kierujemy si˛e rozmiarem i typem plików: vfat). Nast˛epnie wybieramy ewentualnie szyfrowanie i system plików. Sugerujemy ext2 – najszybszy. Minimalny rozmiar to 32MB, zalecamy 512MB lub wi˛ecej. Opcjonalnie rozszerzamy domyślna˛ nazw˛e i potwierdzamy zapis. 56 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.5. Materiały dodatkowe 57 Materiały eCG IT Documentation, Wydanie 1 58 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.5. Materiały dodatkowe 59 Materiały eCG IT Documentation, Wydanie 1 60 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.5. Materiały dodatkowe 61 Materiały eCG IT Documentation, Wydanie 1 62 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 Należy spokojnie poczekać na utworzenie pliku i wyłacznie ˛ komputera. Czcionki Czcionki ttf wystarczy wgrać do katalogu /usr/share/fonts/default/TTF. Można użyć narz˛edzia Manager fontów (Start/Desktop/Desktop). Autor Robert Bednarz ([email protected]) Utworzony 2015-07-18 o 04:24 2.5.3 JVM Menedżer JVM wykorzystywany jest domyślnie w systemie TahrPup. • Wyglad: ˛ Menu/Pulpit/Chtheme wybór motywu GTK; Menu/Pulpit/JVM konfiguracja/Wybierz motyw JVM. • Tapeta: umieść plik graficzny w /root/my-documents/clipart, kliknij prawym klawiszem myszy i wybierz “set background”. • Ikony pulpitu i menu: Menu/Pulpit/Desktop zmiana ikony • Czcionki: Menu/Pulpit/Manager fontu -> Wyglad, ˛ zaznaczamy wszystkie opcje, styl hintingu ustawiamy na 1. • Menedżer plików ROX-Filer: prawy klawisz myszy w pustym oknie, wybierz “Ustawienia”; otwieranie elmentów dwukrotnym klikni˛eciem – sekcja Biurko, ikony menedżera – sekcja Typy/Style. 2.5. Materiały dodatkowe 63 Materiały eCG IT Documentation, Wydanie 1 64 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.5. Materiały dodatkowe 65 Materiały eCG IT Documentation, Wydanie 1 66 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.5. Materiały dodatkowe 67 Materiały eCG IT Documentation, Wydanie 1 68 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.5. Materiały dodatkowe 69 Materiały eCG IT Documentation, Wydanie 1 Ustawienia i programy • Ustawienia Puppy: [JVM] Menu/Ustawienia/Wizard Kreator • Internet kreator połaczenia: ˛ [JVM] Menu/Ustawienia • Zmiana rozmiaru pliku osobistego przechowywania: [JVM] ... • Puppy Manager Pakietów: [JVM] Menu/Ustawienia • Quickpet tahr: [JVM] ... • SFS-załadowanie w locie: [JVM] ... • QuickSetup ustawienia pierwszego uruchamiania: [JVM] Menu/Ustawienia • Restart menedżera okien (RestartWM): [JVM] Menu/Wyjście • WM Switcher – switch windowmanagers: [JVM] Menu/Pulpit • Startup Control – kontrola aplikacji startowych: [JVM] Menu/Ustawienia • Domyślne aplikacje: [JVM] Menu/Ustawienia/Wybór domyślnych aplikacji • Terminale: [JVM] Menu/Narz˛edzia • Ustawienie daty i czasu: [JVM] ... Autor Robert Bednarz ([email protected]) Utworzony 2015-07-18 o 04:24 2.5.4 XFCE Jeżeli nie przypadnie nam do gustu domyślne środowisko systemu TahrPup, czyli JVM/ROX Desktop/ROX-Filer, lub LXPupTahr – LXDE, wystarczy zaktualizować pakiety w narz˛edziu quickpet-tahr, zrestartować je, a nast˛epnie zainstalować pulpit XFCE z kategorii “desktops”. Po zakończeniu instalacji wybieramy narz˛edzie przełaczania ˛ menedżerów okien WM Switcher (Menu/Pulpit/), zaznaczamy XFCE i klikamy przycisk “Restart X”. • Menu “Start”: domyślnie sa˛ na pasku zadań dwa menu, tradycyjny aplet “Programy” i nowszy “Menu Whisker”. Niepotrzebne kliknij prawym klawiszem i wybierz “Usuń”. • Menu Whisker: prawy klawisz na ikonie i “Właściwości”; wpisywanie pierwszych liter programu wyszukuje go w menu. • Wyglad: ˛ Programy/Ustawienia/Wyglad ˛ • Ekran: Programy/Ustawienia/Ekran • Tapeta i ikony pulpitu: prawy klawisz na pulpicie i “Ustawienia”; zakładka “Tło”, opcja “Katalog” pozwala wskazać inny niż domyślny katalog z tapetami, zkładka “Ikony”, “Domyślne” – jakie ikony pokazywać. • Czionki: Start/Ustawienia/Wyglad ˛ -> Czcionki, zaznacz “Antyaliasing”, przyciaganie ˛ “Lekkie”, podpiksele “RGB”. • Menedżer plików Thunar: Programy/Ustawienia/Menedżer plików lub Edycja/Preferencje w programie; zaznaczanie elementów dwukrotnym klikni˛eciem -> zakładka “Zachowanie” • Montowanie: Programy/Ustawienia/Nap˛edy i nośniki wymienne; zaznacz “Montowanie nap˛edów...”, “Montowanie nośników...”, “Przegladanie ˛ nośników...” • Panele (pasek zadań itp.): Programy/Ustawienia/Panel; zakładka “Aplety” pozwala dodawać, usuwać aplety, warto dodać “Wyświetlanie pulpitu”; warto dodać również aktywatory do uruchamiania aplikacji; po zaznaczeniu aktywatora wybierz okno preferencji (druga od dołu ikona z prawej), a nast˛epnie wybierz aplikacj˛e, np. 70 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 Menedżer plików, Emulator terminala, Przegladark˛ ˛ e internetu. Koniecznie dodaj aktywator dla PopShutdown Manager. Kolejność elementów ustal przeciagaj ˛ ac ˛ je mysza.˛ • Obszary robocze: Start/Ustawienia/; ilość – 2. • Powiazania ˛ plików z domyślnymi aplikacjami: prawy klawisz na pliku, “Właściwości” -> “Otwieranie za pomoca”, ˛ wybieramy z listy lub “Inny program”. Ustawienia i programy • Ustawienia Puppy: [XFCE] Programy/Puppy Setup/Puppy Setup • Internet kreator połaczenia: ˛ [XFCE] Programy/Puppy Setup/Internet Connection Wizard • Zmiana rozmiaru pliku osobistego przechowywania: [XFCE] Programy/Akcesoria/Resize personal storage file • Puppy Manager Pakietów: [XFCE] Programy/Puppy Setup/Puppy Package Manager • Quickpet tahr: [XFCE] Programy/Puppy Setup • SFS-załadowanie w locie: [XFCE] Programy/SFS... • QuickSetup ustawienia pierwszego uruchamiania: [XFCE] Programy/Puppy Setup/QuickSetup... • WM Switcher – switch windowmanagers: [XFCE] Programy/Ustawienia • Startup Control – kontrola aplikacji startowych: [XFCE] Programy/Ustawienia/Sesja i uruchamianie • Domyślne aplikacje: [XFCE] Programy/Ustawienia/Preferowane programy • Terminale: [XFCE] Programy/Akcesoria • Ustawienie daty i czasu: [XFCE] Programy/Ustawienia/Set date and time Autor Robert Bednarz ([email protected]) Utworzony 2015-07-18 o 04:24 2.5.5 Problemy Jeśli nie da si˛e uruchomić komputera za pomoca˛ przygotowanego klucza, przeczytaj poniższe wskazówki. 1. Zanim uznasz, że pendrajw nie działa, przetestuj go na innym sprz˛ecie! 2. W niektórych komputerach możliwość uruchamiania z nap˛edu USB trzeba odblokować w BIOS-ie. Odpowiedniego ustawienia poszukaj w np. w opcji “Boot order”. 3. Starsze komputery stacjonarne moga˛ wymagać wejścia do ustawień BIOSU (zazwyczaj klawisz F1, F2 lub DEL) i ustawienia pendrajwa (o ile zostanie wykryty) jako urzadzenia ˛ startowego zamiast np. dysku twardego czy cdromu. Opuszczajac ˛ BIOS zmiany należy zapisać! Komputer restartujemy bez usuwania klucza USB. 4. W przypadku komputerów stacjonarnych, jeżeli nie działaja˛ frontowe gniazda USB, podłacz ˛ klucz z tyłu! 5. Niebootujacy ˛ pendrajw można sformatować za pomoca˛ narz˛edzia HP-USB-Disk-Storage-Format-Tool, a nast˛epnie nagrać jeszcze raz obraz (Lx)PupTahr. 6. Można wypróbować narz˛edzie Linux Live USB Creator. Użyj go do nagrania obrazu (Lx)PupTahr: 7. Spróbuj z innym pendrajwem. 8. Zmień maszyn˛e, być może jest za stara lub za nowa! 2.5. Materiały dodatkowe 71 Materiały eCG IT Documentation, Wydanie 1 72 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.5. Materiały dodatkowe 73 Materiały eCG IT Documentation, Wydanie 1 9. Jeżeli masz bios UEFI z właczonym ˛ mechanizmem SecureBoot, co stanowi norm˛e dla laptopów z preinstalowanym Windows 7/8/... Spróbuj wyłaczyć ˛ zabezpieczenie w biosie, możesz zajrzeć do instrukcji: • pomoc Ubuntu • pomoc Microsoft • wsparcie HP Inne narzedzia ˛ Wskazówka: Cz˛eść narz˛edzi udost˛epnia serwis dobreprogramy.pl, niestety sugeruje użycie dodatkowej aplikacji do pobierania, ukrytej pod przycieskiem “Pobierz program”. Sugerujemy używanie przycisku “Linki bezpośrednie” i wybór odpowiedniej wersji (32-/64-bitowej), jeżeli jest dost˛epna. • USB Image Tool – narz˛edzie do robienia obrazów dysków USB i nagrywania ich na inne pendrajwy. • Image USB – świetny program do tworzenia obrazów nap˛edów USB i nagrywania ich na wiele pendrajwów jednocześnie. • Bootice – opcjonalne narz˛edzie do różnych operacji na dyskach. Za jego pomoca˛ można np. utworzyć, a nast˛epnie odtworzyć kopi˛e MBR pendrajwa. Autor Robert Bednarz ([email protected]) Utworzony 2015-07-18 o 04:24 74 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.5. Materiały dodatkowe 75 Materiały eCG IT Documentation, Wydanie 1 Autor Robert Bednarz ([email protected]) Utworzony 2015-07-18 o 04:24 76 Rozdział 2. Linux Live USB Materiały eCG IT Documentation, Wydanie 1 2.5. Materiały dodatkowe 77 Materiały eCG IT Documentation, Wydanie 1 78 Rozdział 2. Linux Live USB ROZDZIAŁ 3 Biblioteka Qt Poniżej przedstawiamy realizacj˛e przykładowej aplikacji w Qt 5. 3.1 Adresy (Qt5) Niniejszy scenariusz pokazuje, jak zaczać ˛ programowanie z wykorzystaniem biblioteki Qt w wersji 5 przy użyciu dedykowanego środowiska IDE Qt Creator. Celem jest stworzenie prostej 1-okienkowej ksia˛żki adresowej, w której można dodawać dane adresowe powiazane ˛ z określona˛ nazwa,˛ np. imieniem i nazwiskiem. • • • • • • • • Nowy projekt Tworzenie interfejsu Deklaracje i implementacje Sygnały i sloty Dodawanie adresów Tryb nawigacji Edycja i usuwanie Materiały 3.1.1 Nowy projekt Po uruchomieniu aplikacji Qt Creator wybieramy przycisk “New Project”, który uruchamia kreatora aplikacji. W pierwszym oknie “Applications” i “Qt Widget Applications”, co oznacza, że chcemy utworzyć program z interfejsem graficznym oparty na klasie QWidget. W nast˛epnym oknie podajemy nazw˛e projektu, np, “adresy”, oraz wskazujemy ścieżk˛e do katalogu, w którym b˛eda˛ zapisywane pliki wchodzace ˛ w skład projektu. W nast˛epnym oknie wybieramy tzw. “kit”, czyli zestaw definiujacy ˛ docelowe środowisko, kompilator itp. ustawienia. Dost˛epne zestawy musza˛ być wcześniej określone w ustawieniach Qt Creatora Kolejne okno pozwala definiować nazw˛e klasy głównej i klas˛e podstawowa,˛ podajemy “adresy” i wybieramy “QWidget”. W nast˛epnym ostatnim oknie niczego nie zmieniamy, kończymy klikni˛eciem przycisku “Finish”. Efektem działania kreatora b˛edzie utworzenie nast˛epujacych ˛ plików: 1) adresy.h - plik nagłówkowy, tutaj b˛edziemy deklarować wszystkie używane w programie obiekty (elementy interfejsu), a także publiczne sloty, czyli funkcje powiazanie ˛ z określonymi sygnałami (zdarzeniami). 2) adresy.cpp - plik źródłowy, tu znajdzie si˛e kod tworzacy ˛ obiekty interfejsu, łacz ˛ acy ˛ sygnały ze slotami, a wreszcie implementacja slotów. 79 Materiały eCG IT Documentation, Wydanie 1 3) main.cpp - plik źródłowy, w którym tworzona i uruchamiana jest instancja naszej aplikacji. 4) adresy.ui - jak wskazuje rozszerzenie (“ui” - ang. user interface), plik zawierać b˛edzie opis graficznego interfejsu aplikacji zapisany za pomoca˛ znaczników XML. 3.1.2 Tworzenie interfejsu Zaczniemy od utworzenia głównego okna naszej aplikacji. W tym celu dwa razy klikamy plik adresy.ui i przechodzimy do tworzenia formularza. Na poczatku ˛ klikamy obiekt “Grid Layout” z kategorii “Layouts” i rysujemy prostokat ˛ na formularzu tak, aby nie wypełniał go w całości. Dodana kontrolka umożliwia porzadkowanie ˛ innych elementów tworzacych ˛ interfejs w prostokatnej ˛ siatce. Dalej dodamy dwie etykiety, czyli obiekty “Label” z kategorii “Display Widgets”. Staramy si˛e je umieścić jedna nad druga˛ w dodanej przed chwila˛ siatce. Wskazówka: Po wybraniu obiektu i najechaniu na Grid Layout należy obserwować niebieskie podświetlenia, które pojawiaja˛ si˛e w pionie i poziomie, wskazuja˛ one, gdzie umieszczony zostanie dodawany obiekt. Po dwukrotnym klikni˛eciu na dodane etykiety możemy zmienić treść przez nie wyświetlana.˛ Modyfikujemy w ten sposób właściwość text danego obiektu. Etykieta górna powinna zawierać tekst “Nazwa”, dolna - “Adresy”. Informacja: Lista wszystkich obiektów wyświetlana jest po prawej stronie na górze w oknie Hierarchia obiektów. W kolumnie Obiekt widzimy tam nazwy dodanych obiektów, a w kolumnie Klasa nazwy klas, które reprezentuja.˛ Po wskazaniu mysza˛ dowolnego obiektu możemy edytować wszystkie jego właściwości poniżej. Np. nazw˛e obiektu zmienimy w polu objectName. Nazw˛e etykiety górnej ustalamy na “nazwaLbl”, dolnej - na “adresyLbl”. Wskazówka: Konwencji nazywania obiektów jest wiele, ważne żeby konsekwentnie trzymać si˛e wybranej. Tutaj proponujemy uwzgl˛ednianie w nazwie typu obiektu przy użyciu skrótu pisanego z dużej litery, np. “nazwaLbl”. Po prawej stronie etykiety “Nazwa” dodajemy kontrolk˛e Line Edit z grupy Input Widgets o nazwie “nazwaLine”. Poniżej, czyli w drugiej kolumnie, tworzymy obiekt Text Edit z tej samej grupy, co poprzedni o nazwie “adresText”. Powinniśmy uzyskać poniższy układ: Czas na dodanie przycisków pozwalajacych ˛ inicjować działanie aplikacji. Dodajemy wi˛ec 5 przycisków PushButton z kategorii Buttons po prawej stronie i poza(!) obiektem GridLayouts jeden pod drugim. Na samym dole umieszczamy kontrolk˛e Vertical Spacer z kategorii Spacers. Nast˛epnie zaznaczamy wszystkie dodane obiekty, obrysowujac ˛ je myszka,˛ i klikamy ikon˛e Rzmieść w pionie (CTRL+L) na pasku narz˛edziowym. Teraz stworzona˛ grup˛e przeciagamy ˛ na siatk˛e jako 3. kolumn˛e. Musimy zmienić nazwy i tekst dodanych przycisków. Od góry ustawiamy kolejne właściwości (nazwa/tekst): “dodajBtn/Dodaj”, “zapiszBtn/Zapisz”, “anulujBtn/Anuluj”, “edytujBtn/Edytuj”, “usunBtn/Usuń”. W efekcie powinniśmy uzyskać nast˛epujac ˛ a˛ formatk˛e: Musimy dodać jeszcze 3 przyciski pozwalajace ˛ na nawigacj˛e mi˛edzy adresami i wyjście z programu. Poniżej obiektu siatki umieszczamy wi˛ec 2 przyciski (PushButton), zaznaczamy je i klikamy ikon˛e Rozmieść poziomo w splitterze, nast˛epnie przeciagamy ˛ grup˛e na dół 2. kolumny siatki. Na koniec dodajemy jeszcze jeden przycisk na dole 3. kolumny. Dodanym obiektom zmieniamy właściwości (nazwa/tekst): “poprzBtn/Porzedni”, “nastBtn/Nast˛epny”, “koniecBtn/Koniec”. Na koniec zaznaczamy formularz główny, na którym znajduja˛ si˛e wszystkie elementy interfejsu i klikamy przycisk Rozmieść w siatce (CTRL+G). Dzi˛eki temu kontrolki b˛eda˛ skalowane wraz ze zmiana˛ rozmiaru okna. W sumie uzyskujemy poniższy projekt: 80 Rozdział 3. Biblioteka Qt Materiały eCG IT Documentation, Wydanie 1 3.1. Adresy (Qt5) 81 Materiały eCG IT Documentation, Wydanie 1 Możemy uruchomić nasza˛ aplikacj˛e, wybierajac ˛ Budowanie/Uruchom (CTRL+R) lub klikajac ˛ trzecia˛ od dołu ikon˛e zielonego trójkata ˛ w lewej kolumnie Qt Creatora. Powinniśmy zobaczyć podobne do poniższego okno: 3.1.3 Deklaracje i implementacje Po dodaniu elementów interfejsu musimy zadeklarować zmienne, za pomoca˛ których b˛edziemy mogli nimi manipulować. Przechodzimy do pliku adresy.h i wprowadzamy poniższe zmiany: 1 2 #ifndef ADRESY_H #define ADRESY_H 3 4 5 6 7 8 #include #include #include #include #include <QWidget> <QLineEdit> <QTextEdit> <QPushButton> <QTextCodec> 9 10 11 12 namespace Ui { class adresy; } 13 14 15 16 class adresy : public QWidget { Q_OBJECT 17 18 19 20 public: explicit adresy(QWidget *parent = 0); ~adresy(); 21 82 Rozdział 3. Biblioteka Qt Materiały eCG IT Documentation, Wydanie 1 22 23 24 25 26 27 28 29 30 31 32 33 34 private: Ui::adresy *ui; QPushButton *dodajBtn; QPushButton *zapiszBtn; QPushButton *anulujBtn; QPushButton *poprzBtn; QPushButton *nastBtn; QPushButton *edytujBtn; QPushButton *usunBtn; QPushButton *koniecBtn; QLineEdit *nazwaLine; QTextEdit *adresText; }; 35 36 #endif // ADRESY_H Na poczatku ˛ musimy zaimportować klasy, z których skorzystaliśmy przy budowie interfejsu. Najważniejsza˛ jest klasa podstawowa wszystkich elementów interfejsu, czyli QWidget. Kolejne trzy odpowiadaja˛ wykorzystanym przez nas kontrolkom edycyjnym i przyciskom. Dodatkowa klasa QTextCodec pozwoli poprawnie wyświetlać polskie znaki. W wewnatrz ˛ naszej klasy głównej, której deklaracja rozpoczyna si˛e w linii 14., deklarujemy prywatne (private) właściwości, których nazwy odpowiadaja˛ nazwom wcześniej dodanych elementów interfejsu graficznego. Formalnie każda zmienna jest wskaźnikiem do obiektu odpowiedniego typu. W pliku adresy.cpp korzystamy ze zadekarowanych zmiennych, aby ustawić poczatkowe ˛ właściwości obiektów składajacych ˛ si˛e na interfejs użytkownika. 1 2 #include "adresy.h" #include "ui_adresy.h" 3.1. Adresy (Qt5) 83 Materiały eCG IT Documentation, Wydanie 1 3 4 5 6 7 8 adresy::adresy(QWidget *parent) : QWidget(parent), ui(new Ui::adresy) { ui->setupUi(this); 9 QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); 10 11 nazwaLine = new QLineEdit; nazwaLine = ui->nazwaLine; nazwaLine->setReadOnly(true); 12 13 14 15 adresText = new QTextEdit; adresText = ui->adresText; adresText->setReadOnly(true); 16 17 18 19 dodajBtn = new QPushButton; dodajBtn = ui->dodajBtn; 20 21 22 zapiszBtn = new QPushButton; zapiszBtn = ui->zapiszBtn; zapiszBtn->hide(); 23 24 25 26 anulujBtn = new QPushButton; anulujBtn = ui->anulujBtn; anulujBtn->hide(); 27 28 29 30 nastBtn = new QPushButton; nastBtn = ui->nastBtn; nastBtn->setEnabled(false); 31 32 33 34 poprzBtn = new QPushButton; poprzBtn = ui->poprzBtn; poprzBtn->setEnabled(false); 35 36 37 38 edytujBtn = new QPushButton; edytujBtn = ui->edytujBtn; edytujBtn->setEnabled(false); 39 40 41 42 usunBtn = new QPushButton; usunBtn = ui->usunBtn; usunBtn->setEnabled(false); 43 44 45 46 koniecBtn = new QPushButton; koniecBtn = ui->koniecBtn; koniecBtn->setEnabled(true); 47 48 49 50 setWindowTitle(trUtf8("Prosta ksia˛żka adresowa")); 51 52 } 53 54 55 56 57 adresy::~adresy() { delete ui; } W obr˛ebie konstruktora głównej klasy naszej aplikacji o nazwie adresy, którego definicja rozpoczyna si˛e w linii 4., tworzymy instancje klas użytych w interfejsie graficznym. Do zmiennych zadeklarownych w pliku adresy.h 84 Rozdział 3. Biblioteka Qt Materiały eCG IT Documentation, Wydanie 1 przypisujemy obiekty utworzone za pomoca˛ operatora new, a nast˛epnie definiujemy ich poczatkowe ˛ właściwości. Konstruktorowi odpowiada zawzwyczaj destruktur, a wi˛ec działanie, które usuwa stworzony obiekt, w tym wypadku interfejs użytkownika: adresy::~adresy(). Aby określić stan elementów interfejsu wykorzystujemy odpowiednie właściwości i metody reprezentujacych ˛ je obiektów. Np. właściwość setReadOnly(true) blokuje edycj˛e danego elementu, a właściwość setEnabled(false) uniemożliwia klikni˛ecie danego przycisku. Metoda hide() ukrywa obiekt. Instrukcja QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")) określa kodowanie komunikatów w standardzie “UTF-8” używanych w aplikacji, które wprowadzane sa˛ dalej za pomoca˛ funkcji trUtf8(). Tak dzieje si˛e np. podczas określania tytułu okna w wywołaniu setWindowTitle(). Wskazówka: W środowisku MS Windows kodowanie powinno zostać ustawione na Windows-1250. Dzi˛eki powyższym uzupełnieniom po uruchomieniu aplikacji pola nazwy i adresu b˛eda˛ nieaktywne, b˛edziemy mogli natomiast użyć przycisków Dodaj, aby utworzyć nowy wpis, lub Koniec, aby zakończyć aplikacj˛e. 3.1.4 Sygnały i sloty Działanie aplikacji z interfejsem graficznym polega w uproszczeniu na reagowaniu na działania użytkownika, takie jak np. klikni˛ecie, naciśni˛ecie klawisza, przeciagni˛ ˛ ecie itp. Wszystkie zdarzenia generowane z poziomu interfejsu użytkownika w terminologii biblioteki Qt emituja˛ tzw. sygnały. Programista decyduje o tym, które z nich i jak sa˛ obsługiwane, definiujac ˛ tzw. sloty, czyli funkcje powiazane ˛ z określonymi zdarzeniami. Mechanizm sygnałów i slotów umożliwia komunikacj˛e mi˛edzy obiektami aplikacji. Każda z funkcji obsługujacych ˛ zdarzenia musi zostać najpierw zadeklarowana w pliku adresy.h w sekcji public slots:, ich implementacj˛e musimy dopisać później do pliku adresy.cpp. 3.1. Adresy (Qt5) 85 Materiały eCG IT Documentation, Wydanie 1 18 19 20 public: explicit adresy(QWidget *parent = 0); ~adresy(); 21 enum Tryb { nawigujT, dodajT, edytujT }; 22 23 24 25 26 public slots: void dodajKontakt(); void koniec(); 27 28 29 30 31 32 33 34 35 36 37 38 39 private: Ui::adresy *ui; QPushButton *dodajBtn; QPushButton *zapiszBtn; QPushButton *anulujBtn; QPushButton *poprzBtn; QPushButton *nastBtn; QPushButton *edytujBtn; QPushButton *usunBtn; QPushButton *koniecBtn; QLineEdit *nazwaLine; QTextEdit *adresText; 40 Tryb aktTryb; void aktGui(Tryb tryb); QString staraNazwa; QString staryAdres; QMap<QString,QString> kontakty; 41 42 43 44 45 46 47 }; 48 49 #endif // ADRESY_H Oprócz deklaracji slotów w liniach 24-26 dopisujemy deklaracje kilku potrzebnych zmiennych. Definiujemy wi˛ec typ wyliczeniowy Tryb, z którego korzystamy deklarujac ˛ zmienna˛ aktTryb oraz prywatna˛ funkcj˛e pomocnicza˛ aktGui. Posłuża˛ one do określania 1 z 3 stanów działania aplikacji, takich jak: przegladanie ˛ wpisów, dodawanie i ich edycja. Dalej dopisujemy deklaracje zmiennych pomocniczych staraNazwa i staryAdres. Korzystamy tu z typu QString oznaczajacego ˛ dane tekstowe. Na końcu deklarujemy specjalna˛ zmienna˛ kontakty, która posłuży do przechowywania nazw i skojarzonych z nimi adresów w postaci słownika typu QMap. Poszczególne elementy takiej listy maja˛ postać skojarzonych ze soba˛ par (klucz, wartość). connect(dodajBtn, SIGNAL(clicked()), this, SLOT(dodajKontakt())); connect(koniecBtn,SIGNAL(clicked()),this, SLOT(koniec())); 53 54 55 } 56 57 58 59 60 adresy::~adresy() { delete ui; } 61 62 63 64 void adresy::dodajKontakt() { staraNazwa = nazwaLine->text(); staryAdres = adresText->toPlainText(); 65 nazwaLine->clear(); adresText->clear(); 66 67 86 Rozdział 3. Biblioteka Qt Materiały eCG IT Documentation, Wydanie 1 68 aktGui(dodajT); 69 70 } 71 72 73 74 75 76 77 78 79 void adresy::aktGui(Tryb tryb) { aktTryb=tryb; switch (aktTryb) { case dodajT: case edytujT: nazwaLine->setReadOnly(false); nazwaLine->setFocus(Qt::OtherFocusReason); adresText->setReadOnly(false); 80 dodajBtn->setEnabled(false); edytujBtn->setEnabled(false); usunBtn->setEnabled(false); 81 82 83 84 zapiszBtn->show(); anulujBtn->show(); 85 86 87 nastBtn->setEnabled(false); poprzBtn->setEnabled(false); break; case nawigujT: if (kontakty.isEmpty()) { nazwaLine->clear(); adresText->clear(); } nazwaLine->setReadOnly(true); adresText->setReadOnly(true); dodajBtn->setEnabled(true); 88 89 90 91 92 93 94 95 96 97 98 99 int ile=kontakty.size(); edytujBtn->setEnabled(ile >= 1); usunBtn->setEnabled(ile >=1 ); nastBtn->setEnabled(ile > 1); poprzBtn->setEnabled(ile > 1); 100 101 102 103 104 105 zapiszBtn->hide(); anulujBtn->hide(); break; 106 107 108 } 109 110 } 111 112 113 114 void adresy::koniec() { adresy::close(); } Powiazania ˛ mi˛edzy sygnałami i slotami ustalamy w pliku adresy.cpp za pomoca˛ poleceń typu: connect(dodajBtn, SIGNAL(clicked()), this, SLOT(dodajKontakt()));. Funkcja conect() jako pierwszy argument wymaga zmiennej wskazujacej ˛ obiekt, który emituje sygnał określony w 2. argumencie (np. SIGNAL(clicked()), czyli klikni˛ecie), 3. argument określa obiekt, który zostaje powiadomiony o zdarzeniu, w ostatnim argumencie podajemy funkcj˛e, która ma zostać wykonana (SLOT(dodajKontakt())). Jak widać powyżej, na końcu konstruktora naszej klasy adresy wia˛żemy klikni˛ecia przycisków dodajBtn i koniecBtn z funkcjami dodajKontakt() i koniec(). Funkcja dodajKontakt() przygotowuje aplikacj˛e do przełaczenia ˛ w stan dodawania nowych danych. W tym celu najpierw zapami˛etujemy dotychczasowa˛ nazw˛e i adres, a nast˛epnie wywołujemy funkcj˛e pomocnicza˛ z argumentem 3.1. Adresy (Qt5) 87 Materiały eCG IT Documentation, Wydanie 1 typu Tryb oznaczajacym ˛ wymagany stan aplikacji: aktGui(dodajT). Działanie funkcji aktGui(), obsługujacej ˛ stany aplikacji, polega na uaktywnianiu lub wyłaczaniu ˛ określonych elementów interfejsu w zależności od przeprowadzanej przez użytkownika czynności. Np. w trybie dodawania i edycji odblokowujemy możliwość wprowadzania tekstu w polach nazwy (nazwaLine->setReadOnly(false);) i adresu (adresText->setReadOnly(false);), pokazujemy przyciski pozwlajace ˛ na zapis lub anulowanie wywołujac ˛ metod˛e show(). Wyłaczamy ˛ również nawigacj˛e, blokujac ˛ odpowiednie przyciski (metoda setEnabled(false)). Po wejściu w tryb nawigacji czyścimy (clear()) zawartość pól nazwy i adresu, o ile lista kontaktów jest pusta (if (kontakty.isEmpty())). Nast˛epnie uaktywniamy przyciski edycji, usuwania i przegladania, ˛ jeżeli mamy jakieś kontakty. Ilość kontaktów zapisujemy wcześniej w osobnej zmiennej (int ile=kontakty.size();). Na koniec przyciski zapisu i anulowania zostaja˛ zablokowane. Slot koniec() wywoływany jest po klikni˛eciu przycisku Koniec i powoduje zamkni˛ecie aplikacji przy użyciu metody close(). Wywołuje ona m.in. destruktor klasy, co powoduje – w naszym przypadku – usuni˛ecie instancji obiektu interfejsu graficznego (delete ui;). 3.1.5 Dodawanie adresów Pora zaimplementować obsług˛e trybu dodawania danych adresowych. Najpierw do pliku nagłówkowego dopisujemy deklaracje odpowiednich slotów: 25 26 27 28 29 public slots: void dodajKontakt(); void koniec(); void zapiszKontakt(); void anuluj(); Musimy też na poczatku ˛ pliku dodać import klasy QMessageBox pozwalajacej ˛ wyświetlać informacje użytkownikowi. Nast˛epnie przechodzimy do pliku adresy.cpp, w którym trzeba powiazać ˛ sloty zapiszKontakt() i anuluj() ze zdarzeniem klikni˛ecia przycisków zapiszBtn i anulujBtn. Zadanie to proponujemy wykonać samodzielnie :-). Na końcu pliku musimy dopisać definicje powiazanych ˛ funkcji: 118 119 120 void adresy::zapiszKontakt() { QString nazwa = nazwaLine->text(); QString adres = adresText->toPlainText(); 121 if (nazwa == "" || adres == "") { QMessageBox::information(this, trUtf8("Puste pole"),trUtf8("Prosz˛ e wpisać nazw˛ e i adres.")); return; } 122 123 124 125 126 if (aktTryb == dodajT) { if (!kontakty.contains(nazwa)) { kontakty.insert(nazwa, adres); QMessageBox::information(this, trUtf8("Dodano wpis"), trUtf8("Kontakt \"%1\" dodano do ksia˛żki adresowej.").arg(nazwa) } else { QMessageBox::information(this, trUtf8("Nie dodano wpisu"), trUtf8("Przykro, ale kontakt \"%1\" jest już w ksia˛żce adresowej } } 127 128 129 130 131 132 133 134 135 136 137 aktGui(nawigujT); 138 139 } 88 Rozdział 3. Biblioteka Qt Materiały eCG IT Documentation, Wydanie 1 140 141 142 143 144 145 void adresy::anuluj() { nazwaLine->setText(staraNazwa); adresText->setText(staryAdres); aktGui(nawigujT); } Funkcja zapiszKontakt() pobiera tekst wpisany w pola edycyjne za pomoca˛ metod text() oraz toPlainText() i zapisuje je w zmiennych tekstowych. Nast˛epnie sprawdza, czy użytkownik wprowadził obydwie informacje. Jeżeli nie, wyświetla odpowiedni komunikat przy użyciu metody QMessageBox::information(). Pierwszy tekst, który przekazujemy do tej funkcji to tytuł okna dialogowego, drugi – właściwy komunikat. Nast˛epnie, jeżeli aplikacja jest w trybie dodawania, sprawdza, czy podana nazwa nie została zapisana wcześniej na liście kontakty. Jeśli nie (if (!kontakty.contains(nazwa))), dodaje nowe dane (kontakty.insert(nazwa, adres);) i wyświetla potwierdzenie. W przeciwnym razie informuje użytkownika o duplikacie. Na końcu aktywuje tryb nawigacji (aktGui(nawigujT);). Jeżeli użytkownik rozmyśli si˛e i kliknie odpowiedni przycisk, wywoływana jest funkcja anuluj(). Jak widać, przywraca ona w polach edycyjnych poprzednio wprowadzane dane i również aktywuje tryb nawigacji. 3.1.6 Tryb nawigacji Obsługa nawigacji wymaga napisania funkcji obsługujacych ˛ naciśni˛ecie przycisków Nast˛epny i Poprzedni, które staja˛ si˛e aktywne, jeżeli mamy wi˛ecej niż 1 dodany adres. Jak zwykle, zaczynamy od zadeklarowania publicznych slotów nast() i poprz() w pliku nagłówkowym. Dopisanie tych 2 linijek pozostawiamy do samodzielnego wykonania. Podobnie powiazanie ˛ zadeklarowanych slotów z sygnałami (klikni˛eciami) obiektów nastBtn i poprzBtn w konstruktorze klasy adresy. Nast˛epnie dopisujemy implementacj˛e zadeklarowanych funkcji na końcu pliku adresy.cpp: Na końcu pliku musimy dopisać definicje powiazanych ˛ funkcji: 149 150 151 152 153 154 155 156 void adresy::nast() { QString nazwa = nazwaLine->text(); QMap<QString, QString>::iterator i = kontakty.find(nazwa); if (i != kontakty.end()) i++; if (i == kontakty.end()) i = kontakty.begin(); nazwaLine->setText(i.key()); adresText->setText(i.value()); } 157 158 159 160 161 162 163 164 165 void adresy::poprz() { QString nazwa = nazwaLine->text(); QMap<QString, QString>::iterator i = kontakty.find(nazwa); if (i == kontakty.begin()) i = kontakty.end(); i--; nazwaLine->setText(i.key()); adresText->setText(i.value()); } Wyświetlajac ˛ kolejna˛ par˛e powiazanych ˛ danych, tzn. nazw˛e i przypisany jej adres(y), musimy sprawdzić w fukcji nast(), czy mamy kolejny wpis, czy też aktualny jest ostatni. Wtedy należałoby wyświetlić wpis pierwszy. W tym celu pobieramy nazw˛e aktualnie wyświetlonego wpisu i tworzymy obiekt tzw. iteratora inicjowanego przez metod˛e find() i przypisanego do zmiennej i: QMap<QString, QString>::iterator i = kontakty.find(nazwa);. Iterator umożliwia łatwe poruszanie si˛e po liście słowników zapisanych w zmiennej kontakty. Metoda i.key() zwraca nam klucz, a i.value() przypisana˛ mu wartość. Jeżeli bieżacy ˛ wpis nie jest ostatnim inkrementujemy wartość iteratora (if (i != kontakty.end()) i++;). 3.1. Adresy (Qt5) 89 Materiały eCG IT Documentation, Wydanie 1 W przeciwnym wypadku ustawiamy go na pierwszy wpis (i = kontakty.begin();); Na koniec pozostaje wczytanie nazwy (i.key()) i przypisanych jej danych (i.value()) do odpowiednich pól interfejsu. Funkcja poprz() zaczyna si˛e tak samo jak poprzednia, czyli od utworzenia iteratora wskazujacego ˛ na bieżacy ˛ wpis. Jeżeli jesteśmy na poczatku ˛ listy, ustawiamy iterator na element końcowy. Nast˛epnie przechodzimy do elementu końcowego (i--) i wyświetlamy odpowiednie dane. Informacja: Metoda .end() klasy QMap zwraca iterator wskazujacy ˛ na wirtualny (!) element po ostatnim elemencie listy. Dlatego, aby uzyskać dost˛ep do niego, musimy iterator dekrementować (i--). 3.1.7 Edycja i usuwanie Do oprogramowania zostay jeszcze dwa przyciski: btnEdytuj, którego klikni˛ecie powinno wywołać funkcj˛e edytujKontakt(), oraz btnUsun, który wywołuje funkcj˛e usunKontakt(). Samodzielnie dopisujemy deklaracje funkcji do pliku nagłówkowego, a ich powiazania ˛ z sygnałami umieszczamy w pliku źródłowym. Nast˛epnie implementujemy funkcje: 185 186 187 188 189 void adresy::edytujKontakt() { staraNazwa = nazwaLine->text(); staryAdres = adresText->toPlainText(); aktGui(edytujT); } 190 191 192 193 void adresy::usunKontakt() { QString nazwa = nazwaLine->text(); QString adres = adresText->toPlainText(); 194 if (kontakty.contains(nazwa)) { int button = QMessageBox::question(this,trUtf8("Potwierdź usuni˛ ecie"), trUtf8("Czy na pewno usunać ˛ kontakt \"%1\"?").arg(nazwa), QMessageBox::Yes|QMessageBox::No); if (button == QMessageBox::Yes) { poprz(); kontakty.remove(nazwa); QMessageBox::information(this,trUtf8("Usuni˛ eto"), trUtf8("Usuni˛ eto kontakt \"%1\".").arg(nazwa)); } } aktGui(nawigujT); 195 196 197 198 199 200 201 202 203 204 205 206 207 } Przejście do trybu edycji, czyli działanie funkcji edytujKontak(), polega na zapisaniu aktualnie wyświetlanych danych (przydatne, jeżeli użytkownik anuluje zmiany) i uaktywnieniu trybu (aktGui(edytujT);), tzn. odblokowaniu pól tekstowych i odpowiednich przycisków. Usuwanie kontaktów również jest proste. Na poczatku ˛ pobieramy nazw˛e i zwiazany ˛ z nim adres(y). Metoda .contains(nazwa) pozwala sprawdzić, czy lista kontaktów zawiera słownik o podanym kluczu. Nat˛epnie prosimy użytkownika o potwierdzenie operacji. Po jego uzyskaniu najpierw wyświetlamy w aplikacji dane poprzedniego wpisu dzi˛eki wywołaniu zdefiniowanej wcześniej funkcji poprz(), później dopiero usuwamy wpis za pomoca˛ metody .remove(nazwa) i wyświetlamy potwierdzenie. Na koniec aktywujemy tryb nawigacji. Poćwicz sam Spróbuj rozszerzyć napisana˛ aplikacj˛e o możliwość przechowywania danych w pliku lub w bazie na dysku. 90 Rozdział 3. Biblioteka Qt Materiały eCG IT Documentation, Wydanie 1 3.1.8 Materiały 1. Projekt Qt 2. Biblioteka Qt 5 3. Qt Creator 4. Dokumentacja Qt 5 5. Qt Developer Wiki (pl) Pojecia ˛ Qt zestaw bibliotek programistycznych ułatwiajacych ˛ tworzenie aplikacji z interfejsem graficznym w j˛ezykach C++, QML i Java. plik nagłówkowy w j˛ezyku C/C++ plik z rozszerzeniem .h zawierajacy ˛ deklaracje używanych struktur danych, np. klas, zmiennych i funkcji. Implementacja klas i funkcji umieszczana jest w plikach źródłowych. Wi˛ecej o pliku żródłowym: plik źródłowy w j˛ezyku C/C++ plik z rozszerzeniem .c/.cpp zawierajacy ˛ implementacj˛e zadeklarowanych typów złożonych (np. klas) i używanych funkcji, w tym funkcji głównej (main()). Klasa program komputerowy. Obiekt zestaw komponentów i bibliotek wykorzystywany do budowy aplikacji, przykładem jest biblioteka Pythona Flask. public operator widoczności, pola (właściwości) i metody (funkcje) klasy deklarowne po nim sa˛ dost˛epne z każdeg miejsca programu. private operator widoczności, pola (właściwości) i metody (funkcje) klasy deklarowne po nim sa˛ dost˛epne tylko w jej obr˛ebie. Qt Creator wieloplatformowe środowisko IDE (zintegrowane środowisko programistyczne) dla aplikacji pisanych w j˛ezykach C++, JavaScript i QML. Zawiera m.in. debugger i edytor GUI (graficznego interfejsu użytkownika). sygnały zdarzenia generowane za pomoca˛ graficznego interfejsu użytkownika, takie jak klikni˛ecie elementu, edycja, przeciagni˛ ˛ ecie itp. sloty funkcje przypisane sygnałom, definiuja˛ działania podejmowane w przypadku zastnienia danego zdarzenia. Metryka Autor Robert Bednarz ([email protected]) Utworzony 2015-07-18 o 04:24 3.2 Zadania (Qt5) Po zrealizowaniu scenariusza “Adresy” powinieneś być w stanie tworzyć przy użyciu biblioteki Qt proste prohgramy z interfejsem graficznym. Poniżej zamieszczamy kilka propozycji do samodzielnego opracowania. 3.2. Zadania (Qt5) 91 Materiały eCG IT Documentation, Wydanie 1 • • • • • • • Kalkulator Konwerter liczb Konwerter jednostek Równanie kwadratowe Szyfr Twój pomysł Materiały 3.2.1 Kalkulator Stwórz kalkulator pozwalajacy ˛ na wykonywania co najmniej podstawowych działań. Rozwijajac ˛ go, możesz pomyśleć o zaimplementowaniu historii obliczeń. 3.2.2 Konwerter liczb Napisz program pozwalajacy ˛ na konwersj˛e liczb wprowadzanych w systemach liczbowych o podstawie 2, 6, 8, 10 i 16. 3.2.3 Konwerter jednostek Napisz program pozwalajacy ˛ na konwersj˛e jednostek używanych w informatyce, np. bity na kilobajty, megabity na kilobajty itp. 3.2.4 Równanie kwadratowe Napisz program, który po wprowadzeniu wymaganych danych wyświetla i rozwiazuje ˛ równanie kwadratowe. 3.2.5 Szyfr Napisz program, który szyfruje i deszyfruje wprowadzony tekst wybrana˛ metoda,˛ np. szyfrem Cezara lub Vigenère’a. 3.2.6 Twój pomysł Wymyśl i zaimplementuj program własnego pomysłu. 3.2.7 Materiały 1. Projekt Qt 2. Biblioteka Qt 5 3. Qt Creator 4. Dokumentacja Qt 5 5. Qt Developer Wiki (pl) 92 Rozdział 3. Biblioteka Qt Materiały eCG IT Documentation, Wydanie 1 Pojecia ˛ Qt zestaw bibliotek programistycznych ułatwiajacych ˛ tworzenie aplikacji z interfejsem graficznym w j˛ezykach C++, QML i Java. plik nagłówkowy w j˛ezyku C/C++ plik z rozszerzeniem .h zawierajacy ˛ deklaracje używanych struktur danych, np. klas, zmiennych i funkcji. Implementacja klas i funkcji umieszczana jest w plikach źródłowych. Wi˛ecej o pliku żródłowym: plik źródłowy w j˛ezyku C/C++ plik z rozszerzeniem .c/.cpp zawierajacy ˛ implementacj˛e zadeklarowanych typów złożonych (np. klas) i używanych funkcji, w tym funkcji głównej (main()). Klasa program komputerowy. Obiekt zestaw komponentów i bibliotek wykorzystywany do budowy aplikacji, przykładem jest biblioteka Pythona Flask. public operator widoczności, pola (właściwości) i metody (funkcje) klasy deklarowne po nim sa˛ dost˛epne z każdeg miejsca programu. private operator widoczności, pola (właściwości) i metody (funkcje) klasy deklarowne po nim sa˛ dost˛epne tylko w jej obr˛ebie. Qt Creator wieloplatformowe środowisko IDE (zintegrowane środowisko programistyczne) dla aplikacji pisanych w j˛ezykach C++, JavaScript i QML. Zawiera m.in. debugger i edytor GUI (graficznego interfejsu użytkownika). sygnały zdarzenia generowane za pomoca˛ graficznego interfejsu użytkownika, takie jak klikni˛ecie elementu, edycja, przeciagni˛ ˛ ecie itp. sloty funkcje przypisane sygnałom, definiuja˛ działania podejmowane w przypadku zastnienia danego zdarzenia. Metryka Autor Robert Bednarz ([email protected]) Utworzony 2015-07-18 o 04:24 3.3 Metryka Autor Robert Bednarz ([email protected]) Utworzony 2015-07-18 o 04:24 3.3. Metryka 93 Materiały eCG IT Documentation, Wydanie 1 94 Rozdział 3. Biblioteka Qt ROZDZIAŁ 4 Technologie WWW WWW (ang. World Wide Web – ogólnoświatowa sieć) w celu dostarczania użytkownikom hipertekstowych dokumentów wykorzytuje wiele technologii, do których należa˛ m. in.: protokół HTTP(S), j˛ezyki opisu stron HTML, XML, j˛ezyki generujace ˛ strony PHP, Python, arkusze stylów CSS, j˛ezyk skryptowy JavaScript czy technika AJAX. 4.1 GetSimple CMS GetSimple jest przykładem popularnych od dłuższego czasu systemu zarzadzania ˛ treścia˛ (ang. Content Management System, CMS). Zadaniem CMS-ów jest wspomaganie tworzenia serisów intenetowych WWW i wspomaganie zarza˛ dzania nimi przy wykorzystaniu przyjaznych dla użytkownika interfejsów, dzi˛eki czemu nie musi on być specjalista˛ od wspomnianych na wst˛epie technologii WWW. Inne przykłady popularnych CMS-ów to: Drupal, Joomla! czy WordPress. 4.1.1 Pobranie archwium Informacja: GetSimple wymaga działajacego ˛ serwera WWW, przy czym serwery bazodanowe typu MySQL itp. nie sa˛ koniecznie, ponieważ GS przechowuje pliki w formacie XML. W rozdziale narz˛edzia omówiono instalacj˛e instalacj˛e środowiska LAMP (dla Linuksa) i WAMP (Serwer2Go dla Windowsa). Najnowsza˛ wersj˛e GS pobieramy ze strony Download GetSimple CMS. Ściagni˛ ˛ ete archiwum zip umieszczamy w podkatalogu public_html katalogu domowego użytkownika Linuksa lub w podkatalogu htdocs folderu instalacyjnego Serwer2Go. Rozpakowujemy je, a nast˛epnie nazw˛e utworzonego katalogu zmieniamy na gs. Informacja: W środowisku Linux folderowi gs musimy nadać uprawnienia do zapisu i odczytu nie tylko dla właściciela, ale i dla grupy oraz innych. Można to zrobić z poziomu menedżera plików po klikni˛eciu prawym klawiszem myszy nazwy katalogu i wybraniu “Właściwości/Uprawnienia” (zob. zrzut poniżej). Uwaga: na pytanie typu “Zastosować rekursywnie” odpowiadamy twierdzaco. ˛ Można też w katalogu public_html wydać polecenie w terminalu chmod -R 777 gs. Nast˛epnie przechodzimy do przegladarki ˛ (w Windows Serwer2Go musi być uruchomiony!) i rozpoczynamy instalacj˛e wpisujac ˛ w polu adresu: http://127.0.0.1/~nazwa_użytkownika/gs/admin (Linux) lub http://127.0.0.1:4001/gs/admin (Windows). Informacja: W środowisku Linux ewentualne bł˛edy chmod ignorujemy. 95 Materiały eCG IT Documentation, Wydanie 1 96 Rozdział 4. Technologie WWW Materiały eCG IT Documentation, Wydanie 1 4.1. GetSimple CMS 97 Materiały eCG IT Documentation, Wydanie 1 98 Rozdział 4. Technologie WWW Materiały eCG IT Documentation, Wydanie 1 Spolszczenie Jak widać, domyślnie dost˛epny jest j˛ezyk angielski. Można to łatwo zmienić już podczas instalacji (później również). W nowej karcie przegladarki ˛ otwieramy link Download Languages, a na otwartej stronie wchodzimy do sekcji Extend. W polu wyszukiwania wpisujemy polish, po wyświetleniu znalezionych zasobów klikamy link Polish language (spolszczenie) 1.3.7. Ściagamy ˛ spakowane archiwum na dysk. Przenosimy je do folderu gs/admin/lang i tam rozpakowujemy. 4.1.2 Instalacja Wracamy do prz˛egladarki, ˛ odświeżamy stron˛e instalacyjna,˛ np. klawiszem F5, i wybieramy polska˛ wersj˛e j˛ezykowa.˛ Po klikni˛eciu przycisku “Kontynuuj instalacj˛e” na nast˛epnej stronie wpisujemy nazw˛e strony, login i hasło administratora. Po naciśni˛eciu “Instaluj!” może zostać wyświetlona strona z bł˛edem (pod Windowsem) ze wzgl˛edu na brak możliwości wysłania wiadomości e-mail z danymi logowania. Jest to normalne. Wyświetlone hasło możemy ewentualnie zapisać, po czym kilkamy link “Logowanie”. Zobaczymy panel administracyjny, w którym b˛edziemy mogli zmienić hasło klikajac ˛ po prawej stronie “Ustawienia”, a nast˛epnie “Profil użytkownika”. Domyślnie dodana zostanie demonstracyjna strona główna widoczna w panelu “Strony”, która˛ wyświetlimy w przegladarce, ˛ jeżeli klikniemy nazw˛e serwisu w panelu administracyjnym lub wpiszemy 4.1. GetSimple CMS 99 Materiały eCG IT Documentation, Wydanie 1 http://127.0.0.1/~nazwa_użytkownika/gs/ (Linux) lub http://127.0.0.1:4001/gs/ (Windows) w polu adresu. Zobacz galeri˛e Instalacja GetSimple CMS. 4.1.3 Wtyczki Jak wi˛ekszość CMS-ów, GetSimple oferuje mechanizm wtyczek, pozwalajacy ˛ rozszerzać w miar˛e potrzeb funkcjonalność zarówno od strony użytkownika, jak i administratora serwisu. Instalacja wtyczek polega na pobraniu ich ze strony Extend Repository <http://get-simple.info/extend/>, a nast˛epnie rozpakowaniu archiwum zip w podfolderze gs/plugins. Wtyczkami zarzadzamy ˛ w sekcji “Wtyczki” panelu administracyjnego. Tam można je m. in. właczać ˛ lub wyłaczać. ˛ Przykładowe wtyczki • I18N – dodaje wsparcie dla stron w różnych j˛ezykach oraz bardzo użyteczne hierarchiczne menu; • I18N Gallery – dodaje możliwość wygodnego tworzenia galerii zdj˛eć i umieszczania ich na stronach; • I18N Search – umożliwia m. in. wyszukiwanie tekstu na stronach serwisu, ale również tworzenie list zasobów oznaczonych tymi samymi tagami. • I18N Special Pages – pozwala tworzyć strony specjalne typu newsy, artykuły, karty produktów itp. Informacja: W Linuksie po umieszczeniu archiwów zip w podkatalogu gs/plugins wygodnie je rozpakujesz wydajac ˛ w terminalu polecenie typu: unzip nazwa_archiwum.zip. Uwaga: użycie polecenia “Rozpakuj tutaj” w menedżerze plików umieści pliki w dodatkowym i niepotrzebnym podfolderze (o nazwie wtyczki), z którego trzeba je b˛edzie przenieść do folderu nadrz˛ednego (plugins). 100 Rozdział 4. Technologie WWW Materiały eCG IT Documentation, Wydanie 1 4.1. GetSimple CMS 101 Materiały eCG IT Documentation, Wydanie 1 102 Rozdział 4. Technologie WWW Materiały eCG IT Documentation, Wydanie 1 Zawartość przykładowego folderu plugins powinna wygladać ˛ nast˛epujaco: ˛ 4.2 Materiały 1. GetSimple 2. GetSimple – dodatki 4.2.1 Słownik WWW (ang. World Wide Web) – ogólnoświatowa sieć, jedna z najważniejszych usług sieciowych; hipertekstowy, internetowy sposób udost˛epniania informacji. HTTP(S) (ang. Hypertext Transfer Protocol) – protokół przesyłania dokumentów hipertekstowych, protokół sieci WWW za pomoca˛ którego przesyłane sa˛ żadania ˛ udost˛epnienia lub modyfikacji zasobów, określa reguły komunikacji mi˛edzy klientem (np. przegladark ˛ a) ˛ a serwerem, który zwraca odpowiedzi. Zalecane jest używanie wersji szyfrowanej tego protokołu oznaczanego https. HTML HTML (ang. HyperText Markup Language) – hipertekstowy j˛ezyk znaczników, wykorzystywany do tworzenia stron internetowych. Aktualnie zalecana wersja to HTML5. 4.2. Materiały 103 Materiały eCG IT Documentation, Wydanie 1 XML XML (ang. Extensible Markup Language) – rozszerzalny j˛ezyk znaczników, przeznaczony do strukturalnego i semantycznego opisu danych. PHP obiektowy, skryptowy j˛ezyk programowania, służacy ˛ m. in. do generowania po stronie serwera dynamicznych stron internetowych. Python obiektowy j˛ezyk programowania wysokiego poziomu służacy ˛ m. in. do tworzenia aplikacji internetowych, oferuje przyjazna˛ składni˛e, czytelność i klarowność kodu. CSS (ang. Cascading Style Sheets, CSS) – kaskadowe arkusze stylów, j˛ezyk opisu wygladu ˛ stron internetowych, stanowi dopełnienie HTML-a. JavaScript skryptowy j˛ezyk programowania służacy ˛ m. in. do tworzenia aktywnych właściwości stron internetowych, działa po stronie klienta (tj. w przegladarce). ˛ AJAX AJAX (ang. Asynchronous JavaScript and XML) – asynchroniczny JavaScript i XML, sposób tworzenia stron internetowych, które oferujac ˛ dynamiczna˛ zmian˛e zawartości, nie wymagaja˛ przeładowywania, ponieważ komunikuja˛ si˛e z serwerm asynchronicznie. CMS (ang. Content Management System, CMS) – system zarzadzania ˛ treścia,˛ wykorzystujace ˛ różne technologie internetowe, służacy ˛ do tworzenia serwisów internetowych i zarzadzania ˛ nimi. serwer WWW (ang. web server) – oprogramowanie obsługujace ˛ protokół http, podstawowy protokół sieci WWW, służacy ˛ przesyłaniu dokumentów hipertekstowych. interpreter program, który analizuje kod źródłowy, a nast˛epnie go wykonuje. Interpretery sa˛ podstawowym składnikiem j˛ezyków wykorzystywanych do pisania skryptów wykonywanych po stronie klienta WWW (JavaScript) lub serwera (np. Python, PHP). system bazodanowy system zarzadzania ˛ baza˛ danych (ang. Database Management System, DBMS) – oprogramowanie służace ˛ do zarzadzania ˛ bazami danych, np. SQLite, MariaDB, MySQL, PostgreSQL. framework (ang. framework – struktura) – oprogramowanie b˛edace ˛ zestawem narz˛edzi ułatwiajacych ˛ i przyśpieszajacych ˛ tworzenie aplikacji. 4.2.2 Metryka Autor Robert Bednarz ([email protected]) Utworzony 2015-07-18 o 04:24 104 Rozdział 4. Technologie WWW ROZDZIAŁ 5 Python Poniżej przedstawiamy materiał “Toto Lotek” wprowadzajacy ˛ do programowania w j˛ezyku Python, “Python kreśli” to krótkie wprowadzenie do biblioteki matplotlib, w “Czacie” – realizujemy aplikacj˛e internetowa˛ w wersji rozszerzonej przy użyciu frameworka Django. 5.1 Toto Lotek – Python od podstaw W Toto Lotku trzeba zgadywać liczby. Napiszmy prosty program, w którym b˛edziemy mieli podobne zadanie. Użyjemy j˛ezyka Python. • • • • • • • • • • • • • • Mały Lotek Losowanie liczby Zgadywanie Sprawdzanie Do 3 razy sztuka Duży Lotek Losowanie wielu liczb Nasze typy Ile trafiliśmy Uwaga: bł˛edne dane! Funkcje i moduły Ustawienia Historia losowań Wydruk historii 5.1.1 Mały Lotek Zaczynamy od utworzenia pliku o nazwie toto.py w dowolnym katalogu za pomoca˛ dowolnego edytora, np. Geany. Zapis ~$ poniżej oznacza katalog domowy użytkownika. Obowiazkowa ˛ zawartość pliku: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- Pierwsza linia to ścieżka do interpretera Pythona (zob. interpreter), druga linia deklaruje sposób kodowania znaków, dzi˛eki czemu możemy używać polskich znaków. 105 Materiały eCG IT Documentation, Wydanie 1 5.1.2 Losowanie liczby Musimy wylosować liczby, ale zaczniemy od jednej. Potrzebujemy funkcji randint(a, b) z modułu random. Zwróci nam ona liczb˛e całkowita˛ z zakresu <a; b>. Do naszego pliku dopisujemy: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import random 5 6 7 liczba = random.randint(1, 10) print "Wylosowana liczba:",liczba Wylosowana liczba zostanie zapami˛etana w zmiennej liczba (zob. zmienna ). Instrukcja print wydrukuje ja˛ razem z komunikatem na ekranie. Program możemy już uruchomić w terminalu (zob. terminal), wydajac ˛ w katalogu z plikiem polecenie: ~$ python toto.py Efekt działania naszego skryptu: 5.1.3 Zgadywanie Liczb˛e mamy, niech gracz, czyli użytkownik ja˛ zgadnie. Pytanie tylko, na ile prób mu pozwolimy. Zacznijmy od jednej! Dopisujemy zatem: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import random 5 6 7 liczba = random.randint(1, 10) #print "Wylosowana liczba:",liczba 8 9 odp = raw_input("Jaka˛ liczb˛ e od 1 do 10 mam na myśli? ") Na poczatku ˛ zakomentowujemy znakiem # instrukcj˛e drukujac ˛ a˛ wylosowana˛ liczb˛e. Nie b˛edzie wykonywana :) Liczb˛e podana˛ przez użytkownika pobieramy za pomoca˛ instrukcji raw_input() i zapami˛etujemy w zmiennej odp. Uwaga: Zakładamy na razie, że gracz wprowadza poprawne dane, czyli liczby całkowite! Ćwiczenie 1 Dopisz odpowiednie polecenie, które wyświetli liczb˛e podana˛ przez gracza. Przetestuj jego działanie. 106 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 5.1.4 Sprawdzanie Mamy wylosowana˛ liczb˛e i typ gracza, musimy sprawdzić, czy trafił. Uzupełniamy nasz program: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import random 5 6 7 liczba = random.randint(1, 10) #print "Wylosowana liczba:",liczba 8 9 10 odp = raw_input("Jaka˛ liczb˛ e od 1 do 10 mam na myśli? ") #print "Podałeś liczb˛ e: ",odp 11 12 13 14 15 if liczba == int(odp): print "Zgadłeś! Dostajesz długopis!" else: print "Nie zgadłeś. Spróbuj jeszcze raz." Używamy instrukcji warunkowej if, która sprawdza prawdziwość warunku liczba == int(odp) (zob. instrukcja warunkowa). Jeżeli wylosowana i podana liczba sa˛ sobie równe (==), wyświetlamy informacj˛e o wygranej, w przeciwnym razie (else:) zach˛et˛e do ponownej próby. Dodatkowa funkcja int() zamienia podana˛ przez gracza wartość na liczb˛e całkowita.˛ Informacja: Instrukcja raw_input() wszystkie pobrane dane zwraca jako tekst, dlatego jeżeli wprowadzone wartości chcemy wykorzystywać jako liczby, musimy używać funkcji int(), która próbuje podany tekst przekształcić na typ całkowity (integer). Jeżeli nie jest w stanie tego zrobić, zgłasza wyjatek ˛ ValueError. Ich obsług˛e omówimy później. Przetestuj kilkukrotnie działanie programu. 5.1.5 Do 3 razy sztuka Trafienie za pierwszym razem wylosowanej liczby jest bardzo trudne, spróbujmy dać graczowi 3 szanse. Zmieniamy i uzupełniamy kod: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 5.1. Toto Lotek – Python od podstaw 107 Materiały eCG IT Documentation, Wydanie 1 4 import random 5 6 7 liczba = random.randint(1, 10) #print "Wylosowana liczba:",liczba 8 9 10 11 for i in range(3): odp = raw_input("Jaka˛ liczb˛ e od 1 do 10 mam na myśli? ") #print "Podałeś liczb˛ e: ",odp 12 if liczba == int(odp): print "Zgadłeś! Dostajesz długopis!" break else: print "Nie zgadłeś. Spróbuj jeszcze raz." print 13 14 15 16 17 18 Pobieranie i sprawdzanie kolejnych liczb wymaga powórzenia zakodowanych wcześniej operacji. Do tego celu używamy p˛etli for (zob. p˛etla). Umieszczamy w niej blok poprzednio napisanego kodu odpowiednio wci˛ety (zob. formatowanie kodu). Ilość powtórzeń określa wyrażenie i in range(3). Zmienna iteracyjna i to “licznik” powtórzeń. B˛edzie si˛e on zmieniał tyle razy, ile wartości zwróci funkcja range(n). Funkcja ta generuje list˛e liczb całkowitych od 0 do n-1. Ćwiczenie 2 Zamiast si˛e domyślać, sprawdźmy działanie omawianej funkcji w trybie interaktywnym interpretera Pythona. W terminalu wpisz polecenia: ~$ python >>> range(3) >>> for i in range(3) ... print i ... >>> exit() Jak wynika z powyższego, zmienna i przyjmie wartość 0, 1 i 2, czyli p˛etla for wykona si˛e 3 razy. Wszystkie polecenia znajdujace ˛ si˛e wewnatrz ˛ p˛etli również 3 razy, chyba że... Właśnie, a jeżeli użytkownik trafi za 1 lub 2 razem? Wtedy warunek w instrukcji if stanie si˛e prawdziwy, wyświetli si˛e informacja o nagrodzie, a polecenie break przerwie działanie p˛etli. Przetestuj działanie programu, ale wcześniej przeczytaj jeszcze poniższa˛ uwag˛e: Informacja: W kodzie Pythona bardzo ważna˛ rol˛e pełnia˛ wci˛ecia. W obr˛ebie całego pliku musza˛ one być równe (najcz˛eściej 4 spacje i ich wielokrotności), służa˛ bowiem wydzielaniu bloków kodu. Wskazuja˛ wi˛ec, które polecenia, którym sa˛ podporzadkowane. ˛ W naszym przypadku linie 10, 13 i 16 musza˛ mieć wci˛ecia pojedyncze (np. 4 spacje), a linie 14-15, 17-18 podwójne (np. 8 spacji). Inaczej pojawia˛ si˛e bł˛edy IndentationError. Ćwiczenie 3 Uzupełnij kod, tak aby program wyświetlał informacj˛e “Próba 1”, “Próba 2” itd. przed podaniem liczby. Wskazówki: Wykorzystaj zmienna˛ i i sprawdź również w trybie interaktywnym, co si˛e dzieje, kiedy wpiszesz: ~$ python >>> i = 0 >>> print i >>> i = i + 1 >>> print i 108 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 Ćwiczenie 4 Po 3 bł˛ednej próbie program ponownie wyświetla komunikat: “Nie zgadłeś... Spróbuj jeszcze raz.” Użyj intrukcji if z odpowiednim warunkiem i wci˛eciami (!), aby po 3 nieudanej próbie wyświetlić komunikat: “Miałem na myśli liczb˛e: liczba”. Ostateczny wynik działania naszego programu prezentuje si˛e tak: 5.1.6 Duży Lotek Jedna liczba to za mało, wylosujmy ich wi˛ecej! Zasady dużego lotka to typowanie 6 liczb z 49. Ponieważ trafienie jest tu bardzo trudne, napiszemy program w taki sposób, aby można było łatwo dostosować poziom jego trudności. Na poczatku ˛ utwórz nowy plik toto2.py i uzupełnij go wymaganymi liniami wskazujacymi interpreter pythona i użyte kodowanie. Ćwiczenie 5 Niech użytkownik określi ile liczb chce typować i z jakiego zakresu. Pobierz od użytkownika ilość typowanych liczb, podana˛ wartość przechowaj w zmiennej ileliczb. Podobnie pobierz i zapisz maksymalna˛ losowana˛ liczb˛e w zmiennej maksliczba. Na koniec wyświetl komunikat “Wytypuj x z y liczb: ”. Zamiast x i y powinny wyświetlić si˛e podane przez użytkownika wartości. Wskazówka: Skorzystaj z instrukcji raw_iput(), której użyj jako argumentu funkcji int(). 5.1.7 Losowanie wielu liczb Ćwiczenie 6 Jedna˛ wylosowana˛ liczb˛e zapami˛etywaliśmy w jednej zmiennej, ale przechowywanie wielu wartości w osobnych zmiennych nie jest dobrym pomysłem. Najwygodniej byłoby mieć jedna˛ zmienna,˛ w której można zapisać wiele wartości. W Pythonie takim złożonym typem danych jest lista. Przetestuj w interpreterze nast˛epujace ˛ polecenia: ~$ python >>> liczby = [] >>> liczby >>> liczby.append(1) 5.1. Toto Lotek – Python od podstaw 109 Materiały eCG IT Documentation, Wydanie 1 >>> >>> >>> >>> >>> >>> >>> liczby.append(2) liczby.append(4) liczby.append(4) liczby liczby.count(1) liczby.count(4) liczby.count(0) Wskazówka: Zamiast wpisywać w terminalu powtarzajace ˛ si˛e lub podobne polecenia, użyj klawiszy kursora (góra, dół) do przywoływania poprzednich poleceń. Każde przywołane polecenie możesz przed zatwierdzeniem zmienić używajac ˛ klawiszy lewo, prawo, del i backspace. Jak widać po zadeklarowaniu pustej listy (liczby = []), metoda .append() pozwala dodawać do niej wartości, a metoda .count() podaje, ile razy dana wartość wystapiła ˛ w liście. To si˛e nam przyda ;-) Wróćmy do programu i pliku toto2.py. Losowanie wielu liczb to... powtarzajace ˛ si˛e losowanie jednej liczby, czyli p˛etla. Spróbuj użyć poznanej wcześniej instrukcji for, aby dopisać kod losujacy ˛ ileliczb z zakresu ograniczonego przez maksliczba. Wylosowane wartości wydrukuj w terminalu... Przetestuj program, powinien wypisywać kolejne losowane liczby. Kontynuujemy ćwiczenie. Przed p˛etla˛ zadeklaruj pusta˛ list˛e. Wewnatrz ˛ p˛etli umieść polecenie dodajace ˛ wylosowane liczby do listy. Na końcu programu (uwaga na wci˛ecia) wydrukuj zawartość listy. Wielokrotnie przetestuj program; czy lista zawsze zawiera akceptowalne wartości? P˛etla for nie nadaje si˛e do losowania liczb, ponieważ wykonuje si˛e określona˛ ilość razy, a nie możemy zagwarantować, że losowane liczby b˛eda˛ za każdym razem inne. Do wylosowania podanej ilości liczb wykorzystamy wi˛ec p˛etl˛e while wyrażenie_logiczne:, która powtarza kod dopóki podane wyrażenie jest prawdziwe. Uzupełniamy Kod w pliku toto2.py: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import random 5 6 7 8 ileliczb = int(raw_input("Podaj ilość typowanych liczb: ")) maksliczba = int(raw_input("Podaj maksymalna˛ losowana˛ liczb˛ e: ")) #print "Wytypuj",ileliczb,"z",makliczba," liczb: " 9 10 11 12 13 14 15 16 liczby = [] #for i in range(ileliczb): i = 0 while i < ileliczb: liczba = random.randint(1, maksliczba) if liczby.count(liczba) == 0: liczby.append(liczba) 110 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 17 i = i + 1 18 19 print "Wylosowane liczby:",liczby Losowane liczby zapami˛etujemy w liście liczby (zob. lista). Zmienna i to licznik unikalnych wylosowanych liczb, korzystamy z niej w wyrażeniu warunkowym i < ileliczb, które kontroluje powtórzenia p˛etli. W instrukcji warunkowej wykorzystujemy funkcj˛e zliczajac ˛ a˛ wystapienia ˛ wylosowanej wartości w liście (liczby.count(liczba)), aby dodawać (liczby.append(liczba)) do listy liczby wcześniej niepodane. 5.1.8 Nasze typy Przy pobieraniu typów użytkownika użyjemy podobnie jak przed chwila˛ p˛etli while, ale typy zapisywać b˛edziemy w zbiorze, który z założenia nie może zawierać duplikatów (zob. zbiór). Ćwiczenie 7 W interpreterze Pythona przetestuj nast˛epujace ˛ polecenia: ~$ python >>> typy = set() >>> typy.add(1) >>> typy.add(2) >>> typy >>> typy.add(2) >>> typy >>> typy.add(0) >>> typy.add(9) >>> typy Pierwsza instrukcja deklaruje pusty zbiór (typy = set()). Metoda .add() dodaje do zbioru elementy, ale nie da si˛e dodać dwóch takich samych elementów. Druga˛ cecha˛ zbiorów jest to, że ich elementy nie sa˛ w żaden sposób uporzadkowane. ˛ Wykorzystajmy poznany typ, aby pobrać od użytkownika typy liczb. W pliku toto2.py dopisujemy: 20 21 22 23 24 25 26 27 print "Wytypuj",ileliczb,"z",maksliczba," liczb: " typy = set() i = 0 while i < ileliczb: typ = raw_input("Podaj liczb˛ e "+str(i+1)+": ") if typ not in typy: typy.add(typ) i = i + 1 W powyższym kodzie warto zwrócić uwag˛e na sposób sprawdzania, czy podanej liczby nie ma już w zbiorze: if typ not in typy:. Gdybyśmy chcieli sprawdzić, czy liczba jest w zbiorze, użylibyśmy wyrażenia typ in typy. Przetestuj program. 5.1.9 Ile trafiliśmy Określenie ilości trafień w wi˛ekszości j˛ezyków programowania wymagałoby przeszukiwania listy wylosowanych liczb dla każdego podanego typu. W Pythonie możemy użyć arytmetyki zbiorów: wyznaczymy cz˛eść wspólna.˛ 5.1. Toto Lotek – Python od podstaw 111 Materiały eCG IT Documentation, Wydanie 1 Ćwiczenie 8 W interpreterze przetestuj poniższe instrukcje: ~$ python >>> liczby = [1,3,5,7,9] >>> typy = set([2,3,4,5,6]) >>> set(liczby) | typy >>> set(liczby) - typy >>> trafione = set(liczby) & typy >>> len(trafione) Polecenie set(liczby) przekształca list˛e na zbiór. Kolejne operatory zwracaja˛ sum˛e (|), różnic˛e (-) i iloczyn (&), czyli cz˛eść wspólna˛ zbiorów. Ta ostania operacja bardzo dobrze nadaje si˛e do sprawdzenia, ile liczb trafił użytkownik. Funkcja len() zwraca ilość elementów m.in. listy i zbioru. Do pliku toto2.py dopisujemy: 31 32 33 34 35 36 trafione = set(liczby) & typy if trafione: print "\nIlość trafień: ",len(trafione) print "Trafione liczby: ",trafione else: print "Brak trafień. Spróbuj jeszcze raz!" Instrukcja if trafione: sprawdza, czy cz˛eść wspólna zawiera jakiekolwiek elementy. Jeśli tak, drukujemy liczb˛e trafień i trafione liczby. Ćwiczenie 9 Przetestuj program dla 5 typów z 10 liczb. Działa? Jeśli masz watpliwości, ˛ wpisz wylosowane i wytypowane liczby w interpreterze, np.: >>> >>> >>> >>> ... ... >>> liczby = [1,4,2,6,7] typy = set([1,2,3,4,5]) trafione = set(liczby) & typy if trafione: print len(trafione) print trafione Wnioski? Logika kodu jest poprawna, czego dowodzi test w terminalu, ale program nie działa. Dlaczego? Wskazówka: Przypomnij sobie, jakiego typu wartości zwraca funkcja raw_input(). Ćwiczenie 10 Zmodyfikuj program tak, aby wynik jego działania wygladał ˛ nast˛epujaco: ˛ Ćwiczenie 11 Zmień program tak, aby użytkownik mógł 3 razy typować liczby z tej samej serii liczb wylosowanych. Wynik działania programu powinien przypominać poniższy zrzut: Wskazówka: Wykorzystaj p˛etl˛e for. 112 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 5.1. Toto Lotek – Python od podstaw 113 Materiały eCG IT Documentation, Wydanie 1 5.1.10 Uwaga: błedne ˛ dane! Kod naszego programu do tej pory przedstawia si˛e mniej wi˛ecej tak: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import random 5 6 7 ileliczb = int(raw_input("Podaj ilość typowanych liczb: ")) maksliczba = int(raw_input("Podaj maksymalna˛ losowana˛ liczb˛ e: ")) 8 9 10 11 12 13 14 15 16 liczby = [] i = 0 while i < ileliczb: liczba = random.randint(1, maksliczba) print "Wylosowane liczba:",liczba if liczby.count(liczba) == 0: liczby.append(liczba) i = i + 1 17 18 19 20 21 22 23 24 25 26 for i in range(3): print "Wytypuj",ileliczb,"z",maksliczba," liczb: " typy = set() i = 0 while i < ileliczb: typ = int(raw_input("Podaj liczb˛ e "+str(i+1)+": ")) if typ not in typy: typy.add(typ) i = i + 1 27 trafione = set(liczby) & typy if trafione: print "\nIlość trafień: ",len(trafione) print "Trafione liczby: ",trafione else: print "Brak trafień. Spróbuj jeszcze raz!" 28 29 30 31 32 33 34 print "\n"+"x"*40+"\n" # wydrukuj 40 znaków x 35 36 37 print "Wylosowane liczby:",liczby Uruchom powyższy program i podaj ilość losowanych liczb wi˛eksza˛ od maksymalnej losowanej liczby. Program wpada w nieskończona˛ p˛etl˛e! Po chwili zastanowienia dojdziemy do wniosku, że nie da si˛e wylosować np. 6 unikalnych liczb z zakresu 1-5. Ćwiczenie 12 Dodaj odpowiednia˛ instrukcj˛e warunkowa,˛ która w przypadku gdy zmienna ileliczb b˛edzie mniejsza od zmiennej maksliczba wyświetli komunikat “Bł˛edne dane!” i przerwie wykonywanie programu – użyj funkcji exit(). Sprawdź działanie programu. Tetsujemy dalej. Uruchom program i zamiast liczby podaj tekst. Co si˛e dzieje? Uruchom jeszcze raz, ale tym razem jako typy podaj wartości spoza zakresu <0;maksliczba>. Da si˛e to zrobić? 114 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 Jak pewnie zauważyłeś, w pierwszym wypadku zgłoszony zostaje wyjatek ˛ “ValuError” (zob.: wyjatki) ˛ i komunikat invalid literal for int() with base 10, który informuje, że fukncja int() nie jest w stanie przekształcić podanego ciagu ˛ znaków na liczb˛e całkowita.˛ W drugim wypadku podanie nielogicznych typów jest możliwe. Spróbujmy zmodyfikować program tak, aby był nieco odporniejszy na niepoprawne dane: 6 try: 7 ileliczb = int(raw_input("Podaj ilość typowanych liczb: ")) maksliczba = int(raw_input("Podaj maksymalna˛ losowana˛ liczb˛ e: ")) if ileliczb > maksliczba: print "Bł˛ edne dane!" exit() except: print "Bł˛ edne dane!" exit() 8 9 10 11 12 13 14 Do przechwytywania wyjatków ˛ używamy konstrukcji try: ... except: ..., czyli spróbuj wykonać kod w bloku try, a w razie bł˛edów przechwyć wyjatek ˛ ValueError i wykonaj podporzadkowane ˛ instrukcje. W powyższym przypadku wyświetlamy odpowiedni komunikat i kończymy działanie programu (exit()). 28 29 30 31 32 33 while i < ileliczb: try: typ = int(raw_input("Podaj liczb˛ e "+str(i+1)+": ")) except ValueError: print "Bł˛ edne dane!" continue 34 35 36 37 if 0 < typ <= maksliczba and typ not in typy: typy.add(typ) i = i + 1 Pobierajac ˛ typy od użytkownika również musimy spróbować przekształcić podane znaki na liczb˛e (int()) i w razie bł˛edu przechwycić wyjatek ˛ (try...except). Poza tym jednak trzeba sprawdzić, czy użytkownik podaje sensowane typy. Odpowiada za to warunek 0 < typ <= maksliczba. Jest to skrócony zapis wyrażenia: typ > 0 and typ <= maksliczba. Na koniec warto zauważyć, co dzieje si˛e, kiedy przechwytujemy wyjatek. ˛ Nie kończymy programu (exit()), ani nie przerywamy p˛etli (break()), zamiast tego pomijamy dalsze polecenia i wznawiamy wykonywanie p˛etli kolejny raz. Tak działa polecenie continue. 5.1.11 Funkcje i moduły Tam, gdzie w programie wyst˛epuja˛ powtarzajace ˛ si˛e operacje lub zestaw poleceń realizujacy ˛ wyodr˛ebnione zadanie, wskazane jest używanie funkcji. Sa˛ to nazwane bloki kodu, które można grupować w ramach modułów (zob. funkcja, zob. moduł). Funkcje zawarte w modułach można importować do różnych programów. Do tej pory korzystaliśmy np. z funkcji randit() zawartej w module random. Wyodr˛ebnienie funkcji ułatwia sprawdzanie i poprawianie kodu, ponieważ wymusza podział programu na logicznie uporzadkowane ˛ kroki. Jeżeli program korzysta z niewelu funkcji, można umieszczać je na poczatku ˛ pliku programu głównego. Tworzymy wi˛ec nowy plik totomodul.py rozpoczynajacy ˛ si˛e liniami wskazujacymi ˛ interpreter i kodowanie. Umieszczamy w nim nast˛epujacy ˛ kod: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import random 5.1. Toto Lotek – Python od podstaw 115 Materiały eCG IT Documentation, Wydanie 1 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def ustawienia(): """Funkcja pobiera ilość losowanych liczb, maksymalna˛ losowana˛ wartość oraz ilość prób. Pozwala określić stopień trudności gry.""" while 1: try: ile = int(raw_input("Podaj ilość typowanych liczb: ")) maks = int(raw_input("Podaj maksymalna˛ losowana˛ liczb˛ e: ")) if ile > maks: print "Bł˛ edne dane!" continue ilelos = int(raw_input("Ile losowań: ")) return (ile, maks, ilelos) except: print "Bł˛ edne dane!" continue 21 22 23 24 25 26 27 28 29 30 31 def losujliczby(ile, maks): """Funkcja losuje ile unikalnych liczb całkowitych od 1 do maks""" liczby = [] i = 0 while i < ile: liczba = random.randint(1, maks) if liczby.count(liczba) == 0: liczby.append(liczba) i = i + 1 return liczby 32 33 34 35 36 37 38 39 40 41 42 43 44 def pobierztypy(ile, maks): """Funkcja pobiera od użytkownika jego typy wylosowanych liczb""" print "Wytypuj",ile,"z",maks," liczb: " typy = set() i = 0 while i < ile: try: typ = int(raw_input("Podaj liczb˛ e "+str(i+1)+": ")) except ValueError: print "Bł˛ edne dane!" continue 45 if 0 < typ <= maks and typ not in typy: typy.add(typ) i = i + 1 return typy 46 47 48 49 Kod odpowiedzialny za ustawienia gry, losowanie liczb i pobieranie typów użytkownika umieszczony został w osobnych funkcjach sygnalizowanych słowem kluczowym def i wci˛eciami. Funkcje moga˛ przyjmować definiowane w nawiasach dane wejściowe, np. losujliczby(ile, maks), które podajemy jako argumenty w momencie wywołania funkcji. Funkcje moga˛ zwracać dane wyjściowe za pomoca˛ instrukcji return. Warto zauważyć, że można zwracać wi˛ecej niż jedna˛ wartość naraz, np. w postaci tupli (ile, maks, ilelos). Tupla to rodzaj listy, w której nie możemy zmieniać wartości (zob. tupla), jest cz˛esto stosowana do przechowywania i przekazywania stałych danych. Nazwy zmiennych lokalnych w funkcjach sa˛ niezależne od nazw zmiennych w programie głównym, ponieważ definiwane sa˛ w różnych zasi˛egach czy też przestrzeniach nazw. Możliwe jest modyfikowanie zmiennych globalnych dost˛epnych w całym programie, w funkcji musimy tylko umieścić polecenie: global nazwa_zmiennej. 116 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 Wiele wartości zwracanych w tupli przez funkcj˛e ustawienia() można jednocześnie przypisać kilku zmiennym dzi˛eki operacji tzw. rozpakowania tupli: ileliczb, maksliczba, ilerazy = ustawienia(). Dwie pozostałe funkcje zwracaja˛ list˛e wylosowanych liczb i zbiór typów. Program główny po zmianach przedstawia si˛e nast˛epujaco: ˛ 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 from totomodul import ustawienia, losujliczby, pobierztypy 5 6 # program główny 7 8 9 # ustalamy trudność gry ileliczb, maksliczba, ilerazy = ustawienia() 10 11 12 # losujemy liczby liczby = losujliczby(ileliczb, maksliczba) 13 14 15 16 17 18 19 20 21 22 # trzy razy pobieramy typy użytkownika i sprawdzamy, ile liczb trafił for i in range(ilerazy): typy = pobierztypy(ileliczb, maksliczba) trafione = set(liczby) & typy if trafione: print "\nIlość trafień: ",len(trafione) print "Trafione liczby: ",trafione else: print "Brak trafień. Spróbuj jeszcze raz!" 23 24 print "\n"+"x"*40+"\n" # wydrukuj 40 znaków x 25 26 print "Wylosowane liczby:",liczby Na poczatku ˛ z modułu totomodul, którego nazwa jest taka sama jak nazwa pliku, importujemy potrzebne funkcje. Później wywołujemy je podajac ˛ nazw˛e i ewentualne argumenty. W efekcie zwracane przez nie wartości zostaja˛ przypisane podanym zmiennym. Jak widać, program stał si˛e czytelniejszy. Informacja: W rozbudowanych programach dobra˛ praktyka˛ ułatwiajac ˛ a˛ późniejsze przegladanie ˛ i poprawianie kodu jest opatrywanie jego fragmentów komentarzami. Można je umieszczać po znaku #. Z kolei funkcje opatruje si˛e krótkim opisem działania i/lub wymaganych argumentów, ograniczanym potrójnymi cudzysłowami. Notacja """...""" lub ’’’...’’’ pozwala zamieszczać teksty wielowierszowe. Ćwiczenie 13 Użytkownik może typować liczby kilka razy w ramach jednego uruchomienia programu. Powtarzany w p˛etli for kod warto przenieść do funkcji zapisanej w module programu i nazwanej np. wyniki(). Zastanów si˛e, jakie argumenty należy jej przekazać i co powinna zwracać. 5.1.12 Ustawienia Uruchamiajac ˛ wielokrotnie program, musimy podawać wiele danych, aby zadziałał. Dodamy wi˛ec możliwość zapami˛etywania ustawień i ich zmiany. Dane zapisywać b˛edziemy w zwykłym pliku tekstowym. W pliku toto2.py uzupełniamy tylko jedna˛ lini˛e: 5.1. Toto Lotek – Python od podstaw 117 Materiały eCG IT Documentation, Wydanie 1 8 9 # ustawienia gry nick, ileliczb, maksliczba, ilerazy = ustawienia() W pliku totomodul.py zmieniamy funkcj˛e ustawienia() oraz dodajemy dwie nowe: czytaj_ust() i zapisz_ust(). 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import random, os 5 6 7 8 def ustawienia(): """Funkcja pobiera nick użytkownika, ilość losowanych liczb, maksymalna˛ losowana˛ wartość oraz ilość typowań. Ustawienia zapisuje.""" 9 nick = raw_input("Podaj nick: ") nazwapliku = nick + ".ini" gracz = czytaj_ust(nazwapliku) odp = None if gracz: print "Twoje ustawienia:" print "Liczb:",gracz[1] print "Z Maks:",gracz[2] print "Losowań:",gracz[3] odp = raw_input("Zmieniasz (t/n)? ") 10 11 12 13 14 15 16 17 18 19 20 if not gracz or odp.lower() == "t": while 1: try: ile = int(raw_input("Podaj ilość typowanych liczb: ")) maks = int(raw_input("Podaj maksymalna˛ losowana˛ liczb˛ e: ")) if ile > maks: print "Bł˛ edne dane!" continue ilelos = int(raw_input("Ile losowań: ")) break except: print "Bł˛ edne dane!" continue gracz = zapisz_ust(nazwapliku, [nick, str(ile), str(maks), str(ilelos)]) 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 return gracz[0:1] + map(lambda x: int(x), gracz[1:4]) 36 37 38 39 40 41 42 43 44 def czytaj_ust(nazwapliku): if os.path.isfile(nazwapliku): plik = open(nazwapliku, "r") linia = plik.readline() if linia: return linia.split(";") return False 45 46 47 48 49 50 def zapisz_ust(nazwapliku, gracz): plik = open(nazwapliku, "w") plik.write(";".join(gracz)) plik.close() return gracz W funkcji ustawienia() pobieramy nick użytkownika i tworzymy nazw˛e pliku z ustawieniami, nast˛epnie próbujemy je odczytać wywołujac ˛ funkcj˛e czytaj_ust(). Funkcja ta sprawdza, czy podany plik istnieje na dysku i 118 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 otwiera go do odczcytu: plik = open(nazwapliku, "r"). Plik powinien zawierać 1 lini˛e, która przechowuje ustawienia w formacie: nick;ile_liczb;maks_liczba;ile_prób. Po jej odczytaniu za pomoca˛ metody .readline() i rozbiciu na elementy zwracamy ja˛ jako list˛e gracz. Jeżeli uda si˛e odczytać zapisane ustawienia, drukujemy je, a nast˛epnie pytamy, czy użytkownik chce je zmienić. Jeżeli nie znaleźliśmy zapisanych ustawień lub użytkownik nacisnał ˛ klawisz “t” lub “T” wykonujemy poprzedni kod. Na koniec zmiennej gracz przypisujemy list˛e ustwień przekazana˛ do zapisu funkcji zapisz_ust(). Funkcja ta zapisuje dane złaczone ˛ za pomoca˛ średnika w jedna˛ lini˛e do pliku: plik.write(";".join(gracz)). W powyższym kodzie widać, jakie operacje można wykonywać na tekstach, tj.: • operator +: łaczenie ˛ tekstów, • linia.split(";") – rozbijanie tekstu wg podanego znaku na elementy listy, • ";".join(gracz) – złaczanie ˛ elementów listy za pomoca˛ podanego znaku, • odp.lower() – zmiana wszystkich znaków na małe litery, • str(arg) – przekształcanie podanego argumentu na typ tekstowy. Na szczególna˛ uwag˛e zasługuje konstrukcja return gracz[0:1] + map(lambda x: int(x), gracz[1:4]), której używamy, aby zwrócić odczytane/zapisane ustawienia do programu głównego. Dane w pliku przechowujemy, a także pobieramy od użytkownika jako znaki. Natomiast program główny oczekuje 4 wartości typu: znak, liczba, liczba, liczba. Stosujemy wi˛ec notacj˛e wycinkowa˛ (ang. slice), aby wydobyć nick użytkownika: gracz[0:1]. Pierwsza wartość mówi od którego elementu, a druga do którego elementu wycinamy wartości z listy (przećwicz w konsoli Pythona!). Funkcja map() pozwala zastosować do pozostałych 3 elementów – gracz[1:4] – funkcj˛e, która zamienia je w wartości liczbowe. Wykorzystujemy tu wyrażenia lambda, czyli skrócony zapis 1-argumentowej funkcji (zob. mapowanie funkcji). 5.1.13 Historia losowań Skoro umiemy już zapami˛etywać wst˛epne ustawienia programu, możemy również zapami˛etywać losowania użytkownika, tworzac ˛ rejestr do celów informacyjnych i/lub statystycznych. Zadanie wymaga po pierwsze zdefiniowania jakieś struktury, w której b˛edziemy przechowywali dane, po drugie zapisu danych albo w plikach, albo w bazie danych. Na poczatku ˛ dopiszemy kod w programie głównym toto2.py: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 5 6 from totomodul import ustawienia, losujliczby, pobierztypy, wyniki from totomodul import czytaj_json, zapisz_json import time 7 8 # program główny 9 10 11 # ustalamy trudność gry nick, ileliczb, maksliczba, ilerazy = ustawienia() 12 13 14 nazwapliku = nick + ".json" losowania = czytaj_json(nazwapliku) 15 16 17 # losujemy liczby liczby = losujliczby(ileliczb, maksliczba) 18 19 20 21 # pobieramy typy użytkownika i sprawdzamy, ile liczb trafił for i in range(ilerazy): typy = pobierztypy(ileliczb, maksliczba) 5.1. Toto Lotek – Python od podstaw 119 Materiały eCG IT Documentation, Wydanie 1 trafione = wyniki(set(liczby), typy) 22 23 24 25 26 27 28 29 losowania.append({ "czas": time.time(), "dane": (ileliczb, maksliczba), "wylosowane": liczby, "ile": trafione }) 30 31 zapisz_json(nazwapliku, losowania) 32 33 34 print "Wylosowane liczby:",liczby print "\n",losowania Dane graczy zapisywać b˛edziemy w plikach nazwanych nickiem użytkownika z rozszerzeniem ”.json”: nazwapliku = nick + ".json". Informacje o grach umieścimy w liście losowania, która˛ na poczatku ˛ zainicjujemy danymi o grach zapisanymi wcześniej: losowania = czytaj(nazwapliku). Każda gra w liście losowania to słownik. Struktura ta pozwala przechowywać dane w parach “klucz: wartość”, przy czym indeksami moga˛ być napisy: • "czas" – b˛edzie indeksem daty gry (potrzebny import modułu time!), • "dane" – b˛edzie wskaywał tupl˛e z ustawieniami, • "wylosowane" – list˛e wylosowanych liczb, • "ile" – ilość trafień. Na koniec dane ostatniej gry dopiszemy do listy (losowania.append()), a cała˛ list˛e zapiszemy do pliku: zapisz(nazwapliku, losowania). Teraz zobaczmy, jak wygladaj ˛ a˛ funkcje czytaj_json() i zapisz_json() w module totomodul.py: 92 import json 93 94 95 96 97 98 99 100 def czytaj_json(nazwapliku): """Funkcja odczytuje dane w formacie json z pliku""" dane = [] if os.path.isfile(nazwapliku): with open(nazwapliku, "r") as plik: dane = json.load(plik) return dane 101 102 103 104 105 def zapisz_json(nazwapliku, dane): """Funkcja zapisuje dane w formacie json do pliku""" with open(nazwapliku, "w") as plik: json.dump(dane, plik) Kiedy czytamy i zapisujemy dane, ważna˛ sprawa˛ staje si˛e ich format. Najprościej zapisywać dane jako znaki, tak jak zrobiliśmy to z ustawieniami, jednak cz˛esto programy użytkowe potrzebuja˛ zapisywać złożone struktury danych, np. listy, zbiory czy słowniki. Znakowy zapis wymagałby wtedy wielu dodatkowych manipulacji, aby możliwe było poprawne odtworzenie informacji. Prościej jest skorzystać z serializacji, czyli zapisu danych obiektowych (zob. serializacja). Jednym z szerzej stosowanych jest prosty format tekstowy JSON. W funkcji czytaj() zawartość podanego pliki dekodujemy do listy: dane = json.load(plik). Funkcja zapisz() oprócz nazwy pliku wymaga listy danych. Po otwarciu pliku w trybie zapisu "w", co powoduje wyczyszczenie jego zawartości, dane sa˛ serializowane i zapisywane formacie JSON: json.dump(dane, plik). Dobra˛ praktyka˛ jest zwalnianie uchwytu do otwartego pliku i przyddzielonych mu zasobów poprzez jego zamkni˛ecie: plik.close(). Tak robiliśmy w funkcjach czytajacych ˛ i zapisujacych ˛ ustawienia. Teraz jednak pliki otworzy- 120 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 liśmy przy użyciu konstrukcji typu with open(nazwapliku, "r") as plik:, która zadba o ich własciwe ˛ zmakni˛ecie. Przetestuj, przynajmniej kilkukrotnie, działanie programu. Ćwiczenie 14 Załóżmy, że jednak chcielibyśmy zapisywać histori˛e losowań w pliku tekstowym, którego poszczególne linie zawierałyby dane jednego losowania, np.: wylosowane:[4, 5, 7];dane:(3, 10);ile:0;czas:1434482711.67 Funkcja zapisujaca ˛ dane mogłaby wygladać ˛ np. tak: Napisz funkcj˛e czytaj_str() odczytujac ˛ a˛ tak zapisane dane. Funkcja powinna zwrócić list˛e słowników. 5.1.14 Wydruk historii [todo] 5.2 Python kreśli Jedna˛ z pot˛eżniejszych biliotek Pythona jest matplotlib, która służy do tworzenia różnego rodzaju wykresów. Pylab to API ułatwiajace ˛ korzystanie z omawianej biblioteki na wzór środowiska Matlab. Poniżej pokazujemy, jak łatwo przy użyciu Pythona wizualizować wykresy różnych funkcji. Najłatwiej zainstalować wymagana˛ bibliotek˛e wydajac ˛ polecenie z uprawnieniami roota w terminalu: ~# pip install matplotlib • Funkcja liniowa • Dwie funkcje 5.2.1 Funkcja liniowa Zabw˛e zacznijmy w konsoli Pythona: import pylab x = [1,2,3] y = [4,6,5] pylab.plot(x,y) pylab.show() Tworzenie wykresów jest proste. Musimy mieć zbiór wartości x i odpowiadajacy ˛ im zbiór wartości y. Obie listy przekazujemy jako argumenty funkcji plot(), a nast˛epnie rysujemy funkcja˛ show(). Spróbujmy zrealizować bardziej złożone zadanie. ZADANIE: wykonaj wykres funkcji f(x) = a*x + b, gdzie x = <-10;10> z krokiem 1, a = 1, b = 2. W pliku pylab01.py umieszczamy poniższy kod: 5.2. Python kreśli 121 Materiały eCG IT Documentation, Wydanie 1 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import pylab 5 6 7 8 a = 1 b = 2 x = range(-10,11) # lista argumentów x 9 10 11 12 y = [] # lista wartości for i in x: y.append(a*i + b) 13 14 15 16 17 pylab.plot(x,y) pylab.title('Wykres f(x) = a*x - b') pylab.grid(True) pylab.show() Na poczatku ˛ dla ułatwienia importujemy interfejs pylab. Nast˛epnie post˛epujemy wg omówionego schematu: zdefiniuj dziedzin˛e argumentów funkcji, a nast˛epnie zbiór wyliczonych wartości. W powyższym przypadku generujemy list˛e wartości x za pomoca˛ funkcji range() – co warto przetestować w interaktywnej konsoli Pythona. Wartości y wyliczamy w p˛etli i zapisujemy w liście. Dodatkowe metody: title() ustawia tytuł wykresu, grid() włacza ˛ wyświetlanie pomocniczej siatki. Uruchom program. Ćwiczenie 1 Można ułatwić użytkownikowi testowanie funkcji, umożliwiajac ˛ mu podawanie współczynników a i b. Zastap ˛ odpowiednie przypisania instrukcjami pobierajacymi ˛ dane od użytkownika. Nie zapomnij przekonwertować danych tekstowych na liczby całkowite. Przetestuj zmodyfikowany kod. Ćwiczenie 2 W konsoli Pythona wydajemy nast˛epujace ˛ polecenia: >>> >>> >>> ... >>> >>> a = 2 x = range(11) for i in x: print a + i y = [a + i for i in range(11)] y Powyższy przykład pokazuje kolejne ułatwienie dost˛epne w Pythonie, czyli wyrażenie listowe, które zwi˛eźle zast˛epuje p˛etl˛e i zwraca list˛e wartości. Jego działanie należy rozumieć nast˛epujaco: ˛ dla każdej wartości i (nazwa zmiennej dowolna) w liście x wylicz wyrażenie a + i i umieść w liście y. Wykorzystajmy wyrażenie listowe w naszym programie: 6 7 8 a = int(raw_input('Podaj współczynnik a: ')) b = int(raw_input('Podaj współczynnik b: ')) x = range(-10,11) # lista argumentów x 9 10 11 # wyrażenie listowe wylicza dziedzin˛ e y y = [a*i + b for i in x] # lista wartości 122 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 5.2.2 Dwie funkcje ZADANIE: wykonaj wykres funkcji f(x), gdzie x = <-10;10> z krokiem 0.5, f(x) = x*x/3 dla x < 1 i x > 0, f(x) = x/(-3) + a dla x <= 0. Współczynnik a podaje użytkownik. Ćwiczenie 2 Zanim zrealizujemy zadanie przećwiczmy w konsoli Pythona nast˛epujacy ˛ kod: >>> >>> >>> >>> >>> import pylab x = pylab.frange(-10, 11, 0.5) x y = [i**2 for i in x if i <= 0] len(y) Wykonanie zadania wymaga umieszczenia na wykresie dwóch funkcji. Wykorzystamy funkcj˛e frange, która zwraca list˛e wartości zmiennoprzecinkowych (zob. typ typy danych) z zakresu określonego przez dwa pierwsze argumenty i z krokiem wyznaczonym przez argument trzeci. Druga˛ przydatna˛ konstrukcja˛ b˛edzie wyrażenie listowe uzupełnione o instrukcj˛e warunkowa,˛ która ogranicza wartości, dla których obliczane jest podane wyrażenie. W pliku pylab02.py umieszczamy poniższy kod: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 5 6 # ZADANIE: wykonaj wykres funkcji f(x), gdzie x = <-10;10> z krokiem 1 # f(x) = x*x/3 dla x < 1 i x > 0 # f(x) = x/-3 + a dla x <= 0 7 8 import pylab 9 10 11 x = pylab.frange(-10, 11, 0.5) # lista argumentów x y1 = [i**2/3 for i in x if i < 1 or i > 0] 12 13 14 15 16 pylab.plot(x,y1) pylab.title('Wykres f(x)') pylab.grid(True) pylab.show() Uruchom program. Udało si˛e nam zrealizować pierwsza˛ cz˛eść zadania. Spróbujmy zakodować cz˛eść druga.˛ Dopisujemy: 13 14 a = int(raw_input("Podaj współczynnik a: ")) y2 = [i/-3 + a for i in x if i <= 0] 15 16 pylab.plot(x,y1,x,y2) Po pobraniu współczynnika a od użytkownika tworzymy wyrażenie listowe wyliczajace ˛ druga˛ dziedzin˛e wartości. Nast˛epnie do argumentów przekazywanych funkcji plot() dodajemy drug˛e par˛e list. Spróbuj uruchomić program. Nie działa, dostajemy komunikat ValueError: x and y must have same first dimension, czyli listy wartości x i y w którejś z par nie zawieraja˛ tyle samo elementów. Ćwiczenie 3 Przetestujmy kod w konsoli Pythona: 5.2. Python kreśli 123 Materiały eCG IT Documentation, Wydanie 1 >>> >>> >>> >>> >>> >>> >>> >>> >>> import pylab x = pylab.frange(-10, 11, 0.5) y1 = [i**2/3 for i in x if i < 1 or i > 0] len(x) == len(y1) a = 2 y2 = [i/-3 + a for i in x if i <= 0] len(x) == len(y2) len(x) len(y2) Uwaga: nie zamykaj tej sesji konsoli, zaraz si˛e nam jeszcze przyda. Szybko zauważymy, że lista y2 zawiera mniej wartości niż dziedzina x. Co należy z tym zrobić? Jak wynika z warunków zadania, wartości y2 obliczane sa˛ tylko dla argumentów mniejszych od zera. Zatem trzeba ograniczyć list˛e x, tak aby zawierała tylko wartości z odpowiedniego przedziału. Wróćmy do konsoli Pythona: Ćwiczenie 4 >>> >>> >>> >>> >>> >>> x x[0] x[0:5] x[:5] x[:len(y2)] len(x[:len(y2)]) Z pomoca˛ przychodzi nam wydobywanie z listy wartości wskazywanych przez indeksy liczone od 0. Jednak prawdziwym ułatwieniem jest notacja wycinania (ang. slice), która pozwala podać pierwszy i ostatni indeks interesujacego ˛ nas zakresu. Zmieniamy wi˛ec wywołanie funkcji plot(): pylab.plot(x,y1,x[:len(y2)],y2) Uruchom i przetestuj działanie programu. Ćwiczenie 5 Spróbuj samodzielnie przygotować wykres funkcji kwadratowej: f(x) = a*x^2 + b*x + c, gdzie x = <-10;10> z krokiem 1, przyjmij nast˛epujace ˛ wartości współczynników: a = 1, b = -3, c = 1. Uzyskany wykres powinien wygladać ˛ nast˛epujaco: ˛ Jeżli masz ochot˛e na wi˛ecej, daj znać! 5.3 Czat (wersja rozszerzona) Zastosowanie Pythona i frameworka Django do stworzenia aplikacji internetowej Czat; prostego czata, w którym zarejestrowani użytkownicy b˛eda˛ mogli wymieniać si˛e krótkimi wiadomościami. 124 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 5.3. Czat (wersja rozszerzona) 125 Materiały eCG IT Documentation, Wydanie 1 • • • • • • • • • • • • • Projekt i aplikacja Model – Widok – Kontroler Model danych i baza Panel administracyjny Widoki i szablony Rejestrowanie użytkowników Logowanie i wylogowywanie Widoki ogólne Obsługa wiadomości Wiadomości jeszcze raz Szablony Style, skrpyty, pliki Materiały 5.3.1 Projekt i aplikacja Tworzymy nowy projekt Django, a nast˛epnie uruchamiamy lokalny serwer, który pozwoli śledzić post˛ep pracy. W katalogu domowym wydajemy polecenia w terminalu: ~$ django-admin.py startproject czat ~$ cd czat ~/czat$ python manage.py runserver Powstanie katalog projektu czat i podkatalog aplikacji o takiej samej nazwie chatter. Po wpisaniu w przegladarce ˛ adresu 127.0.0.1:8000 zobaczymy stron˛e powitalna.˛ Informacja: Domyślnie serwer nasłuchuje na porcie 8000, można go jednak uruchomić na innyn, jeżeli domyślny byłby zaj˛ety. Wystarczy, że do polecenia dodamy np.: 127.0.0.1:8080. W systemach Linux możemy kliknać ˛ w terminalu prawym klawiszem adres http://127.0.0.1:8000/ i wybrać polecenie “Otwórz link”, aby szybko otworzyć domyślna˛ przegladark˛ ˛ e. Lokalny serwer deweloperski zatrzymujemy za pomoca˛ skrótu Ctrl+C. Jeden projekt może zawierać wiele aplikacji zapisywanych w osobnych podkatalogach katalogu projektu. 126 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 Rozpoczynamy od modyfikacji ustawień projektu, tak aby korzystał z polskiej wersji j˛ezykowej oraz lokalnych ustawień daty i czasu. Musimy również zarejestrować nasza˛ aplikacj˛e w projekcie. W pliku setting.py zmieniamy nast˛epujace ˛ linie: # czat/czat/settings.py # rejestrujemy aplikacje INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'czat', # rejestrujemy aplikacj˛ e ) LANGUAGE_CODE = 'pl' # ustawienie j˛ ezyka TIME_ZONE = 'Europe/Warsaw' # ustawienie strefy czasowej Ostrzeżenie: Uwaga: jeżeli w plikach Pythona chcemy stosować polskie znaki, m.in. w komentarzach, na poczatku ˛ każdego pliku powinna znaleźć si˛e linia definiujaca ˛ kodowanie: # -*- coding: utf-8 -*-. 5.3.2 Model – Widok – Kontroler W projektowaniu aplikacji internetowych za pomoca˛ Django odwołujemy si˛e do wzorca M(odel)V(iew)C(ontroller), czyli Model–Widok–Kontroler 1 , co pozwala na oddzielenie danych od ich prezentacji oraz logiki aplikacji. Funkcje kolejnych elementów sa˛ nast˛epujace: ˛ • Modele – model w Django reprezentuje źródło informacji; sa˛ to klasy Pythona odwzorowujace ˛ pojedyncze tabele w bazie danych 2 ; każda klasa zawiera właściwości odpowiadajace ˛ polom tabeli, może też zawierać funkcje wykonujace ˛ operacje na danych. Instancja takiej klasy odpowiada rekordowi danych. Modele definiujemy w pliku models.py. • Widoki – widok w Django to funkcja czy klasa Pythona, która na podstawie żadań ˛ www (dla danych adresów URL) zwraca odpowiedź, najcz˛eściej w postaci kodu HTML generowanego w szablonach (templates); odpowiedzia˛ może być również przekierowanie na inny adres, jakiś dokument lub obrazek. Django zawiera wiele widoków wbudowanych. Widoki modyfikujemy lub definiujemy w pliku views.py. • Kontroler – kontroler to mechanizm kierujacy ˛ kolejne żadania ˛ do odpowiednich widoków na podstawie wzorców adresów URL zawartych w pliku urls.py. 5.3.3 Model danych i baza Pisanie aplikacji zaczynamy od zdefiniowania modelu, czyli klasy opisujacej ˛ tabel˛e zawierajac ˛ a˛ wiadomości. Instancje tej klasy b˛eda˛ konkretnymi wiadomościami utworzonymi przez użytkowników systemu. Każda wiadomość b˛edzie zwierała treść, dat˛e dodania oraz autora wiadomości (użytkownika). W pliku ~/czat/czat/models.py, który musimy utworzyć, wpisujemy: 1 Twórcy Django traktuja˛ jednak ten wzorzec elastycznie, mówiac ˛ że ich framework wykorzystuje wzorzec MTV, czyli model (model), szablon (template), widok (view). 2 Takie odwzorowanie nosi nazw˛ e mapowania obiektowo-relacyjnego (ORM). ORM odwzorowuje struktur˛e bazy na obiekty Pythona. 5.3. Czat (wersja rozszerzona) 127 Materiały eCG IT Documentation, Wydanie 1 1 2 # -*- coding: utf-8 -*# czat/czat/models.py 3 4 5 from django.db import models from django.contrib.auth.models import User 6 7 8 9 10 11 class Wiadomosc(models.Model): """Klasa reprezentujaca ˛ wiadomość w systemie""" tekst = models.CharField(max_length=250) data_pub = models.DateTimeField() autor = models.ForeignKey(User) Jak widać, podczas opisywania klasy Wiadomosc podajemy nazwy poszczególnych właściwości (pól) oraz typy przechowywanych w nich danych. Po zdefiniowaniu przynajmniej jednego modelu możemy utworzyć baz˛e danych dla naszej aplikacji, czyli wszystkie potrzebne tabele. Podczas tworzenia bazy Django pyta o nazw˛e, email i hasło administratora. Podajemy te dane po wydaniu w katalogu projektu w terminalu polecenia: ~/czat $ python manage.py syncdb Informacja: Domyślnie Django korzysta z bazy SQLite, która przechowywana jest w jednym pliku db.sqlite3 w katalogu aplikacji. Warto zobaczyć, jak wyglada. ˛ Potrzebny b˛edzie jednak interpreter, który w razie potrzeby doinstalujemy poleceniem apt-get install sqlite3. Nast˛epnie W terminalu wydajemy polecenie python manage.py dbshell, które uruchamia interpreter bazy. Nast˛epnie możemy wylistować utworzone tabele poleceniem .tables. Możemy również zobaczyć jakie instrukcje SQL-a zostały użyte do utworzenia naszej tabeli: .schema czat_wiadomosc. Z interpretera wychodzimy poleceniem .quit. 128 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 5.3.4 Panel administracyjny Django pozwala szybko utworzyć panel administratora dla projektu, dzi˛eki czemu b˛edziemy mogli zaczać ˛ dodawać użytkowików i wprowadzać dane. Tworzymy wi˛ec plik ~/czat/czat/admin.py i rejestrujemy w nim nasz model jako element panelu administracyjnego: 1 2 # -*- coding: utf-8 -*# czat/czat/admin.py 3 4 5 from django.contrib import admin from czat.models import Wiadomosc # importujemy nasz model 6 7 8 # rejestrujemy model Wiadomosc w panelu administracyjnym admin.site.register(Wiadomosc) Ostrzeżenie: Jeżeli korzystamy z Django w wersji 1.6 przed skorzystaniem z panelu administratora jako superużytkownik, musimy dodać lini˛e: admin.autodiscover() w pliku urls.py przed deklaracja˛ zmiennej urlpatterns. (Zobacz kod nr 7 poniżej.) Po ewentualnym ponownym uruchomieniu serwera wchodzimy na adres 127.0.0.1:8080/admin/. Logujemy si˛e podajac ˛ dane wprowadzone podczas tworzenia bazy. Otrzymamy dost˛ep do panelu administracyjnego, w którym możemy dodawać nowych użytkowników i wiadomości 3 . W panelu administratora widać, że etykiety oznaczajace ˛ pojedyncza˛ wiadomość jak i wiele wiadomości nie sa˛ spolszczone, podobnie pole daty oznaczone jest etykieta˛ “Data pub”. Aby w pełni spolszczyć nasz model, w pliku models.py dopisujemy: 1 2 # -*- coding: utf-8 -*# czat/czat/models.py 3 4 5 from django.db import models from django.contrib.auth.models import User 3 Bezpieczna aplikacja powinna dysponować osobnym mechanizmem rejestracji użytkowników i dodawania wiadomości, tak by nie trzeba było udost˛epniać panelu administracyjnego osobom postronnym. 5.3. Czat (wersja rozszerzona) 129 Materiały eCG IT Documentation, Wydanie 1 130 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 6 7 8 9 10 11 class Wiadomosc(models.Model): """Klasa reprezentujaca ˛ wiadomość w systemie""" tekst = models.CharField(u'wiadomość', max_length=250) data_pub = models.DateTimeField(u'data publikacji') autor = models.ForeignKey(User) 12 13 14 15 16 class Meta: # ustawienia dodatkowe verbose_name = u'wiadomość' # nazwa obiektu w j˛ ezyku polskim verbose_name_plural = u'wiadomości' # nazwa obiektów w l.m. ordering = ['data_pub'] # domyślne porzadkowanie ˛ danych Jak widać, w definicjach każdego pola jako pierwszy argument możemy dopisać spolszczona˛ etykiet˛e, np. u’data publikacji’. W podklasie Meta podajemy natomiast nazwy modelu w liczbie pojedynczej i mnogiej. Po odświeżeniu panelu adminitracyjnego (np. klawiszem F5) nazwy zostana˛ spolszczone. Informacja: Prefix u przed łańcuchami znaków oznacza kodowanie unikod (ang. unicode) umożliwiajace ˛ wyświetlanie m.in. znaków narodowych. Ćwiczenie 1 Po zalogowaniu na konto administratora dodajemy użytkownika “adam”. Na stronie szczegółów, która wyświetli si˛e po jego utworzeniu, zaznaczamy opcj˛e “W zespole”, nast˛epnie w panelu “Dost˛epne uprawnienia” zaznaczmy opcje dodawania (add), zmieniania (change) oraz usuwania (del) wiadomości (wpisy typu: “czat | wiadomosc | Can add wiadomosc”) i przypisujemy je użytkownikowi naciskajac ˛ strzałk˛e w prawo. Przelogowujemy si˛e na konto “adam” i dodajemy kilka przykładowych wiadomości. Jak można zauważyć, dodane wiadomości wyświetlaja˛ si˛e na liście jako “Wiadomosc object”. Aby to poprawić, 5.3. Czat (wersja rozszerzona) 131 Materiały eCG IT Documentation, Wydanie 1 uzupełniamy definicj˛e naszego modelu o funkcj˛e, której zadaniem jest “autoprezentacja”, czyli wyświetlenie treści wiadomości. W pliku models.py dopisujemy zachowujac ˛ wci˛ecia (!): def __unicode__(self): return self.tekst # "autoprezentacja" 18 19 5.3.5 Widoki i szablony Panel administracyjny już mamy, ale po wejściu na stron˛e główna˛ zwykły użytkownik niczego ciekawego poza standardowym powitaniem Django nie widzi. Zajmiemy si˛e teraz stronami po stronie (:-)) użytkownika. Dodawanie stron w Django polega na wykorzystywaniu widoków wbudowanych lub tworzeniu nowych. Sa˛ to klasy lub funkcje Pythona wykonujace ˛ jakieś operacje po stronie serwera w odpowiedzi na żadania ˛ klienta. Widoki powia˛ 132 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 zane sa˛ z określonymi adresami url. Widoki najcz˛eściej zwracaja˛ kod HTML wyrenderowany na podstawie szablonów, do których możemy przekazywać dodatkowe dane 4 , np. z bazy. Dla przejrzystości przyj˛eto, że w katalogu aplikacji (czat/czat): 1. plik views.py zawiera definicj˛e widoków, w tym wywołania szablonów, 2. plik url.py zawiera reguły łacz ˛ ace ˛ adresy url z widokami, 3. w katalogu czat/czat/templates/czat zapisujemy szablony pod nazwami określonymi w wywołuja˛ cych je widokach, np. index.html. Aby utworzyć stron˛e główna,˛ stworzymy pierwszy widok, czyli funkcj˛e index() 5 , która˛ powia˛żemy z adresem URL głównej strony (/). Najprostszy widok zwraca podany tekst. W pliku views.py umieszczamy: 1 2 # -*- coding: utf-8 -*# czat/czat/views.py 3 4 from django.http import HttpResponse 5 6 7 8 def index(request): """Strona główna aplikacji.""" return HttpResponse("Witaj w aplikacji Czat!") Informacja: Warto zapami˛etać, że każda˛ funkcj˛e, formularz czy widok udost˛epniane przez Django, których chcemy użyć, musimy najpierw zaimportować za pomoca˛ klauzuli typu from <skad> ˛ import <co>. W pliku urls.py przede wszystkim importujemy widoki naszej aplikacji: from czat import views. Nast˛epnie widok index() łaczymy ˛ z adresem URL strony głównej (/): 1 2 # -*- coding: utf-8 -*# czat/czat/urls.py 3 4 5 6 from django.conf.urls import patterns, include, url from django.contrib import admin from czat import views # importujemy zdefiniowane w pliku views.py widoki 7 8 admin.autodiscover() # potrzebne tylko w Django 1.6 9 10 11 urlpatterns = patterns('', url(r'^$', views.index, name='index'), 12 url(r'^admin/', include(admin.site.urls)), 13 14 ) Podstawowa˛ funkcja˛ jest tu url(), która jako pierwszy parametr przyjmuje wyrażenie regularne oznaczane skrótem r przed łańcuchem. Symbol ^ oznacza poczatek ˛ łańcucha, $ – koniec. Zapis u’^$’ to adres główny serwera. Drugi parametr wskazuje widok (funkcj˛e), która ma obsłużyć dany adres. Trzeci parametr name pozwala zapami˛etać skojarzenie url-a i widoku pod nazwa,˛ której b˛edzie można użyć np. do wygenerowania adresu linku. Skoro mamy widok i przypisaliśmy go do jakiegoś adresu URL, możemy przetestować działanie aplikacji. Po wywołaniu strony głównej powinniśmy zobaczyć tekst podany jako argument funkcji HttpResponse() w pliku views.py: Zazwyczaj odpowiedzia˛ na wywołanie jakiegoś adresu URL b˛edzie jednak jakaś strona zapisana w j˛ezyku HTML. Szablony takich stron umieszczamy w podkatalogu templates/nazwa aplikacji. Tworzymy wi˛ec katalog: 4 Dane z bazy przekazywane sa˛ do szablonów za pomoca˛ Pythonowego słownika. Renderowanie polega na odszukaniu pliku szablonu, zasta˛ pieniu przekazanych zmiennych danymi i odesłaniu całości (HTML + dane) do użytkownika. 5 Nazwa index() jest przykładowa, funkcja mogłaby si˛ e nazywać inaczej. 5.3. Czat (wersja rozszerzona) 133 Materiały eCG IT Documentation, Wydanie 1 ~/czat/czat$ mkdir -p templates/czat Nast˛epnie tworzymy szablon, czyli plik ~/czat/czat/templates/czat/index.html, który zawiera: 1 2 3 4 5 6 7 <!-- czat/czat/templates/czat/index.html --> <html> <head></head> <body> <h1>Witaj w aplikacji Czat!</h1> </body> </html> W pliku views.py zmieniamy instrukcje odpowiedzi: 1 2 # -*- coding: utf-8 -*# czat/czat/views.py 3 4 5 #from django.http import HttpResponse from django.shortcuts import render 6 7 8 9 10 def index(request): """Strona główna aplikacji.""" #return HttpResponse("Witaj w aplikacji Czat!") return render(request, 'czat/index.html') Po zaimportowaniu funkcji render() używamy jej do zwrócenia szablonu. Jako pierwszy argument podajemy parametr request, a jako drugi nazw˛e szablonu uwzgl˛edniajac ˛ a˛ katalog nadrz˛edny. Po wpisaniu adresu 127.0.0.1:8000/ zobaczymy tekst, który umieściliśmy w szablonie: 134 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 5.3.6 Rejestrowanie użytkowników Zamiast zakładać konta użytkownikom w panelu administracyjnym udost˛epnimy im możliwość samodzielnego rejestrowania. Zadanie to można zrealizować na dwa sposoby. Pierwszy polega na “r˛ecznym” utworzeniu formularza i obsłużeniu przesłanych danych w skojarzonym widoku (funkcji). Przygotujemy wi˛ec widok rejestruj(), który: 1. zwróci formularz przygotowany w szablonie rejestruj.html w odpowiedzi na żadanie ˛ typu GET, wysłane przez przegladark˛ ˛ e po wejściu użytkownika na stron˛e rejestracji pod adres URL /rejestruj; 2. obsłuży żadanie ˛ typu POST, czyli wysłanie danych z formularza na serwer, sprawdzi poprawność przesłanych danych (nazw˛e użytkownika i hasło) i utworzy konto; 3. zaloguje nowego użytkownika i przekieruje go na stron˛e główna.˛ Zaczynamy od uzupełnienia pliku views.py. Dodajemy widok rejestruj(): 7 8 9 10 11 def index(request): """Strona główna aplikacji.""" #return HttpResponse("Witaj w aplikacji Czat!") kontekst = {'user': request.user} return render(request, 'czat/index.html', kontekst) 12 13 14 15 16 from from from from django.shortcuts import redirect django.core.urlresolvers import reverse django.contrib.auth import authenticate, login, logout django.contrib import messages 17 18 19 20 def rejestruj(request): """Rejestracja nowego użytkownika.""" from django.contrib.auth.forms import UserCreationForm 21 22 23 24 25 26 27 28 29 30 31 32 if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): form.save() messages.success(request, "Zostałeś zarejestrowany.") user = authenticate( username=form.data['username'], password=form.data['password1']) login(request, user) messages.success(request, "Zostałeś zalogowany.") return redirect(reverse('index')) 33 34 35 kontekst = {'form': UserCreationForm()} return render(request, 'czat/rejestruj.html', kontekst) Powyższy kod pokazuje m .in., w jaki sposób przekazać dowolne dane do szablonu. Używamy słownika, w naszym przypadku nazwanego kontekst, który zostaje użyty jako trzeci argument funkcji render(). W ten sposób przekazujemy obiekt użytkownika w widoku strony głównej i formularz w widoku rejestracji. Warto także zauważyć, jak tworzymy komunikaty zwrotne dla użytkownika. Wykorzystujemy wbudowany w Django system komunikatów: messages.success(request, "Zostałeś zarejestrowany."). Tak utworzone komunikaty możemy odczytać w każdym szablonie ze zmiennej messages (zob. niżej szablon index.html). Tworzymy nowy szablon ~/czat/czat/templates/czat/rejestruj.html: 1 2 3 <!-- czat/czat/templates/czat/rejestruj.html --> <html> <body> 5.3. Czat (wersja rozszerzona) 135 Materiały eCG IT Documentation, Wydanie 1 4 5 6 7 8 9 10 11 <h1>Rejestracja użytkownika</h1> <form method="POST"> {% csrf_token %} {{ form.as_p }} <button type="submit">Zarejestruj</button> </form> </body> </html> Wia˛żemy adres URL rejestruj/ z utworzonym widokiem. Do pliku urls.py dopisujemy: 10 11 12 urlpatterns = patterns('', url(r'^$', views.index, name='index'), url(r'^rejestruj/', views.rejestruj, name='rejestruj'), 13 url(r'^admin/', include(admin.site.urls)), 14 15 ) Gdybyśmy już teraz odwiedzili adres 127.0.0.1:8000/rejestruj, powinniśmy zobaczyć poniższy formularz: Modyfikujemy również szablon strony głównej: 1 2 3 4 5 <!-- czat/czat/templates/czat/index.html --> <html> <head></head> <body> <h1>Witaj w aplikacji Czat!</h1> 6 {% if messages %} <ul> {% for komunikat in messages %} <li>{{ komunikat|capfirst }}</li> {% endfor %} 7 8 9 10 11 136 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 12 13 </ul> {% endif %} 14 15 16 17 18 19 20 21 {% if user.is_authenticated %} <p>Jesteś zalogowany jako {{ user.username }}.</p> {% else %} <p><a href="{% url 'rejestruj' %}">Zarejestruj si˛ e</a></p> {% endif %} </body> </html> W szablonach dost˛epne sa˛ podstawowe instrukcje sterujace, ˛ takie jak np. {% for %} czy {% if %}. T˛e pierwsza˛ wykorzystamy do wyświetlenia komunikatów użytkownikowi, druga˛ m. in. do sprawdzenia, czy stron˛e odwiedza użytkownik uwierzytelniony. Dane przekazane do szablonu możemy wyświetlać stosujac ˛ odpowiednia˛ notacj˛e, np.: {{ user.username }}. Dodatkowo wyświetlane dane można obrabiać za pomoca˛ filtrów, np. {{ komunikat|capfirst }} – w tym wypadku wszystkie komunikaty zostana˛ wyświetlone z wielkiej litery. W pliku index.html umieszczamy również link do strony rejestracji, który wyświetlany b˛edzie tylko użytkownikom niezalogowanym. Aby wygenerować adres strony w atrybucie href używamy funkcji url, za która˛ podajemy w cudzysłowach nazw˛e nadana˛ adresowi w pliku urls.py, np.: {% url ’rejestruj’ %}. Ćwiczenie 2 Po ewentualnym ponownym uruchomieniu serwera, zarejestruj nowego użytkownika o nazwie “ewa”. Powinieneś zobaczyć poniższa˛ stron˛e: W przegladarce ˛ wpisz adres 127.0.0.1:8000/rejestruj, aby przejść do strony rejestracji. Na stronie wyświetla si˛e formularz, mimo że jesteś już zarejestrowany i zalogowany. Spróbuj zmienić szablon rejestruj.html, tak aby zalogowanym użytkownikom wyświetlał si˛e tekst “Jesteś już zarejestrowany” oraz link do strony głównej, a niezalogowanym formularz rejestracji. Wskazówka: Wykorzystaj tag {% if warunek %} i obiekt user, tak jak zrobiliśmy to w widoku index() i dopisz odpowiedni kod w widoku rejestruj(). Przykładowy efekt poprawnego wykonania ćwiczenia: 5.3. Czat (wersja rozszerzona) 137 Materiały eCG IT Documentation, Wydanie 1 5.3.7 Logowanie i wylogowywanie Skoro użytkownicy moga˛ si˛e rejestrować, trzeba umożliwić im również logowanie i wylogowywanie z serwisu. Również to zadanie można zrobić dwojako. Pierwszy sposób to tak jak w przypadku rejestracji stworzenie widoków w pliku views.py i powiazanie ˛ ich z adresami w pliku urls.py. Na poczatku ˛ jak zawsze importujemy wymagane funkcje, później dopisujemy widok loguj() i wyloguj() w pliku views.py: 37 38 39 def loguj(request): """Logowanie użytkownika""" from django.contrib.auth.forms import AuthenticationForm 40 if request.method == 'POST': form = AuthenticationForm(request, request.POST) if form.is_valid(): login(request, form.get_user()) messages.success(request, "Zostałeś zalogowany!") return redirect(reverse('index')) 41 42 43 44 45 46 47 kontekst = {'form': AuthenticationForm()} return render(request, 'czat/loguj.html', kontekst) 48 49 50 51 52 53 54 55 def wyloguj(request): """Wylogowanie użytkownika""" logout(request) messages.info(request, "Zostałeś wylogowany!") return redirect(reverse('index')) Podobnie jak w przypadku rejestrowania użytkowników, korzystamy z wbudowanego w Django formularza logowania AuthenticationForm. Dzi˛eki temu nie musimy “r˛ecznie” sprawdzać poprawności podanych danych, robi to metoda is_valid() formularza. Jeżeli nie zwróci ona bł˛edu, możemy zalogować użytkownika za pomoca˛ funkcji login(), której przekazujemy obiekty HttpRequest (przesłane żadanie) ˛ i User – obiekt użytkownika zwrócony przez metod˛e get_user() formularza. Wylogowanie polega na użyciu funkcji logout(request) – wyloguje ona użytkownika, którego dane zapisane sa˛ w przesłanym żadaniu. ˛ Jak widać, do logowania potrzebujemy szablonu. Najprościej utworzyć go na podstawie szablonu rejestruj.html. Otwórzmy go i zapiszmy do pliku ~/czat/czat/templates/czat/loguj.html. Później wystarczy dostosować wywietlany tekst. Szablon z uwzgl˛ednieniem zmian wprowadzonych w ćwiczeniu 138 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 2. może wygladać ˛ tak: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <!-- czat/czat/templates/czat/loguj.html --> <html> <body> <h1>Logowanie użytkownika</h1> {% if user.is_authenticated %} <p>Jesteś już zalogowany jako {{ user.username }}. <br /><a href="/">Strona główna</a></p> {% else %} <form method="POST"> {% csrf_token %} {{ form.as_p }} <button type="submit">Zaloguj</button> </form> {% endif %} </body> </html> Pozostaje skojarzenie odpowiednich adresów URL z utworzonymi widokami. W pliku urls.py dopisujemy reguły: 10 11 12 13 14 urlpatterns = patterns('', url(r'^$', views.index, name='index'), url(r'^rejestruj/', views.rejestruj, name='rejestruj'), url(r'^loguj/', views.loguj, name='loguj'), url(r'^wyloguj/', views.wyloguj, name='wyloguj'), 15 url(r'^admin/', include(admin.site.urls)), 16 17 ) Możesz przetestować działanie dodanych funkcji wywołujac ˛ w przegladarce ˛ adresy: 127.0.0.1:8000/loguj i 127.0.0.1:8000/wyloguj. Przykładowy formularz wyglada ˛ tak: Informacja: Podsumujmy: działanie wszystkich omówionych do tej pory widoków jest podobne. Po przejściu pod adres określony w pliku urls.py, np. 127.0.0.1:8000/loguj/, wywoływany jest powiazany ˛ z nim widok zdefiniowany w pliku views.py, np. loguj(). Tego typu operacja generuje żadanie ˛ typu GET, w odpowiedzi na które zwracany jest szablon (np. loguj.html) wyświetlajacy ˛ przekazny do niego formularz (np. AuthenticationForm). Po wypełnieniu formularza użytkownik wciska odpowiedni przycisk, który inicjuje żadanietypu ˛ POST, a wi˛ec przesyłanie danych na serwer. Widoki rejestruj() i loguj() wychwytuja˛ i przetwarzaja˛ takie żadania, ˛ tj. rejestruja˛ 5.3. Czat (wersja rozszerzona) 139 Materiały eCG IT Documentation, Wydanie 1 lub loguja˛ użytkownika. W odpowiedzi użytkownik zostaje przekierowany z odopiwednim komunikatem na ston˛e główna.˛ Ćwiczenie 3 Adresów logowania i wylogowywania nikt w serwisach nie wpisuje r˛ecznie. Wstaw zatem odpowiednie linki do szablonu strony głównej. Użytkownik niezalogowany powinien zobaczyć odnośnik Zaloguj, użytkownik zalogowany – Wyloguj. Przykładowe strony moga˛ wygladać ˛ tak: 5.3.8 Widoki ogólne Zajmiemy sie teraz drugim sposobem stworzenia formularza rejestracji, logowania i wylogowania. Formularze te bowiem działaja,˛ ale nie do końca tak jak powinny. Spróbuj zarejestrować dodanego już użytkownika, albo przesłać niepełny formularz. Zauważysz, że nie dostajemy żadnej informacji o bł˛edach. Można oczywiście dopisać ich obsług˛e do odpowiednich widoków lub wygenerować je w szablonach, ale... wcale nie trzeba tego robić. W przypadku prostych aplikacji wystarcza˛ wbudowane w Django widoki ogólne (ang. generic views) i formularze. 140 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 Wszystko da si˛e zrobić w pliku urls.py, który zmieniamy nast˛epujaco: ˛ 10 11 12 from django.views.generic.edit import CreateView from django.contrib.auth.forms import UserCreationForm, AuthenticationForm from django.core.urlresolvers import reverse_lazy 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 urlpatterns = patterns('', url(r'^$', views.index, name='index'), #url(r'^rejestruj/', views.rejestruj, name='rejestruj'), #url(r'^loguj/', views.loguj, name='loguj'), #url(r'^wyloguj/', views.wyloguj, name='wyloguj'), url(r'^rejestruj/', CreateView.as_view( template_name='czat/rejestruj.html', form_class=UserCreationForm, success_url='/'), name='rejestruj'), url(r'^loguj/', 'django.contrib.auth.views.login', {'template_name': 'czat/loguj.html'}, name='loguj'), url(r'^wyloguj/', 'django.contrib.auth.views.logout', {'next_page': reverse_lazy('index')}, name='wyloguj'), 29 url(r'^admin/', include(admin.site.urls)), 30 31 ) Na poczatku ˛ importujemy widok przeznaczony do dodawania danych (CreateView), nast˛epnie formularz tworzenia użytkownika (UserCreationForm) i logowania (AuthenticationForm). Do generowania adresów url potrzebna b˛edzie również funkcja reverse_lazy(). Nast˛epnie zakomentowujemy dotychczasowe powiazania ˛ adresów i widoków. Dodajemy natomiast nowe. Do adresu /rejestruj przypisujemy wywołanie metody as_view() widoku ogólnego CreateView. Do obsłużenia adresów /loguj i /wyloguj używamy dedykowanych widoków login i logout. Na działanie widoków wpływaja˛ przekazywane im w różny sposób ustawienia właściwości, takie jak: • template_name – szablon, który zostanie użyty do zwrócenia odpowiedzi; • form_class – formularz, który zostanie przekazany do szablonu; • success_url – adres, na który nastapi ˛ przekierowanie w przypadku braku bł˛edów (np. po udanej rejestracji); • next_page – adres strony, na który nastapi ˛ przekierowanie użytkownika po wykonaniu żadanych ˛ akcji (np. udanym wylogowaniu). Pozostaje nam jeszcze określić stron˛e, na która˛ powinien zostać przekierowany użytkownik po udanym zalogowaniu. W tym wypadku na końcu pliku settings.py definujemy wartość zmiennej LOGIN_REDIRECT_URL: # czat/czat/settings.py from django.core.urlresolvers import reverse_lazy LOGIN_REDIRECT_URL = reverse_lazy('index') To wszystko. Zauważ, że funkcje rejestruj(), loguj() i wyloguj(), które umieściliśmy wczesniej w pliku views.py nie sa˛ już potrzebne! Przetestuj teraz działanie formularza rejestracji! Spróbuj dodać zarejestrowanego już użytkownika, wysłać pusty lub niekompletny formularz. 5.3.9 Obsługa wiadomości Chcemy, by zalogowani użytkownicy mogli przegladać ˛ wiadomości wszystkich użytkowników, zmieniać, usuwać i dodawać własne. Najprostszy sposób to skorzystanie z omówionych wyżej ogólnych widoków wbudowanych. 5.3. Czat (wersja rozszerzona) 141 Materiały eCG IT Documentation, Wydanie 1 Informacja: Django oferuje wbudowane widoki ogólne przeznaczone do typowych operacji: • DetailView i ListView – (ang. generic display view) widoki przeznaczone do prezentowania szczegółów i listy danych; • FormView, CreateView, UpdateView i DeleteView – (ang. generic editing views) widoki przeznaczone do wyświetlania formularzy ogólnych, w szczególności służacych ˛ dodawaniu, uaktualnianiu, usuwaniu obiektów (danych). Do wyświetlania listy wiadomości użyjemy klasy ListView. Do pliku urls.py dopisujemy importy: from django.contrib.auth.decorators import login_required from django.views.generic.list import ListView, DeleteView from czat.models import Wiadomosc – i wia˛żemy adres /wiadomosci z wywołaniem widoku: url(r'^wiadomosci/', login_required( ListView.as_view( model=Wiadomosc, context_object_name='wiadomosci', paginate_by=10), login_url='/loguj'), name='wiadomosci'), 32 33 34 35 36 37 38 Zakładamy, że wiadomości moga˛ ogladać ˛ tylko użytkownicy zalogowani. Dlatego całe wywołanie widoku umieszczamy w funkcji login_required(). Parametr login_url określa adres, na który przekierowany zostanie niezalogowany użytkownik. W wywołaniu ListView.as_view() wykorzystujemy kolejne właściwości modyfikujace ˛ działanie widoków: • model – podajemy model, którego dane zostana˛ pobrane z bazy; • context_object_name – pozwala zmienić domyślna˛ nazw˛e (object_list) listy obiektów przekazanych do szablonu; • paginate_by– pozwala ustawić ilość ilość obiektów wyświetlanych na stronie. Potrzebujemy jeszcze szablonu, którego Django szuka pod domyślna˛ nazwa˛ <nazwa modelu>_list.html, czyli w naszym przypadku tworzymy plik ~/czat/czat/templates/czat/wiadomosc_list.html: 1 2 3 4 <!-- czat/czat/templates/czat/wiadomosc_list.html --> <html> <body> <h1>Wiadomości</h1> 5 {% if messages %} <ul> {% for komunikat in messages %} <li>{{ komunikat|capfirst }}</li> {% endfor %} </ul> {% endif %} 6 7 8 9 10 11 12 13 <h2>Lista wiadomości:</h2> <ol> {% for wiadomosc in wiadomosci %} <li> <strong>{{ wiadomosc.autor.username }}</strong> ({{ wiadomosc.data_pub }}): <br /> {{ wiadomosc.tekst }} 14 15 16 17 18 19 142 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 20 21 22 </li> {% endfor %} </ol> 23 24 25 </body> </html> W tym momencie widok wiadomości powinien już działać. Przetestuj! Ćwiczenie 4 W szablonie listy wiadomości przed znacznikiem zamykajacym ˛ </body> dodaj link do strony głównej. Przykładowy efekt prezentujemy poniżej (zrzut zawiera również elementy, które dodamy później): Dodawanie wiadomości zrealizujemy wykorzystujac ˛ widok CreateView. Ponieważ nasz model wiadomości zawiera klucz obcy, mianowicie pole autor, tym razem dostosujemy klas˛e widoku w pliku views.py. Dzi˛eki temu b˛edziemy mogli rozszerzyć standardowa˛ funkcjonalność widoku. 58 59 60 from django.views.generic.edit import CreateView from czat.models import Wiadomosc from django.utils import timezone 61 62 63 64 class UtworzWiadomosc(CreateView): model = Wiadomosc fields = ['tekst', 'data_pub'] 5.3. Czat (wersja rozszerzona) 143 Materiały eCG IT Documentation, Wydanie 1 context_object_name = 'wiadomosci' success_url = '/wiadomosc' 65 66 67 def get_initial(self): initial = super(UtworzWiadomosc, self).get_initial() initial['data_pub'] = timezone.now() return initial 68 69 70 71 72 def get_context_data(self, **kwargs): kwargs['wiadomosci'] = Wiadomosc.objects.filter( autor=self.request.user) return super(UtworzWiadomosc, self).get_context_data(**kwargs) 73 74 75 76 77 def form_valid(self, form): wiadomosc = form.save(commit=False) wiadomosc.autor = self.request.user wiadomosc.save() return super(UtworzWiadomosc, self).form_valid(form) 78 79 80 81 82 Dostosowujac ˛ widok ogólny tworzymy oparta˛ na nim klas˛e UtworzWiadomosc. Z nieomówionych dotad ˛ ustawień widoku widzimy właściwość fields – pozwala ona określić w postaci listy pola, które maja˛ znaleźć si˛e na formularzu. Jak widać, pomijamy pole autor. Właśnie dlatego musimy nadpisać metod˛e form_valid(), która sprawdza poprawność przesłanych danych i zapisuje je w bazie. Żadanie ˛ POST otrzymane od użytkownika nie b˛edzie zawierało danych autora. Musimy je uzupełnić. Polecenie wiadomosc = form.save(commit=False) tworzy obiekt wiadomości, ale go nie zapisuje. Dzi˛eki temu w nast˛epnych instrukcjach możemy uzupełnić dane autora, po czym jeszcze raz zapisujemy wiadomość, tym razem w bazie. Metoda get_initail() pozwala ustawić domyślne wartości dla wybranych pól. Wykorzystujemy ja˛ do zainicjowania pola data_pub aktualna data.˛ Metoda get_context_data() z punktu widzenia dodawania wiadomości nie jest potrzebna, ale używamy jej po to, aby na jednej stronie obok formularza dodawania wiadomości wyświetlić ich list˛e. Inicjujemy ja˛ poleceniem Wiadomosc.objects.filter(autor=self.request.user) wybierajacym ˛ wiadomości utworzone przez zalogowanego użytkownika. Ćwiczenie 5 Podobnie jak wyżej potrzebujemy szablonu, który dla widoków dodawania domyślnie nazywaja˛ si˛e <nazwa modelu>_form. Na podstawie szablonu wiadomosc_list.html utwórz szablon wiadomosc_form.html>. Przed lista˛ wiadomości umieść kod wyświetlajacy ˛ formularz: <h2>Dodaj wiadomość</h2> <form method="POST"> {% csrf_token %} {{ form.as_p }} <button type="submit">Zapisz</button> </form> 14 15 16 17 18 19 Dodamy teraz dwa widoki przeznaczone do aktualizaowania i usuwania wpisów. Zakładamy, że adresy do tych operacji b˛eda˛ miały postać: /aktualizuj/id_wiadomości oraz /usun/id_wiadomości, gdzie id_wiadomosci jest identyfikatorem obiektu do usuni˛ecia. Tym razem zaczniemy od zmian w pliku urls.py: url(r'^aktualizuj/(?P<pk>\d+)/', login_required( views.AktualizujWiadomosc.as_view(), login_url='/loguj'), name='aktualizuj'), url(r'^usun/(?P<pk>\d+)/', login_required( 42 43 44 45 144 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 5.3. Czat (wersja rozszerzona) 145 Materiały eCG IT Documentation, Wydanie 1 DeleteView.as_view( model=Wiadomosc, template_name='czat/wiadomosc_usun.html', success_url='/wiadomosci'), login_url='/loguj'), name='usun'), 46 47 48 49 50 Nowościa˛ w powyższym kodzie sa˛ wyrażenia regularne definiujace ˛ adresy z dodatkowym parametrem, np. r’^aktualizuj/(?P<pk>\d+)/’. Cz˛eść /(?P<pk>\d+) oznacza, że oczekujemy liczby dziesi˛etnej, która zostanie zapisana do zmiennej o nazwie pk – nazwa jest tu skrótem od ang. wyrażenia primary key, co znaczy “klucz główny”. Zmienna ta oznaczać b˛edzie identyfikator wiadomości i dost˛epna b˛edzie w widokach. Usuwanie danych realizujemy za pomoca˛ widoku DeleteView, który powinien być już zaimportowany razem z widokiem ListView. Domyślny szablon dla tego widoku przyjmuje nnazw˛e <nazwa-modelu>_confir_delete.html, dlatego uproścliśmy jego nazw˛e we właściwości template_name. Ćwiczenie 6 Utwórz szablon wiadomosc_usun.html wzorujac ˛ sie na wcześniejszych szablonach. Zamiast instrukcji wyświetlajacej ˛ formularz umieść kod: <p>Czy na pewno chcesz usunać ˛ wiadomość:<br />"{{ object }}"?</p> 17 Nieco wi˛ecej pracy wymaga dostosowanie widoku aktualizacji. AktualizujWiadomosc oparta˛ na widoku ogólnym UpdateView: 85 W pliku views.py utworzymy klas˛e from django.views.generic.edit import UpdateView 86 87 88 89 class AktualizujWiadomosc(UpdateView): model = Wiadomosc from czat.forms import AktualizujWiadomoscForm 146 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 90 91 92 93 form_class = AktualizujWiadomoscForm context_object_name = 'wiadomosci' template_name = 'czat/wiadomosc_form.html' success_url = '/wiadomosci' 94 95 96 97 98 def get_context_data(self, **kwargs): kwargs['wiadomosci'] = Wiadomosc.objects.filter( autor=self.request.user) return super(AktualizujWiadomosc, self).get_context_data(**kwargs) 99 100 101 102 def get_object(self, queryset=None): wiadomosc = Wiadomosc.objects.get(id=self.kwargs['pk']) return wiadomosc Formularz generowany przez Django dla danego widoku można dostosować. Odpowiada za to właściwość form_class, której przypisujemy utworzona˛ w nowym pliku forms.py klas˛e modyfikujac ˛ a˛ domyślne ustawienia: 1 # -*- coding: utf-8 -*- 2 3 4 from django.forms import ModelForm, TextInput from czat.models import Wiadomosc 5 6 7 8 9 10 11 class AktualizujWiadomoscForm(ModelForm): class Meta: model = Wiadomosc fields = ['tekst', 'data_pub'] exclude = ['autor'] widgets = {'tekst': TextInput(attrs={'size': 80})} Klasa AktualizujWiadomoscForm oparta jest na wbudowanej klasie ModelForm. Właściwości podklasy Meta pozwalaja˛ określić cechy formularza wyświetlanego przez widok, który go wykorzystuje: • model – oznacza to samo co w widokach, czyli model, dla którego tworzony jest formularz; • fields – to samo co w widokach, lista pól do wyświetlenia; • exclude – opcjonalnie lista pól do pomini˛ecia; • widgets – słownik, którego klucze oznaczaja˛ pola danych, a ich wartości odpowiadajace ˛ im w formularza HTML typy pól i ich właściwości, np. rozmiar. Na wyjaśnienie zasługuje jeszcze metoda get_object() widoku aktualizacji. Jej zadanie to wybranie z bazy danych i zwrócenie do formularza wiadomości, której identyfikator został przekazany w adresie pod nazwa˛ pk: wiadomosc = Wiadomosc.objects.get(id=self.kwargs[’pk’]). Ćwiczenie 7 Żeby przetestować aktualizowanie i usuwanie wiadomości, w szablonie wiadomosc_list.html wygeneruj linki Edytuj i Usuń tylko dla wiadomości utworzonych przez zalogowanego użytkownika. Wstaw w odpowiednie miejsce szablonu poniższy kod: 20 21 22 23 {% if wiadomosc.autor.username == user.username %} <a href="{% url 'aktualizuj' wiadomosc.id %}">Edytuj</a> • <a href="{% url 'usun' wiadomosc.id %}">Usuń</a> {% endif %} Dodaj również te same linki do listy wiadomości na stronach dodawania i aktualizowania. 5.3. Czat (wersja rozszerzona) 147 Materiały eCG IT Documentation, Wydanie 1 148 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 5.3.10 Wiadomości jeszcze raz Dodawanie wiadomości można zrealizować bez wbudowanych widoków ogólnych. Potrzebować b˛edziemy widoku o nazwie np. wiadomosci() do wyświetlania (żadania ˛ GET) i dodawania wiadomości (żadania ˛ POST), który zwracał b˛edzie szablon np. wiadomosci.html. Widok ten powia˛żemy z adresem /wiadomosci. Do pliku views.py dodajemy importy i kod funkcji: 105 from django.contrib.auth.decorators import login_required 106 107 108 109 @login_required(login_url='/loguj') def wiadomosci(request): """Dodawanie i wyświetlanie wiadomości""" 110 111 112 113 114 115 116 117 118 119 120 121 if request.method == 'POST': tekst = request.POST.get('tekst', '') if not 0 < len(tekst) <= 250: messages.error(request, "Wiadomość nie może być pusta, może mieć maks. 250 znaków!") else: wiadomosc = Wiadomosc(tekst=tekst, data_pub=timezone.now(), autor=request.user) wiadomosc.save() return redirect(reverse('wiadomosci')) 122 123 124 125 wiadomosci = Wiadomosc.objects.all() kontekst = {'user': request.user, 'wiadomosci': wiadomosci} return render(request, 'czat/wiadomosci.html', kontekst) Widać powyżej, że treść przesłanej wiadomości wydobywamy ze słownika request.POST za pomoca˛ metody get(’tekst’, ’’). Jej pierwszy argument odpowiada nazwie pola formularza użytej w szablonie, które chcemy odczytać. Drugi argument oznacza wartość domyślna,˛ przydatna,˛ jeśli pole b˛edzie niedost˛epne. Po sprawdzeniu długości wiadomości, możemy ja˛ utworzyć wykorzystujac ˛ konstruktor naszego modelu Wiadomosc(tekst=tekst, data_pub=timezone.now(), autor=request.user). W formie nazwanych argumentów podajemy mu wartości kolejnych pól. Zapisanie nowej wiadomości w bazie sprowadza si˛e do polecenia wiadomosc.save(). Na koniec przekierowujemy użytkownika do tego samego widoku, ale tym razem jest to żadanie ˛ typu GET. W odpowiedzi na nie pobieramy wszystkie wiadomości z bazy (Wiadomosc.objects.all()), i przekazujemy do szablonu, który zwracamy użytkownikowi. Zadaniem szablonu zapisanego w pliku ~/czat/czat/templates/wiadomosci.html jest wyświetlenie komunikatów zwrotnych, formularza dodawania wiadomości i listy wiadomości dodanych. 1 2 3 4 <!-- czat/czat/templates/czat/wiadomosci.html --> <html> <body> <h1>Wiadomości</h1> 5 6 7 8 9 10 11 12 {% if messages %} <ul> {% for komunikat in messages %} <li>{{ komunikat|capfirst }}</li> {% endfor %} </ul> {% endif %} 13 14 15 16 <h2>Dodaj wiadomość</h2> <form method="POST"> {% csrf_token %} 5.3. Czat (wersja rozszerzona) 149 Materiały eCG IT Documentation, Wydanie 1 <input type="text" name="tekst" /> <button type="submit">Zapisz</button> </form> 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <h2>Lista wiadomości:</h2> <ol> {% for wiadomosc in wiadomosci %} <li> <strong>{{ wiadomosc.autor.username }}</strong> ({{ wiadomosc.data_pub }}): <br /> {{ wiadomosc.tekst }} </li> {% endfor %} </ol> </body> </html> Ćwiczenie 8 Powia˛ż widok wiadomosci() z adresem /wiadomosci w pliku urls.py, nadajac ˛ mu nazw˛e wiadomosci, a nast˛epnie uzupełnij szablon widoku głównego, aby zalogowanym użytkownikom wyświetlał si˛e link prowadzacy ˛ do strony z wiadomościami. W szablonie wiadomosci.html dodaj link do strony głównej i link wylogowania. Wskazówka: Definicje w pliku urls.py sprawdzane sa˛ po kolei, zwracany jest widok przypisany pierwszemu napotaknemu dopasowaniu adresu. Jeżeli chcemy przetestować działanie widoku wiadomosci() dla wykorzystanego już adresu /wiadomosci przypisanego wbudowanemu widokowi ListView, powiazanie ˛ należy umieścić przed nim, np. na poczatku. ˛ Zaloguj si˛e i przetestuj wyświetlanie 6 i dodawanie wiadomości pod adresem 127.0.0.1:8000/wiadomosci/. Sprawdź, co si˛e stanie po wysłaniu pustej wiadomości. Poniższe zrzuty prezentuja˛ efekty naszej pracy: 5.3.11 Szablony Zapewne zauważyłeś, że wi˛ekszość kodu w szablonach, a zatem i w stronach HTML, które z nich powstaja,˛ si˛e powtarza, albo jest bardzo podobna. Biorac ˛ pod uwag˛e schematyczna˛ budow˛e stron WWW jest to nieuniknione. Django dysponuje wbudowanym silnikiem szablonów, który ułatwia ich tworzenie. Szablony, jak można było zauważyć, składaja˛ si˛e ze zmiennych i tagów. Zmienne, które ujmowane sa˛ w podwójne nawiasy sześciokatne ˛ {{ zmienna }}, zast˛epowane sa˛ konkretnymi wartościami. Tagi z kolei, oznaczane notacja˛ {% tag %} tworza˛ mini-j˛ezyk szablonów i pozwalaja˛ kontrolować logik˛e budowania treści. Najważniejsze tagi, {% if warunek %}, {% for wyrażenie %}, {% url nazwa %} – już stosowaliśmy. Spróbujmy uprościć i ujednolicić nasze szablony. Zacznijmy od szablonu bazowego, który umieścimy w pliku ~/czat/czat/templates/czat/baza.html: 1 2 3 4 5 6 7 8 <!-- czat/czat/templates/czat/baza.html --> <!DOCTYPE html> <html lang="pl"> <meta charset="utf-8" /> <head> <title>{% block tytul %} System wiadomości Czat {% endblock tytul %}</title> </head> <body> 6 Jeżeli nie dodałeś do tej pory żadnej wiadomości, lista na poczatku ˛ b˛edzie pusta. 150 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 5.3. Czat (wersja rozszerzona) 151 Materiały eCG IT Documentation, Wydanie 1 <h1>{% block naglowek %} Witaj w aplikacji Czat! {% endblock %}</h1> 9 10 {% block komunikaty %} {% if messages %} <ul> {% for komunikat in messages %} <li>{{ komunikat|capfirst }}</li> {% endfor %} </ul> {% endif %} {% endblock %} 11 12 13 14 15 16 17 18 19 20 {% block tresc %} {% endblock %} 21 22 {% if user.is_authenticated %} {% block linkilog %} {% endblock %} <p><a href="{% url 'wyloguj' %}">Wyloguj si˛ e</a></p> {% else %} {% block linkizalog %} {% endblock %} {% endif %} 23 24 25 26 27 28 29 30 31 32 {% block linki %} {% endblock %} </body> </html> Jest to zwykły tekstowy dokument, zawierajacy ˛ schemat strony utworzony z wymaganych znaczników HTML oraz bloki zdefiniowane za pomoca˛ tagów mini-j˛ezyka szablonów. W pliku tym umieszczamy stała˛ i wspólna˛ struktur˛e stron w serwisie (np. nagłówek, menu, sekcja treści, stopka itp.) oraz wydzielamy bloki, których treść b˛edzie można zmieniać w szablonach konkretnych stron. Wykorzystujac ˛ szablon podstawowy, zmieniamy stron˛e główna,˛ czyli plik index.html: 1 2 <!-- czat/czat/templates/czat/index.html --> {% extends "czat/baza.html" %} 3 {% block naglowek %}Witaj w aplikacji Czat!{% endblock %} 4 5 {% block linkilog %} <p>Jesteś zalogowany jako {{ user.username }}.</p> <p><a href="{% url 'wiadomosc' %}">Dodaj wiadomość</a></p> <p><a href="{% url 'wiadomosci' %}">Lista wiadomości</a></p> {% endblock %} 6 7 8 9 10 11 {% block linkizalog %} <p><a href="{% url 'loguj' %}">Zaloguj si˛ e</a></p> <p><a href="{% url 'rejestruj' %}">Zarejestruj si˛ e</a></p> {% endblock %} 12 13 14 15 Jak widać, szablon dziedziczy z szablonu bazowego. Odpowiada za to tag {% extends plik_bazowy %}. Dalej pomijamy strukturalne znaczniki HTML zdefiniowane w bazie, zast˛epujemy natomiast zawartość bloków, które uznajemy za potrzebne na danej stronie. Post˛epujac ˛ na tej samej zasadzie modyfikujemy szablon rejestracji: 1 2 <!-- czat/czat/templates/czat/rejestruj.html --> {% extends "czat/baza.html" %} 3 {% block naglowek %}Rejestracja użytkownika{% endblock %} 4 5 152 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 6 7 8 9 10 11 12 13 14 {% block tresc %} {% if not user.is_authenticated %} <form method="POST"> {% csrf_token %} {{ form.as_p }} <button type="submit">Zarejestruj</button> </form> {% endif %} {% endblock %} 15 16 17 18 {% block linkilog %} <p>Jesteś już zarejestrowany jako {{ user.username }}.</p> {% endblock %} 19 20 21 22 {% block linki %} <p><a href="{% url 'index' %}">Strona główna</a></p> {% endblock %} Ćwiczenie 9 Wzorujac ˛ si˛e na podanych przykładach zmień pozostałe szablony tak, aby opierały si˛e na szablonnie bazowym. Wyglad ˛ stron nie powinien ulec zmianie! 5.3.12 Style, skrpyty, pliki Nasze szablony zyskały na zwi˛ezłości i przejrzystości, ale nadal pozbawione sa˛ elementarnych dla dzisiejszych stron WWW zasobów, takich jak style CSS, skrypty JavaScript czy zwykłe obrazki. Jak je dołaczyć? ˛ Przede wszystkim potrzebujemy osobnego katalogu ~czat/czat/static/czat. W terminalu w katalogu projektu (!) wydajemy polecenie: ~/czat $ mkdir -p czat/static/czat W powyższym katalogu tworzy si˛e zazwyczaj podkatalogi dla różnych typów zasobów, np. css, js czy img. Sugerujemy, żeby je utworzyć. Tworzymy również przykładowy plik ~/czat/czat/static/czat/css/style.css: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 body { margin: 1em; padding: 0; font-family: Helvetica, Arial, sans-serif; font-size: 12pt; background: lightgreen url('../img/django.png') no-repeat fixed top right; } a { text-decoration: none; } a:hover { text-decoration: underline; } a:visited { text-decoration: none; } .clearfix { clear: both; } h1 { font-size: 1.8em; font-weight: bold; margin-top: 20px;} h2 { font-size: 1.4em; font-weight: bold; margin-top: 20px;} p { font-szie: 1em; font-family: Arial, sans-serif; } .fr { float: right; } 5.3. Czat (wersja rozszerzona) 153 Materiały eCG IT Documentation, Wydanie 1 Do podkatalogu ~/czat/czat/static/czat/img wrzucamy obrazki z podanego archiwum. Wreszcie zmieniamy szablon podstawowy: 1 {% load staticfiles %}<!DOCTYPE html> 2 <link rel="stylesheet" type="text/css" href="{% static 'czat/css/style.css' %}" /> 3 4 <div id="obrazki"> <img src="{% static 'czat/img/python.png' %}" width="300" /> <img src="{% static 'czat/img/sqlite.png' %}" width="300" /> </div> 5 6 7 8 Kod z linii 1. {% load staticfiles %} umieszczamy na samym poczatku ˛ dokumentu. Konfiguruje on ścieżk˛e do zasobów. Kod z linii 3. wklejamy za znacznikiem <title> w sekcji <head>. Ilustruje on, jak dołaczamy ˛ style czy skrypty, mianowicie używamy tagu {% static plik %}. Wreszcie kod z linii 5-8 zawierajacy ˛ znacznik <div> z obrazkami wstawiamy na końcu pliku przed znacznikiem zamykajacym ˛ </body>. Widzimy tu, jak wstawiać obrazki. Wyglad ˛ strony głównej po zmianach: Ćwiczenie 10 W szablonie bazowym stwórz osobny block umożliwiajacy ˛ zast˛epowanie wstawionych obrazków. Zmień dowolny szablon inny niż strona główna tak, aby wyświetlał inne obrazki. Pami˛etaj o zapisaniu dodatkowych obrazków do odpowiedniego katalogu! Wskazówka: Tag {% load staticfiles %} musisz wstawić do każdego szablonu (w dowolnym miejscu), w którym chcesz odwoływać si˛e do plików z katalogu static. 154 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 Na powyższym zrzucie widać wykonane ćwiczenie, czyli użycie dodatkowych obrazków. Jednak strona nie wyglada ˛ dobrze, ponieważ treść podpowiedzi nachodzi na logo Django (oczywiście przy małym rozmiarze okna przegladarki). ˛ Spróbujemy temu zaradzić. Wykorzystamy prosty skrypt JQuery. Na poczatku ˛ ściagnij ˛ bibliotek˛e i skrypt z podanego archiwum. Rozpakuj pliki do katalogu static/js. Nast˛epnie do szablonu podstawowego baza.html dodaj przed tagiem zamykajacym ˛ </body> kod: <script src="{% static 'czat/js/jquery.js' %}"></script> <script src="{% static 'czat/js/czat.js' %}"></script> 1 2 Po odświeżeniu strony powinnieneś zobaczyć poprawiona˛ stron˛e: cdn. 5.3.13 Materiały Słownik aplikacja program komputerowy. 5.3. Czat (wersja rozszerzona) 155 Materiały eCG IT Documentation, Wydanie 1 156 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 framework zestaw komponentów i bibliotek wykorzystywany do budowy aplikacji, przykładem jest biblioteka Pythona Flask. HTML j˛ezyk znaczników wykorzystywany do formatowania dokumentów, zwłaszcza stron WWW. CSS j˛ezyk służacy ˛ do opisu formy prezentacji stron WWW. HTTP protokół przesyłania dokumentów WWW. GET typ żadania ˛ HTTP, służacy ˛ do pobierania zasobów z serwera WWW. POST typ żadania ˛ HTTP, służacy ˛ do umieszczania zasobów na serwerze WWW. logowanie proces autoryzacji i uwierzytelniania użytkownika w systemie. ORM mapowanie obiektowo-relacyjne, oprogramowanie służace ˛ do przekształcania struktur bazy danych na obiekty klasy danego j˛ezyka oprogramowania. serwer deweloperski (testowy) serwer www używany w czasie prac nad oprogramowaniem. serwer WWW serwer obsługujacy ˛ protokół HTTP. baza danych program przeznaczony do przechowywania i przetwarzania danych. szablon wzorzec (nazywany czasem templatka) ˛ strony WWW wykorzystywany do renderowania widoków. URL ustandaryzowany format adresowania zasobów w internecie (przykład). model schematy i źródła danych aplikacji. widok we Flasku lub Django jest to funkcja, która obsługuje żadania ˛ wysyłane przez użytkownika i na ich podstawie przygotowuje dane zwracane do przegladarki, ˛ najcz˛eściej w formie strony WWW. kontroler logika aplikacji, w Django zawarta w funkcji obsługujacej ˛ widok. 1. O Django http://pl.wikipedia.org/wiki/Django_(informatyka) 2. Strona projektu Django https://www.djangoproject.com/ 3. Co to jest framework? http://pl.wikipedia.org/wiki/Framework 4. Co nieco o HTTP i żadaniach ˛ GET i POST http://pl.wikipedia.org/wiki/Http Źródła • czat_pr.zip Metryka Autor Robert Bednarz Utworzony 2015-07-18 o 04:24 5.4 Pythonizmy 5.4. Pythonizmy 157 Materiały eCG IT Documentation, Wydanie 1 • • • • • • • • Operatory * i ** P˛etle Iteratory Generatory wyrażeń Wyrażenia listowe Generatory Pliki Materiały Python jest j˛ezykiem wydajnym i zwartym dzi˛eki wbudowanym mechanizmom ułatwiajacym ˛ wykonywanie typowych i cz˛estych zadań programistycznych. Podane niżej przykłady należy przećwiczyć w konsoli Pythona, która˛ uruchamiamy poleceniem w terminalu: ~$ python 5.4.1 Operatory * i ** Operator * służy rozpakowaniu listy zawierajacej ˛ wiele argumentów, które chcemy przekazać do funkcji: 1 2 3 # wygeneruj liczby parzyste od 2 do 10 lista = [2,11,2] range(*lista) Operator ** potrafi z kolei rozpakować słownik, dostarczajac ˛ funkcji nazwanych argumentów (ang. keyword argument): 1 2 def kalendarz(data, wydarzenie): print "Data:", data,"\nWydarzenie:", wydarzenie 3 4 5 slownik = {"data" : "10.02.2015", "wydarzenie" : "szkolenie"} kalendarz(**slownik) 5.4.2 P˛etle P˛etla to podstawowa konstrukcja wykorzystywana w j˛ezykach programowania. Python oferuje różne sposoby powtarzania wykonywania określonych operacji, niekiedy wygodniejsze lub zwi˛eźlejsze niż p˛etle. Sa˛ to przede wszystkim generatory wyrażeń i wyrażenia listowe, a także funkcje map() i filter(). 1 2 3 kwadraty = [] for x in range(10): kwadraty.append(x**2) 5.4.3 Iteratory Obiekty, z których p˛etle odczytuja˛ kolejne dane to iteratory (ang. iterators) Reprezentuja˛ one strumień danych, z którego zwracaja˛ tylko jedna˛ kolejna˛ wartość na raz za pomoca˛ metody __next()__. Jeżeli w strumieniu nie ma wi˛ecej danych, wywoływany jest wyjatek ˛ StopIteration. Wbudowana funkcja iter() zwraca iterator utworzony z dowolnego iterowalnego obiektu. Iteratory wykorzystujemy do przegladania ˛ list, tupli, słowników czy plików używajac ˛ instrukcji for x in y, w której y jest obiektem iterowalnym równoważnym wyrażeniu iter(y). Np.: 158 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 1 2 3 lista = [2, 4, 6] for x in lista: print x 4 5 6 7 slownik = {'Adam':1, 'Bogdan':2 , 'Cezary':3} for x in slownik: print(x, slownik(x)) List˛e (czyli obiekt iterowalny), zawierajac ˛ a˛ tuple (klucz, wartość) można wykorzystać do utworzenia słownika, np.: 1 2 lista = [('Polska','Warszawa'), ('Berlin','Niemcy'), ('Francja','Paryż')] dict(lista) 5.4.4 Generatory wyrażeń Jeżeli chcemy wykonać jakaś ˛ operacj˛e na każdym elemencie sekwencji lub wybrać podzespół elementów spełniajacy ˛ określone warunki, stosujemy generatory wyrażeń (ang. generator expressions), które zwracaja˛ iteratory. Poniższy przykład wydrukuje wszystkie imiona z dużej litery: 1 2 3 4 wyrazy = ['anna', 'ala', 'ela', 'wiola', 'ola'] imiona = (imie.capitalize() for imie in wyrazy) for imie in imiona: print imie Schemat składniowy generatora jest nast˛epujacy: ˛ ( wyrażenie for wyr in sekwencja if warunek ) – przy czym: • wyrażenie – powinno zawierać zmienna˛ z p˛etli for • if warunek – klauzula ta jest opcjonalna i działa jak filtr eliminujacy ˛ wartości nie spełniajace ˛ warunku Gdybyśmy chcieli wybrać tylko imiona 3-literowe w wyrażeniu, użyjemy wspomnianej opcjonalnej klauzuli if warunek: 1 imiona = (imie.capitalize() for imie in wyrazy if len(imie) == 3) Omawiane wyrażenia można zagnieżdzać. Przykłady podajemy niżej. 5.4.5 Wyrażenia listowe Jeżeli nawiasy okragłe ˛ w generatorze wyrażeń zamienimy na kwadratowe dostaniemy wyrażenie listowe (ang. list comprehensions), które – jak wskazuje nazwa – zwraca list˛e: 1 2 # wszystkie poniższe wyrażenia listowe możemy przypisać do zmiennych, # aby móc później korzystać z utworzonych list 3 4 5 # lista kwadratów liczb od 0 do 9 [x**2 for x in range(10)] 6 7 8 9 # lista dwuwymiarowa [20,40] o wartościach a a = int(raw_input("Podaj liczb˛ e całkowtia: ˛ ")) [[a for y in xrange(20)] for x in xrange(40)] 10 11 12 # lista krotek (x, y), przy czym x != y [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y] 13 14 # utworzenie listy 3-literowych imion i ich pierwszych liter 5.4. Pythonizmy 159 Materiały eCG IT Documentation, Wydanie 1 15 16 wyrazy = ['anna', 'ala', 'ela', 'wiola', 'ola'] [ [imie, imie[0]] for imie in wyrazy if len(imie) == 3 ] 17 18 19 20 # zagnieżdzone wyrażenie listowe tworzace ˛ list˛ e współrz˛ ednych # opisujacych ˛ tabel˛ e [ (x,y) for x in range(5) for y in range(3) ] 21 22 23 24 # zagnieżdzone wyrażenie listowe wykorzystujace ˛ filtrowanie danych # lista kwadratów z zakresu {5;50} [ y for y in [ x**2 for x in range(10) ] if y > 5 and y < 50 ] Wyrażenia listowe w elegancki i wydajny sposób zast˛epuja˛ takie rozwiazania, ˛ jak: • p˛etla • mapowanie funkcji • wyrażenia lambda • filtrowanie danych Mapowanie funkcji Funkcja map() funkcj˛e podana˛ jako pierwszy argument stosuje do każdego elementu sekwencji podanej jako argument drugi: 1 2 def kwadrat(x): return x**2 3 4 kwadraty = map(kwadrat, range(10)) Wyrażenia lambda Słowo kluczowe lambda pozwala utworzyć zwi˛ezły odpowiednik prostej, jednowyrażeniowej funkcji. Poniższy przykład należy rozumieć nast˛epujaco: ˛ do każdej liczby wygenerowanej przez funkcj˛e range() zastosuj funkcj˛e w postaci wyrażenia lambda podnoszac ˛ a˛ wartość do kwadratu, a uzyskane wartości zapisz w liście kwadraty. 1 kwadraty = map(lambda x: x**2, range(10)) Funkcje lambda cz˛esto stosowane sa˛ w poleceniach sortowania jako wyrażenie zwracajace ˛ klucz (wartość), wg którego maja˛ zostać posortowane elementy. Jeżeli np. mamy list˛e tupli opisujac ˛ a˛ uczniów: 1 2 3 4 5 6 uczniowie = [ ('jan','Nowak','1A',15), ('ola','Kujawiak','3B',17), ('andrzej','bilski','2F',16), ('kamil','czuja','1B',14) ] – wywołanie sorted(uczniowie) zwróci nam list˛e posortowana˛ wg pierwszego elementu każdej tupli, czyli imienia. Jeżeli jednak chcemy sortować wg np. klasy, użyjemy parametru key, który przyjmuje jednoargumentowa˛ funkcj˛e zwracajac ˛ a˛ odpowiedni klucz do sortowania, np.: sorted(uczniowie, key=lambda x: x[2]). W funkcjach min(), max() podobnie używamy wyrażeń lambda jako argumentu parametru key, aby wskazać wartości, dla których wyszukujemy minimum i maksimum, np.: max(uczniowie, key=lambda x: x[3]) – zwróci najstarszego ucznia. 160 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 Filtrowanie danych Funkcja filter() jako pierwszy argument pobiera funkcj˛e zwracajac ˛ a˛ True lub False, stosuje ja˛ do każdego elementu sekwencji podanej jako argument drugi i zwraca tylko te, które spełniaja˛ założony warunek: 1 2 wyrazy = ['anna', 'ala', 'ela', 'wiola', 'ola'] imiona = filter(lambda imie: len(imie) == 3, wyrazy) 5.4.6 Generatory Generatory (ang. generators) to funkcje ułatwiajace ˛ tworzenie iteratorów. Od zwykłych funkcji różnia˛ si˛e tym, że: • zwracaja˛ iterator za pomoca˛ słowa kluczowego yield, • zapami˛etuja˛ swój stan z momentu ostatniego wywołania, sa˛ wi˛ec wznawialne (ang. resumable), • zwracaja˛ nast˛epna˛ wartość ze strumienia danych podczas kolejnych wywołań metody next(). Najprostszy przykład generatora zwracajacego ˛ kolejne liczby parzyste: def gen_parzyste(N): for i in range(N): if i % 2 == 0 yield i gen = gen_parzyste(10) gen.next() gen.next() ... 5.4.7 Pliki Przećwicz alternatywne sposoby otwierania plików: 1 2 3 4 f = open('test.txt', 'r') for line in f: print line[0] f.close() 5 6 7 8 with open("text.txt", "r") as txt: for line in txt: print line 9 10 11 for line in open('test.txt', 'r'): print line[0] 5.4.8 Materiały 1. http://pl.wikibooks.org/wiki/Zanurkuj_w_Pythonie 2. http://brain.fuw.edu.pl/edu/TI:Programowanie_z_Pythonem 3. http://pl.python.org/docs/tut/ 4. http://en.wikibooks.org/wiki/Python_Programming/Input_and_Output 5. https://wiki.python.org/moin/HandlingExceptions 5.4. Pythonizmy 161 Materiały eCG IT Documentation, Wydanie 1 6. http://learnpython.org/pl 7. http://www.checkio.org 8. http://www.codecademy.com 9. https://www.coursera.org 5.5 Słownik Pythona j˛ezyk interpretowany j˛ezyk, który jest tłumaczony i wykonywany “w locie”, np. Python lub PHP. Tłumaczeniem i wykonywaniem programu zajmuje si˛e specjalny program nazwany interpreterem j˛ezyka. interpreter program, który analizuje kod źródłowy, a nast˛epnie go wykonuje. Interpretery sa˛ podstawowym składnikiem j˛ezyków wykorzystywanych do pisania skryptów wykonywanych po stronie klienta WWW (JavaScript) lub serwera (np. Python, PHP). Interpreter Pythona jest interaktywny, tzn. można w nim wydawać polecenia i obserwować ich działanie, co pozwala wygodnie uczyć si˛e i testować oprogramowanie. Uruchamiany jest w terminalu, zazwyczaj za pomoca˛ polecenia python. formatowanie kodu Python wymaga formatowania kodu za pomoca˛ wci˛eć, podstawowym wymogiem jest stosowanie takich samych wci˛eć w obr˛ebie pliku, np. 4 spacji i ich wielokrotności. Wci˛ecia odpowiadaja˛ nawiasom w innych j˛ezykach, służa˛ grupowaniu instrukcji i wydzielaniu bloków kodu. Bł˛edy wci˛eć zgłaszane sa˛ jako wyjatki ˛ IndentationError. zmienna nazwa określajaca ˛ jakaś ˛ zapami˛etywana˛ i wykorzystywana˛ w programie wartość lub struktur˛e danych. Zmienna może przechowywać pojedyncze wartości określonego typu, np.: imie = "Anna", jak i rozbudowane struktury danych, np.: imiona = (’Ala’, ’Ola’, ’Ela’). W nazwach zmiennych nie używamy znaków narodowych, nie rozpoczynamy ich od cyfr. typy danych Wszystkie dane w Pythonie sa˛ obiektami i jako takie przynależa˛ do określonego typu, który determinuje możliwe na nich operacje. W pewnym uproszczeniu podstawowe typy danych to: string – napis (łańcuch znaków), podtyp sekwencji; integer – dodatnie i ujemne liczby całkowite; float – liczba zmiennoprzecinkowa (separatorem jest kropka); boolean – wartość logiczna True (prawda, 1) lub False (fałsz, 0), podtyp typu całkowitego. operatory Arytmetyczne: +, -, *, /, //, %, ** (pot˛egowanie); znak + znak (konkatenacja napisów); znak * 10 (powielenie znaków); Przypisania: =, +=, -=, *=, /=, %=, **=, //=; Logiczne: and, or, not; Fałszem logicznym sa: ˛ liczby zero (0, 0.0), False, None (null), puste kolekcje ([], (), {}, set()), puste napisy. Wszystko inne jest prawda˛ logiczna.˛ Zawierania: in, not in; Porównania: ==, >, <, <>, <=, >= != (jest różne). Operator * rozpakowuj˛e list˛e paramterów przekazana˛ funkcji. Operator ** rozpakuje słownik. lista jedna z podstawowych struktur danych, indeksowana sekwencja takich samych lub różnych elementów, które można zmieniać. Przypomina tabele z innych j˛ezyków programowania. Np. imiona = [’Ala’, ’Ola’, ’Ela’]. Deklaracja pustej listy: lista = []. tupla podbnie jak lista, zawiera indeksowana˛ sekwencj˛e takich samych lub różnych elementów, ale nie można ich zmieniać. Cz˛esto służy do przechowywania lub przekazywania ustawień, stałych wartości itp. Np. imiona = (’Ala’, ’Ola’, ’Ela’). 1-elementowa˛ tupl˛e należy zapisywać z dodatkowym przecinkiem: tupla1 = (1,). zbiór nieuporzadkowany, ˛ nieindeksowany zestaw elementów tego samego lub różnych typów, nie może zawierać duplikatów, obsługuje charakterystyczne dla zbiorów operacje: sum˛e, iloczyn oraz różnic˛e. Np. imiona = set([’Ala’, ’Ola’, ’Ela’]). Deklaracja pustego zbioru: zbior = set(). słownik typ mapowania, zestaw par elementów w postaci “klucz: wartość”. Kluczami moga˛ być liczby, ciagi ˛ znaków czy tuple. Wartości moga˛ być tego samego lub różnych typów. Np. osoby = {’Ala’: ’Lipiec’ , 162 Rozdział 5. Python Materiały eCG IT Documentation, Wydanie 1 ’Ola’: ’Maj’, ’Ela’: ’Styczeń’}. Dane ze słownika łatwo wydobyć: slownik[’klucz’], lub zmienić: slownik[’klucz’] = wartosc. Deklaracja pustego słownika: slownik = dict(). instrukcja warunkowa podstawowa konstrukcja w programowaniu, wykorzystuje wyrażenie logiczne przyjmujace ˛ wartość True (prawda) lub False (fałsz) do wyboru odpowiedniego działania. Umożliwia rozgałezianie kodu. Np.: if wiek < 18: print "Treść zabroniona" else: print "Zapraszamy" p˛etla podstawowa konstrukcja w programowaniu, umożliwia powtarzanie fragmentów kodu zadana˛ ilość razy (p˛etla for) lub dopóki podane wyrażenie logiczne jest prawdziwe (p˛etla while). Należy zadbać, aby p˛etla była skończona za pomoca˛ odpowiedniego warunku lub instrukcji przeywajacej ˛ powtarzanie. Np.: for i in range(11): print i zmienna iteracyjna zmienna wyst˛epujaca ˛ w p˛etli, której wartość zmienia si˛e, najcz˛eściej jest zwi˛ekszana (inkremntacja) o 1, w każdym wykonaniu p˛etli. Może pełnić rol˛e “licznika” powtórzeń lub być elementem wyrażenia logicznego wyznaczajacego ˛ koniec działania p˛etli. iteratory (ang. iterators) – obiekt reprezentujacy ˛ sekwencj˛e danych, zwracajacy ˛ z niej po jednym elemencie na raz przy użyciu metody next(); jeżeli nie ma nast˛epnego elementu, zwracany jest wyjatek ˛ StopIteration. Funkcja iter() potrafi zwrócić iterator z podanego obiektu. generatory wyrażeń (ang. generator expressions) – zwi˛ezły w notacji sposób tworzenia iteratorów według składni: ( wyrażenie for wyraz in sekwencja if warunek ) wyrażenie listowe (ang. list comprehensions) – efektywny sposób tworzenia list na podstawie elementów dowolnych sekwencji, na których wykonywane sa˛ te same operacje i które opcjonalnie spełniaja˛ określone warunki. Składnia: [ wyrażenie for wyraz in sekwencja if warunek ] mapowanie funkcji w kontekście funkcji map() oznacza zastosowanie danej funkcji do wszystkich dostarczonych wartości wyrażenia lambda zwane czasem funkcjami lambda, sposób zwi˛ezłego zapisywania prostych funkcji w postaci pojedynczych wyrażeń filtrowanie danych selekcja danych na podstawie jakichś kryteriów wyjatki ˛ to komunikaty zgłaszane przez interpreter Pythona, pozwalajace ˛ ustalić przyczyny bł˛ednego działania kodu. funkcja blok cz˛esto wykonywanego kodu wydzielony słowem kluczowym def, opatrzony unikalna˛ w danym zasi˛egu nazwa; ˛ może przyjmować dane i zwracać wartości za pomoca˛ słowa kluczowego return. moduł plik zawierajacy ˛ wiele zazwyczaj cz˛esto używanych w wielu programach funkcji lub klas; zanim skorzystamy z zawartych w nim fragmentów kodu, trzeba je lub cały moduł zaimportować za pomoca˛ słowa kluczowego import. serializacja proces przekształcania obiektów w strumień znaków lub bajtów, który można zapisać w pliku (bazie) lub przekazać do innego programu. 5.6 Słownik systemowy terminal to tekstowa powłoka, pozwalajaca ˛ sterować praca˛ komputera za pomoca˛ poleceń wpisywanych z klawiatury (lub wklejanych ze schowka). W systemach Linux cz˛esto da si˛e go uruchomić skrótem Win+T lub 5.6. Słownik systemowy 163 Materiały eCG IT Documentation, Wydanie 1 Ctrl+Alt+T. Jeśli skróty nie działaja˛ szukamy w menu start. Skrót Ctrl+Shift+T pozwala otworzyć kolejna˛ kart˛e terminala, w każdej karcie możemy robić coś innego. JSON JavaScript Object Notation – prosty tekstowy format wymiany danych oparty na podzbiorze j˛ezyka JavaScript, obsługiwany przez wi˛ekszość j˛ezyków programowania, szeroko stosowany w aplikacjach internetowych. API [todo] 5.7 Propozycja scenariuszy warsztatów • python_warsztaty.pdf 5.8 Metryka Autor Robert Bednarz ([email protected]) Utworzony 2015-07-18 o 04:24 164 Rozdział 5. Python ROZDZIAŁ 6 Gra robotów 6.1 Zasady i zaczynamy RobotGame to gra, w której walcza˛ ze soba˛ programy – roboty na planszy o wymiarach 19x19 pól. Celem gry jest umieszczenie na niej jak najwi˛ekszej ilości robotów w ciagu ˛ 100 rund rozgrywki. Czarne pola (ang. obstacle) wyznaczaja˛ granic˛e areny walk, zielone pola (ang. spawn points) to punkty wejścia, w których co 10 rund pojawia si˛e po 5 robotów, każdy z 50 punktami HP (ang. health points) na starcie. W każdej rundzie każdy robot musi wybrać jedno z nast˛epujacych ˛ działań: • Ruch (ang. move) na przyległe pole w pionie (góra, dół) lub poziomie (lewo, prawo). W przypadku, kiedy w polu docelowym znajduje si˛e lub znajdzie si˛e inny robot nast˛epuje kolizja i utrata po 5 punktów HP. • Atak (ang. attack) na przyległe pole, wrogi robot na tym polu traci 8-10 punktów HP. • Samobójstwo (ang. suicide) – robot ginie pod koniec rundy zabierajac ˛ wszystkim wrogim robotom obok po 15 punktów HP. • Obrona (ang. guard) – robot pozostaje w miejscu, tracac ˛ połow˛e punktów HP w wyniku ataku lub samobójstwa. W grze nie można uszkodzić własnych robotów. 165 Materiały eCG IT Documentation, Wydanie 1 6.1.1 Sztuczna inteligencja Zadaniem gracza jest stworzenie sztucznej inteligencji robota, która pozwoli mu w określonych sytuacjach na arenie wybrać odpowiednie działanie. Trzeba wi˛ec: określić dana˛ sytuacj˛e, ustalić działanie robota, zakodować je i przetestować, np.: 1. Gdzie ma iść robot po po wejściu na aren˛e? 2. Działanie: “Idź do środka”. 3. Jaki kod umożliwi robotowi realizowanie tej reguły? 4. Czy to działa? Aby ułatwić budowanie robota, przedstawiamy kilka przykładowych reguł i “klocków”, z których można zaczać ˛ składać swojego robota. Pokazujemy również, jak testować swoje roboty. Nie podajemy jednak “przepisu” na robota najlepszego. Do tego musisz dojść sam. 6.1.2 Środowisko testowe Do budowania i testowania robotów używamy biblioteki rg z pakietu rgkit. Przygotujemy wi˛ec środowisko deweloperskie w katalogu robot. Uwaga: Jeżeli korzystasz z polecanej przez nas na warsztaty dystrybucji LxPupTahr, środowisko testowe jest już przygotowane w katlogu ~/robot. W terminalu wydajemy polecenia: ~$ mkdir robot; cd robot ~robot$ virtualenv env ~robot$ source env/bin/activate (env):~/robot$ pip install rgkit Dodatkowo instalujemy pakiet zawierajacy ˛ roboty open source, nast˛epnie symulator ułatwiajacy ˛ testowanie, a na koniec tworzymy skrót do jego uruchamiania: (env):~/robot$ git clone https://github.com/mpeterv/robotgame-bots bots (env):~/robot$ git clone https://github.com/mpeterv/rgsimulator.git (env):~/robot$ ln -s rgsimulator/rgsimulator.py symuluj Po wykonaniu wszystkich powyższych poleceń i komendy ls -l powinniśmy zobaczyć: Kolejne wersje robota proponujemy zapisywać w plikach robot01.py, robot02.py itd. B˛edziemy mogli je uruchamiać lub testować za pomoca˛ poleceń: (env)~/robot$ rgrun robot01.py robot02.py (env)~/robot$ rgrun bots/stupid26.py robot01.py (env)~/robot$ python ./symuluj robot01.py robot02.py Obsługa symulatora: • Klawisz F: utworzenie robota-przyjaciela w zaznaczonym polu. • Klawisz E: utworzenie robota-wroga w zaznaczonym polu. • Klawisze Delete or Backspace: usuni˛ecie robota z zaznaczonego pola. • Klawisz H: zmiana punktów HP robota. • Klawisz C: wyczyszczenie planszy gry. 166 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 6.1. Zasady i zaczynamy 167 Materiały eCG IT Documentation, Wydanie 1 • Klawisz Spacja: pokazuje planowane ruchy robotów. • Klawisz Enter: uruchomienie rundy. • Klawisz G: tworzy i usuwa roboty w punktach wejścia (ang. spawn locations), “generowanie robotów”. Uwaga: Opisana instalacja zakłada użycie środowiska wirtualnego tworzonego przez polecenie virtualenv, dlatego przed uruchomieniem rozgrywki lub symulacji trzeba pami˛etać o wydaniu w katalogu robot polecenia source env/bin/activate. Poleceniem deactivate opuszczamy środowisko wirtualne. 6.2 RG – klocki 1 Wskazówka: • Każdy “klocek” można testować osobno, a później w połaczeniu ˛ z innymi. Warto i trzeba zmieniać kolejność stosowanych reguł! 6.2.1 Idź do środka To b˛edzie nasza domyślna reguła. Umieszczamy ja˛ w pliku robot01.py zawierajacym ˛ niezb˛edne minimum działajacego ˛ bota: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import rg 5 6 class Robot: 7 def act(self, game): 8 9 # idź do środka planszy, ruch domyślny return ['move', rg.toward(self.location, rg.CENTER_POINT)] 10 11 Metody i właściwości biblioteki rg: • rg.toward(poz_wyj, poz_cel) – zwraca nast˛epne położenie na drodze z bieżacego ˛ miejsca do podanego. • self.location – pozycja robota, który podejmuje działanie (self). • rg.CENTER_POINT – środek areny. 6.2.2 W środku broń sie˛ lub giń Co powinien robić robot, kiedy dojdzie do środka? Może si˛e bronić lub popełnić samobójstwo: 1 2 3 # jeżeli jesteś w środku, broń si˛ e if self.location == rg.CENTER_POINT: return ['guard'] 4 5 # LUB 6 7 # jeżeli jesteś w środku, popełnij samobójstwo 168 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 8 9 if self.location == rg.CENTER_POINT: return ['suicide'] 6.2.3 Atakuj wrogów obok Wersja wykorzystujaca ˛ p˛etl˛e. 1 2 3 4 5 6 # jeżeli obok sa˛ przeciwnicy, atakuj # wersja z p˛ etla˛ przegladaj ˛ ac ˛ a˛ wszystkie pola zaj˛ ete przez roboty for poz, robot in game.robots.iteritems(): if robot.player_id != self.player_id: if rg.dist(poz, self.location) <= 1: return ['attack', poz] Metody i właściwości biblioteki rg: • Słownik game.robots zawiera dane wszystkich robotów na planszy. Metoda .iteritems() zwraca indeks poz, czyli położenie (x,y) robota, oraz słownik robot opisujacy ˛ jego właściwości, czyli: – player_id – identyfikator gracza, do którego należy robot; – hp – ilość punktów HP robota; – location – tupla (x, y) oznaczajaca ˛ położenie robota na planszy; – robot_id – identyfikator robota w Twojej drużynie. • rg.dist(poz1, poz1) – zwraca matematyczna˛ odległość mi˛edzy dwoma położeniami. 6.2.4 Robot podstawowy Łacz ˛ ac ˛ omówione wyżej trzy podstawowe reguły, otrzymujemy robota podstawowego: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import rg 5 6 class Robot: 7 8 9 10 11 def act(self, game): # jeżeli jesteś w środku, broń si˛ e if self.location == rg.CENTER_POINT: return ['guard'] 12 13 14 15 16 17 # jeżeli wokół sa˛ przeciwnicy, atakuj for poz, robot in game.robots.iteritems(): if robot.player_id != self.player_id: if rg.dist(poz, self.location) <= 1: return ['attack', poz] 18 19 20 # idź do środka planszy return ['move', rg.toward(self.location, rg.CENTER_POINT)] Wybrane działanie robota zwracamy za pomoca˛ instrukcji return. Zwróć uwag˛e, jak ważna jest w tej wersji kodu kolejność umieszczenia reguł, pierwszy spełniony warunek powoduje wyjście z funkcji, wi˛ec pozostałe możliwości nie sa˛ już sprawdzane! 6.2. RG – klocki 1 169 Materiały eCG IT Documentation, Wydanie 1 Powyższy kod można przekształcić wykorzystujac ˛ zmienna˛ pomocnicza˛ ruch, inicjowana˛ działaniem domyślnym, które może zostać zmienione przez kolejne reguły. Dopiero na końcu zwracamy ustalona˛ akcj˛e: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import rg 5 6 class Robot: 7 def act(self, game): # działanie domyślne: ruch = ['move', rg.toward(self.location, rg.CENTER_POINT)] 8 9 10 11 if self.location == rg.CENTER_POINT: ruch = ['guard'] 12 13 14 for poz, robot in game.robots.iteritems(): if robot.player_id != self.player_id: if rg.dist(poz, self.location) <= 1: ruch = ['attack', poz] 15 16 17 18 19 return ruch 20 Ćwiczenie 1 Przetestuj działanie robota podstawowego wystawiajac ˛ go do gry z samym soba˛ w symulatorze. Zaobserwuj zachowanie si˛e robotów tworzac ˛ różne układy poczatkowe: ˛ (env)~/robot$ python ./symuluj robot04a.py robot04b.py 6.2.5 Możliwe ulepszenia Robota podstawowego można rozbudowywać na różne sposoby przy użyciu różnych technik kodowania. Proponujemy wi˛ec wersj˛e **A** oparta˛ na funkcjach i listach oraz wersj˛e **B** oparta˛ na zbiorach. Obie wersje implementuja˛ te same reguły, jednak efekt końcowy nie wcale nie musi być identyczny. Przetestuj i przekonaj si˛e sam. 6.3 RG – klocki 2A Wskazówka: • Każdy “klocek” można testować osobno, a później w połaczeniu ˛ z innymi. Warto i trzeba zmieniać kolejność stosowanych reguł! 6.3.1 Typy pól Zobaczmy, w jaki sposób dowiedzieć si˛e, w jakim miejscu si˛e znajdujemy, gdzie wokół mamy wrogów lub pola, na które można wejść. Dysponujac ˛ takimi informacjami, b˛edziemy mogli podejmować bardziej przemyślane działania. Wykorzystamy kilka pomocniczych funkcji. 170 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 Czy to wejście? # funkcja zwróci prawd˛ e, jeżeli "poz" wskazuje punkt wejścia def czy_wejscie(poz): if 'spawn' in rg.loc_types(poz): return True return False Metody i właściwości biblioteki rg: • gr.loc_types(poz) – zwraca typ pola wskazywanego przez poz: – invalid – poza granicami planszy(np. (-1, -5) lub (23, 66)); – normal – w ramach planszy; – spawn – punkt wejścia robotów; – obstacle – pola zablokowane ograniczajace ˛ aren˛e. Czy obok jest wróg? # funkcja zwróci prawd˛ e, jeżeli "poz" wskazuje wroga def czy_wrog(poz): if game.robots.get(poz) != None: if game.robots[poz].player_id != self.player_id: return True return False # lista wrogów obok wrogowie_obok = [] for poz in rg.locs_around(self.location): if czy_wrog(poz): wrogowie_obok.append(poz) # warunek sprawdzajacy, ˛ czy obok sa˛ wrogowie if len(wrogowie_obok): pass W powyższym kodzie metoda .get(poz) pozwala pobrać dane robota, którego kluczem w słowniku jest poz. Metody i właściwości biblioteki rg: • rg.locs_around(poz, filter_out=None) – zwraca list˛e położeń sasiaduj ˛ acych ˛ z poz. Jako filter_out można podać typy położeń do wyeliminowania, np.: rg.locs_around(self.location, filter_out=(’invalid’, ’obstacle’)). Wskazówka: Definicje funkcji i list należy wstawić na poczatku ˛ metody Robot.act() – przed pierwszym użyciem. Wykorzystujac ˛ powyższe “klocki” możemy napisać robota realizujacego ˛ nast˛epujace ˛ reguły: 1. Opuść jak najszybciej wejście; 2. Atakuj wrogów obok; 3. W środku broń si˛e; 4. W ostateczności idź do środka. 6.3. RG – klocki 2A 171 Materiały eCG IT Documentation, Wydanie 1 Implementacja Przykładowa implementacja może wygladać ˛ nast˛epujaco: ˛ 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import rg 5 6 class Robot: 7 def act(self, game): 8 9 def czy_wejscie(poz): if 'spawn' in rg.loc_types(poz): return True return False 10 11 12 13 14 def czy_wrog(poz): if game.robots.get(poz) != None: if game.robots[poz].player_id != self.player_id: return True return False 15 16 17 18 19 20 # lista wrogów obok wrogowie_obok = [] for poz in rg.locs_around(self.location): if czy_wrog(poz): wrogowie_obok.append(poz) 21 22 23 24 25 26 # jeżeli jesteś w punkcie wejścia, opuść go if czy_wejscie(self.location): return ['move', rg.toward(self.location, rg.CENTER_POINT)] 27 28 29 30 # jeżeli obok sa˛ przeciwnicy, atakuj if len(wrogowie_obok): return ['attack', wrogowie_obok.pop()] 31 32 33 34 # jeżeli jesteś w środku, broń si˛ e if self.location == rg.CENTER_POINT: return ['guard'] 35 36 37 38 # idź do środka planszy return ['move', rg.toward(self.location, rg.CENTER_POINT)] 39 40 Metoda .pop() zastosowana do listy zwraca jej ostatni element. Ćwiczenie 1 Zapisz powyższa˛ implementacj˛e w katalogu robot i przetestuj ja˛ w symulatorze, a nast˛epnie wystaw ja˛ do walki z robotem podstawowym. Poeksperymentuj z kolejnościa˛ reguł, która określa ich priorytety! 6.3.2 Atakuj, jeśli nie umrzesz Warto atakować, ale nie wtedy, gdy grozi nam śmierć. Można przyjać ˛ zasad˛e, że atakujemy tylko wtedy, kiedy suma potencjalnych uszkodzeń b˛edzie mniejsza niż zdrowie naszego robota. Zmień wi˛ec dotychczasowe reguły ataku wroga 172 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 korzystajac ˛ z poniższych “klocków”: # WERSJA A # jeżeli suma potencjalnych uszkodzeń jest mniejsza od naszego zdrowia # funkcja zwróci prawd˛ e def czy_atak(): if 9*len(wrogowie_obok) < self.hp: return True return False Metody i właściwości biblioteki rg: • self.hp – ilość punktów HP robota. Ćwiczenie 2 Dodaj powyższa˛ reguł˛e do poprzedniej wersji robota. 6.3.3 Ruszaj sie˛ bezpiecznie Zamiast iść na oślep lepiej wchodzić czy uciekać na bezpieczne pola. Za “bezpieczne” przyjmiemy na razie pole puste, niezablokowane i nie b˛edace ˛ punktem wejścia. # WERSJA A # funkcja zwróci prawd˛ e jeżeli pole poz b˛ edzie puste def czy_puste(poz): if ('normal' in rg.loc_types(poz)) and not ('obstacle' in rg.loc_types(poz)): if game.robots.get(poz) == None: return True return False puste = [] # lista pustych pól obok bezpieczne = [] # lista bezpiecznych pól obok for poz in rg.locs_around(self.location): if czy_puste(poz): puste.append(poz) if czy_puste(poz) and not czy_wejscie(poz): bezpieczne.append(poz) 6.3.4 Atakuj 2 kroki obok Jeżeli w odległości 2 kroków jest przeciwnik, zamiast iść w jego kierunku i narażać si˛e na szkody, lepiej go zaatakuj, aby nie mógł bezkarnie si˛e do nas zbliżyć. # WERSJA A # funkcja zwróci prawd˛ e, jeżeli w odległości 2 kroków z przodu jest wróg def zprzodu(l1, l2): if rg.wdist(l1, l2) == 2: if abs(l1[0] - l2[0]) == 1: return False else: return True return False # funkcja zwróci współrz˛ edne pola środkowego mi˛ edzy dwoma innymi 6.3. RG – klocki 2A 173 Materiały eCG IT Documentation, Wydanie 1 # oddalonymi o 2 kroki def miedzypole(p1, p2): return (int((p1[0]+p2[0]) / 2), int((p1[1]+p2[1]) / 2)) for poz, robot in game.get('robots').items(): if czy_wrog(poz): if rg.wdist(poz, self.location) == 2: if zprzodu(poz, self.location): return ['attack', miedzypole(poz, self.location)] if rg.wdist(rg.toward(loc, rg.CENTER_POINT), self.location) == 1: return ['attack', rg.toward(poz, rg.CENTER_POINT)] else: return ['attack', (self.location[0], poz[1])] 6.3.5 Składamy reguły Ćwiczenie 3 Jeżeli czujesz si˛e na siłach, spróbuj dokładać do robota w wersji A (opartego na funkcjach) po jednej z przedstawionych reguł, czyli: 1) Atakuj, jeśli nie umrzesz; 2) Ruszaj si˛e bezpiecznie; 3) Atakuj na 2 kroki. Przetestuj w symulatorze każda˛ zmian˛e. Omówione reguły można poskładać w różny sposób, np. tak: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import rg 5 6 class Robot: 7 def act(self, game): 8 9 def czy_wejscie(poz): if 'spawn' in rg.loc_types(poz): return True return False 10 11 12 13 14 def czy_wrog(poz): if game.robots.get(poz) != None: if game.robots[poz].player_id != self.player_id: return True return False 15 16 17 18 19 20 def czy_atak(): if 9*len(wrogowie_obok) < self.hp: return True return False 21 22 23 24 25 def czy_puste(poz): if ('normal' in rg.loc_types(poz)) and not ('obstacle' in rg.loc_types(poz)): if game.robots.get(poz) == None: return True return False 26 27 28 29 30 31 puste = [] # lista pustych pól obok bezpieczne = [] # lista bezpiecznych pól obok 32 33 174 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 34 35 36 37 38 39 for poz in rg.locs_around(self.location): if czy_puste(poz): puste.append(poz) if czy_puste(poz) and not czy_wejscie(poz): bezpieczne.append(poz) 40 41 42 43 44 45 46 47 48 # funkcja zwróci prawd˛ e, jeżeli w odległości 2 kroków z przodu jest wróg def zprzodu(l1, l2): if rg.wdist(l1, l2) == 2: if abs(l1[0] - l2[0]) == 1: return False else: return True return False 49 50 51 52 53 # funkcja zwróci współrz˛ edne pola środkowego mi˛ edzy dwoma innymi # oddalonymi o 2 kroki def miedzypole(p1, p2): return (int((p1[0]+p2[0]) / 2), int((p1[1]+p2[1]) / 2)) 54 55 56 57 58 59 # lista wrogów obok wrogowie_obok = [] for poz in rg.locs_around(self.location): if czy_wrog(poz): wrogowie_obok.append(poz) 60 61 62 63 # jeżeli jesteś w punkcie wejścia, opuść go if czy_wejscie(self.location): return ['move', rg.toward(self.location, rg.CENTER_POINT)] 64 65 66 67 68 69 70 # jeżeli obok sa˛ przeciwnicy, atakuj, o ile to bezpieczne if len(wrogowie_obok): if czy_atak(): return ['attack', wrogowie_obok.pop()] elif bezpieczne: return ['move', bezpieczne.pop()] 71 72 73 74 75 76 77 78 79 80 # jeżeli wróg jest o dwa kroki, atakuj for poz, robot in game.get('robots').items(): if czy_wrog(poz) and rg.wdist(poz, self.location) == 2: if zprzodu(poz, self.location): return ['attack', miedzypole(poz, self.location)] if rg.wdist(rg.toward(poz, rg.CENTER_POINT), self.location) == 1: return ['attack', rg.toward(poz, rg.CENTER_POINT)] else: return ['attack', (self.location[0], poz[1])] 81 82 83 84 # jeżeli jesteś w środku, broń si˛ e if self.location == rg.CENTER_POINT: return ['guard'] 85 86 87 # idź do środka planszy return ['move', rg.toward(self.location, rg.CENTER_POINT)] 6.3. RG – klocki 2A 175 Materiały eCG IT Documentation, Wydanie 1 6.3.6 Możliwe ulepszenia Poniżej pokazujemy “klocki”, których możesz użyć, aby zoptymalizować robota. Zamieszczamy również list˛e pytań do przemyślenia, aby zach˛ecić ci˛e do samodzielnego konstruowania najlepszego robota :-) Atakuj najsłabszego # wersja A # funkcja zwracajaca ˛ atak na najsłabszego wroga obok def atakuj(): r = wrogowie_obok[0] for poz in wrogowie_obok: if game.robots[poz]['hp'] > game.robots[r]['hp']: r = poz return ['attack', r] Inne • Czy warto atakować, jeśli obok jest wi˛ecej niż 1 wróg? • Czy warto atakować 1 wroga obok, ale mocniejszego od nas? • Jeżeli nie można bezpiecznie si˛e ruszyć, może lepiej si˛e bronić? • Jeśli jesteśmy otoczeni przez wrogów, może lepiej popełnić samobójstwo... Proponujemy, żebyś sam zaczał ˛ wprowadzać i testować zasugerowane ulepszenia. Możesz też zajrzeć do drugiego drugiego i trzeciego zestawu klocków opartych na zbiorach. 6.4 RG – klocki 2B Wskazówka: • Każdy “klocek” można testować osobno, a później w połaczeniu ˛ z innymi. Warto i trzeba zmieniać kolejność stosowanych reguł! 6.4.1 Typy pól Zobaczmy, w jaki sposób dowiedzieć si˛e, w jakim miejscu si˛e znajdujemy, gdzie wokół mamy wrogów lub pola, na które można wejść. Dysponujac ˛ takimi informacjami, b˛edziemy mogli podejmować bardziej przemyślane działania. Wykorzystamy wyrażenia zbiorów (ang. set comprehensions) (zob. wyrażenie listowe) i operacje na zbiorach (zob. zbiór). Czy to wejście? # wszystkie pola na planszy jako współrz˛ edne (x, y) wszystkie = {(x, y) for x in xrange(19) for y in xrange(19)} # punkty wejścia (spawn) wejscia = {poz for poz in wszystkie if 'spawn' in rg.loc_types(poz)} 176 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 # warunek sprawdzajacy, ˛ czy "poz" jest w punkcie wejścia if poz in wejscia: pass Metody i właściwości biblioteki rg: • gr.loc_types(poz) – zwraca typ pola wskazywanego przez poz: – invalid – poza granicami planszy(np. (-1, -5) lub (23, 66)); – normal – w ramach planszy; – spawn – punkt wejścia robotów; – obstacle – pola zablokowane ograniczajace ˛ aren˛e. Czy obok jest wróg? Wersja oparta na zbiorach wykorzystuje różnic˛e i cześć wspólna˛ zbiorów. # pola zablokowane zablokowane = {poz for poz in wszystkie if 'obstacle' in rg.loc_types(poz)} # pola zaj˛ ete przez nasze roboty przyjaciele = {poz for poz in game.robots if game.robots[poz].player_id == self.player_id} # pola zaj˛ ete przez wrogów wrogowie = set(game.robots) - przyjaciele # pola sasiednie ˛ sasiednie = set(rg.locs_around(self.location)) - zablokowane # pola obok zaj˛ ete przez wrogów wrogowie_obok = sasiednie & wrogowie # warunek sprawdzajacy, ˛ czy obok sa˛ wrogowie if wrogowie_obok: pass Metody i właściwości biblioteki rg: • rg.locs_around(poz, filter_out=None) – zwraca list˛e położeń sasiaduj ˛ acych ˛ z poz. Jako filter_out można podać typy położeń do wyeliminowania, np.: rg.locs_around(self.location, filter_out=(’invalid’, ’obstacle’)). Wskazówka: Definicje zbiorów należy wstawić na poczatku ˛ metody Robot.act() – przed pierwszym użyciem. Wykorzystujac ˛ powyższe “klocki” możemy napisać robota realizujacego ˛ nast˛epujace ˛ reguły: 1. Opuść jak najszybciej wejście; 2. Atakuj wrogów obok; 3. W środku broń si˛e; 4. W ostateczności idź do środka. Implementacja Przykładowa implementacja może wygladać ˛ nast˛epujaco: ˛ 6.4. RG – klocki 2B 177 Materiały eCG IT Documentation, Wydanie 1 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import rg 5 6 class Robot: 7 def act(self, game): 8 9 # wszystkie pola wszystkie = {(x, y) for x in xrange(19) for y in xrange(19)} # punkty wejścia wejscia = {poz for poz in wszystkie if 'spawn' in rg.loc_types(poz)} # pola zablokowane zablokowane = {poz for poz in wszystkie if 'obstacle' in rg.loc_types(poz)} # pola zaj˛ ete przez nasze roboty przyjaciele = {poz for poz in game.robots if game.robots[poz].player_id == self.player_id} # pola zaj˛ ete przez wrogów wrogowie = set(game.robots) - przyjaciele # pola sasiednie ˛ sasiednie = set(rg.locs_around(self.location)) - zablokowane # pola sasiednie ˛ zaj˛ ete przez wrogów wrogowie_obok = sasiednie & wrogowie 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # działanie domyślne: ruch = ['move', rg.toward(self.location, rg.CENTER_POINT)] 25 26 27 # jeżeli jesteś w punkcie wejścia, opuść go if self.location in wejscia: ruch = ['move', rg.toward(self.location, rg.CENTER_POINT)] 28 29 30 31 # jeżeli jesteś w środku, broń si˛ e if self.location == rg.CENTER_POINT: ruch = ['guard'] 32 33 34 35 # jeżeli obok sa˛ przeciwnicy, atakuj if wrogowie_obok: ruch = ['attack', wrogowie_obok.pop()] 36 37 38 39 return ruch 40 Metoda .pop() zastosowana do zbioru zwraca element losowy. Ćwiczenie 1 Zapisz powyższa˛ implementacj˛e w katalogu robot i przetestuj ja˛ w symulatorze, a nast˛epnie wystaw ja˛ do walki z robotem podstawowym. Poeksperymentuj z kolejnościa˛ reguł, która określa ich priorytety! Wskazówka: Do kontrolowania logiki działania robota zamiast rozłacznych ˛ instrukcji warunkowych: if war1: ... if war2: ... itd. można użyć instrukcji złożonej: if war1: ... elif war2: ... [elif war3: ...] else: .... 178 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 6.4.2 Atakuj, jeśli nie umrzesz Warto atakować, ale nie wtedy, gdy grozi nam śmierć. Można przyjać ˛ zasad˛e, że atakujemy tylko wtedy, kiedy suma potencjalnych uszkodzeń b˛edzie mniejsza niż zdrowie naszego robota. Zmień wi˛ec dotychczasowe reguły ataku wroga korzystajac ˛ z poniższych “klocków”: # WERSJA B # jeżeli obok sa˛ przeciwnicy, atakuj if wrogowie_obok: if 9*len(wrogowie_obok) >= self.hp: pass else: ruch = ['attack', wrogowie_obok.pop()] Metody i właściwości biblioteki rg: • self.hp – ilość punktów HP robota. Ćwiczenie 2 Dodaj powyższa˛ reguł˛e do poprzedniej wersji robota. 6.4.3 Ruszaj sie˛ bezpiecznie Zamiast iść na oślep lepiej wchodzić czy uciekać na bezpieczne pola. Za “bezpieczne” przyjmiemy na razie pole puste, niezablokowane i nie b˛edace ˛ punktem wejścia. # WERSJA B # zbiór bezpiecznych pól bezpieczne = sasiednie - wrogowie_obok - wejscia - przyjaciele 6.4.4 Atakuj 2 kroki obok Jeżeli w odległości 2 kroków jest przeciwnik, zamiast iść w jego kierunku i narażać si˛e na szkody, lepiej go zaatakuj, aby nie mógł bezkarnie si˛e do nas zbliżyć. # WERSJA B wrogowie_obok2 = {poz for poz in sasiednie if (set(rg.locs_around(poz)) & wrogowie)} - przyjaciele if wrogowie_obok2: ruch = ['attack', wrogowie_obok2.pop()] 6.4.5 Składamy reguły Ćwiczenie 3 Jeżeli czujesz si˛e na siłach, spróbuj dokładać do robota w wersji B (opartego na zbiorach) po jednej z przedstawionych reguł, czyli: 1) Atakuj, jeśli nie umrzesz; 2) Ruszaj si˛e bezpiecznie; 3) Atakuj na 2 kroki. Przetestuj w symulatorze każda˛ zmian˛e. Omówione reguły można poskładać w różny sposób, np. tak: W wersji B opartej na zbiorach: 6.4. RG – klocki 2B 179 Materiały eCG IT Documentation, Wydanie 1 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import rg 5 6 class Robot: 7 def act(self, game): 8 9 # wszystkie pola wszystkie = {(x, y) for x in xrange(19) for y in xrange(19)} # punkty wejścia wejscia = {poz for poz in wszystkie if 'spawn' in rg.loc_types(poz)} # pola zablokowane zablokowane = {poz for poz in wszystkie if 'obstacle' in rg.loc_types(poz)} # pola zaj˛ ete przez nasze roboty przyjaciele = {poz for poz in game.robots if game.robots[poz].player_id == self.player_id} # pola zaj˛ ete przez wrogów wrogowie = set(game.robots) - przyjaciele # pola sasiednie ˛ sasiednie = set(rg.locs_around(self.location)) - zablokowane # pola sasiednie ˛ zaj˛ ete przez wrogów wrogowie_obok = sasiednie & wrogowie wrogowie_obok2 = {poz for poz in sasiednie if (set(rg.locs_around(poz)) & wrogowie)} - przyja # pola bezpieczne bezpieczne = sasiednie - wrogowie_obok - wejscia - przyjaciele 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 # działanie domyślne: ruch = ['move', rg.toward(self.location, rg.CENTER_POINT)] 28 29 30 # jeżeli jesteś w punkcie wejścia, opuść go if self.location in wejscia: ruch = ['move', rg.toward(self.location, rg.CENTER_POINT)] 31 32 33 34 # jeżeli jesteś w środku, broń si˛ e if self.location == rg.CENTER_POINT: ruch = ['guard'] 35 36 37 38 # jeżeli obok sa˛ przeciwnicy, atakuj, o ile to bezpieczne if wrogowie_obok: if 9*len(wrogowie_obok) >= self.hp: if bezpieczne: ruch = ['move', bezpieczne.pop()] else: ruch = ['attack', wrogowie_obok.pop()] 39 40 41 42 43 44 45 46 if wrogowie_obok2: ruch = ['attack', wrogowie_obok2.pop()] 47 48 49 return ruch 50 6.4.6 Możliwe ulepszenia Poniżej pokazujemy “klocki”, których możesz użyć, aby zoptymalizować robota. Zamieszczamy również list˛e pytań do przemyślenia, aby zach˛ecić ci˛e do samodzielnego konstruowania najlepszego robota :-) 180 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 Atakuj najsłabszego # wersja B # funkcja znajdujaca ˛ najsłabszego wroga obok z podanego zbioru (bots) def minhp(bots): return min(bots, key=lambda x: game.robots[x].hp) if wrogowie_obok: ... else: ruch = ['attack', minhp(wrogowie_obok)] Najkrócej do celu Funkcji mindist() można użyć do znalezienia najbliższego wroga, aby iść w jego kierunku, kiedy opuścimy punkt wejścia: # WERSJA B # funkcja zwraca ze zbioru pól (bots) pole najbliższe podanego celu (poz) def mindist(bots, poz): return min(bots, key=lambda x: rg.dist(x, poz)) najblizszy_wrog = mindist(wrogowie,self.location) Inne • Czy warto atakować, jeśli obok jest wi˛ecej niż 1 wróg? • Czy warto atakować 1 wroga obok, ale mocniejszego od nas? • Jeżeli nie można bezpiecznie si˛e ruszyć, może lepiej si˛e bronić? • Jeśli jesteśmy otoczeni przez wrogów, może lepiej popełnić samobójstwo... • Spróbuj zmienić akcj˛e domyślna.˛ • Spróbuj użyć jednej złożonej instrukcji warunkowej! Proponujemy, żebyś sam zaczał ˛ wprowadzać i testować zasugerowane ulepszenia. Możesz też zajrzeć do trzeciego zestawu klocków. 6.5 RG – klocki 3B 6.5.1 Robot dotychczasowy Na podstawie reguł i klocków z cz˛eści pierwszej mogliśmy stworzyć nast˛epujacego ˛ robota: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import rg 5 6 class Robot: 7 8 def act(self, game): 6.5. RG – klocki 3B 181 Materiały eCG IT Documentation, Wydanie 1 9 wszystkie = {(x, y) for x in xrange(19) for y in xrange(19)} wejscia = {poz for poz in wszystkie if 'spawn' in rg.loc_types(poz)} zablokowane = {poz for poz in wszystkie if 'obstacle' in rg.loc_types(poz)} przyjaciele = {poz for poz in game.robots if game.robots[poz].player_id == self.player_id} wrogowie = set(game.robots) - przyjaciele 10 11 12 13 14 15 sasiednie = set(rg.locs_around(self.location)) - zablokowane wrogowie_obok = sasiednie & wrogowie wrogowie_obok2 = {poz for poz in sasiednie if (set(rg.locs_around(poz)) & wrogowie)} - przyja bezpieczne = sasiednie - wrogowie_obok - wrogowie_obok2 - wejscia - przyjaciele 16 17 18 19 20 def mindist(bots, poz): return min(bots, key=lambda x: rg.dist(x, poz)) 21 22 23 if wrogowie: najblizszy_wrog = mindist(wrogowie,self.location) else: najblizszy_wrog = rg.CENTER_POINT 24 25 26 27 28 # działanie domyślne: ruch = ['guard'] 29 30 31 if self.location in wejscia: if bezpieczne: ruch = ['move', mindist(bezpieczne, rg.CENTER_POINT)] elif wrogowie_obok: if 9*len(wrogowie_obok) >= self.hp: if bezpieczne: ruch = ['move', mindist(bezpieczne, rg.CENTER_POINT)] else: ruch = ['attack', wrogowie_obok.pop()] elif wrogowie_obok2: ruch = ['attack', wrogowie_obok2.pop()] elif bezpieczne: ruch = ['move', mindist(bezpieczne, najblizszy_wrog)] 32 33 34 35 36 37 38 39 40 41 42 43 44 45 return ruch 46 Jego działanie opiera si˛e na wyznaczeniu zbiorów pól określonego typu zastosowaniu nast˛epujacych ˛ reguł: 1. jeżeli nie ma nic lepszego, broń si˛e, 2. z punktu wejścia idź bezpiecznie do środka; 3. atakuj wrogów wokół siebie, o ile to bezpieczne, jeżeli nie, idź bezpiecznie do środka; 4. atakuj wrogów dwa pola obok; 5. idź bezpiecznie na najbliższego wroga. Spróbujemy go ulepszyć dodajac, ˛ ale i prezycujac ˛ reguły. 6.5.2 Śledź wybrane miejsca Aby unikać niepotrzebnych kolizji, nie należy wchodzić na wybrane wcześniej pola. Trzeba wi˛ec zapami˛etywać pola wybrane w danej rundzie. Przed klasa˛ Robot definiujemy dwie zmienne globalne, nast˛epnie na poczatku ˛ metody .act() inicjujemy dane: 182 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 # zmienne globalne runda_numer = 0 # numer rundy wybrane_pola = set() # zbiór wybranych w rundzie pól # inicjacja danych # wyzeruj zbiór wybrane_pola przy pierwszym robocie w rundzie global runda_numer, wybrane_pola if game.turn != runda_numer: runda_numer = game.turn wybrane_pola = set() Do zapami˛etywania wybranych w rundzie pól posłuża˛ funkcje ruszaj() i stoj(): # jeżeli si˛ e ruszamy, zapisujemy docelowe pole def ruszaj(poz): wybrane_pola.add(poz) return ['move', poz] # jeżeli stoimy, zapisujemy zajmowane miejsce def stoj(act, poz=None): wybrane_pola.add(self.location) return [act, poz] Ze zbioru bezpieczne wyłaczamy ˛ wybrane pola i stosujemy nowe funkcje: # ze zbioru bezpieczne wyłaczamy ˛ wybrane_pola bezpieczne = sasiednie - wrogowie_obok - wrogowie_obok2 - \ wejscia - przyjaciele - wybrane_pola # stosujemy nowy kod w regule "atakuj wroga dwa pola obok" elif wrogowie_obok2 and self.location not in wybrane_pola: # stosujemy funkcje "ruszaj()" i "stoj()" # zamiast: ruch = ['move', mindist(bezpieczne, rg.CENTER_POINT)] ruch = ruszaj(mindist(bezpieczne, rg.CENTER_POINT)) # zamiast: ruch = ['attack', wrogowie_obok.pop()] ruch = stoj('attack', wrogowie_obok.pop()) # zamiast: ruch = ['move', mindist(bezpieczne, najblizszy_wrog)] ruch = ruszaj(mindist(bezpieczne, najblizszy_wrog)) Wskazówka: Można zapami˛etywać wszystkie wybrane ruchy lub tylko niektóre. Przetestuj, czy ma to wpływ na skuteczność AI. 6.5.3 Atakuj najsłabszego Do tej pory atakowaliśmy przypadkowego robota wokół nas, lepiej wybrać najsłabszego. # funkcja znajdujaca ˛ najsłabszego wroga obok def minhp(bots): return min(bots, key=lambda x: game.robots[x].hp) elif wrogowie_obok: ... 6.5. RG – klocki 3B 183 Materiały eCG IT Documentation, Wydanie 1 else: ruch = stoj('attack', minhp(wrogowie_obok)) Funkcja minhp() poda nam położenie najsłabszego wroga. Argument parametru key, czyli wyrażenie lambda zwraca właściwość robotów, czyli punkty HP, wg której sa˛ porównywane. 6.5.4 Samobójstwo lepsze niż śmierć? Jeżeli grozi nam śmierć, a nie ma bezpiecznego miejsca, aby uciec, lepiej popełnić samobójstwo: # samobójstwo lepsze niż śmierć elif wrogowie_obok: if bezpieczne: ... else: ruch = stoj('suicide') 6.5.5 Unikaj nierównych starć Nie warto walczyć z przeważajac ˛ a˛ liczba˛ wrogów. elif wrogowie_obok: if 9*len(wrogowie_obok) >= self.hp: ... elif len(wrogowie_obok) > 1: if bezpieczne: ruch = ruszaj(mindist(bezpieczne, rg.CENTER_POINT)) else: ruch = stoj('attack', minhp(wrogowie_obok)) 6.5.6 Goń najsłabszych Zamiast atakować słabego uciekajacego ˛ robota, lepiej go gonić, może trafi w gorsze miejsce... elif wrogowie_obok: ... else: cel = minhp(wrogowie_obok) if game.robots[cel].hp <= 5: ruch = ruszaj(cel) else: ruch = stoj('attack', minhp(wrogowie_obok)) 6.5.7 Robot zaawansowany Po dodaniu/zmodyfikowaniu omwionych powyej reguł kod naszego robota może wygladać ˛ tak: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import rg 5 6 runda_numer = 0 # numer rundy 184 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 7 wybrane_pola = set() # zbiór wybranych w rundzie pól 8 9 class Robot: 10 11 def act(self, game): 12 13 14 15 16 global runda_numer, wybrane_pola if game.turn != runda_numer: runda_numer = game.turn wybrane_pola = set() 17 18 19 20 21 # jeżeli si˛ e ruszamy, zapisujemy docelowe pole def ruszaj(loc): wybrane_pola.add(loc) return ['move', loc] 22 23 24 25 26 # jeżeli stoimy, zapisujemy zajmowane miejsce def stoj(act, loc=None): wybrane_pola.add(self.location) return [act, loc] 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 # wszystkie pola wszystkie = {(x, y) for x in xrange(19) for y in xrange(19)} # punkty wejścia wejscia = {poz for poz in wszystkie if 'spawn' in rg.loc_types(poz)} # pola zablokowane zablokowane = {poz for poz in wszystkie if 'obstacle' in rg.loc_types(poz)} # pola zaj˛ ete przez nasze roboty przyjaciele = {poz for poz in game.robots if game.robots[poz].player_id == self.player_id} # pola zaj˛ ete przez wrogów wrogowie = set(game.robots) - przyjaciele # pola sasiednie ˛ sasiednie = set(rg.locs_around(self.location)) - zablokowane # pola sasiednie ˛ zaj˛ ete przez wrogów wrogowie_obok = sasiednie & wrogowie wrogowie_obok2 = {poz for poz in sasiednie if (set(rg.locs_around(poz)) & wrogowie)} - przyja # pola bezpieczne bezpieczne = sasiednie - wrogowie_obok - wrogowie_obok2 - wejscia - przyjaciele - wybrane_pol 45 46 47 48 # funkcja znajdujaca ˛ najsłabszego wroga obok z podanego zbioru (bots) def mindist(bots, loc): return min(bots, key=lambda x: rg.dist(x, loc)) 49 50 51 52 53 if wrogowie: najblizszy_wrog = mindist(wrogowie,self.location) else: najblizszy_wrog = rg.CENTER_POINT 54 55 56 # działanie domyślne: ruch = ['guard'] 57 58 59 60 # jeżeli jesteś w punkcie wejścia, opuść go if self.location in wejscia: ruch = ruszaj(mindist(bezpieczne, rg.CENTER_POINT)) 61 62 63 64 # jeżeli jesteś w środku, broń si˛ e if self.location == rg.CENTER_POINT: ruch = ['guard'] 6.5. RG – klocki 3B 185 Materiały eCG IT Documentation, Wydanie 1 65 # jeżeli obok sa˛ przeciwnicy, atakuj, o ile to bezpieczne, # najsłabszego wroga if wrogowie_obok: if 9*len(wrogowie_obok) >= self.hp: if bezpieczne: ruch = ruszaj(mindist(bezpieczne, rg.CENTER_POINT)) else: ruch = ['attack', minhp(wrogowie_obok)] 66 67 68 69 70 71 72 73 74 if wrogowie_obok2 and self.location not in wybrane_pola: ruch = ['attack', wrogowie_obok2.pop()] 75 76 77 return ruch 78 Na koniec trzeba przetestować robota. Czy rzeczywiście jest lepszy od poprzednich wersji? 6.5.8 Podsumowanie Nie myśl, że zastosowanie wszystkich powyższych reguł automatycznie ulepszy robota. Weź pod uwag˛e fakt, że roboty pojawiaja˛ si˛e w losowych punktach, oraz to, że strategia przeciwnika może być inna od zakładanej. Zaproponowane połaczenie ˛ klocków nie musi być optymalne. Przetestuj kolejne wersje robotów, ustal ich zalety i wady, eksperymentuj, aby znaleźć lepsze rozwiazania. ˛ 6.6 RG – dokumentacja RobotGame to gra, w której walcza˛ ze soba˛ programy – boty. Poniżej nieautoryzowane tłumaczenie oryginalnej dokumentacji oraz materiałów dodatkowych: 6.6.1 Zasady gry W Grze robotów piszesz programy kierujace ˛ walczacymi ˛ dla Ciebie robotami. Plansza˛ gry jest siatka o wymiarach 19x19 pól. Celem gry jest umieszczenie na niej jak najwi˛ekszej ilości robotów w ciagu ˛ 100 rund rozgrywki. 186 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 Czarne kwadraty to pola, na które nie ma wst˛epu. Wyznaczaja˛ kolista˛ aren˛e dla walk robotów. Zielone kwadraty oznaczaja˛ punkty wejścia do gry. Co 10 rund po 5 robotów każdego gracza rozpoczyna walk˛e w losowych punktach wejścia (ang. spawn points). Roboty z poprzednich tur pozostajace ˛ w tych punktach gina.˛ Każdy robot rozpoczyna gr˛e z 50 punktami HP (ang. health points). Roboty moga˛ działać (przemieszczać si˛e, atakować itd.) na przyległych kwdratach w pionie (góra, dół) i w poziomie (lewo, prawo). W każdej rundzie możliwe sa˛ nast˛epujace ˛ działania robota: • Ruch na przyległy kwadrat. Jeżeli znajduje si˛e tam już robot lub inny robot próbuje zajać ˛ to samo miejsce, obydwa traca˛ 5 punktów HP z powodu kolizji, a ruch(y) nie dochodzi(a) ˛ do skutku. Jeżeli jednak robot chce przejść na pole zaj˛ete przez innego, a ten drugi opuszcza zajmowane pole, ruch jest udany. Minimum cztery roboty w kwadracie, przemieszczajace ˛ si˛e zgodnie ze wskazówkami zegara, b˛eda˛ mogły si˛e poruszać, podobnie dowolna ilość robotów w kole. (Roboty nie moga˛ bezpośrednio zamieniać si˛e miejscami!) • Atak na przyległy kwadrat. Jeżeli w atakowanym kwadracie znajdzie si˛e robot pod koniec rundy, np. robot pozostał w miejscu lub przeszedł na nie, robot ten traci od 8 do 10 punktów HP w nast˛epstwie uszkodzeń. • Samobójstwo – robot ginie pod koniec rundy, zabierajac ˛ 15 punktów HP wszystkim robotom w sasiedztwie. ˛ • Obrona – robot pozostaje w miejscu, tracac ˛ połow˛e punktów HP wskutek ataku lub samobójstwa, nie odnosi uszkodzeń z powodu kolizji. W grze nie można uszkodzić własnych robotów. Kolizje, ataki i samobójstwa wyrzadzaj ˛ a˛ szkody tylko przeciwnikom. Wygrawa gracz, który po 100 rundach ma najwi˛eksza˛ liczb˛e robotów na planszy. Zadaniem gracza jest zakodowanie sztucznej inteligencji (ang. AI – artificial itelligance), dla wszystkie swoich robotów. Aby wygrać, roboty gracza musza˛ ze soba˛ współpracować, np. żeby otoczyć przeciwnika. Informacja: Niniejsza dokumentacja jest nieautoryzowanym tłumaczeniem oficjalnej dokumentacji dost˛epnej na stonie RobotGame. 6.6.2 Rozpoczynamy Spis treści • Rozpoczynamy – Tworzenie robota – Odczytywanie właściwości robota – Przykładowy robot 6.6. RG – dokumentacja 187 Materiały eCG IT Documentation, Wydanie 1 Tworzenie robota Podstawowa struktura klasy reprezentujacej ˛ każdego robota jest nast˛epujaca: ˛ class Robot: def act(self, game): return [<some action>, <params>] Na poczatku ˛ gry powstaje jedna instanacja klasy Robot. Oznacza to, że właściwości klasy oraz globalne zmienne modułu sa˛ współdzielone mi˛edzy wywołaniami. W każdej rundzie system wywołuje metod˛e act tej instancji dla każdego robota, aby określić jego działanie. (Uwaga: na poczatku ˛ przeczytaj reguły.) Metoda act musi zwrócić jedna˛ z nast˛epujacych ˛ odpowiedzi: ['move', (x, y)] ['attack', (x, y)] ['guard'] ['suicide'] Jeżeli metoda act zwróci wyjatek ˛ lub bł˛edne polecenie, robot pozostaje w obronie, ale jeżeli powtórzy si˛e to zbyt wiele razy, gracz zostanie zmuszony do kapitulacji. Szczegóły omówiono w dziale Zabezbieczenia. Odczytywanie właściwości robota Każdy robot, przy użyciu wskaźnika self, udost˛epnia nast˛epujace ˛ właściwości: • location – położenie robota w formie tupli (x, y); • hp – punkty zdrowia wyrażone liczba˛ całkowita˛ • player_id – identyfikator gracza, do którego należy robot (czyli oznaczenie “drużyny”) • robot_id – unikalny identyfikator robota, ale tylko w obr˛ebie “drużyny” Dla przykładu: kod self.hp – zwróci aktualny stan zdrowia robota. W każdej rundzie system wywołujac ˛ metod˛e act udost˛epnia jej stan gry w nast˛epujacej ˛ strukturze game: { # słownik wszystkich robotów na polach wyznaczonych # przez {location: robot} 'robots': { (x1, y1): { 'location': (x1, y1), 'hp': hp, 'player_id': player_id, # jeżeli robot jest w twojej drużynie 'robot_id': robot_id }, # ...i pozostałe roboty }, # ilość odbytych rund (wartość poczatkowa ˛ 0) 'turn': turn } Wszystkie roboty w strukturze game[’robots’] sa˛ instancjami specjalnego słownika udost˛epniajacego ˛ ich właściwości, co przyśpiesza kodowanie. Tak wi˛ec nast˛epujace ˛ konstrukcje sa˛ tożsame: 188 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 game['robots'][location]['hp'] game['robots'][location].hp game.robots[location].hp Poniżej zwi˛ezły przykład drukujacy ˛ położenie wszystkich robotów z danej drużyny: class Robot: def act(self, game): for loc, robot in game.robots.items(): if robot.player_id == self.player_id: print loc Przykładowy robot Poniżej mamy kod prostego robota, który można potraktować jako punkt wyjścia. Robot, jeżeli znajdzie wokół siebie przeciwnka, atakuje go, w przeciwnym razie przemieszcza si˛e do środka planszy (rg.CENTER_POINT). import rg class Robot: def act(self, game): # if we're in the center, stay put if self.location == rg.CENTER_POINT: return ['guard'] # if there are enemies around, attack them for loc, bot in game.robots.iteritems(): if bot.player_id != self.player_id: if rg.dist(loc, self.location) <= 1: return ['attack', loc] # move toward the center return ['move', rg.toward(self.location, rg.CENTER_POINT)] Użyliśmy, jak widać modułu rg, który zostanie omówiony dalej. Informacja: Podczas gry tworzona jest tylko jedna instancja robota, w której można zapisywać trwałe dane. Informacja: Niniejsza dokumentacja jest nieautoryzowanym tłumaczeniem oficjalnej dokumentacji dost˛epnej na stonie RobotGame. 6.6.3 Biblioteka rg Gra robotów udost˛epnia bibliotek˛e ułatwiajac ˛ a˛ programowanie. Zawarta jest w module rg, który importujemy na poczatku ˛ pliku instrukcja˛ import rg. Uwaga: Położenie robota (loc) reprezentowane jest przez tupl˛e (x, y). rg.dist(loc1, loc2) Zwraca matematyczna˛ odległość mi˛edzy dwoma położeniami. 6.6. RG – dokumentacja 189 Materiały eCG IT Documentation, Wydanie 1 rg.wdist(loc1, loc2) Zwraca różnic˛e w ruchach mi˛edzy dwoma położeniami. Ponieważ robot nie może poruszać si˛e na ukos, jest to suma dx + dy. rg.loc_types(loc) Zwraca list˛e typów położeń wskazywanych przez loc. Możliwe wartości to: • invalid – poza granicami planszy(np. (-1, -5) lub (23, 66)); • normal – w ramach planszy; • spawn – punkt wejścia robotów; • obstacle – pola, na które nie można si˛e ruszyć (szare kwadraty). Metoda nie ma dost˛epu do kontekstu gry, np. wartość obstacle nie oznacza, że na sprawdzanym kwadracie nie ma wrogiego robota; wiemy tylko, że dany kwadrat jest przeszkoda˛ na mapie. Zwrócona lista może zawierać kombinacje wartości typu: [’normal’, ’obstacle’]. rg.locs_around(loc, filter_out=None) Zwraca list˛e położeń sasiaduj ˛ acych ˛ z loc. Jako drugi argument filter_out można podać list˛e typów położeń do wyeliminowania. Dla przykładu: rg.locs_around(self.location, filter_out=(’invalid’, ’obstacle’)) – poda list˛e kwadratów, na które można wejść. rg.toward(current_loc, dest_loc) Zwraca nast˛epne położenie na drodze z bieżacego ˛ miejsca do podanego. Np. poniższy kod: import rg class Robot: def act(self, game): if self.location == rg.CENTER_POINT: return ['suicide'] return ['move', rg.toward(self.location, rg.CENTER_POINT)] – skieruje robota do środka planszy, gdzie popełni on samobójstwo. rg.CENTER_POINT Stała (ang. constant) definiujaca ˛ położenie środkowego punktu planszy. rg.settings Specjalny typ słownika (AttrDict) zawierajacy ˛ ustawienia gry. • rg.settings.spawn_every – ilość rozegranych rund od wejścia robota do gry; • rg.settings.spawn_per_player - ilość robotów wprowadzonych przez gracza; • rg.settings.robot_hp – domyślna ilość punktów HP robota; 190 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 • rg.settings.attack_range – tupla (minimum, maksimum) przechowujaca ˛ zakres uszkodzeń wyrza˛ dzonych przez atak; • rg.settings.collision_damage – uszkodzenia wyrzadzone ˛ przez kolizj˛e; • rg.settings.suicide_damage – uszkodzenia wyrzadzone ˛ przez samobójstwo; • rg.settings.max_turns – liczba rund w grze. Czy w danym położeniu jest robot Ponieważ struktura game.robots jest słownikiem robotów, w którym kluczami sa˛ położenia, a wartościami roboty, można użyć testu (x, y) in game.robots, który zwróci True, jeśli w danym położeniu jest robot, lub Flase w przeciwnym razie. Informacja: Niniejsza dokumentacja jest nieautoryzowanym tłumaczeniem oficjalnej dokumentacji dost˛epnej na stonie RobotGame. 6.6.4 Testowanie robotów Pakiet rgkit Do budowania i testowania robotów używamy pakietu rgkit. W tym celu przygotowujemy środowisko deweloperskie, zawierajace ˛ bibliotek˛e rg: ~$ mkdir robot; cd robot ~robot/$ virtualenv env ~robot/$ source env/bin/activate (env):~robot$ pip install rgkit Po wykonaniu powyższych poleceń i zapisaniu implementacji klasy Robot np. w pliku ~/robot/robot01.py możemy uruchamiać gr˛e przeciwko samemu sobie: (env)~/robot$ rgrun robot01.py robot01.py Jeżeli utworzymy inne implementacje robotów, np. w pliku ~/robot/robot02.py skonfrontujemy je poleceniem: (env)~/robot$ rgrun robot01.py robot02.py Przydatne opcje polecenia rgrun: • -H – symulacja bez UI • -r – roboty wprowadzane losowo zamiast symetrycznie. Uwaga: Pokazana powyżej instalacja zakłada użycie środowiska wirtualnego tworzonego przez pakiet virtualenv, dlatego przed uruchomieniem symulacji, a także przed użyciem omówionego niżej pakietu robotgame-bots trzeba pami˛etać o wydaniu w katalogu robot polecenia: ~/robot$ source env/bin/activate Roboty open-source Swoje roboty warto wystawić do gry przeciwko przykładowym robotom dostarczanym przez projekt robotgame-bots: Instalacja sprowadza si˛e do wykonania polecenia w utworzonym wcześniej katalogu robot: 6.6. RG – dokumentacja 191 Materiały eCG IT Documentation, Wydanie 1 ~/robot$ git clone https://github.com/mpeterv/robotgame-bots bots Wynikiem polecenia b˛edzie utworzenia podkatalogu ~/robot/bots zawierajacego ˛ kod przykładowych robotów. List˛e dost˛epnych robotów najłatwiej użyskać wydajac ˛ polecenie: (env)~/robot$ ls bots Aby zmierzyć si˛e z wybranym robotem – na poczatek ˛ sugerujemy stupid26.py – wydajemy polecenie: (env)~/robot$ rgrun mojrobot.py bots/stupid26.py Od czasu do czasu można zaktualizować dost˛epne roboty poleceniem: ~/robot/bots$ git pull --rebase origin master Symulator rg Bardzo przydatny jest symulator zachowania robotów. Instalacja w katalogu robot: ~/robot$ git clone https://github.com/mpeterv/rgsimulator.git Nast˛epnie uruchamiamy symulator podajac ˛ jako parametr nazw˛e przynajmniej jednego robota (można dwóch): (env)~/robot$ rgsimulator/rgsimulator.py robot01.py [robot02.py] Symulatorem sterujemy za pomoca˛ klawiszy: • Klawisze kursora lub WASD do zaznaczania pól. • Klawisz F: utworzenie robota-przyjaciela w zaznaczonym polu. • Klawisz E: utworzenie robota-wroga w zaznaczonym polu. • Klawisze Delete or Backspace: usuni˛ecie robota z zaznaczonego pola. • Klawisz H: zmiana punktów HP robota. • Klawisz T: zmiana rundy. • Klawisz C: wyczyszczenie planszy gry. • Klawisz Spacja: pokazuje planowane ruchy robotów. • Klawisz Enter: uruchomienie rundy. • Klawisz L: załadowanie meczu z robotgame.net. Należy podać tylko numer meczu. • Klawisz K: załadowanie podanej rundy z załadowanego meczu. Also updates the simulator turn counter. • Klawisz P: zamienia kod robotów gracza 1 z 2. • Klawisz O: ponowne załadowanie kodu obydwu robotów. • Klawisz N: zmienia działanie robota, wyznacza “nast˛epne działanie”. • Klawisz G: tworzy i usuwa roboty w punktach wejścia (ang. spawn locations), “generowanie robotów”. Wskazówka: W Linuksie warto utworzyć sobie przyjazny link do wywoływania symulatora. W katalogu robot wydajemy polecenia: (env)~/robot$ ln -s rgsimulator/rgsimulator.py symuluj (env)~/robot$ symuluj robot01.py [robot02.py] 192 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 Informacja: Niniejsza dokumentacja jest nieautoryzowanym tłumaczeniem oficjalnej dokumentacji dost˛epnej na stonie RobotGame, a także RobotGame – rgkit. Opis działania symulatora robotów przetłumaczono na podstawie strony projektu Rgsimulator. 6.6.5 Strategia podstawowa Przykład robota 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import rg 5 6 class Robot: 7 8 9 10 11 def act(self, game): # jeżeli jesteś w środku, broń si˛ e if self.location == rg.CENTER_POINT: return ['guard'] 12 13 14 15 16 17 # jeżeli wokół sa˛ przeciwnicy, atakuj for poz, robot in game.robots.iteritems(): if robot.player_id != self.player_id: if rg.dist(poz, self.location) <= 1: return ['attack', poz] 18 19 20 # idź do środka planszy return ['move', rg.toward(self.location, rg.CENTER_POINT)] Z powyższego kodu wynikaja˛ trzy zasady: • broń si˛e, jeżeli jesteś w środku planszy; • atakuj przeciwnika, jeżeli jest obok; • idź do środka. To pozwala nam rozpoczać ˛ gr˛e, ale wiele możemy ulepszyć. Wi˛ekszość usprawnień (ang. feature), które zostana˛ omówione, to rozszerzenia wersji podstawowej. Konstruujac ˛ robota, można je stosować wybiórczo. Kolejne reguły Rozbudujemy przykład podstawowy. Oto lista reguł, które warto rozważyć: • Reguła 1: Opuść punkt wejścia. Pozostawanie w punkcie wejścia nie jest dobre. Sprawdźmy, czy jesteśmy w punkcie wejścia i czy powinniśmy z niego wyjść. Nawet wtedy, gdy jest ktoś do zaatakowania, ponieważ nie chcemy zostać zamkni˛eci w pułapce wejścia. • Reguła 2: Uciekaj, jeśli masz zginać. ˛ Przykładowy robot atakuje aż do śmierci. Ponieważ jednak wygrana zależy od liczby pozostałych robotów, a nie ich zdrowia, bardziej opłaca si˛e zachować robota niż poświ˛ecać go, żeby zadał dodakowe obrażenia przeciwnikowi. Jeżeli wi˛ec jesteśmy zagrożeni śmiercia,˛ uciekamy, a nie giniemy na próżno. • Reguła 3: Atakuje przeciwnika o dwa kroki od ciebie. 6.6. RG – dokumentacja 193 Materiały eCG IT Documentation, Wydanie 1 Przyjrzyj si˛e grajacemu ˛ wg reguł robotowi, zauważysz, że kiedy wchodzi na pole atakowane przez przeciwnika, odnosi obrażenia. Dlatego, jeśli prawdopodobne jest, że przeciwnik może znaleźć si˛e w naszym sasiedztwie, ˛ trzeba go zatakować. Dzi˛eki temu nit si˛e do nas bezkarnie nie zbliży. Informacja: Połaczenie ˛ ucieczki i ataku w kierunku przeciwnika naprawd˛e jest skuteczne. Każdy agresywny wróg zanim nas zaatakuje, sam spotyka si˛e z atakiem. Jeżeli w por˛e odskoczysz, zanim si˛e zbliży, działanie takie możesz powtórzyć. Technika ta nazywana jest w grach kiting, a jej działanie ilustruje poniższa animacja: Zwróć uwag˛e na słabego robota ze zdrowiem 8 HP, który podchodzi do mocnego robota z 50 HP, a nast˛epnie ucieka. Zbliżajac ˛ si˛e atakuje pole, na które wchodzi przeciwnik, ucieka i ponawia działanie. Trwa to do momentu, kiedy silniejszy robot popełni samobójstwo (co w tym wypadku jest mało przydatne). Wszystko bez uszczerbku na zdrowiu słabszego robota. • Reguła 4: Wchodź tylko na wolne pola. Przykładowy robot idzie do środka planszy, ale w wielu wypadkach lepiej zrobić coś innego. Np. iść tam, gdzie jest bezpiecznie, zamiast narażać si˛e na bezużyteczne niebezpieczeństwo. Co jest bowiem ryzykowne? Po wejściu na plansz˛e ruch na pole przeciwnika lub wchodzenie w jego sasiedztwo. ˛ Wiadomo też, że nie możemy wchodzić na zaj˛ete pola i że możemy zmniejszyć ilość kolizji, nie wchodzac ˛ na pola zaj˛ete przez nasza˛ drużyn˛e. • Reguła 5: Idź na wroga, jeżeli go nie ma w zasi˛egu dwóch kroków. Po co iść do środka, skoro mamy inne bezpieczne możliwości? Wprawdzie stanie w punkcie wejścia jest złe, ale to nie znaczy, że środek planszy jest dobry. Lepszym wyborem jest ruch w kierunku, ale nie na pole, przeciwnika. W połaczeniu ˛ z atakiem daje nam to lepsza˛ kontrol˛e nad plansza.˛ Później przekonamy si˛e jeszcze, że sa˛ sytuacje, kiedy wejście na potencjalnie niebezpieczne pole warte jest ryzyka, ale na razie poprzestańmy na tym, co ustaliliśmy. Łaczenie ˛ ulepszeń Zapiszmy wszystkie reguły w pseudokodzie. Możemy użyć do tego jednej rozbudowanej instrukcji warunkowej if/else. jeżeli jesteś w punkcie wejścia: rusz si˛ e bezpiecznie (np. poza wejście) jeżeli jeddnak mamy przeciwnika o krok dalej: jeżeli możemy umrzeć: ruszamy si˛ e w bezpieczne miejsce w przeciwnym razie: atakujemy przeciwnika jeżeli jednak mamy przeciwnika o dwa kroki dalej: atakujemy w jego kierunku jeżeli mamy bezpieczny ruch (i nikogo wokół siebie): ruszamy si˛ e bezpiecznie, ale w kierunku przeciwnika w przeciwnym razie: bronimy si˛ e w miejscu, bo nie ma gdzie ruszyć si˛ e lub atakować Implementacja Do zakodowania omówionej logiki potrzebujemy struktury danych gry z jej ustawieniami i kilku funkcji. Pami˛etajmy, że jest wiele sobosobów na zapisanie kodu w Pythonie. Poniższy w żdanym razie nie jest optymalny, ale działa jako przykład. 194 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 Zbiory zamiast list Dla ułatwienia użyjemy pythonowych zbiorów razem z funkcja˛ set() i wyrażeniami zbiorów (ang. set comprehensions). Informacja: Zbiory i operacje na nich omówiono w dokumentacji zbiorów, podobnie przykłady wyrażeń listowych i odpowiadajacych ˛ im p˛etli. Podstawowe operacje na zbiorach, których użyjemy to: • | lub suma – zwraca zbiór wszystkich elementów zbiorów; • - lub różnica – zbiór elementów obecnych tylko w pierwszym zbiorze; • & lub iloczyn – zwraca zbiór elementów wyst˛epujacych ˛ w obydwu zbiorach. Załóżmy, że zaczniemy od wygenerowania nast˛epujacych ˛ list: drużyna – członkowie drużyny, wrogowie – przeciwnicy, wejścia – punkty wejścia oraz przeszkody – położenia zablokowane, tzn. szare kwadraty. Zbiory pól Aby ułatwić implementacj˛e omówionych ulepszeń, przygotujemy kilka zbiorów reprezentujacych ˛ pola różnych kategorii na planszy gry. W tym celu używamy wyrażeń listowych (ang. list comprehensions). # zbiory pól na planszy # wszystkie pola wszystkie = {(x, y) for x in xrange(19) for y in xrange(19)} # punkty wejścia (spawn) wejscia = {loc for loc in wszystkie if 'spawn' in rg.loc_types(loc)} # pola zablokowane (obstacle) zablokowane = {loc for loc in wszystkie if 'obstacle' in rg.loc_types(loc)} # pola zaj˛ ete przez nasze roboty przyjaciele = {loc for loc in game.robots if game.robots[loc].player_id == self.player_id} # pola zaj˛ ete przez wrogów wrogowie = set(game.robots) - przyjaciele Warto zauważyć, że zbiór wrogich robotów otrzymujemy jako różnic˛e zbioru wszystkich robotów i tych z naszej drużyny. Wykorzystanie zbiorów Przy poruszaniu si˛e i atakowaniu mamy tylko cztery możliwe kierunki, które zwraca funkcja rg.locs_around. Możemy wykluczyć położenia zablokowane (ang. obstacle), ponieważ nigdy ich nie zajmujemy i nie atakujemy. Iloczyn zbiorów sasiednie & wrogowie da nam zbiór przeciwników w sasiedztwie: ˛ # pola sasiednie ˛ sasiednie = set(rg.locs_around(self.location)) - zablokowane # pola sasiednie ˛ zaj˛ ete przez wrogów wrogowie_obok = sasiednie & wrogowie 6.6. RG – dokumentacja 195 Materiały eCG IT Documentation, Wydanie 1 Aby odnaleźć wrogów oddalonych o dwa kroki, szukamy przyległych kwadratów, obok których sa˛ przeciwnicy. Wyłaczamy ˛ sasiednie ˛ pola zaj˛ete przez członków drużyny. # pola zaj˛ ete przez wrogów w odległości 2 kroków wrogowie_obok2 = {loc for loc in sasiednie if (set(rg.locs_around(loc)) & wrogowie)} - przyjaciele Teraz musimy sprawdzić, które z położeń sa˛ bezpieczne. Usuwamy pola zajmowane przez przeciwników w odległości 1 i 2 kroków. Pozbywamy si˛e także punktów wejścia, nie chcemy na nie wracać. Podobnie, aby zmniejszyć możliwość kolizji, wyrzucamy pola zajmowane przez drużyn˛e. W miar˛e komplikowania logiki b˛edzie można zastapić ˛ to ograniczenie dodatkowym warunkiem, ale na razie to najlepsze, co możemy zrobić. bezpieczne = sasiednie - wrogowie_obok - wrogowie_obok2 - wejscia - przyjaciele Potrzebujemy funkcji, która wybierze ze zbioru położeń pole najbliższe podanego. Możemy użyć tej funkcji do znajdowania najbliższego wroga, jak również do wyboru pola z bezpiecznej listy. Możemy wi˛ec wybrać ruch najbardziej przybliżajacy ˛ nas do założonego celu. def mindist(bots, loc): return min(bots, key=lambda x: rg.dist(x, loc)) Możemy użyć metody pop() zbioru, aby pobrać jego dowolny element, np. przeciwnika, którego zaatakujemy. Żeby dowiedzieć si˛e, czy jesteśmy zagrożeni śmiercia,˛ możemy pomnożyć liczb˛e sasiaduj ˛ acych ˛ przeciwników przez średni poziom uszkodzeń (9 punktów HP) i sprawdzić, czy mamy wi˛ecej siły. Ze wzgl˛edu na sposób napisania funkcji minidist() trzeba pami˛etać o przekazywaniu jej niepustych zbiorów. Jeśli np. zbiór przeciwników b˛edzie pusty, funkcja zwróci bład. ˛ Składamy wszystko razem Po złożeniu wszystkich kawałków kodu razem otrzymujemy przykładowa˛ implemetacj˛e robota wyposażonego we wszystkie założone wyżej właściwości: 1 2 #! /usr/bin/env python # -*- coding: utf-8 -*- 3 4 import rg 5 6 class Robot: 7 def act(self, game): 8 9 wszystkie = {(x, y) for x in xrange(19) for y in xrange(19)} wejscia = {poz for poz in wszystkie if 'spawn' in rg.loc_types(poz)} zablokowane = {poz for poz in wszystkie if 'obstacle' in rg.loc_types(poz)} przyjaciele = {poz for poz in game.robots if game.robots[poz].player_id == self.player_id} wrogowie = set(game.robots) - przyjaciele 10 11 12 13 14 15 sasiednie = set(rg.locs_around(self.location)) - zablokowane wrogowie_obok = sasiednie & wrogowie wrogowie_obok2 = {poz for poz in sasiednie if (set(rg.locs_around(poz)) & wrogowie)} - przyja bezpieczne = sasiednie - wrogowie_obok - wrogowie_obok2 - wejscia - przyjaciele 16 17 18 19 20 def mindist(bots, poz): return min(bots, key=lambda x: rg.dist(x, poz)) 21 22 23 if wrogowie: najblizszy_wrog = mindist(wrogowie,self.location) else: 24 25 26 196 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 27 najblizszy_wrog = rg.CENTER_POINT 28 29 30 # działanie domyślne: ruch = ['guard'] 31 32 33 34 35 36 37 38 39 40 41 42 43 44 if self.location in wejscia: if bezpieczne: ruch = ['move', mindist(bezpieczne, rg.CENTER_POINT)] elif wrogowie_obok: if 9*len(wrogowie_obok) >= self.hp: if bezpieczne: ruch = ['move', mindist(bezpieczne, rg.CENTER_POINT)] else: ruch = ['attack', wrogowie_obok.pop()] elif wrogowie_obok2: ruch = ['attack', wrogowie_obok2.pop()] elif bezpieczne: ruch = ['move', mindist(bezpieczne, najblizszy_wrog)] 45 46 return ruch Informacja: Niniejsza dokumentacja jest swobodnym i nieautoryzowanym tłumaczeniem materiałów dost˛epnych na stonie Robotgame basic strategy. 6.6.6 Strategia pośrednia Zacznijmy od znanego W poprzednim poradniku (Strategia podstawowa) zacz˛eliśmy od bota realizujacego ˛ nast˛epujace ˛ zasady: • Broń si˛e w środku planszy • Atakuj wrogów obok • Idź do środka Zmieniliśmy lub dodaliśmy nast˛epujace ˛ reguły: • Opuść wejście • Uciekaj, jeśli masz zginać ˛ • Atakuj wrogów dwa kroki obok • Wchodź na bezpieczne, niezaj˛ete pola • Idź na wroga, jeśli w pobliżu go nie ma Do powyższych dodamy kolejne reguły w postaci fragmentów kodu, które trzeba zintergrować z dotychczasowa˛ implementacja˛ bota, aby go ulepszyć. Śledź wybierane miejsca To raczej złożona funkcja, ale jest potrzebna, aby zmniejszyć ilość kolizji. Dotychczasowe boty drużyny próbuja˛ wejść na to samo miejsce i atakuja˛ si˛e nawzajem. Co prawda nie tracimy wtedy pukntów życia, ale (prawie) zawsze mamy lepszy wybór. Jeżeli b˛edziemy śledzić wszystkie wybrane przez nas ruchy w ramach rundy, możemy uniknać ˛ niepotrzebnych kolizji. Niestety, to wymaga wielu fragementów kodu. 6.6. RG – dokumentacja 197 Materiały eCG IT Documentation, Wydanie 1 Na poczatku ˛ dodamy zmienna,˛ która posłuży do sprawdzenia, czy dany robot jest pierwszym wywoływanym w rundzie. Jeżeli tak, musimy wyczyścić list˛e poprzednich ruchów i zaktualizować licznik rund. Odpowiedni kod trzeba umieścić na poczatku ˛ metody Robot.act: Uwaga: Trzeba zainicjować zmienna˛ globalna˛ runda_numer. global runda_numer, wybrane_pola if game.turn != runda_numer: runda_numer = game.turn wybrane_pola = set() Kolejne fragmenty odpowiadać b˛eda˛ za zapami˛etywanie wykonywanych ruchów. Kod najwygodniej umieścić w pojedynczych funkcjach, które zanim zwróca˛ wybrany ruch, zapisza˛ go na liście. Warto zauważyć, że zapisywane b˛eda˛ współrz˛edne pól, na które wchodzimy lub na których pozostajemy (obrona, atak, samobójstwo). Funkcje musza˛ znaleźć si˛e w metodzie Robot.act, aby współdzieliły jej przestrzeń nazw. # Jeżeli si˛ e ruszamy, zapisujemy docelowe pole def ruszaj(loc): wybrane_pola.add(loc) return ['move', loc] # Jeżeli pozostajemy w miejscu, zapisujemy aktualne położenie # przy użyciu self.location def stoj(act, loc=None): wybrane_pola.add(self.location) return [act, loc] Nast˛epnym krokiem jest usuni˛ecie listy wybrane_pola ze zbioru bezpiecznych pól, które sa˛ podstawa˛ dalszych wyborów: bezpieczne = sasiednie - wrogowie_obok - wrogowie_obok2 \ - wejscia - przyjaciele - wybrane_pola Roboty atakujace ˛ przeciwnika o dwa kroki obok cz˛esto go otaczaja˛ (to dobrze), ale jednocześnie blokuja˛ innych członków drużyny. Dlatego możemy wykluczać ataki na pola wrogowie_obok2, jeśli znajduja˛ si˛e na liście wykonanych ruchów. [Robots that attack two moves away often form a perimeter around the enemy (a good thing) but it prevents your own bots from run across the line. For that reason we can choose to not let a robot do an an adjacent_enemy2 attack if they are sitting in a taken spot.] elif wrogowie_obok2 and self.location not in wybrane_pola: Na koniec podmieniamy kod zwracajacy ˛ ruchy: ruch = ['move', mindist(bezpieczne, najblizszy_wrog)] ruch = ['attack', wrogowie_obok.pop()] – tak aby wykorzystywał nowe funkcje: ruch = ruszaj(mindist(bezpieczne, najblizszy_wrog)) ruch = stoj('attack', wrogowie_obok.pop()) Warto pami˛etać, że roboty nie moga˛ zamieniać si˛e miejscami. Wprawdzie jest możliwe zakodowanie tego, ale zamiana nie dojdzie do skutku. 198 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 Atakuj najsłabszego wroga Każdy udany atak zmniejsza punkty HP wrogów tak samo, ale wynik gry zależy od liczby pozostałych przy życiu robotów, a nie od ich żywotności. Dlatego korzystniejsze jest wyeliminowanie słabego bota niż atakowanie/osłabienie silnego. Odpowiednia˛ funkcj˛e umieścimy w funkcji Robot.act i użyjemy do wyboru robota z listy zamiast dotychczasowej funkcji .pop(), która zwracała losowe roboty. # funkcja znajdujaca ˛ najsłabszego robota def minhp(bots): return min(bots, key=lambda x: robots[x].hp) elif wrogowie_obok: ... else: ruch = stoj('attack', minhp(wrogowie_obok)) Samobójstwo lepsze niż śmierć Na razie usiłujemy uciec, jeżeli grozi nam śmierć, ale czasami może si˛e nam nie udać, bo natkniemy si˛e na atakuja˛ cego wroga. Jeżeli brak bezpiecznego ruchu, a grozi nam śmierć, o ile pozostaniemy w miejscu, możemy popełnić samobójstwo, co osłabi wrogów bardziej niż atak. elif wrogowie_obok: if 9*len(wrogowie_obok) >= self.hp: if bezpieczne: ruch = ruszaj(mindist(safe, rg.CENTER_POINT)) else: ruch = stoj('suicide') else: ruch = stoj('attack', minhp(wrogowie_obok)) Unikaj nierównych starć W walce jeden na jednego nikt nie ma przewagi, ponieważ wróg może odpowiadać atakiem na każdy nasz atak, jeżeli jesteśmy obok. Ale gdy wróg ma liczebna˛ przewag˛e, atakujac ˛ dwoma robotami naszego jednego, dostaniemy podwójnie za każdy wyprowadzony atak. Dlatego należy uciekać, jeśli wrogów jest wi˛ecej. Warto zauważyć, że jest to kluczowa zasada w da˛żeniu do zwyci˛estwa w Grze robotów, nawet w rozgrywkach na najwyższym poziomie. Walka z wykorzystaniem przewagi jest zreszta˛ warunkiem wygranej w wi˛ekszości pojedynków. elif wrogowie_obok: if 9*len(wrogowie_obok) >= self.hp: ... elif len(wrogowie_obok) > 1: if bezpieczne: ruch = ruszaj(mindist(safe, rg.CENTER_POINT)) else: ruch = stoj('attack', minhp(wrogowie_obok)) Goń słabe roboty Możemy założyć, że słabe roboty b˛eda˛ uciekać. Zamiast atakować podczas ucieczki, powinniśmy je gonić. W ten sposób możemy wymusić kolejny ruch w nast˛epnej turze, dzi˛eki czemu trafia˛ być może w gorsze miejsce. Bierzemy 6.6. RG – dokumentacja 199 Materiały eCG IT Documentation, Wydanie 1 pod uwag˛e roboty, które maja˛ maksymalnie 5 punktów HP, nawet gdy zaatakuja˛ zamiast uciekać, zgina˛ w wyniku uszkodzeń z powodu kolizji. elif wrogowie_obok: ... else: cel = minhp(wrogowie_obok) if game.robots[cel].hp <= 5: ruch = ruszaj(cel) else: ruch = stoj('attack', minhp(wrogowie_obok)) Trzeba pami˛etać, że startegia gonienia słabego robota ma jedna˛ oczywista˛ wad˛e. Jeżeli słaby robot wybierzez obron˛e, goniacy ˛ odniesie uszkodzenia z powodu kolizji, broniacy ˛ nie. Można temu przeciwdziałać wybierajac ˛ atak, a nie pogoń – koło si˛e zamyka. Podsumowanie Poniżej zestawienie reguł, które dodaliśmy: • Śledź wybierane miejsca • Atakuj najsłabszego wroga • Samobójstwo lepsze niż śmierć • Unikaj nierównych starć • Goń słabe roboty Dodanie powyższych zmian umożliwi stworzenie robota podobnego do simplebot z pakietu open-source. Sprawdź jego kod, aby ulepszyć swojego. Do tej pory tworzyliśmy robota walczacego ˛ według zbioru kilku reguł, ale w nast˛epnym materiale poznamy roboty inaczej decydujace ˛ o ruchach, dodatkowo wykorzystujace ˛ kilka opartych na zasadach sztuczek. Jeśli jesteś gotów, sprawdź “Zaawansowane strategie” (już wkrótce...) Informacja: Niniejsza dokumentacja jest swobodnym i nieautoryzowanym tłumaczeniem materiałów dost˛epnych na stonie Robotgame Intermediate Strategy. Informacja: Niniejsza dokumentacja jest nieautoryzowanym tłumaczeniem oficjalnej dokumentacji dost˛epnej na stronie RobotGame oraz materiałów dodatkowych dost˛epnych na stronie robotgame robots and scripts. 6.7 Słownik Pythona j˛ezyk interpretowany j˛ezyk, który jest tłumaczony i wykonywany “w locie”, np. Python lub PHP. Tłumaczeniem i wykonywaniem programu zajmuje si˛e specjalny program nazwany interpreterem j˛ezyka. interpreter program, który analizuje kod źródłowy, a nast˛epnie go wykonuje. Interpretery sa˛ podstawowym składnikiem j˛ezyków wykorzystywanych do pisania skryptów wykonywanych po stronie klienta WWW (JavaScript) lub serwera (np. Python, PHP). Interpreter Pythona jest interaktywny, tzn. można w nim wydawać polecenia i obserwować ich działanie, co pozwala wygodnie uczyć si˛e i testować oprogramowanie. Uruchamiany jest w terminalu, zazwyczaj za pomoca˛ polecenia python. 200 Rozdział 6. Gra robotów Materiały eCG IT Documentation, Wydanie 1 formatowanie kodu Python wymaga formatowania kodu za pomoca˛ wci˛eć, podstawowym wymogiem jest stosowanie takich samych wci˛eć w obr˛ebie pliku, np. 4 spacji i ich wielokrotności. Wci˛ecia odpowiadaja˛ nawiasom w innych j˛ezykach, służa˛ grupowaniu instrukcji i wydzielaniu bloków kodu. Bł˛edy wci˛eć zgłaszane sa˛ jako wyjatki ˛ IndentationError. zmienna nazwa określajaca ˛ jakaś ˛ zapami˛etywana˛ i wykorzystywana˛ w programie wartość lub struktur˛e danych. Zmienna może przechowywać pojedyncze wartości określonego typu, np.: imie = "Anna", jak i rozbudowane struktury danych, np.: imiona = (’Ala’, ’Ola’, ’Ela’). W nazwach zmiennych nie używamy znaków narodowych, nie rozpoczynamy ich od cyfr. typy danych Wszystkie dane w Pythonie sa˛ obiektami i jako takie przynależa˛ do określonego typu, który determinuje możliwe na nich operacje. W pewnym uproszczeniu podstawowe typy danych to: string – napis (łańcuch znaków), podtyp sekwencji; integer – dodatnie i ujemne liczby całkowite; float – liczba zmiennoprzecinkowa (separatorem jest kropka); boolean – wartość logiczna True (prawda, 1) lub False (fałsz, 0), podtyp typu całkowitego. operatory Arytmetyczne: +, -, *, /, //, %, ** (pot˛egowanie); znak + znak (konkatenacja napisów); znak * 10 (powielenie znaków); Przypisania: =, +=, -=, *=, /=, %=, **=, //=; Logiczne: and, or, not; Fałszem logicznym sa: ˛ liczby zero (0, 0.0), False, None (null), puste kolekcje ([], (), {}, set()), puste napisy. Wszystko inne jest prawda˛ logiczna.˛ Zawierania: in, not in; Porównania: ==, >, <, <>, <=, >= != (jest różne). Operator * rozpakowuj˛e list˛e paramterów przekazana˛ funkcji. Operator ** rozpakuje słownik. lista jedna z podstawowych struktur danych, indeksowana sekwencja takich samych lub różnych elementów, które można zmieniać. Przypomina tabele z innych j˛ezyków programowania. Np. imiona = [’Ala’, ’Ola’, ’Ela’]. Deklaracja pustej listy: lista = []. tupla podbnie jak lista, zawiera indeksowana˛ sekwencj˛e takich samych lub różnych elementów, ale nie można ich zmieniać. Cz˛esto służy do przechowywania lub przekazywania ustawień, stałych wartości itp. Np. imiona = (’Ala’, ’Ola’, ’Ela’). 1-elementowa˛ tupl˛e należy zapisywać z dodatkowym przecinkiem: tupla1 = (1,). zbiór nieuporzadkowany, ˛ nieindeksowany zestaw elementów tego samego lub różnych typów, nie może zawierać duplikatów, obsługuje charakterystyczne dla zbiorów operacje: sum˛e, iloczyn oraz różnic˛e. Np. imiona = set([’Ala’, ’Ola’, ’Ela’]). Deklaracja pustego zbioru: zbior = set(). słownik typ mapowania, zestaw par elementów w postaci “klucz: wartość”. Kluczami moga˛ być liczby, ciagi ˛ znaków czy tuple. Wartości moga˛ być tego samego lub różnych typów. Np. osoby = {’Ala’: ’Lipiec’ , ’Ola’: ’Maj’, ’Ela’: ’Styczeń’}. Dane ze słownika łatwo wydobyć: slownik[’klucz’], lub zmienić: slownik[’klucz’] = wartosc. Deklaracja pustego słownika: slownik = dict(). instrukcja warunkowa podstawowa konstrukcja w programowaniu, wykorzystuje wyrażenie logiczne przyjmujace ˛ wartość True (prawda) lub False (fałsz) do wyboru odpowiedniego działania. Umożliwia rozgałezianie kodu. Np.: if wiek < 18: print "Treść zabroniona" else: print "Zapraszamy" p˛etla podstawowa konstrukcja w programowaniu, umożliwia powtarzanie fragmentów kodu zadana˛ ilość razy (p˛etla for) lub dopóki podane wyrażenie logiczne jest prawdziwe (p˛etla while). Należy zadbać, aby p˛etla była skończona za pomoca˛ odpowiedniego warunku lub instrukcji przeywajacej ˛ powtarzanie. Np.: for i in range(11): print i zmienna iteracyjna zmienna wyst˛epujaca ˛ w p˛etli, której wartość zmienia si˛e, najcz˛eściej jest zwi˛ekszana (inkremntacja) o 1, w każdym wykonaniu p˛etli. Może pełnić rol˛e “licznika” powtórzeń lub być elementem wyrażenia logicznego wyznaczajacego ˛ koniec działania p˛etli. 6.7. Słownik Pythona 201 Materiały eCG IT Documentation, Wydanie 1 iteratory (ang. iterators) – obiekt reprezentujacy ˛ sekwencj˛e danych, zwracajacy ˛ z niej po jednym elemencie na raz przy użyciu metody next(); jeżeli nie ma nast˛epnego elementu, zwracany jest wyjatek ˛ StopIteration. Funkcja iter() potrafi zwrócić iterator z podanego obiektu. generatory wyrażeń (ang. generator expressions) – zwi˛ezły w notacji sposób tworzenia iteratorów według składni: ( wyrażenie for wyraz in sekwencja if warunek ) wyrażenie listowe (ang. list comprehensions) – efektywny sposób tworzenia list na podstawie elementów dowolnych sekwencji, na których wykonywane sa˛ te same operacje i które opcjonalnie spełniaja˛ określone warunki. Składnia: [ wyrażenie for wyraz in sekwencja if warunek ] mapowanie funkcji w kontekście funkcji map() oznacza zastosowanie danej funkcji do wszystkich dostarczonych wartości wyrażenia lambda zwane czasem funkcjami lambda, sposób zwi˛ezłego zapisywania prostych funkcji w postaci pojedynczych wyrażeń filtrowanie danych selekcja danych na podstawie jakichś kryteriów wyjatki ˛ to komunikaty zgłaszane przez interpreter Pythona, pozwalajace ˛ ustalić przyczyny bł˛ednego działania kodu. funkcja blok cz˛esto wykonywanego kodu wydzielony słowem kluczowym def, opatrzony unikalna˛ w danym zasi˛egu nazwa; ˛ może przyjmować dane i zwracać wartości za pomoca˛ słowa kluczowego return. moduł plik zawierajacy ˛ wiele zazwyczaj cz˛esto używanych w wielu programach funkcji lub klas; zanim skorzystamy z zawartych w nim fragmentów kodu, trzeba je lub cały moduł zaimportować za pomoca˛ słowa kluczowego import. serializacja proces przekształcania obiektów w strumień znaków lub bajtów, który można zapisać w pliku (bazie) lub przekazać do innego programu. 202 Rozdział 6. Gra robotów ROZDZIAŁ 7 Galerie 7.1 GetSimple CMS 7.1.1 Instalacja 7.2 Metryka Autor Robert Bednarz ([email protected]) Utworzony 2015-07-18 o 04:24 203 Materiały eCG IT Documentation, Wydanie 1 204 Rozdział 7. Galerie ROZDZIAŁ 8 Indeks • genindex 205 Materiały eCG IT Documentation, Wydanie 1 206 Rozdział 8. Indeks Indeks Symbols iteratory, 163, 202 środowisko IDE, 27 środowisko graficzne, 28 J A j˛ezyk interpretowany, 162, 200 JavaScript, 104 JSON, 164 AJAX, 104 API, 164 aplikacja, 155 B K Klasa, 91, 93 kontroler, 157 baza danych, 157 L C lista, 162, 201 logowanie, 157 CMS, 104 CSS, 104, 157 D Debian, 27 F filtrowanie danych, 163, 202 formatowanie kodu, 162, 201 framework, 28, 104, 157 funkcja, 163, 202 G generatory wyrażeń, 163, 202 GET, 157 GNU Compiler Collection, 27 GPL, 27 H HTML, 103, 157 HTTP, 157 HTTP(S), 103 I instrukcja warunkowa, 163, 201 interpreter, 28, 104, 162, 200 M mapowanie funkcji, 163, 202 MinGw, 27 model, 157 moduł, 163, 202 O Obiekt, 91, 93 operatory, 162, 201 ORM, 157 P p˛etla, 163, 201 PHP, 104 plik źródłowy, 91, 93 plik nagłówkowy, 91, 93 POST, 157 private, 91, 93 public, 91, 93 Python, 104 Q Qt, 27, 91, 93 Qt Creator, 27, 91, 93 207 Materiały eCG IT Documentation, Wydanie 1 S słownik, 162, 201 serializacja, 163, 202 serwer deweloperski (testowy), 157 serwer WWW, 28, 104, 157 sloty, 91, 93 sygnały, 91, 93 system bazodanowy, 28, 104 szablon, 157 T terminal, 163 tupla, 162, 201 typy danych, 162, 201 U URL, 157 W widok, 157 WWW, 103 wyjatki, ˛ 163, 202 wyrażenia lambda, 163, 202 wyrażenie listowe, 163, 202 X XML, 104 Xubuntu 14.04, 27 Z zbiór, 162, 201 zmienna, 162, 201 zmienna iteracyjna, 163, 201 208 Indeks