Nadszedł czas na integrację „świata użytkownika” ze „światem programisty”. Ma ona na celu umożliwienie użycia samodzielnie pisanych programów, nawet jeśli nie są to kompletne aplikacje — tworzenie tych ostatnich pozostawiamy profesjonalistom — jako pełnowartościowych elementów własnego środowiska pracy. W tym rozdziale pokazujemy kilka propozycji przeprowadzenia takiej integracji.
Podczas lektury i towarzyszących jej eksperymentów przydatna będzie znajomość zasad uruchamiania programów oraz umiejętność użytkowania arkuszy kalkulacyjnych, programów graficznych i systemów relacyjnych baz danych.
Będziemy posługiwać się, nie zawsze do końca precyzyjnie,
pojęciami: moduł, pakiet, biblioteka.
Moduły już poznaliśmy w podrozdziale 5.4.3.;
są to w najprostszym przypadku zwykłe pliki źródłowe Pythona
zawierające deklaracje przeznaczone do wykorzystania w innych plikach źródłowych.
Pakiety są zestawami modułów, umieszczonymi w kartotekach instalacyjnych Pythona.
Słowo biblioteka
będzie używane w kilku kontekstach: dla oznaczenia
kodu przechowywanego w systemowym pliku bibliotecznym (np. dll
lub so
), dla oznaczenia takiego właśnie pliku biblioteki systemowej,
wreszcie dla oznaczenia pakietu czy modułu współpracującego z taką biblioteką.
W każdym z tych przypadków istotny jest fakt, że korzysta się z kodu istniejącego
niezależnie od nas, a co za tym idzie — trzeba dopasować pisany program
do wymagań bibliotek, z których korzystamy. Ważne jest również, że zawartość
bibliotek i sposób korzystania z ich zasobów są swego rodzaju standardami
(a przynajmniej przyczyniają się do powstawania standardów).
Korzystanie z bibliotek jest wskazane z wielu powodów. Zazwyczaj umożliwiają one wykonanie konkretnych skomplikowanych prac bez pisania skomplikowanego kodu — cała niewdzięczna „czarna robota” została wykonana przez autorów biblioteki. Pozwala to od razu przejść na „bardzo wysoki” poziom, bez żmudnego ustalania szczegółów technicznych.
Jeżeli biblioteka zapewnia dostęp do innej aplikacji, często korzysta w tym celu z jakiegoś uniwersalnego mechanizmu. Obiekty są wtedy „ściągane” z uruchamianej aplikacji. Jest duża szansa, że taki sam sposób komunikacji, takie same nazwy i właściwości obiektów, będą możliwe z innego języka programowania.
Z drugiej strony, komunikacja pomiędzy aplikacjami jest stosunkowo powolna. Dlatego warto ją ograniczyć do minimum.
Poznać całość? nie ma szans… przed nami przegląd najważniejszych zastosowań…
Celem interfejsu użytkowego jest umożliwienie przekazywania do programu danych i odbierania od niego wyników oraz sterowania jego pracą.
Przedstawiony niżej przegląd obejmuje najważniejsze rodzaje interfejsów użytkowych. Począwszy od najprostszych, są to: standardowe strumienie danych wejściowych i wynikowych, czytanie i tworzenie plików, odczytywanie argumentów wywołania z wiersza poleceń, a na koniec — elementy sterowania aplikacją za pomocą wybranych elementów środowiska graficznego. Przegląd nie jest kompletny i nie wyczerpuje tematyki.
Dane wejściowe pobiera się za pomocą instrukcji
input(komunikat)
(w Pythonie 2: raw_input(komunikat)
)
albo w drodze czytania z pliku sys.stdin
.
Wyniki wyprowadza się za pomocą funkcji
print(wyrażenie)
(w Pythonie 2 print
jest słowem kluczowym a nie funkcją
i nawias nie jest konieczny)
lub poprzez zapisywanie ich do pliku sys.stdout
.
Najważniejszą zaletą interfejsu strumieniowego jest łatwość przekierowania wejścia i wyjścia. Taki program może być używany w roli filtra.
Główną wadą interfejsu strumieniowego jest jego niska atrakcyjność dla użytkownika-niespecjalisty oraz fakt, że tego typu program operuje na jednym źródle danych oraz na jednym strumieniu wyników.
Przede wszystkim wtedy, kiedy chcemy, by nasz program był narzędziem
współpracującym z innymi narzędziami typu filtr, np. grep
,
sed
, tr
itp.
Interfejs strumieniowy jest najprostszy do zaprojektowania, więc warto go stosować także w programach o jednym wejściu i jednym wyjściu, pisanych doraźnie dla jednokrotnego wykonania złożonej czynności.
Dane wejściowe są czytane z pliku o wskazanej nazwie.
Wyniki zapisywane są do pliku o wskazanej nazwie.
Nazwy plików mogą być określane przez użytkownika.
Przede wszystkim wtedy, kiedy chcemy, by nasz program czytał dane z plików, przetwarzał je i zapisywał dane wynikowe w plikach. Może to być program narzędziowy działający wsadowo, lub program interaktywny pobierający z pliku jedynie dane, zaś polecenia od operatora przyjmujący innym sposobem.
Wygodnym i często wykorzystywanym zastosowaniem interfejsu plikowego jest zarządzanie konfiguracją programu. Dane konfiguracyjne są odczytywane przy starcie programu z określonego pliku, zaś w chwili zmiany ustawień lub przy zakończeniu pracy plik ten jest nadpisywany.
Rozpoznawanie argumentów wiersza poleceń jest cenną właściwością wielu porządnie napisanych programów, nawet jeżeli ich podstawowy interfejs jest inny. Na przykład większość edytorów odczytuje argument wywołania jako nazwę pliku przeznaczonego do edycji.
Umożliwienie przekazywania programowi argumentów (np. nazw plików danych) za pomocą wiersza poleceń istotnie zwiększa elastyczność jego użytkowania. Pozwala to m.in. na wywoływanie go z innych programów z zamiarem przetworzenia wskazanego pliku, lub na przypisanie mu obsługi danego typu plików w środowisku graficznym. Program taki będzie też „intuicyjnie” reagował na upuszczenie na jego ikonkę ikonki pliku przeznaczonego do przetworzenia.
W programie Pythona argumenty wiersza poleceń są odczytywane
z tablicy argv
zadeklarowanej w module sys
.
Pierwszy element tej tablicy, opatrzony indeksem 0,
zawiera nazwę uruchomionego programu.
Tylko wtedy, kiedy zamierzamy uruchamiać nasz program z parametrem, oznaczającym np. nazwę pliku danych do wczytania. Zwłaszcza wtedy, kiedy zamierzamy skojarzyć nasz program z określonym formatem lub rozszerzeniem pliku w środowisku operacyjnym, i uruchamiać go np. przez kliknięcie w ikonkę pliku z danymi. Także wtedy, kiedy chcemy uzupełnić inny interfejs (np. strumieniowy lub graficzny) o możliwość reagowania na opcje wiersza poleceń.
Jednym z podstawowych celów graficznych środowisk użytkownika jest dostarczanie interfejsu dla programów użytkowych.
Program z interfejsem graficznym pobiera dane od użytkownika za pośrednictwem formularzy ekranowych, okien dialogowych, lub przez manipulację różnego typu obiektami graficznymi. W podobny sposób przedstawiane są dane wynikowe. Możliwa jest też prezentacja graficzna wyników.
Z punktu widzenia autora programu, obsługa elementów środowiska odbywa się przez użycie podprogramów i struktur danych zadeklarowanych w bibliotekach związanych z konkretnym środowiskiem. Są to na ogół duże i skomplikowane pakiety.
Moduł easygui
umożliwia łatwe wywoływanie typowych
okien i formularzy dialogowych. easygui
nie daje
bezpośredniej możliwości obsługi „głównego okna aplikacji”
ani graficznej prezentacji danych. Zakres funkcjonalności jest więc
ograniczony, lecz wystarcza dla obsługi doraźnie pisanych programów
roboczych, nie przeznaczonych do komercyjnego rozpowszechniania.
Autorem easygui
jest amerykański inżynier oprogramowania Stephen Ferg.
easygui
nie jest standardowym elementem dystrybucji
Pythona, lecz nie wymaga szczególnych zabiegów instalacyjnych.
Argumenty typu tekstowego przekazywane oknom dialogowym easygui
muszą być kodowane w UTF-8 lub w UNICODE.
import easygui
easygui
easygui.msgbox(komunikat)
wynik = easygui.boolbox(komunikat, tytuł okna, lista opcji)
wynik = easygui.indexbox(komunikat, tytuł okna, lista opcji)
wynik = easygui.choicebox(komunikat, tytuł okna, lista opcji)
nazwapliku = easygui.fileopenbox(None, tytuł okna, domyślna nazwa pliku)
nazwapliku = easygui.filesavebox(None, tytuł okna, domyślna nazwa pliku)
wynik = easygui.enterbox(komunikat, tytuł okna, wartość domyślna)
wynik = easygui.integerbox(komunikat, tytuł okna, wartość domyślna, ograniczenie dolne, ograniczenie górne)
wynik = easygui.multenterbox(komunikat, tytuł okna, lista etykiet, lista wartości)
easygui.textbox(komunikat, tytuł okna, tekst)
http://www.ferg.org/easygui/
oraz http://easygui.sourceforge.net
Dialogi pozwalające na wybór pliku danych i pliku wynikowego
Przede wszystkim wtedy, kiedy chcemy używać naszego programu interaktywnie, jako aplikacji użytkowej w środowisku graficznym. Także wtedy, kiedy zamierzamy dzielić się z innymi naszymi dokonaniami programistycznymi.
Pythonem można się posłużyć także dla stworzenia programu będącego pełnowartościową aplikacją graficzną. Użycie specjalistycznych bibliotek przeznaczonych do tego celu pozwala precyzyjnie opisać wygląd i działanie każdego elementu interfejsu użytkownika: okna głównego, menu, okien dialogowych, kontrolek, listew narzędziowych, wyświetlanego tekstu i grafiki, itp.
Bibliotek przeznaczonych do zarządzania interfejsem graficznym jest sporo. Należą do nich przede wszystkim:
Tkinter
.
Wchodzi on w skład każdej typowej instalacji Pythona.gtk
będącego
częścią Pythonowej biblioteki PyGTK.
Od roku 2012 w użyciu jest wydanie 3 środowiska GTK+,
którego obsługą zajmuje się biblioteka GObject.
PyQt
(której rozwojem zarządza firma Riverbank Computing),
oraz PySide
(zarządzaną przez społeczność programistów skupioną wokół projektu Qt).
wx
wchodzącego w skład Pythonowej biblioteki WxPython.Wszystkie wymienione biblioteki są przenośne: ten sam kod, o ile został starannie napisany, będzie działać w różnych systemach operacyjnych. Możliwe jest też użycie bibliotek zależnych od systemu operacyjnego, np. dostęp do systemowych bibliotek interfejsu graficznego Windows zapewnia pakiet PyWin32.
W opanowanie tego typu narzędzi programistycznych trzeba zainwestować sporo czasu i wysiłku. Jest to raczej zadanie dla profesjonalistów. Dlatego, oprócz oznaczonych wyżej odsyłaczy „dla ciekawskich”, obejdziemy się bez przykładów.
Sposób i zakres wykorzystania bibliotek jest regulowany przez ich producentów za pośrednictwem licencji. Wymienione wyżej pakiety oprogramowania różnią się licencjami, jednak wszystkie one dopuszczają nieodpłatne wykorzystanie w zastosowaniach niekomercyjnych.
Projektowanie graficznego interfejsu użytkownika jest zadaniem skomplikowanym. Napisano wiele aplikacji pomocniczych pozwalających przenieść tę czynność z poziomu pisania kodu na poziom projektowania graficznego. Generowanie kodu źródłowego odbywa się w nich automatycznie. Kilka przykładów takich aplikacji zamieszczamy w galerii ilustracji.
Wspomniany wyżej moduł easygui
korzysta
z zasobów biblioteki TkInter.
Stąd m.in. wynika jego przenośność — powinien współpracować
z każdą typową instalacją Pythona.
Wtedy, kiedy chcemy, by nasz program był kompletną aplikacją, zbliżoną pod względem sposobu obsługi do innych aplikacji dostępnych w graficznym środowisku użytkownika.
Moduł os
Funkcja spawnv
uruchamia za pośrednictwem systemu operacyjnego
wskazany plik wykonywalny i przekazuje mu wyszczególnione argumenty.
import os os.spawnv(tryb, program, argumenty)
tryb
os.P_WAIT
: z wykonywaniem następnych instrukcji poczekaj, aż wywołany program zakończy pracęos.P_NOWAIT
: nie czekaj na nic, przejdź do wykonywania następnej instrukcjiprogram
argumenty
sys.argv
)
Pamiętajmy, że znak '\'
ma specjalne znaczenie w napisach.
W nazwach plików bezpieczniej jest używać ukośników '/'
niż '\'
(np. '/home/Ja/dane/proba1.txt'
lub
'c:/Users/Ja/dane/proba1.txt'
,
'ftp://mat.up.wroc.pl/pub/dane/proba1.txt'
).
Będą one poprawnie rozumiane we wszystkich systemach operacyjnych.
Można też pisać '\\'
zamiast '\'
(np. 'c:\\Users\\Ja\\dane\\proba1.txt'
),
ale tylko w systemach używających znaku '\'
jako separatora w ścieżkach dostępu
— czyli w systemach Windows.
W systemach operacyjnych UNIX i MacOS X
moduł os
zawiera podobną funkcję spawnvp
,
która nie wymaga podawania pełnej ścieżki dostępu do uruchamianego programu,
o ile znajduje się on w kartotece wymienionej w systemowej ścieżce poszukiwań
PATH
.
Funkcja os.system
, wywoływana w następujący sposób
os.system(polecenie + ' ' + argumenty)
ma działanie podobne do os.spawnv
, z tym że:
str
,
Program Pythona czeka na zakończenie procesu wywołanego typ sposobem.
Zatem chcąc użyć os.system()
do wywołania programu interaktywnego
albo długo trwającego procesu warto uruchomić go „w tle”, bez czekania
na jego zakończenie:
&
os.system('scite plik &')
start
os.system('start scite plik')
Moduł os
zawiera także wiele innych pożytecznych funkcji.
Zainteresowanych odsyłamy do dokumentacji.
Uruchomienie SciTE i wczytanie do jego sesji pliku z wynikiem obliczeń.
Nic nie stoi na przeszkodzie, by wyniki sformatować (np. znacznikami HTML), zaś zamiast edytora znakowego do prezentacji użyć przeglądarki WWW albo edytora formatującego.
Prezentacja graficzna wyników via Gnuplot (grafika w oknie ekranowym lub w pliku graficznym).
Idea: program przygotowuje plik wynikowy oraz skrypt sterujący zachowaniem programu graficznego. Następnie uruchamiana jest aplikacja graficzna, która na podstawie skryptu przygotowuje wykres ekranowy lub plik graficzny z wykresem.
Efektem działania wyżej przytoczonego kodu są następujące pliki:
Plik z wynikami obliczeń | Skrypt przygotowany dla Gnuplota | Wykres utworzony za pomocą przygotowanego skryptu (oglądasz zawartość pliku SVG) |
---|---|---|
Przykład nie jest zbyt mądry, bo tak prosty wykres można było równie dobrze przygotować samym Gnuplotem bez pisania programu. Zależało mi jednak, by część obliczeniowa programu była możliwie krótka.
Prezentacja ekranowa grafiki ma zadanie wyłącznie demonstracyjne.
Przy poważnych zastosowaniach dyrektywy set terminal
i set output
zostałyby umieszczone w skrypcie
przed plot
, zaś pause
i replot
w ogóle nie byłyby potrzebne.
Inną metodą zaprzęgnięcia Gnuplota do prezentacji wyników obliczeń otrzymanych w programie Pythona jest użycie specjalnie w tym celu opracowanego modułu Gnuplot-py. Jego zastosowanie zostanie przedstawione w podrozdziale 11.5.1.
Wtedy, kiedy chcemy, by nasz program użył innego programu, na przykład do przeprowadzenia części obliczeń lub do zaprezentowania wyników. Funkcjonalność tej metody jest ograniczona przez argumenty wywołania wybranego programu. Zauważmy, że wywołując go, korzystamy z interfejsu wiersza poleceń lub strumieniowego, a w ograniczonym zakresie z plikowego; możliwości interaktywnego sterowania wywoływanym programem są dla nas w tych przypadkach bezużyteczne.
Biblioteki obsługi baz danych za pomocą Pythona pozwalają przesyłać do systemu bazodanowego zapytania SQL, a następnie tworzyć obiekty typu lista zawierające tabele z odpowiedziami. Są to pełnoprawne obiekty Pythona, a sposób ich dalszego wykorzystania zależy od potrzeb i pomysłowości.
W skład dokumentacji środowiska programistycznego Pythona wchodzą zalecenia zwane Python Database API Specification. Precyzują one zasady tworzenia bibliotek dostępu do baz danych. Przestrzeganie ich przez twórców oprogramowania daje gwarancję, że sposób komunikacji programu z bazą danych nie zależy od systemu bazodanowego.
import nazwa_modułu
polaczenie = nazwa_modułu.connect(opcje)
connect
umożliwiającą nawiązanie połączenia z serwerem. Szczegóły wywołania zależne od rodzaju bazy danych
zostaną podane w następnych podrozdziałach.
kursor = polaczenie.cursor()
wynik = kursor.execute(zapytanie)
kursor
za pomocą którego zostało wysłane zapytanie.
wynik.description
lubkursor.description
kursor
za którego pośrednictwem wysłano zapytanie, lub obiektu wynik
zawierającego przechwyconą odpowiedź.
wynik.fetchall()
lubkursor.fetchall()
kursor
za którego pośrednictwem wysłano zapytanie, lub obiektu wynik
zawierającego przechwyconą odpowiedź.
wynik.fetchone()
lubkursor.fetchone()
None
.
Do odczytania wyników można użyć obiektu kursor
za którego pośrednictwem wysłano zapytanie, lub obiektu wynik
zawierającego przechwyconą odpowiedź.
polaczenie.commit()
połączenie
.
Istnieją opcje pozwalające wyłączyć konieczność zatwierdzania transakcji.
polaczenie.rollback()
połączenie
.
Istnieją opcje pozwalające wyłączyć konieczność zatwierdzania transakcji.
kursor.close()
polaczenie.close()
W praktyce nie wszystkie biblioteki stosują się do tych zaleceń, co sprawia, że dostęp do baz poszczególnych systemów może przebiegać w specyficzny dla nich sposób. Trzeba także pamiętać o różnicach wynikających ze specyfiki dialektów języka SQL charakterystycznych dla danego systemu. Była o tym mowa we wcześniejszej części opracowania, poświęconej relacyjnym bazom danych.
Niżej dajemy wskazówki dotyczące podstaw komunikacji z kilkoma popularnymi systemami zarządzania bazami danych.
Wtedy, kiedy mamy do czynienia z dużą i zmienną ilością danych o dobrze opisanej strukturze. Zwłaszcza wtedy, kiedy te same dane są używane przez grupę osób, a także wtedy, kiedy niezbędny jest dostęp do najnowszej wersji często aktualizowanych danych. Także wtedy, kiedy w pracy musimy coś robić z danymi przechowywanymi w bazie danych, i w celu usprawnienia tego procesu tworzymy nasz program. Nawet wtedy, kiedy przewidujemy jednorazowe wykorzystanie programu, ale ilość lub charakter danych sprawia, że zarządzanie nimi za pośrednictwem bazy danych uprości pracę (np. grupowanie i uśrednianie danych pomiarowych).
Do współpracy z bazami PostgreSQL można wykorzystać jeden z kilku pakietów, np. PsychoPG albo PygreSQL.
Pakiet PsycoPg jest wciąż rozwijany. Rozwój pakietu PygreSQL został zakończony.
PsycoPg jest zgodny z zaleceniami Python Database API Specification.
import psycopg2
polaczenie = psycopg2.connect(user = użytkownik, password=hasło, host=adres serwera, database=nazwa bazy, sslmode='prefer')
Pozostałe aspekty użytkowania modułu psycopg2
są zgodne
z ogólnymi ramami Python DB API Spec., przedstawionymi
na początku podrozdziału 11.3.
pgdb
z pakietu PygreSQLInną możliwością komunikacji z bazami systemu PostgreSQL jest użycie pakietu PygreSSQL.
Moduł pgdb
z pakietu PygreSQL jest zgodny
z zaleceniami Python Database API Specification.
import pgdb
pgdb
i umożliwia korzystanie z jego zasobów.polaczenie = pgdb.connect(opcje, użytkownik, hasło, adres serwera, nazwa bazy)
Pozostałe aspekty użytkowania modułu pgdb
są zgodne
z ogólnymi ramami Python DB API Spec., przedstawionymi
na początku podrozdziału 11.3.
pg
z pakietu PygreSQLModuł pg
z pakietu PygreSQL jest prostszy w użyciu
od pgdb
, lecz nie odznacza się zgodnością z zaleceniami
Python Database API Specification.
import pg
pg
i umożliwia korzystanie z jego zasobów.polaczenie = pg.connect(nazwa bazy, adres serwera, port sieciowy, opcje, użytkownik, hasło)
polaczenie.close()
wyniki = pg.query(zapytanie)
wyniki.ntuples()
naglowki = wyniki.listfields()
dane = wyniki.getresult()
del obiekt
Wszystkie przykłady dotyczą nawiązania połączenia z tą samą bazą i przedstawienia pobranych z niej danych. Argumenty połączenia: adres serwera, nazwa bazy, nazwa użytkownika i hasło dostępu winny być znane uczestnikom ćwiczeń; można je też otrzymać od autora dokumentacji.
Przykłady różnią się jedynie wykorzystanym interfejsem:
w pierwszej kolejności pokazano użycie modułu psycopg
.
Przykład drugi (pgdb
) różni się od pierwszego wyłącznie
nazwą modułu i sposobem zainicjowania połączenia.
Prostszy, ale mniej uniwersalny pg
pokazano w ostatniej kolejności.
Dostęp do bazy systemu PostgreSQL za pomocą modułu |
|
Dostęp do bazy systemu PostgreSQL za pomocą modułu |
|
Dostęp do bazy systemu PostgreSQL za pomocą modułu |
W przeciwieństwie do innych wymienionych tu systemów zarządzania bazami danych, SQLite nie wymaga serwera. Cała „maszyneria” zapewniająca obsługę danych jest zawarta w sterowniku, zaś bazy danych mieszczą się w pojedynczych plikach. Z tego powodu SQLite jest często wykorzystywany jako baza danych zintegrowana z oprogramowaniem użytkowym (tzw. system wbudowany).
sqlite3
Moduł sqlite3
wraz z niezbędnymi bibliotekami systemowymi
wchodzi w skład domyślnej instalacji Pythona. Zatem wykorzystanie
bazy danych SQLite w programach pisanych w Pythonie
nie wymaga dodatkowych zabiegów instalacyjnych ani konfiguracyjnych.
SQLite3 jest zgodny z zaleceniami Python Database API Specification.
import sqlite3
sqlite3
i umożliwia korzystanie z jego zasobów.polaczenie = sqlite3.connect(nazwa_pliku)
Pozostałe aspekty użytkowania modułu sqlite3
są zgodne
z ogólnymi ramami Python DB API Spec., przedstawionymi
na początku podrozdziału 11.3.
Zamieszczony niżej przykład jest analogiczny do przykładu z bazą PostgreSQL-ową przedstawionego uprzednio. Jedyne różnice wynikają z odmiennego sposobu inicjowania połączenia i z odmiennego kodowania znaków w kolumnach tekstowych. Mogą się także pojawić różnice spowodowane odmiennością dialektów SQL w obu systemach.
Dostęp do bazy systemu SQLite za pomocą modułu Plik z bazą danych wykorzystaną w tym przykładzie można pobrać z serwera. Baza ta zawiera fragment większej bazy używanej podczas zajęć. |
Inne popularne systemy obsługi baz danych, np.
MySQLdb
instalowany za pomocą pakietu MySQL-python
kinterbasdb
pyodbc
Moduły: MySQLdb, KInterbasDB i pyodbc są zgodne z zaleceniami Python Database API Specification.
Przykładów nie będzie…
Arkusze kalkulacyjne, podobnie jak języki programowania, służą do przeprowadzania obliczeń. Jednak stopień automatyzacji możliwy do osiągnięcia w programie źródłowym jest o wiele wyższy niż w skoroszycie arkusza, przy bardziej zwartej notacji. Prowadzenie skomplikowanych lub wielowariantowych obliczeń za pomocą arkusza jest zazwyczaj mało wygodne i mało efektywne.
Z drugiej strony, arkusze kalkulacyjne nadają się do prostych obliczeń, zwłaszcza projektowanych doraźnie. Atrakcyjna bywa również łatwość i trwałość prezentacji graficznej wyników.
Dlatego warto umieć integrować oba środowiska. W bieżącym podrozdziale pokażemy, jak wykorzystać arkusze kalkulacyjne jako źródło danych i miejsce przechowywania wyników generowanych przez programy Pythona. Pokażemy także możliwość uruchomienia programów źródłowych Pythona bezpośrednio z roboczego środowiska arkusza. W obu tych metodach program Pythona współpracuje z procesem aplikacji użytkowej obsługującej arkusz i wymienia z nią dane. Dodatkowy podrozdział wskazuje technikę bezpośredniego dostępu do skoroszytów roboczych w wybranych formatach użytkowych, bez pośrednictwa aplikacji arkusza.
Przy takim podejściu skoroszyt arkusza będzie służył programowi Pythona do przechowywania danych wejściowych i/lub wynikowych. Oba rodzaje danych będą z punktu widzenia użytkownika arkusza stałymi, tj. nie będą powiązane ze sobą za pośrednictwem formuł arkusza.
Najwięcej uwagi poświęcimy współpracy z systemami Microsoft Excel i OpenOffice Calc. Krótka wzmianka dotyczy współpracy z systemem Gnumeric. Dwa ostatnie systemy dają możliwość uruchamiania programów Pythona z poziomu własnego środowiska użytkowego.
Wtedy, kiedy dane wejściowe zapisane są w skoroszycie, ale obliczenia są zbyt skomplikowane jak na użycie arkusza. Wtedy, kiedy identycznym przekształceniom musimy poddać dane z wielu skoroszytów, lub zamierzamy w przyszłości robić to wielokrotnie. Wtedy, kiedy program dostarcza danych wynikowych, które z przyczyn niezależnych od nas winny być „pakowane” w skoroszyty. Wreszcie wtedy, kiedy dane dostajemy w skoroszytach, ale chcemy je wykorzystać we własnym programie.
W systemie operacyjnym Windows istnieje protokół wymiany danych między działającymi programami, noszący nazwę COM (Component Object Model, ‘obiektowy model komponentów’). Pozwala on na zarządzanie obiektami znajdującymi się w pamięci danej aplikacji z poziomu innej aplikacji.
Metody przedstawione w tym podrozdziale mogą być stosowane tylko w środowisku Windows; przypomnijmy, że z Pythona możemy korzystać w większości współczesnych systemów operacyjnych. Z drugiej strony, możliwość korzystania z COM nie ogranicza się do Pythona; w innych językach programowania interfejsu tego używa się bardzo podobnie.
W programach Pythona użytkowanie interfejsu COM
następuje za pośrednictwem modułu win32com.client
.
Nie należy on do standardowej dystrybucji; szczegóły dotyczące instalacji
omówiono w rozdziałach poświęconych temu tematowi.
Oprócz modułu win32com.client
istnieje również niezależny od niego
moduł comtypes.client
. Ma on w zasadzie to samo przeznaczenie, i większość
funkcji obu modułów pokrywa się. Jednak operacje wymiany danych z aplikacjami posługującymi
się własnymi rozszerzeniami standardu COM wymagają użycia comtypes
.
Wstępną czynnością przy korzystaniu z COM jest albo nawiązanie kontaktu z uruchomionym wcześniej procesem wybranej aplikacji, albo jej uruchomienie za pomocą funkcji COM. W sesji Pythona sesja aplikacji jest udostępniona jako skomplikowany, hierarchiczny obiekt. Struktura i sposób obsługi tego obiektu nie zależą od Pythona, tylko od wywołanej aplikacji, i są takie same we wszystkich językach programowania mających dostęp do COM. Niektóre środowiska programistyczne (np. PythonWin) udostępniają podpowiedzi dotyczące obiektów COM.
Podczas pracy program Pythona przekazuje wywołanej aplikacji polecenia (wywołując odpowiednie metody wspomnianego obiektu) i wymienia z nią dane przez pobieranie lub przypisywanie wartości jego polom oraz korzystając z jego metod.
Dostęp do zawartości obiektów COM jest dość powolny, dlatego zaleca się korzystać z nich tylko w celu pobrania danych wejściowych lub przekazania wyników. Korzystanie z zawartości zewnętrznego obiektu w roli pamięci wewnętrznej przechowującej dane pośrednie obliczeń jest niewskazane.
Omówimy zastosowanie interfejsu COM do komunikacji z sesją arkusza kalkulacyjnego Microsoft Excel.
Podczas korzystania z interfejsu COM Python przekazuje zlecenia wybranej aplikacji. Dotyczy to także zleceń otwarcia i zapisywania przez nią plików. Python nie otwiera skoroszytu excelowskiego, tylko przekazuje ścieżkę dostępu do pliku Excelowi jako daną tekstową.
W przeciwieństwie do Pythona, Excel nie rozumie
znaku /
jako separatora nazw kartotek. Dlatego wskazując lokalizację pliku
do otwarcia przez Excel lub inną aplikację COM
należy w ścieżkach używać \\
zamiast /
(Python zinterpretuje
ciągi sterujące \\
jako \
i w takiej postaci przekaże je
Excelowi).
import win32com.client
import comtypes.client
sesja = win32com.client.Dispatch(nazwa)
sesja
umożliwiający komunikację z jej sesją.
nazwa
sesja = win32com.client.Dispatch('Excel.Application')
sesja = comtypes.client.CreateObject(nazwa)
sesja
umożliwiający komunikację z jej sesją.
W przeciwieństwie do win32com.client.Dispatch()
zawsze uruchamia nowy proces aplikacji.
sesja = win32com.client.GetActiveObject(nazwa)
sesja = comtypes.client.GetActiveObject(nazwa)
sesja
umożliwiający komunikację z jej sesją.
W przeciwieństwie do win32com.client.Dispatch()
nie podejmuje próby uruchomienia aplikacji.
sesja.Visible
sesja.Visible = 1
sprawia, że okno staje się widoczne.
W poważnych zastosowaniach nie ma to żadnego znaczenia,
lecz podczas nauki lub testowania może być wygodnesesja.Quit()
sesja.Workbooks
skoroszyt = sesja.Workbooks.Add()
skoroszyt = sesja.Workbooks.Open(nazwapliku)
xls
)skoroszyt.Save()
skoroszyt.SaveAs(nazwapliku, format)
format
można pominąćskoroszyt.Close()
skoroszyt.Close(True)
wymusza zapisanie do pliku;
wariant skoroszyt.Close(False)
wymusza zamknięcie bez zapisywaniaskoroszyt.Worksheets()
skoroszyt.Worksheets.Count
arkusz = skoroszyt.Worksheets(numer)
arkusz = skoroszyt.Worksheets(nazwa)
arkusz = skoroszyt.Worksheets.Add()
skoroszyt.Worksheets(numer).Delete()
arkusz.Name
arkusz.Cells(wiersz, kolumna)
arkusz.Range(zakres)
str
,
np. obszar = arkusz.Range("a1:d3")
arkusz.Range(komórka1, komórka2)
Cell
,
np. obszar = arkusz.Range(arkusz.Cells(1,1), arkusz.Cells(3,4))
obszar.Cells(wiersz, kolumna)
komorka.Value
zmienna = komorka.Value
komorka.Value = wyrażenie
Value
, komórki mają inne właściwościxlsx
)xlrd
i xlwt
W przeciwieństwie do modułu win32com.client
, który komunikuje się z aplikacjami użytkowymi
za pośrednictwem systemowych protokołów Windows, moduł xlwt
(podobnie jak starszy od niego pyExcelerator
)
nie wymaga ani istnienia specyficznych mechanizmów systemowych (takich jak COM),
ani osobnych aplikacji (takich jak Excel); będzie też działał w środowisku
operacyjnym innym, niż Windows. Jego „wiedza” dotycząca obsługi
formatu XLS nie jest kompletna, lecz pozwala na swobodne generowanie prostych skoroszytów.
Uzupełnia go moduł xlrd
, który pozwala odczytywać
istniejące skoroszyty XLS
za pomocą samego Pythona.
Przykładów nie będzie…
openpyxl
i xlsxwriter
W bieżących wersjach Excela
jako domyślny format przechowywania skoroszytów
stosowany jest format Office Open XML.
Plikom w tym formacie przypisane jest typowo rozszerzenie xlsx
.
Do obsługi (osobno odczyt, osobno generowanie) takich plików przydatne będą
moduły openpyxl
oraz xlsxwriter
.
Przykładów nie będzie…
Pakiet biurowy OpenOffice ma swój interfejs programistyczny, zwany UNO (Universal Network Objects). Można z niego korzystać w dowolnym systemie operacyjnym, za pomocą kilku języków programowania, w tym także Pythona.
Uwagi zamieszczone w tym rozdziale z niewielkimi zmianami znajdują zastosowanie także do pakietu LibreOffice, który jest rozwijany przez niezależną fundację na nieco innej licencji.
Współpraca programu Pythona z OpenOffice polega na komunikacji sesji obu programów i wymianie danych między nimi. Do sterowania tą współpracą służą polecenia umieszczane w kodzie źródłowym programu Pythona. OpenOffice służy w roli serwera danych. Sesja OpenOffice może być uruchomiana na innym komputerze, a nawet w innym systemie operacyjnym.
W sesji programu korzystającego z UNO sesja OpenOffice’a jest reprezentowana przez obiekt o hierarchicznej budowie. Jego pola reprezentują środowisko, dokumenty i ich części składowe (np. skoroszyt, jego arkusze, komórki zakresy komórek), a metody odpowiadają m.in. operacjom na danych. Obiekty dostarczane przez UNO są przejmowane bezpośrednio od aplikacji — w tym sensie, że nie trzeba się troszczyć o zdefiniowanie ich wewnętrznej struktury.
Przy korzystaniu z uno
otwieranie i zapisywanie plików arkusza
jest dokonywane przez OpenOffice. Odpowiednie
polecenia uno
akceptują adresy URL plików.
Separatorem nazw katalogów w ścieżkach musi więc być znak zwykłego ukośnika /
niezależnie od tego, w jakim systemie operacyjnym pracuje program Pythona
i serwer danych OpenOffice.
Jedna z metod dostępu do interfejsu
UNO z poziomu Pythona
polega na wykorzystaniu modułu uno
należącego
do biblioteki PyUNO,
dostarczanej wraz z OpenOffice.
Każda instalacja OpenOffice albo zawiera własną instalację Pythona, albo współpracuje ze standardową instalacją Pythona w systemie operacyjnym. W pierwszym przypadku „zwykła” instalacja Pythona nie ma dostępu do PyUNO, a zatem i do komunikacji z OpenOffice.
W związku z tym do testowania programów korzystających z uno
niezbędne może się okazać użycie debuggera
współpracującego z interpreterem Pythona dostarczonym wraz
z OpenOffice.
Współczesne oprogramowanie będące kontynuacją pakietu OpenOffice.org, czyli Apache OpenOffice i LibreOffice, różnią się w wielu technicznych szczegółach. Między innymi, każde z nich posiada wbudowany interpreter Pythona w innej wersji: 2.6.x albo 3.x. Rozwój obu pakietów przebiega niezależnie, więc należy także liczyć się z rosnącymi różnicami w bibliotekach obsługi.
import uno
uno
do środowiska Pythonasesja = …
Otrzymany w jego wyniku obiektlocalContext = uno.getComponentContext() resolver = localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext) # host i port muszą wskazywać na adres uruchomionego serwera danych ctx = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" ) sesja = ctx.ServiceManager.createInstanceWithContext( "com.sun.star.frame.Desktop", ctx)
sesja
reprezentuje sesję
aplikacji OpenOffice — za jej pośrednictwem można
zarządzać dowolnymi dokumentami, m.in. skoroszytami arkusza kalkulacyjnegoskoroszyt = sesja.getCurrentComponent()
skoroszyt = loadComponentFromURL('private:factory/scalc', '_blank', 0, ())
skoroszyt = loadComponentFromURL('file:///sciezka-do-pliku', '_blank', 0, ())
()
— opisuje sposób importowania danych
(zagadnienia importu nie omawiamy)skoroszyt.store()
skoroszyt.storeAsURL(nazwapliku, opcje)
opcje
można przekazać dodatkowe opcje zapisu, np. użycie danego formatu wynikowego albo zapisu z hasłem.
Przy zapisie w domyślnym formacie argument ten można pominąćskoroszyt.close(wymuszone)
wymuszone
decyduje o bezwarunkowym zamknięciu dokumentu (w przypadku wartości False
system ma prawo odmówić zamknięcia, np. podczas drukowania albo kiedy dokument zawiera
niezapisane modyfikacje).skoroszyt.Sheets
skoroszyt.Sheets.Count
arkusz = skoroszyt.Sheets.getByIndex(numer)
arkusz = skoroszyt.Sheets.getByName(nazwa)
skoroszyt.Sheets.insertNewByName(nazwa, pozycja)
skoroszyt.Sheets.removeByName(nazwa)
arkusz.Name
arkusz.setName(nazwa)
arkusz.getCellByPosition(kolumna, wiersz)
arkusz.getCellRangeByName(zakres)
str
,
np. obszar = arkusz.getCellRangeByName("a1:d3")
arkusz.getCellRangeByPosition(kolumna1, wiersz1, kolumna2, wiersz2)
obszar = arkusz.getCellRangeByPosition(0, 0, 3, 2)
obszar.getCellByPosition(kolumna, wiersz)
zmienna = komorka.getValue()
zmienna = komorka.getString()
komorka.setValue(wyrażenie)
komorka.setString(napis)
komorka.NumberFormat
Najpierw zadbamy o uruchomienie serwera danych z poziomu powłoki systemu operacyjnego.
Można to zrobić na tym samym lub na innym komputerze. Dodatkowe opcje przekazane serwerowi
decydują o sposobie odbierania poleceń (socket
, host
, port
dla połączenia sieciowego; pipe
, name
dla połączenia lokalnego przez potok),
a także o używaniu lub nieużywaniu interfejsu graficznego (opcja -headless
).
soffice "-accept=socket,host=localhost,port=2002;urp;"
lub
soffice "-accept=pipe,name=nazwa;urp;"
Potem uruchamiamy program Pythona komunikujący się z uruchomionym
uprzednio serwerem. Trzeba zadbać o zgodność opcji host
i port
(ew. pipe
i name
) z wartościami ustalonymi przy uruchomieniu serwera.
W systemach operacyjnych Windows pakiet OpenOffice może być sterowany także za pomocą mechanizmu COM, omówionego wyżej na przykładzie arkusza Excel. Zamieszczony niżej kod realizuje tym właśnie sposobem zadanie będące przedmiotem poprzednich prezentacji.
Tutaj mechanizm uruchomienia aplikacji jest charakterystyczny dla wywołań COM, natomiast sposób zarządzania obiektami udostępnionymi przez sesję OpenOffice jest charakterystyczny dla tego pakietu — czyli identyczny, jak w przypadku UNO.
W Pythonie można tworzyć dodatki rozszerzające użyteczność OpenOffice, w pełni zintegrowane z jego zasobami. Dodatki takie, zwane zazwyczaj makropoleceniami, mają w tym przypadku postać funkcji Pythona o zerowej liczbie parametrów. Zatem plik źródłowy z makropoleceniami jest raczej modułem z deklaracjami funkcji, niż pojedynczym programem.
Funkcje te będą wywoływane z poziomu użytkownika środowiska OpenOffice (np. przez menu Narzędzia/Makra/Zarządzaj makrami/Python, przez przycisk narzędziowy lub przez skrót klawiaturowy).
Do komunikacji z sesją OpenOffice służy ten sam moduł PyUNO.
W pliku źródłowym zawierającym makropolecenia nie trzeba go deklarować, gdyż robi to domyślnie
sesja Pythona wywoływana przez OpenOffice.
W kodzie makropolecenia OpenOffice wygodnie jest odwoływać się
do obiektu XSCRIPTCONTEXT
, który reprezentuje gotowe dowiązanie do sesji programu.
Plik źródłowy z makropoleceniami musi być umieszczony w jednej z kartotek
przeznaczonych na skrypty Pythona w ramach instalacji
OpenOffice. Każdy użytkownik ma taką kartotekę;
domyślnie jest to podkartoteka user/Scripts/python
osobistej
kartoteki konfiguracyjnej pakietu. Z przechowywanych w niej modułów korzystać
może jedynie jej właściciel.
Istnieje również systemowa kartoteka skryptów; domyślnie jest to podkartoteka
share/Scripts/python
systemowej kartoteki instalacyjnej pakietu.
Umieszczenie w niej pliku zazwyczaj wymaga uprawnień administracyjnych,
a przechowywane w niej moduły są do dyspozycji wszystkich użytkowników.
Przygotowanie takich modułów wymaga pewnego doświadczenia o tyle, że OpenOffice nie ma edytora ani debuggera kodu Pythona, zaś przetestowanie programu przed jego zintegrowaniem ze środowiskiem jest tylko częściowo możliwe.
odfpy
Przykładów nie będzie…
Gnumeric
Przykładów nie będzie… (swoją drogą Gnumerica warto znać — robi bardzo porządne wektorowe wykresy)…
Użycie języka programowania do zadań związanych z grafiką i wizualizacją danych może być przeprowadzone na różnych poziomach. Pomocne będą następujące narzędzia:
Opisy i przykłady zamieszczone niżej dotyczą wykorzystania dwóch pakietów grafiki prezentacyjnej: Gnuplot oraz pyplot. Są to narzędzia przeznaczone dla użytkowników–praktyków, zwłaszcza inżynierów i naukowców.
Wtedy, kiedy zamierzamy stosunkowo łatwo wygenerować wykres przedstawiający nasze dane. Innych sytuacji nie omawiamy.
W powtórnym spotkaniu z Gnuplotem użyjemy pakietu o nazwie Gnuplot-py. Jest to „prawdziwy” interfejs programistyczny, wygodniejszy w użyciu niż metoda przedstawiona w podrozdziale 11.2.2.
Pakiet Gnuplot-py jest rozprowadzany w postaci kodu źródłowego.
Instalacja polega na rozpakowaniu archiwum i wykonaniu polecenia python setup.py install
.
Instalacja Gnuplot-py wymaga uprzedniej instalacji
pakietu NumPy.
Po zainstalowaniu pakietu Gnuplot-py zyskujemy dostęp
do Pythonowego modułu Gnuplot
, którego obiekty obsługują
wszystkie istotne aspekty tworzenia grafiki za pomocą Gnuplota
bezpośrednio z poziomu kodu Pythona.
Nie trzeba ani jawnie uruchamiać Gnuplota za pomocą
funkcji systemu operacyjnego, ani konstruować dla niego pliku ze skryptem.
import Gnuplot
Gnuplot
do środowiska wykonawczegosesja = Gnuplot.Gnuplot()
sesja
do komunikacji z nimsesja = Gnuplot.Gnuplot(persist = True)
sesja.plot(obiekt)
obiekt
może być m.in.
ciągiem punktów dwuwymiarowych, tekstem z „wzorem”
funkcji jednej zmiennej, wynikiem działania funkcji
Gnuplot.Data()
albo Gnuplot.Func()
(patrz niżej)sesja.splot(obiekt)
obiekt
może być m.in.
ciągiem punktów dwuwymiarowych, tekstem z „wzorem”
funkcji jednej zmiennej, wynikiem działania funkcji
Gnuplot.Data()
, Gnuplot.File()
albo Gnuplot.Func()
(patrz niżej)sesja.replot(obiekt)
obiekt
lub obiekty do budowanego
wykresu. Tym różni się do dwóch poprzednio wymienionych funkcji, które
zawsze kreślą nowy wykres. Wywołana bez argumentu funkcja replot()
przerysowuje wykres, co bywa przydatne przy jego zapisywaniu do pliku
sesja(polecenie)
polecenie
do wykonania jako polecenie systemu Gnuplot.
Może to być m.in. ustalenie domyślnego stylu kreślenia wykresów,
rozmiaru znaczników, typu układu współrzędnych itp. Np. zakresy
osi układu współrzędnych ustalimy za pomocą poleceń postaci
sesja('set xrange [xmin : xmax]') sesja('set yrange [ymin : ymax]')
dane = Gnuplot.Data(danex, opcje)
dane = Gnuplot.Data(danex, daney, opcje)
dane = Gnuplot.Data(danexy, opcje)
plot
, splot
albo replot
.
opcje
decydują o wizualnych właściwościach utworzonej serii danych
dane = Gnuplot.File(nazwa_pliku, opcje)
plot
, splot
albo replot
dane = Gnuplot.Func(tekst, opcje)
plot
, splot
albo replot
opcje
poleceń Gnuplot.Data()
, Gnuplot.File()
, Gnuplot.Func()
with_ = sposób kreślenia serii
title = tekst
using = wykaz kolumn
sesja.title(tekst)
sesja.xlabel(tekst)
sesja.ylabel(tekst)
sesja.zlabel(tekst)
sesja.hardcopy(nazwa_pliku, nazwa_terminala)
terminalem— odpowiednio do Gnuplotowego polecenia
set terminal nazwa_terminala
. Domyślnym terminalem
jest 'postscript'
, generujący wydruki w formacie PostScript
sesja('set terminal nazwa_terminala')
sesja('set output "nazwa_pliku"')
sesja.replot()
terminalem. Omawiany sposób daje nieco większą elastyczność, niż poprzednio wymienione polecenie
sesja.hardcopy()
. Polecenia
set terminal …
i set output …
obowiązują w dalszym ciągu sesji aż do ich odwołania
sesja.close()
del sesja
W dokumentacji niektórych funkcji modułu Gnuplot
, np. Gnuplot.Data()
,
występuje parametr odpowiedzialny za ustalenie stylu kreślenia linii (odpowiednio np.
do frazy with lines
w poleceniach samego Gnuplota).
Tradycyjnie nosił on nazwę with
.
Słowo with
stało się słowem kluczowym języka Python począwszy od wersji 2.6,
i jego użycie jako nazwy parametru nie jest już dozwolone. Dlatego w najnowszych wersjach modułu
Gnuplot
zastąpiono nazwę parametru with
nazwą with_
.
Przy używaniu modułu Gnuplot
w systemach Windows
możemy spotkać się z przypadkiem braku synchronizacji między procesem
Gnuplota tworzącym plik graficzny, a procesem
Pythona zamykającym obiekt rysunku. Powoduje on,
że obiekt jest niszczony zanim dane zostaną przepisane do pliku, wskutek czego
plik pozostaje pusty. Dla uniknięcia tej niedogodności zaleca się rozpoczynać
sesję Gnuplota za pomocą polecenia
Gnuplot.Gnuplot(persist = True)
.
Można też na krótko wstrzymać pracę programu między poleceniem nakreślenia
rysunku a zniszczeniem obiektu rysunkowego. Da się to zrobić np. za pomocą
funkcji time.sleep(sekundy)
lub wywołując jakąkolwiek
funkcję czekającą na reakcję użytkownika. Jest to przydatne zwłaszcza w systemach,
w których opcja persist
nie jest dostępna.
Idea: program za pośrednictwem pakietu Gnuplot
wywołuje program graficzny
i przekazuje mu polecenia kreślenia przygotowanych danych. Ostatnie polecenia generują
plik graficzny z wykresem.
Wyniki obliczeń są zwykłymi listami Pythona, więc w razie potrzeby da się je zapisać do pliku zwykłymi metodami. Jednak dla celów utworzenia wykresu nie jest to konieczne. Efektem działania wyżej przytoczonego kodu jest plik graficzny.
Rysunek SVG utworzony poleceniem sesja.hardcopy() |
Zrzut okna Gnuplota z wykresem |
---|---|
Pakiet pyplot jest jedną z części dużej biblioteki MatPlotLib, zapewniającej komplet środków do tworzenia dwuwymiarowej grafiki wektorowej i prezentacyjnej. Zestawem narzędzi i możliwościami MatPlotLib bardziej przypomina edytory użytkowe grafiki wektorowej, niż typowe moduły prezentacji danych. Rysunki tworzone przy użyciu MatPlotLib wyróżniają się przy tym bardzo wysoką jakością.
MatPlotLib posiada obszerną dokumentację,
dostępną w wersji online na stronie dystrybucyjnej pakietu,
a wersji skróconej — w ramach polecenia help
.
MatPlotLib współpracuje z pakietem NumPy i wymaga jego instalacji.
pyplot jest rozbudowanym pakietem i może być używany
na wiele sposobów. Przy tym rzeczy proste da się w nim uzyskać bardzo łatwo.
W najprostszym ujęciu, w danej chwili pyplot obsługuje
tworzenie jednego rysunku, w którego skład wchodzi jeden lub więcej wykresów.
Podstawowym poleceniem kreślenia jest pyplot.plot()
.
Rysunek staje się widoczny po użyciu polecenia pyplot.show()
.
W przeciwieństwie do pakietu Gnuplot-py, który akceptuje rysowanie danych opisanych przez tablice punktów, podstawową strukturą danych dla pyplota są jednowymiarowe tablice liczb. Zatem zamiast przykładowej listy punktów
[(0.0, 0.0), (0.25, 0.125), (0.5, 0.25), (0.75, 0.5625), (1.0, 1.0)]
pyplot będzie „wolał” dane w postaci dwóch ciągów wartości współrzędnych x i y:
[0.0, 0.25, 0.5, 0.75, 1.0] [0.0, 0.125, 0.25, 0.5625, 1.0]
Przyczyna leży w zamiarze twórców pyplota, pragnących ułatwić korzystanie z pakietu użytkownikom środowiska obliczeniowego MatLab.
W seriach przekazywanych do narysowania może brakować niektórych wartości.
Braki takie są oznaczane za pomocą wartości None
.
Na wykresie nie są one rysowane.
Rysunek pyplota jest do pewnego stopnia interaktywny;
po wykonaniu polecenia pyplot.show()
program generuje okno
przedstawiające rysunek. Jest ono wyposażone w kontrolki, za których pomocą
da się ustalić wybrane parametry konfiguracji, skalować rysunek, a także zapisać
go w pliku graficznym.
Wśród formatów wynikowych pyplota są najważniejsze formaty grafiki wektorowej, m.in. PostScript, PDF i SVG, oraz grafiki rastrowej, m.in. PNG i JPEG.
matplotlib.pyplot
Wynikami funkcji pyplota są obiekty składowe tworzonego rysunku. Fakt ten pomijamy w opisach, gdyż przy tworzeniu grafiki o elementarnym charakterze i stopniu złożoności nie będzie on wykorzystywany.
Teksty powinny być przekazywane na rysunek jako dane unikodowe.
import matplotlib.pyplot
from matplotlib import pyplot
pyplot
do pamięci programupyplot.plot(dane, opcje)
dane
pyplot.plot(x, y, opcje)
x
i rzędnych z listy y
. Obie listy muszą składać się z liczb
i mieć tę samą długośćpyplot.plot(x1, y1, opcje1, x2, y2, opcje2,
itd.)
opcje
polecenia plot
'g-s'
oznacza linię ciągłą ze znacznikami w kształcie kwadratów barwy zielonej,
natomiast 'bo'
oznacza niebieskie kółka nie połączone linią
pyplot.title(tekst)
pyplot.xlabel(tekst)
pyplot.ylabel(tekst)
pyplot.axis([x0, x1, y0, y1])
pyplot.axis('equal')
pyplot.grid()
pyplot.text(x, y, tekst)
pyplot.legend(legenda)
pyplot.savefig(nazwa_pliku)
pyplot.show()
pyplot.close()
Cztery przykłady poniżej prezentują użycie modułu pyplot
do utworzenia podstawowych typów wykresów stosowanych w prezentacji danych.
Użyte w nich dane nie mają żadnego realnego znaczenia.
Dodatkowo zademonstrowano w nich (w ograniczonym zakresie)
sterowanie fontem użytym do opisu rysunków.
Kreślenie figur geometrycznych | Wykres słupkowy |
---|---|
Rysunki wygenerowane poleceniem pyplot.savefig()
(format oglądanego rysunku zależy od konfiguracji przeglądarki) |
|
Wykres liniowy | Wykres punktowy |
Rysunki wygenerowane poleceniem pyplot.savefig()
(format oglądanego rysunku zależy od konfiguracji przeglądarki) |
|
Dwa następne przykłady pokazują, jak użyć pyplot
-a
do wizualizacji danych o realnym znaczeniu: obliczonych przez
pewien proces albo pobranych z zewnętrznego źródła.
Elementarny wykres funkcji | Prezentacja wyników zapytania do bazy danych |
---|---|
Plik z bazą danych wykorzystaną w tym przykładzie można
pobrać z serwera. | |
Wykres wygenerowany poleceniem pyplot.savefig() (oglądasz zawartość pliku PDF albo PNG, zależnie od konfiguracji przeglądarki) |
Zrzut ekranowy okna otrzymanego poleceniem pyplot.show() |
Temat jest tak obszerny, że wykracza poza ramy tego opracowania. Poprzestaniemy na minimalnym zestawie informacji, uzupełnionym elementarnymi przykładami. Najpierw opiszemy, jak wygenerować prosty rysunek DXF dla środowiska CAD. Następnie wskażemy sposób komunikowania się z sesją AutoCADa, bez omawiania szczegółów. O poważnych zastosowaniach możemy porozmawiać przy innej okazji.
Rysunki w formacie wektorowym DXF (ang. ‘Drawing Interchange Format’) opracowanym przez firmę Autodesk są rozumiane przez większość współczesnych środowisk projektowych typu CAD. Jest to format tekstowy, co znakomicie ułatwia tworzenie plików graficznych przez niezależne oprogramowanie.
Z poziomu Pythona rysunki DXF da się tworzyć na kilka sposobów.
Jedna z możliwości polega na użyciu modułu sdxf
.
Jest to niewielki (22 kB kodu) moduł opracowany w roku 2005 przez pracującego w Holandii
grafika i programistę Stani Michielsa. Moduł sdxf
mieści się w pojedynczym pliku
sdxf.py
i nie wymaga żadnych zabiegów instalacyjnych.
(Ten sam kod źródłowy pod nazwą dxfLibrary.py
wchodził w skład pakietu graficznego
Blender. Obecnie jego rolę przejęły inne, podobne moduły.)
Funkcje zadeklarowane w module sdxf
pozwalają utworzyć w pamięci
programu obiekt reprezentujący dwu- albo trójwymiarowy rysunek wektorowy,
uzupełniać go o wybrane typowe elementy rysunku CAD-owskiego,
oraz zapisać go w postaci pliku graficznego w formacie DXF.
W module sdxf
nie ma narzędzi pozwalających na odczytanie rysunku z pliku
ani narzędzi wizualizacyjnych.
Niżej zamieszczamy skrótowy (a zatem niekompletny) opis użytkowania
modułu sdxf
zakończony prostym przykładem.
import sdxf
rys = sdxf.Drawing(fileName = nazwa)
rys.save()
rys.saveas(nazwa)
obiekt.append(element)
element
.
Elementy te są generowane przez odrębne funkcje, wymienione niżejsdxf.Block(name = nazwa)
sdxf.Layer(name = nazwa)
sdxf.Point(point = (x,y) )
sdxf.Point(point = (x,y,z) )
sdxf.Line(points = [(x1,y1), (x2,y2)])
sdxf.Line(points = [(x1,y1,z1), (x2,y2,z2)])
sdxf.PolyLine(points = [(x,y), …, (x,y)])
sdxf.PolyLine(points = [(x,y,z), …, (x,y,z)])
sdxf.Arc(center = (x,y), radius = r, startAngle = a, endAngle = b)
sdxf.Arc(center = (x,y,z), radius = r, startAngle = a, endAngle = b)
sdxf.Circle(center = (x,y), radius = r)
sdxf.Circle(center = (x,y,z), radius = r)
sdxf.Rectangle(point = (x,y), width = szer, height = wys)
sdxf.Rectangle(point = (x,y,z), width = szer, height = wys)
sdxf.Face(points = [(x,y,z), …, (x,y,z)])
sdxf.Solid(points = [(x,y,z), …, (x,y,z)])
sdxf.Text(text = komunikat, point = (x,y) )
sdxf.Text(text = komunikat, point = (x,y,z) )
sdxf.MText(text = komunikat, point = (x,y) )
sdxf.MText(text = komunikat, point = (x,y,z) )
append()
Załączone kody źródłowe ilustrują zastosowanie modułu sdxf
do wygenerowania prostych rysunków (por.
„dyżurne przykłady” rysunków
w dziale poświęconym grafice wektorowej).
Przykład z lewej strony dotyczy utworzenia symbolu Yang-yin;
przykład z prawej demonstruje utworzenie wielokąta płaskiego
na podstawie dostarczonej listy współrzędnych wierzchołków.
W drugim wierszu tabeli dostępne są pliki wynikowe w formacie DXF; wiersz trzeci przedstawia okna aplikacji użytkowej (AutoCAD lub Blender) po otwarciu tych plików.
Inna stosunkowo prosta metoda tworzenia plików DXF
polega na wykorzystaniu pakietu graficznego Gnuplot.
Jest on wyposażony w końcówki (terminale) do generowania danych graficznych
w różnych formatach. Format DXF jest obsługiwany przez
terminal o nazwie dxf
, włączany poleceniem Gnuplota
set terminal dxf
.
Metody współpracy kodu Pythona z Gnuplotem zostały omówione w podrozdziałach 11.2.2. i 11.5.1.
Ponieważ Gnuplot jest narzędziem do prezentacji graficznej danych, użycie go jest szczególnie wygodne wtedy, kiedy rysunki mają zawierać przede wszystkim wykresy. Jednak utworzenie tą samą metodą typowego kompletnego rysunku technicznego również jest możliwe.
Środowiska CAD mają na ogół własne języki skryptowe; do najbardziej ugruntowanych w tradycji należy dialekt LISPa zwany AutoLISP, opracowany pierwotnie dla systemu AutoCAD. Niektóre pakiety użytkowe CAD są wyposażone w interfejsy programistyczne korzystające z protokołów dostarczanych przez system operacyjny, takich jak wspomniany już COM lub .NET. W takim przypadku dostęp do środowiska roboczego z poziomu programu napisanego w Pythonie nie stwarza zasadniczych trudności — co nie znaczy, że jest banalny.
W tym podrozdziale podamy kilka szczegółów technicznych pozwalających na podstawową komunikację z sesją AutoCADa za pomocą interfejsu COM. Podobne metody powinny umożliwić także współpracę z innymi środowiskami CAD wyposażonymi w interfejs COM, np. IntelliCAD czy też BricsCAD.
Pełna współpraca programu Pythonowego z AutoCADem
wymaga wymiany informacji o typach danych charakterystycznych dla AutoCADa.
Funkcja ta nie jest w pełni realizowana w module win32com.client
. Pełną obsługę
konwersji typów znajdziemy w podobnym pod względem przeznaczenia i sposobu użycia
module comtypes.client
. Jest on częścią odrębnego pakietu
ComTypes i w związku z tym wymaga osobnej instalacji.
import win32com.client
win32com
. Używanie obu modułów naraz nie jest wskazaneimport comtypes.client
comtypes
. Używanie obu modułów naraz nie jest wskazanesesja = win32com.client.Dispatch(nazwa)
sesja
umożliwiający komunikację z jej sesją.
nazwa
sesja = win32com.client.Dispatch('AutoCAD.Application')
sesja = comtypes.client.CreateObject(nazwa)
sesja
umożliwiający komunikację z jej sesją.
W przeciwieństwie do win32com.client.Dispatch()
zawsze uruchamia nowy proces aplikacji.
sesja = win32com.client.GetActiveObject(nazwa)
sesja = comtypes.client.GetActiveObject(nazwa)
sesja
umożliwiający komunikację z jej sesją.
W przeciwieństwie do win32com.client.Dispatch()
nie podejmuje próby uruchomienia aplikacji.
sesja.Quit()
sesja.Documents
rys = sesja.Documents.Open(nazwa)
rys = sesja.Documents.Add(nazwa)
rys = sesja.ActiveDocument
rys
reprezentujący bieżący
rysunek w sesji programurys.Save()
rys
w przypisanym mu plikurys.SaveAs(nazwa)
rys
w pliku o wskazanej nazwierys.Close()
rys
warstwy = rys.Layers
rys.Layers.Count
warstwa = rys.Layers(numer)
model = rys.ModelSpace
model.Count
element = model.Item(numer)
element.ObjectName
element.GetBoundingBox()
element.Delete()
kopia = element.Copy()
element.Move(FromPoint, ToPoint)
element.Rotate(BasePoint, RotationAngle)
element.TransformBy(Macierz)
element.Update()
element = model.AddPoint(Point)
element = model.AddLine(StartPoint, EndPoint)
element = model.AddPolyline(Points)
element = model.AddSpline(Points, KierPocz, KierKon)
element = model.AddCircle(Center, Radius)
element = model.AddArc(Center, Radius, StartAngle, EndAngle)
element = model.AddText(TextString, Point)
comtypes.gen.AutoCAD
comtypes
.
Szczegóły wykorzystania zilustrowano w załączonym niżej przykładzie
obiekt.QueryInterface(typ)
comtypes
. Nie ma jej w obiektach otrzymanych za pomocą
pakietu win32com
.
Szczegóły wykorzystania zilustrowano w załączonym niżej przykładzie
obiekt = win32com.client.CastTo(element, typ)
element
generuje powiązany z nim obiekt wskazanego typu. Argument typ
musi
być nazwą typu obiektu, znaną interfejsowi COM w chwili wykonywania konwersji
(nie jest to typ danych znany z Pythona). Szczegóły wykorzystania zilustrowano
w załączonym niżej przykładzie
Wyróżnione argumenty
funkcji generujących
obiekty graficzne nie są zwykłymi danymi liczbowymi, tekstowymi, listami ani krotkami,
tylko obiektami w odpowiednich formatach wymaganych przez AutoCADa.
Przy określaniu ich wartości niezbędna jest odpowiednia konwersja danych ze standardowych
typów Pythona.
win32com.client
nie posiada mechanizmów do przeprowadzenia tej konwersji.comtypes.client
umożliwia tę konwersję za pomocą typu array
zdefiniowanego w standardowym module Pythona o nazwie array
.
Wyróżnione elementy
rysunku otrzymywane jako
wartości metod obiektów COM są obiektami interfejsu COM.
Ich szczegółowy format jest określony przez wewnętrzne procedury AutoCADa.
Dla dogodnego odczytania lub modyfikacji ich wartości niezbędna jest ich konwersja
do postaci zrozumiałej dla Pythona.
win32com.client
do przeprowadzenia takiej konwersji służy funkcja
win32com.client.CastTo(obiekt, nazwa_typu)
, przy czym
nazwa_typu
jest ciągiem znaków;comtypes.client
do przeprowadzenia takiej konwersji
służy metoda obiekt.QueryInterface(typ)
,
przy czym argument typ
powinien być elementem kolekcji typów
właściwych dla danej aplikacji; w przypadku AutoCADa
znajduje się ona w module comtypes.gen.AutoCAD
.Wynik konwersji jest obiektem, zawierającym dane w postaci liczbowej i nadal powiązanym z rysunkiem. Za jego pośrednictwem możliwa jest modyfikacja rysunku.
Przedstawiony niżej krótki program wywołuje sesję AutoCADa, wczytuje do niej plik DXF, przegląda jego elementy i niektóre z nich modyfikuje, po czym zapisuje rysunek w formacie DWG i kończy pracę.
Program w pierwszej wersji korzysta z modułu Biblioteka ComTypes wymaga osobnej instalacji. |
|
Druga wersja programu korzysta ze znanego już nam modułu |
Z oczywistych przyczyn program ten da się uruchomić jedynie w systemach Windows z zainstalowanym oprogramowaniem AutoCAD.
Wtedy, kiedy zależy nam na pobraniu danych z rysunku projektowego, np. w celu dokonania obliczeń na ich podstawie. Wtedy, kiedy istniejący rysunek trzeba wzbogacić lub zaktualizować korzystając z danych spoza środowiska CAD (na przykład — choć nie tylko — z wyników wykonanych przez nas obliczeń).
Wyspecjalizowane moduły, takie jak PyACAD, pozwalają nie tylko na korzystanie w programach Pythona z zasobów AutoCAD lub IntelliCAD, ale także na wywoływanie programów Pythona z wnętrza środowiska CAD. Autor uczciwie przyznaje, że nigdy z nich nie korzystał, i nie jest mu znana ich merytoryczna wartość.
Istnieje także proste środowisko użytkowe CAD w całości napisane w Pythonie. Nosi ono nazwę PythonCAD. Jest ono swego rodzaju ciekawostką, interesującą dla amatorów, lecz zbyt ograniczoną dla zastosowań profesjonalnych. Nie zapewnia też zgodności formatów danych z profesjonalnymi systemami.
numpy
Modułu numpy
warto użyć, kiedy mamy do wykonania
obliczenia wektorowe lub macierzowe.
Do reprezentacji wektorów służą obiekty typu array
.
Podstawowe operacje arytmetyczne są na nich wykonywane wyraz po wyrazie.
array
z modułu numpy
jest czymś innym,
niż typ array
z modułu array
.
Różnice między listami i wektorami zilustrujemy elementarnym przykładem.
import numpy a = [1.0, 2.0, -3.0] # deklaracja listy b = numpy.array([1.0, 2.0, -3.0]) # deklaracja wektora type(a) <type 'list'> type(b) <type 'numpy.ndarray'> a[1] # pobieranie elementu z listy 2.0 b[1] # pobieranie elementu z wektora 2.0 a + a # łączenie list [1.0, 2.0, -3.0, 1.0, 2.0, -3.0] b + b # dodawanie wektorów array([ 2., 4., -6.]) 2 * a # zwielokrotnianie list [1.0, 2.0, -3.0, 1.0, 2.0, -3.0] 2 * b # mnożenie wektora przez skalar array([ 2., 4., -6.]) a ** 2 # nic sensownego Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int' b ** 2 # działania na składowych wektora (tu: potęgowanie) array([ 1., 4., 9.]) a.append(7.0) # dołączanie elementu do listy b.append(7.0) # wektory mają niezmienną liczbę elementów Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'numpy.ndarray' object has no attribute 'append'
Typ array
może służyć także do reprezentacji tablic wielowymiarowych,
ale będą one wyposażone jedynie w obsługę działań typowych dla wektorów,
tj. wyraz po wyrazie. Do wykonywania operacji typowo macierzowych służy inny typ danych.
Do reprezentacji macierzy służą obiekty typu matrix
.
Składają się one z wierszy będących wektorami o jednakowej długości.
Podstawowe operacje arytmetyczne na danych typu matrix
są wykonywane zgodnie z regułami rachunku macierzowego. Posiadają one ponadto
metody dokonujące transponowania, obliczania wyznacznika, odwracania macierzy
i innych operacji algebry liniowej.
Podobnie jak w przypadku obiektów array
, także i macierze mają wymiar
raz na zawsze ustalony w momencie inicjowania. Elementy wektorów i macierzy mogą być
zmieniane dowolnie w drodze przypisywania.
Odmiennie niż w przypadku zagnieżdżonych list, do elementów macierzy odwołujemy się
za pomocą podwójnego indeksu umieszczonego w jednym nawiasie kwadratowym
(np. m[i,j]
, a nie m[i][j]
).
Istnieje też odpowiednik znanej z list i napisów notacji wycinkowej (z dwukropkami) pozwalający odnosić się do podmacierzy, w tym m.in. do pełnych wierszy i kolumn. Nie będziemy go w tym miejscu opisywać.
Poniżej kilka elementarnych przykładów.
import numpy a = numpy.matrix([1.0, 2.0, -3.0]) # deklaracja wektora wierszowego (macierz 1 × n) b = a.transpose() # transpozycja macierzy a matrix([[ 1., 2., -3.]]) b matrix([[ 1.], [ 2.], [-3.]]) a.shape # wymiar macierzy a (3, 1) b.shape # wymiar macierzy b (1, 3) a * b # mnożenie macierzy matrix([[ 14.]]) b * a matrix([[ 1., 2., -3.], [ 2., 4., -6.], [-3., -6., 9.]]) m = numpy.matrix([[1.0,0.0,0.0],[0.0,2.0,0.0],[0.0,0.0,3.0]]) # deklaracja macierzy kwadratowej m matrix([[ 1., 0., 0.], [ 0., 2., 0.], [ 0., 0., 3.]]) m[2, 2] # pobieranie elementu z macierzy 3.0 m[2, 2] = -3 # zmiana wartości elementu macierzy m matrix([[ 1., 0., 0.], [ 0., 2., 0.], [ 0., 0., -3.]]) a * m # mnożenie macierzy matrix([[ 1., 4., 9.]]) m * b matrix([[ 1.], [ 4.], [ 9.]]) m * m # mnożenie macierzy kwadratowej przez siebie matrix([[1., 0., 0.], [0., 4., 0.], [0., 0., 9.]]) m * a # próba pomnożenia macierzy o niezgodnych wymiarach Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python2.5/site-packages/numpy/core/defmatrix.py", line 263, in __mul__ return N.dot(self, asmatrix(other)) ValueError: objects are not aligned m ** (-1) # wyznaczanie macierzy odwrotnej matrix([[ 1. , 0. , 0. ], [ 0. , 0.5 , 0. ], [-0. , -0. , -0.33333333]]) numpy.linalg.det(m) # obliczanie wyznacznika -6.0
Rozwiązanie układu równań liniowych:
import numpy a = numpy.matrix([[2.0, 1.0, 0.0], [1.0, 2.0, 1.0], [0.0, 1.0, 2.0]]) b = numpy.matrix([1.0, 1.0, 2.0]).T x = numpy.linalg.solve(a, b) a matrix([[ 2., 1., 0.], [ 1., 2., 1.], [ 0., 1., 2.]]) b matrix([[ 1.], [ 1.], [ 2.]]) x matrix([[ 0.75], [-0.5 ], [ 1.25]])
Jak już wspomniano wcześniej, moduł numpy
jest wykorzystywany
w wielu innych modułach i bibliotekach Pythona. Dlatego w praktyce
jego instalacja jest koniecznością, nawet jeżeli nie planujemy jego bezpośredniego użycia.
Pamiętajmy też, że operacje wektorowe i macierzowe realizowane za pośrednictwem
numpy
są na ogół o wiele szybsze niż analogiczne operacje programowane
bezpośrednio na listach.
scipy
sympy
Przykładów nie będzie…
Przykładów nie będzie…