Spis treści Skorowidz Poziom główny Poziom nadrzędny ©
«« Początek »» Koniec

Moduły i pakiety, czyli zabawy cudzymi zabawkami

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ń…

Interfejs użytkownika

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.

Interfejs strumieniowy

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.

Przykład

[]

Kiedy program powinien być wyposażony w interfejs strumieniowy?

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.

Interfejs plikowy

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.

Kiedy program powinien być wyposażony w interfejs plikowy?

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.

Interfejs wiersza poleceń

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.

Przykład

[]

Kiedy program powinien być wyposażony w interfejs wiersza poleceń?

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ń.

Elementy graficznego interfejsu użytkowego

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
deklaracja użycia biblioteki easygui
easygui.msgbox(komunikat)
Okno z komunikatem wymagające potwierdzenia
wynik = easygui.boolbox(komunikat, tytuł okna, lista opcji)
decyzja typu Tak/Nie (ściślej: wynik = czy wybrano pierwszą z podanych opcji)
wynik = easygui.indexbox(komunikat, tytuł okna, lista opcji)
wybór z listy; zwraca numer wybranej opcji (numeracja jak zwykle od zera)
wynik = easygui.choicebox(komunikat, tytuł okna, lista opcji)
wybór z listy; wynik zwraca tekst wybranej wartości
nazwapliku = easygui.fileopenbox(None, tytuł okna, domyślna nazwa pliku)
dialog wyboru nazwy otwieranego pliku; po zatwierdzeniu zwraca pełną ścieżkę dostępu do wskazanego pliku. Nie pozwala wybrać nazwy pliku nieistniejącego
nazwapliku = easygui.filesavebox(None, tytuł okna, domyślna nazwa pliku)
dialog wyboru nazwy zapisywanego pliku; w przeciwieństwie do poprzedniego dialogu pozwala podać legalną nazwę nieistniejącego pliku, zaś w przypadku wybrania pliku istniejącego ostrzega przed niebezpieczeństwem nadpisania go
wynik = easygui.enterbox(komunikat, tytuł okna, wartość domyślna)
okno dialogowe umożliwiające wprowadzenie danej tekstowej
wynik = easygui.integerbox(komunikat, tytuł okna, wartość domyślna, ograniczenie dolne, ograniczenie górne)
okno dialogowe umożliwiające wprowadzenie danej całkowitoliczbowej
wynik = easygui.multenterbox(komunikat, tytuł okna, lista etykiet, lista wartości)
deklaruje formularz z wieloma polami wejściowymi opisanymi przez lista etykiet, o wartościach domyślnych zadanych przez lista wartości. Wynik jest listą wartości tekstowych wczytanych z formularza po jego zatwierdzeniu.
easygui.textbox(komunikat, tytuł okna, tekst)
definiuje okno z wyświetlaną zawartością tekstową; tekst może być napisem lub listą napisów dowolnej długości
inne…
patrz http://www.ferg.org/easygui/ oraz http://easygui.sourceforge.net

Przykład

Dialogi pozwalające na wybór pliku danych i pliku wynikowego

[] []

Kiedy program powinien być wyposażony w interfejs graficzny?

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.

Profesjonalne środowiska graficzne

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:

[ logo Tk ] biblioteka Tk [» przykład dla ciekawskich »]
obsługiwana z poziomu Pythona za pośrednictwem modułu Tkinter. Wchodzi on w skład każdej typowej instalacji Pythona.
[ logo GTK+ ] biblioteka GTK+ [» przykład dla ciekawskich »]
opracowana pierwotnie na potrzeby pakietu graficznego GIMP. Obecnie używana w wielu programach i systemach użytkowych (m.in. w środowiskach graficznych GNOME i XFCE) Wersje 2.x biblioteki GTK+ były obsługiwane z poziomu Pythona za pośrednictwem modułu 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.
[ logo Qt ] biblioteka Qt [» przykład dla ciekawskich »]
opracowana przez firmę Trolltech, obecnie jest własnością firmy Digia. Używana m.in. w środowisku graficznym KDE, w niektórych urządzeniach przenośnych, a także w przeglądarce internetowej Opera. Obsługiwana z poziomu Pythona za pośrednictwem bibliotek PyQt (której rozwojem zarządza firma Riverbank Computing), oraz PySide (zarządzaną przez społeczność programistów skupioną wokół projektu Qt).
[ logo wxWidgets ] biblioteka WxWidgets [» przykład dla ciekawskich »]
jest przenośną nakładką na kilka bibliotek systemowych w różnych systemach operacyjnych. Obsługiwana z poziomu Pythona za pośrednictwem modułu 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.

Kiedy program powinien korzystać z profesjonalnych środowisk graficznych?

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.

Uruchamianie zewnętrznych programów

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 instrukcji
program
ścieżka dostępu do pliku wykonywalnego
argumenty
  • muszą tworzyć listę (taką samą, jak tablica sys.argv)
  • pierwszy element: nazwa programu
  • dalsze elementy: argumenty wywołania

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:

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:

Moduł os zawiera także wiele innych pożytecznych funkcji. Zainteresowanych odsyłamy do dokumentacji.

Przykład: prezentacja zawartości tekstowego pliku wynikowego

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.

Przykład: utworzenie wykresu na podstawie wyników obliczeń

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)
[] [] [ wykres 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.

Kiedy program powinien uruchamiać inne programy za pomocą funkcji systemu operacyjnego?

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.

Bazy danych

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
wczytuje moduł i umożliwia korzystanie z jego zasobów.
polaczenie = nazwa_modułu.connect(opcje)
każdy moduł zgodny z Python DB API Spec. ma metodę 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()
kursor jest obiektem służącym do bezpośredniej komunikacji z bazą.
wynik = kursor.execute(zapytanie)
przesyła zapytanie SQL do bazy i odbiera obiekt z tablicą wyników. Do odczytania wyników można także użyć obiektu kursor za pomocą którego zostało wysłane zapytanie.
wynik.description lub
kursor.description
udostępnia nagłówek tabeli wynikowej z nazwami kolumn. 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ź.
wynik.fetchall() lub
kursor.fetchall()
udostępnia zawartość całej tabeli wynikowej. Inne podobne metody umożliwiają pobranie części wyników. 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ź.
wynik.fetchone() lub
kursor.fetchone()
udostępnia kolejny jeden wiersz tabeli wynikowej. W sytuacji, kiedy wszystkie wiersze zostały już pobrane, zwraca wartość 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()
ostatecznie zatwierdza zmiany stanu bazy spowodowane żądaniami wysłanymi przy użyciu kursorów należących do obiektu połączenie. Istnieją opcje pozwalające wyłączyć konieczność zatwierdzania transakcji.
polaczenie.rollback()
ostatecznie porzuca niezatwierdzone zmiany stanu bazy spowodowane żądaniami wysłanymi przy użyciu kursorów należących do obiektu połączenie. Istnieją opcje pozwalające wyłączyć konieczność zatwierdzania transakcji.
kursor.close()
zamyka obiekt kursora.
polaczenie.close()
zamyka połączenie z bazą.

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.

Kiedy program powinien współpracować z bazą 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).

PostgreSQL

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.

Pakiet PsycoPg

PsycoPg jest zgodny z zaleceniami Python Database API Specification.

import psycopg2
wczytuje moduł i umożliwia korzystanie z jego zasobów.
polaczenie = psycopg2.connect(user = użytkownik, password=hasło, host=adres serwera, database=nazwa bazy, sslmode='prefer')
otwiera komunikacją z bazą o podanych parametrach połączenia sieciowego.

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.

Moduł pgdb z pakietu PygreSQL

Inną 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
wczytuje moduł pgdb i umożliwia korzystanie z jego zasobów.
polaczenie = pgdb.connect(opcje, użytkownik, hasło, adres serwera, nazwa bazy)
nawiązanie połączenia z serwerem

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.

Moduł pg z pakietu PygreSQL

Moduł 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
wczytuje moduł pg i umożliwia korzystanie z jego zasobów.
polaczenie = pg.connect(nazwa bazy, adres serwera, port sieciowy, opcje, użytkownik, hasło)
nawiązanie połączenia z serwerem
polaczenie.close()
rozłączenie z serwerem
wyniki = pg.query(zapytanie)
przekazanie zapytania SQL i odbiór odpowiedzi
wyniki.ntuples()
obliczenie ilości wierszy odpowiedzi
naglowki = wyniki.listfields()
tworzy listę z tekstowymi nagłówkami kolumn odpowiedzi
dane = wyniki.getresult()
tworzy listę zawierającą rekordy odpowiedzi. Każdy rekord jest listą pól; typy jej elementów są zgodne z typami wartości pobranych z bazy
del obiekt
zwolnienie obiektu (jak wszędzie w Pythonie)

Przykłady

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 psycopg2

[]

Dostęp do bazy systemu PostgreSQL za pomocą modułu pgdb

[]

Dostęp do bazy systemu PostgreSQL za pomocą modułu pg

SQLite

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).

Moduł 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
wczytuje moduł sqlite3 i umożliwia korzystanie z jego zasobów.
polaczenie = sqlite3.connect(nazwa_pliku)
otwiera połączenie z bazą umieszczoną w pliku o podanej nazwie. W przypadku, kiedy plik nie istnieje, zostanie podjęta próba jego utworzenia.

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.

Przykład

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 sqlite3

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ęć.

Bazy danych innych systemów

Inne popularne systemy obsługi baz danych, np.

Moduły: MySQLdb, KInterbasDB i pyodbc są zgodne z zaleceniami Python Database API Specification.

Przykładów nie będzie…

Arkusze kalkulacyjne

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.

Kiedy program powinien współpracować ze środowiskiem arkusza kalkulacyjnego?

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.

Komunikacja z sesją Excela

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).

Dostęp do sesji Excela za pomocą COM

Zarządzanie sesją programu
import win32com.client
albo
import comtypes.client
umożliwia korzystanie z COM przez program Pythona. Używamy tylko jednego z tych modułów; nigdy obu naraz
sesja = win32com.client.Dispatch(nazwa)
uruchamia aplikację, albo nawiązuje łączność z uruchomionym wcześniej procesem aplikacji, i tworzy obiekt sesja umożliwiający komunikację z jej sesją.
nazwa
zarejestrowana w systemie (inna niż nazwa polecenia), np. dla Excela jest to
sesja = win32com.client.Dispatch('Excel.Application')
sesja = comtypes.client.CreateObject(nazwa)
uruchamia aplikację i tworzy obiekt i tworzy obiekt 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)
albo
sesja = comtypes.client.GetActiveObject(nazwa)
nawiązuje łączność z uruchomionym wcześniej procesem aplikacji, i tworzy obiekt sesja umożliwiający komunikację z jej sesją. W przeciwieństwie do win32com.client.Dispatch() nie podejmuje próby uruchomienia aplikacji.
Zarządzanie dokumentami w sesji Excela
sesja.Visible
zwraca status widoczności okna uruchomionej aplikacji. Przypisanie 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ć wygodne
sesja.Quit()
kończy pracę aplikacji
sesja.Workbooks
lista otwartych dokumentów
skoroszyt = sesja.Workbooks.Add()
tworzy nowy dokument
skoroszyt = sesja.Workbooks.Open(nazwapliku)
otwiera dokument z pliku (domyślnie xls)
skoroszyt.Save()
zapisuje dokument ze skoroszytem w pliku pod domyślną lub dotychczasową nazwą
skoroszyt.SaveAs(nazwapliku, format)
zapisuje dokument w pliku pod nową nazwą; argument format można pominąć
skoroszyt.Close()
zamyka dokument, w razie potrzeby pytając o zapisanie zmian. Wariant skoroszyt.Close(True) wymusza zapisanie do pliku; wariant skoroszyt.Close(False) wymusza zamknięcie bez zapisywania
Zarządzanie arkuszami w skoroszycie
skoroszyt.Worksheets()
lista arkuszy w skoroszycie
skoroszyt.Worksheets.Count
liczba arkuszy w dokumencie
arkusz = skoroszyt.Worksheets(numer)
daje dostęp do arkusza o wskazanym numerze. Arkusze są numerowane kolejnymi liczbami całkowitymi począwszy od 1 (inaczej niż w OpenOffice). Numeracja jest zgodna z wizualnym porządkiem zakładek
arkusz = skoroszyt.Worksheets(nazwa)
daje dostęp do arkusza o wskazanej nazwie
arkusz = skoroszyt.Worksheets.Add()
tworzy nowy arkusz w dokumencie
skoroszyt.Worksheets(numer).Delete()
usuwa arkusz z dokumentu
Zarządzanie zawartością pojedynczego arkusza
arkusz.Name
przechowuje nazwę arkusza
arkusz.Cells(wiersz, kolumna)
daje dostęp do komórek w arkuszu
  • podwójna numeracja: wiersz, kolumna (inaczej niż w OpenOffice)
  • numeracja rozpoczyna się od 1 (inaczej niż w OpenOffice)
arkusz.Range(zakres)
reprezentuje zespół komórek, czyli blok, adresowany tak samo, jak w interfejsie użytkownika, za pomocą danej typu str, np. obszar = arkusz.Range("a1:d3")
arkusz.Range(komórka1, komórka2)
reprezentuje zespół komórek, czyli blok, zdefiniowany przez wskazanie dwóch narożnych komórek będących obiektami typu Cell, np. obszar = arkusz.Range(arkusz.Cells(1,1), arkusz.Cells(3,4))
obszar.Cells(wiersz, kolumna)
daje dostęp do komórek w bloku — analogicznie jak w całym arkuszu. Numeracja jest liczona względem bloku i rozpoczyna się od 1
komorka.Value
  • reprezentuje wartość komórki
  • da się jej używać jak zmiennej
  • zmienna = komorka.Value
  • komorka.Value = wyrażenie
  • róbmy to tylko przy pobieraniu danych i wysyłaniu wyników — nie utrwalajmy tym sposobem wyników obliczeń pośrednich
Oprócz Value, komórki mają inne właściwości
format (bardzo rozbudowany)
formuła
których nie omawiamy
Oprócz arkuszy, skoroszyty posiadają inne elementy składowe
  • np. wykresy
  • których nie omawiamy

Przykład

[]

Bezpośrednie zarządzanie zawartością skoroszytów XLS oraz Office Open XML (xlsx)

Moduły 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…

Moduły 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…

Komunikacja z sesją OpenOffice

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.

Dostęp do sesji arkusza kalkulacyjnego OpenOffice Calc przez moduł PyUNO

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.

Nie zapomnij wcześniej uruchomić serwera danych
(szczegóły w przykładzie niżej)
import uno
wczytuje moduł uno do środowiska Pythona
sesja = …
kod inicjujący połączenie z serwerem; na szczęście można go skopiować z gotowca:
localContext = 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)
Otrzymany w jego wyniku obiekt sesja reprezentuje sesję aplikacji OpenOffice — za jej pośrednictwem można zarządzać dowolnymi dokumentami, m.in. skoroszytami arkusza kalkulacyjnego
Zarządzanie skoroszytami
skoroszyt = sesja.getCurrentComponent()
udostępnia bieżący dokument (niekoniecznie będący skoroszytem)
skoroszyt = loadComponentFromURL('private:factory/scalc', '_blank', 0, ())
tworzy nowy skoroszyt
skoroszyt = loadComponentFromURL('file:///sciezka-do-pliku', '_blank', 0, ())
otwiera istniejący dokument. Można podać sieciowy adres URL. W przypadku otwierania pliku o formacie innym niż domyślny, ostatni argument — wyżej równy pustej liście () — opisuje sposób importowania danych (zagadnienia importu nie omawiamy)
skoroszyt.store()
zapisuje dokument ze skoroszytem w pliku pod domyślną lub dotychczasową nazwą
skoroszyt.storeAsURL(nazwapliku, opcje)
zapisuje dokument w pliku pod nową nazwą. Za pomocą argumentu 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)
zamyka dokument. Wartość logiczna 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).
Zarządzanie arkuszami w skoroszycie
skoroszyt.Sheets
kolekcja arkuszy w skoroszycie
skoroszyt.Sheets.Count
liczba arkuszy w dokumencie
arkusz = skoroszyt.Sheets.getByIndex(numer)
daje dostęp do arkusza o wskazanym numerze. Arkusze są numerowane kolejnymi liczbami całkowitymi począwszy od 0 (inaczej niż w Excelu). Numeracja jest zgodna z wizualnym porządkiem zakładek
arkusz = skoroszyt.Sheets.getByName(nazwa)
daje dostęp do arkusza o wskazanej nazwie
skoroszyt.Sheets.insertNewByName(nazwa, pozycja)
tworzy nowy arkusz w dokumencie
skoroszyt.Sheets.removeByName(nazwa)
usuwa arkusz z dokumentu
Zarządzanie zawartością pojedynczego arkusza
arkusz.Name
przechowuje nazwę arkusza; przypisanie nowej wartości zmienia nazwę
arkusz.setName(nazwa)
zmienia nazwę arkusza
arkusz.getCellByPosition(kolumna, wiersz)
daje dostęp do komórek w arkuszu
  • podwójna numeracja: kolumna, wiersz (inaczej niż w Excelu)
  • numeracja rozpoczyna się od zera (inaczej niż w Excelu)
arkusz.getCellRangeByName(zakres)
reprezentuje zespół komórek, czyli blok, adresowany tak samo, jak w interfejsie użytkownika, za pomocą danej typu str, np. obszar = arkusz.getCellRangeByName("a1:d3")
arkusz.getCellRangeByPosition(kolumna1, wiersz1, kolumna2, wiersz2)
reprezentuje zespół komórek, czyli blok, zdefiniowany przez wskazanie kolumny początkowej, wiersza początkowego, kolumny końcowej oraz wiersza końcowego bloku, np. obszar = arkusz.getCellRangeByPosition(0, 0, 3, 2)
obszar.getCellByPosition(kolumna, wiersz)
daje dostęp do komórek w bloku — analogicznie jak w całym arkuszu. Numeracja jest liczona względem bloku i rozpoczyna się od 0
Wartość komórki
  • zmienna = komorka.getValue()
  • zmienna = komorka.getString()
  • komorka.setValue(wyrażenie)
  • komorka.setString(napis)
  • róbmy to tylko przy pobieraniu danych i wysyłaniu wyników — nie utrwalajmy tym sposobem wyników obliczeń pośrednich
Oprócz zawartości, komórki mają inne właściwości
komorka.NumberFormat
decyduje o formacie prezentacyjnym danych liczbowych. Każdy standardowy format ma swój kod liczbowy, np.
  • wartość 1 oznacza liczbę całkowitą,
  • 2 — liczbę dziesiętną,
  • 60 i 61 — notację półlogarytmiczną,
  • 36 — datę dd.mm.yyyy,
  • 10 i 11 — wartości procentowe,
  • 99 — wartość logiczną, itd.
format (bardzo rozbudowany)
formuła
których nie omawiamy
Oprócz arkuszy, skoroszyty posiadają inne elementy składowe
  • np. wykresy
  • których nie omawiamy

Przykład

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.

[]

Zarządzanie sesją OpenOffice za pośrednictwem interfejsu COM

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.

Kod Pythona w sesji OpenOffice

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.

Przykład

[]

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.

Bezpośrednie zarządzanie zawartością dokumentów formatu OpenDocument

Moduł odfpy

Przykładów nie będzie…

Kod Pythona w sesji arkusza kalkulacyjnego Gnumeric

Przykładów nie będzie… (swoją drogą Gnumerica warto znać — robi bardzo porządne wektorowe wykresy)…

Grafika

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.

Kiedy program powinien współpracować z oprogramowaniem graficznym?

Wtedy, kiedy zamierzamy stosunkowo łatwo wygenerować wykres przedstawiający nasze dane. Innych sytuacji nie omawiamy.

Jeszcze raz Gnuplot

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.

Składnia wybranych elementów pakietu Gnuplot-py

import Gnuplot
ładuje moduł Gnuplot do środowiska wykonawczego
sesja = Gnuplot.Gnuplot()
uruchamia proces Gnuplota i ustanawia obiekt sesja do komunikacji z nim
sesja = Gnuplot.Gnuplot(persist = True)
proces Gnuplota uruchomiony tym sposobem pozostanie aktywny nawet po usunięciu obiektu reprezentującego go w Pythonie
sesja.plot(obiekt)
główna instrukcja służąca do budowania wykresów dwuwymiarowych. 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)
główna instrukcja służąca do budowania wykresów trójwymiarowych. 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)
dodaje 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)
przekazuje tekstowy argument 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)
tworzy obiekt reprezentujący ciąg danych, nadający się do narysowania poleceniem plot, splot albo replot. opcje decydują o wizualnych właściwościach utworzonej serii danych
dane = Gnuplot.File(nazwa_pliku, opcje)
tworzy obiekt reprezentujący serię danych pobraną z pliku o wskazanej nazwie, nadający się do narysowania poleceniem plot, splot albo replot
dane = Gnuplot.Func(tekst, opcje)
tworzy obiekt reprezentujący funkcję, nadający się do narysowania poleceniem plot, splot albo replot
opcje poleceń Gnuplot.Data(), Gnuplot.File(), Gnuplot.Func()
with_ = sposób kreślenia serii
pozwala zdefiniować postać, grubość i kolor linii oraz kształt i wielkość znaczników
title = tekst
określa nazwę serii widoczną w legendzie
using = wykaz kolumn
w przypadku danych pobieranych z pliku określa, które kolumny danych są podstawą tworzonej serii
sesja.title(tekst)
ustalenie tytułu wykresu
sesja.xlabel(tekst)
sesja.ylabel(tekst)
sesja.zlabel(tekst)
ustalenie opisów osi wykresu
sesja.hardcopy(nazwa_pliku, nazwa_terminala)
zapisuje wykres do pliku o wskazanej nazwie i formacie. Format wynikowy zależy od użycia końcówki graficznej, zwanej w Gnuplocie 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()
powyższa sekwencja trzech poleceń zapisuje wykres do pliku o wskazanej nazwie i formacie. Format wynikowy zależy od użycia końcówki graficznej, zwanej w Gnuplocie 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()
kończy działanie Gnuplota
del sesja
kończy działanie Gnuplota, po czym usuwa z pamięci obiekt zarządzający jego sesją

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.

Przykład

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
[ wykres ] [ zrzut ]

Graficzna prezentacja danych za pomocą pyplot-a

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.

Przegląd wybranych poleceń pakietu 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
lub
from matplotlib import pyplot
wczytuje moduł pyplot do pamięci programu
pyplot.plot(dane, opcje)
dołącza do rysunku serię danych o odciętych będących kolejnymi liczbami naturalnymi (począwszy od zera) i rzędnych z listy dane
pyplot.plot(x, y, opcje)
dołącza do rysunku serię danych o odciętych pobranych z listy 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.)
dołącza do rysunku kilka serii danych; każda opisana przez jedną lub dwie tablice liczbowe i opcjonalną dyrektywę formatowania
opcje polecenia plot
tekst opisujący symbolicznie następujące właściwości graficzne serii danych:
  • kolor: co najwyżej jeden z symboli b g r c m y k w
  • typ linii: co najwyżej jeden z symboli - -- -. :
  • typ znacznika: co najwyżej jeden z symboli o + ^ v > < s x D . ,
np. '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)
określa tytuł rysunku
pyplot.xlabel(tekst)
określa opis poziomej osi układu współrzędnych
pyplot.ylabel(tekst)
określa opis pionowej osi układu współrzędnych
pyplot.axis([x0, x1, y0, y1])
określa zakresy obu osi układu współrzędnych. Uwaga: dodanie serii danych do wykresu może zmienić zakresy osi
pyplot.axis('equal')
deklaruje przyjęcie jednakowych długości dla jednostek obu osi (nieskażona skala rysunku)
pyplot.grid()
nakłada siatkę na obszar wykresu
pyplot.text(x, y, tekst)
wstawia we wskazane miejsce na rysunku informację tekstową
pyplot.legend(legenda)
wstawia na wykres legendę z opisem serii danych
pyplot.savefig(nazwa_pliku)
zapisuje rysunek w pliku graficznym o wskazanym formacie
pyplot.show()
przedstawia rysunek w interaktywnym oknie ekranowym. Po jego zamknięciu rysunek jest tracony
pyplot.close()
usuwa z pamięci elementy bieżącego rysunku. Przydatne, jeśli chcemy nakreślić nowy rysunek po zamknięciu albo zapisaniu poprzedniego

Kilka przykładów

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
[ kod źródłowy ] [ kod źródłowy ]
Rysunki wygenerowane poleceniem pyplot.savefig() (format oglądanego rysunku zależy od konfiguracji przeglądarki)
[ wykres ] [ wykres ]
Wykres liniowy Wykres punktowy
[ kod źródłowy ] [ kod źródłowy ]
Rysunki wygenerowane poleceniem pyplot.savefig() (format oglądanego rysunku zależy od konfiguracji przeglądarki)
[ wykres ] [ wykres ]

Przykład

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.
Baza ta zawiera fragment większej bazy używanej podczas zajęć.

[ kod źródłowy ] [ kod źródłowy ]
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()
[ wykres ] [ zrzut ekranu ]

Środowiska CAD

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.

Grafika DXF

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
wczytuje moduł do programu
rys = sdxf.Drawing(fileName = nazwa)
tworzy nowy rysunek
rys.save()
zapisuje rysunek do pliku DXF o domyślnej nazwie
rys.saveas(nazwa)
zapisuje rysunek do pliku DXF o wskazanej nazwie
obiekt.append(element)
dołącza do obiektu (rysunku, warstwy, bloku) obiekt podrzędny (warstwę, blok, obiekt graficzny) opisany przez element. Elementy te są generowane przez odrębne funkcje, wymienione niżej
sdxf.Block(name = nazwa)
tworzy nową grupę obiektów. Grupa może być dołączana do rysunku, warstwy bądź innej grupy. Grupa jest przeznaczona do integrowania obiektów wchodzących w jej skład, więc z samej natury powinna zawierać inne obiekty. Nazwany blok może być wielokrotnie włączany do tego samego rysunku w różnych miejscach
sdxf.Layer(name = nazwa)
tworzy nową warstwę obiektów. Warstwa jest częścią składową rysunku, obiekty graficzne mogą być do niej przypisane
sdxf.Point(point = (x,y) )
sdxf.Point(point = (x,y,z) )
tworzy punkt w przestrzeni dwuwymiarowej albo trójwymiarowej
sdxf.Line(points = [(x1,y1), (x2,y2)])
sdxf.Line(points = [(x1,y1,z1), (x2,y2,z2)])
tworzy odcinek linii prostej łączący podane punkty
sdxf.PolyLine(points = [(x,y), …, (x,y)])
sdxf.PolyLine(points = [(x,y,z), …, (x,y,z)])
tworzy linię łamaną lub gładką łączącą podane punkty
sdxf.Arc(center = (x,y), radius = r, startAngle = a, endAngle = b)
sdxf.Arc(center = (x,y,z), radius = r, startAngle = a, endAngle = b)
tworzy łuk okręgu o zadanych parametrach
sdxf.Circle(center = (x,y), radius = r)
sdxf.Circle(center = (x,y,z), radius = r)
tworzy okrąg o zadanych parametrach
sdxf.Rectangle(point = (x,y), width = szer, height = wys)
sdxf.Rectangle(point = (x,y,z), width = szer, height = wys)
tworzy prostokąt
sdxf.Face(points = [(x,y,z), …, (x,y,z)])
tworzy ścianę obiektu trójwymiarowego
sdxf.Solid(points = [(x,y,z), …, (x,y,z)])
tworzy obiekt trójwymiarowy
sdxf.Text(text = komunikat, point = (x,y) )
sdxf.Text(text = komunikat, point = (x,y,z) )
tworzy wpis tekstowy w okolicy podanego punktu
sdxf.MText(text = komunikat, point = (x,y) )
sdxf.MText(text = komunikat, point = (x,y,z) )
tworzy wielowierszowy wpis tekstowy w okolicy podanego punktu
i inne
generują elementy graficzne przeznaczone do dołączania do głównego obiektu rysunku, do bloku lub do warstwy za pomocą metody append()

Przykład

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.

[ kod źródłowy ] [ kod źródłowy ]
[ plik źródłowy rysunku ] [ plik źródłowy rysunku ]
[ zrzut ekranu ] [ zrzut ekranu ]

Generowanie rysunków DXF za pośrednictwem Gnuplota

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.

Współpraca z aplikacjami CAD

Ś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.

Zarządzanie sesją programu
import win32com.client
umożliwia korzystanie z COM przez program Pythona za pośrednictwem modułu win32com. Używanie obu modułów naraz nie jest wskazane
import comtypes.client
umożliwia korzystanie z COM przez program Pythona za pośrednictwem modułu comtypes. Używanie obu modułów naraz nie jest wskazane
sesja = win32com.client.Dispatch(nazwa)
uruchamia aplikację, albo nawiązuje łączność z uruchomionym wcześniej procesem aplikacji, i tworzy obiekt sesja umożliwiający komunikację z jej sesją.
nazwa
zarejestrowana w systemie (inna niż nazwa polecenia), np. dla AutoCADa jest to
sesja = win32com.client.Dispatch('AutoCAD.Application')
sesja = comtypes.client.CreateObject(nazwa)
uruchamia aplikację i tworzy obiekt i tworzy obiekt 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)
nawiązuje łączność z uruchomionym wcześniej procesem aplikacji, i tworzy obiekt sesja umożliwiający komunikację z jej sesją. W przeciwieństwie do win32com.client.Dispatch() nie podejmuje próby uruchomienia aplikacji.
sesja.Quit()
kończy pracę aplikacji
Zarządzanie rysunkami w sesji programu
sesja.Documents
daje dostęp do kolekcji otwartych rysunków
rys = sesja.Documents.Open(nazwa)
otwiera rysunek z pliku o wskazanej nazwie
rys = sesja.Documents.Add(nazwa)
tworzy nowy, pusty rysunek
rys = sesja.ActiveDocument
tworzy obiekt rys reprezentujący bieżący rysunek w sesji programu
rys.Save()
zapisuje rysunek reprezentowany przez obiekt rys w przypisanym mu pliku
rys.SaveAs(nazwa)
zapisuje rysunek reprezentowany przez obiekt rys w pliku o wskazanej nazwie
rys.Close()
zamyka rysunek reprezentowany przez obiekt rys
Zarządzanie elementami rysunku (wybór)
warstwy = rys.Layers
daje dostęp do kolekcji warstw danego rysunku. Każdy obiekt graficzny jest przypisany do pewnej warstwy
rys.Layers.Count
zwraca liczbę warstw w rysunku
warstwa = rys.Layers(numer)
daje dostęp do warstwy o wskazanym numerze
model = rys.ModelSpace
daje dostęp do przestrzeni modelu danego rysunku. Ta część rysunku zawiera wszystkie obiekty graficzne
model.Count
zwraca liczbę obiektów w przestrzeni modelu
element = model.Item(numer)
pobiera z przestrzeni modelu obiekt o wskazanym numerze
element.ObjectName
zwraca tekst opisujący typ elementu
element.GetBoundingBox()
zwraca informację o współrzędnych narożników prostokąta/kostki ograniczającej element
element.Delete()
usuwa obiekt z rysunku
kopia = element.Copy()
tworzy obiekt będący kopią danego elementu
element.Move(FromPoint, ToPoint)
element.Rotate(BasePoint, RotationAngle)
element.TransformBy(Macierz)
i inne
przekształcają wskazany element rysunku
element.Update()
zatwierdza zmiany w elemencie rysunku i aktualizuje jego wyświetlanie
element = model.AddPoint(Point)
dodaje nowy punkt do modelu
element = model.AddLine(StartPoint, EndPoint)
dodaje nową linię do modelu
element = model.AddPolyline(Points)
dodaje nową linię łamaną do modelu
element = model.AddSpline(Points, KierPocz, KierKon)
dodaje nową linię krzywą sklejaną do modelu
element = model.AddCircle(Center, Radius)
dodaje nowy okrąg do modelu
element = model.AddArc(Center, Radius, StartAngle, EndAngle)
dodaje nowy odcinek łuku do modelu
element = model.AddText(TextString, Point)
dodaje nowy obiekt tekstowy do modelu
comtypes.gen.AutoCAD
kolekcja wewnętrznych typów AutoCADa w ramach pakietu comtypes. Szczegóły wykorzystania zilustrowano w załączonym niżej przykładzie
obiekt.QueryInterface(typ)
metoda konwersji typów dostępna w obiektach COM pakietu 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)
na podstawie obiektu COM danego jako 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

Uwagi

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.

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.

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.

Przykład

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ę.

[ AutoCAD via comtypes ]

Program w pierwszej wersji korzysta z modułu comtypes.client. Umożliwia on, podobnie do win32com, dostęp do obiektów COM, zapewniając przy tym pełną obsługę dynamicznych typów danych generowanych przez aplikacje. Polecamy tę metodę.

Biblioteka ComTypes wymaga osobnej instalacji.

[ AutoCAD via win32com ]

Druga wersja programu korzysta ze znanego już nam modułu win32com.client. Pozwala on przeglądać składniki rysunku i odczytywać ich parametry. Niestety, jego użyteczność w przypadku modyfikacji rysunku jest ograniczona wskutek trudności z zamianą typów obiektowych Pythona na typy obiektowe COM. W obecnym stanie odradzamy komunikację z AutoCADem za pośrednictwem biblioteki win32com, choć sytuacja może ulec poprawie wraz z pojawieniem się jej nowszej wersji.

Z oczywistych przyczyn program ten da się uruchomić jedynie w systemach Windows z zainstalowanym oprogramowaniem AutoCAD.

Kiedy program powinien współpracować z oprogramowaniem CAD?

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ń).

Inne możliwości

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.

Poważne obliczenia

Zaawansowane obliczenia numeryczne: moduł numpy

Modułu numpy warto użyć, kiedy mamy do wykonania obliczenia wektorowe lub macierzowe.

Wektory

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.

Macierze

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.

Dalsze narzędzia do obliczeń i wizualizacji

Przykładów nie będzie…

Przegląd innych zastosowań Pythona

Przykładów nie będzie…

Pytania kontrolne

  1. Co to jest moduł Pythona?
  2. Jak się używa modułu?
  3. Co może zawierać moduł?
  4. Co to jest biblioteka Pythona?
  5. Podaj przykłady wykorzystania bibliotek użytkowych w programach Pythona
  6. Podaj przykłady współpracy programów Pythona z aplikacjami użytkowymi
© Copyright 2000–2016 by Jan Jełowicki, Katedra Matematyki Uniwersytetu Przyrodniczego we Wrocławiu
Ostatnia modyfikacja we wrześniu 2020 r.
janj@aqua.up.wroc.pl
http://karnet.up.wroc.pl/~jasj