Programowanie

.NET

Janusz Gołdasz Iwona Gołdasz .NET + Python = IronPython

laczego ktoś mógłby chcieć korzystać z Py- thona na platformie .NET? Z jakich mecha- D nizmów (narzędziach) dostępnych w świecie .NET może programista Pythona skorzystać? Odpo- wiedź jest jedna: możliwość wyboru. Wybór języka pro- gramowania sprowadza się często do indywidualnych preferencji i cech/typu projektu, nad którym pracujemy. Python w przedstawianej implementacji jest inte- resującym rozwiązaniem na platformy .NET i (fanom Javy polecamy Jythona) i wydaje się, iż ma Rysunek 1. Odtworzony dokument XML wszelkie dane ku temu, aby przyciągnąć do siebie nowych zwolenników. Skryptowy Python, stworzony gnięcia IronPythona do grafiki 3D na przykładzie biblio- we wczesnych latach 90-tych przez Guido van Rosu- teki Irrlicht .NET 3D. Zakładamy opanowanie przez czy- uma, jest dziś wykorzystywany praktycznie na każ- telnika podstaw programowania w Pythonie i .NET. Ko- dej powszechnej platformie (Windows, Max, Lunux/ niecznie trzeba zwrócić uwagę, iż IronPython i CPython Unix) od komputerów stacjonarnych po palmtopy i te- to dwie różne implementacje tego samego języka. Róż- lefony komórkowe (Nokia) w takich dziedzinach jak, nic w chwili obecnej jest sporo – od bardzo trywialnych, wizualizacja i gry, networking, aplikacje webowe, de- które sprowadzają się do wyświetlania różnych komuni- sktopowe, inżynieryjne, bazy danych, etc. Jest uży- katów o błędach, po takie, które wynikają z nieobecno- wany do tworzenia prostych skryptów, jak i dużych ści takiego czy innego modułu, np. cmath lub os. aplikacji (Zope, Plone). Poniżej postaramy się przed- stawić jego implementację na platformę .NET. Narzędzia Standardowo, IronPython (binaria i kody źródłowe do IronPython pobrania z CodePlex) daje użytkownikowi zestaw po- Twórca IronPythona, , jest znany z wcze- dobnych narzędzi programistycznych co inne imple- śniejszych (udanych) implementacji Pythona na maszy- mentacje tego języka (CPython). Do dyspozycji jest nę wirtualną Javy – . Projekt na platformę .NET konsola interpretera poleceń (ipy.exe), a uruchamianie powstał około roku 2003 i jest przykładem świetnej skryptów i poleceń IronPythona odbywa się w znanych i szybkiej implementacji dynamicznego języka skrypto- nam postaciach: wsadowo lub interaktywnie. W chwi- wego w środowisku CLR (platformy .NET i Mono). Inte- li obecnej, jedyne środowisko programistyczne (IDE) resujące jest oparcie się przez twórców na środowisku pozwalające tworzyć/edytować skrypty w tym języku CLR, co w rezultacie zapewnia wykorzystywanie biblio- to Visual Studio 2005 koniecznie w wersji Standard lub tek .NET w tworzonych skryptach, aplikacjach desktopo- wyższej. W chwili obecnej brak wsparcia dla IronPytho- wych i webowych przy użyciu IronPythona. Co ciekawe, na w wersji Visual Studio Express, choć istnieje ono przy zachowaniu pewnych reguł, możliwe jest korzysta- w Web Developer Express (po zainstalowaniu dodatko- nie ze standardowych bibliotek CPythona celem zwięk- wego pakietu integrującego IronPythona z ASP .NET). szania funkcjonalności tworzonej aplikacji o potrzebne Do tworzenia przykładów zawartych w niniejszym ar- elementy. Współpracę IronPythona z .NET pokażemy tykule autorzy korzystali z Visual Studio Express (C#) na kilku wybranych przykładach. Zaczniemy od napisa- oraz popularnego IDLE (Python). nia skryptu w Pythonie w rodzaju prostej przeglądarki pli- ków XML z wykorzystaniem kontrolki TreeView i formula- Skrypty rza Form. Tworzenie nowych rozszerzeń (klas) w C# dla Omówienie IronPythona rozpoczniemy od skryptu, któ- IronPythona pokażemy na przykładzie podobnej aplika- rego działanie polegać ma na odczytaniu pliku XML, cji, gdzie komponent przeglądarki osadzimy tym razem a następnie odtworzeniu jego struktury w kontrolce w wyświetlanym formularzu. Korzystanie z interprete- TreeView jak na Rysunku 1. Treść skryptu przedstawio- ra IronPythona przedstawimy na przykładzie aplikacji – no w Listingu 1. Rozpoczynamy od zapewnienia sobie słownika. Na koniec, omówimy prosty przykład zaprzę- dostępu do standardowych modułów CPythona. Robi- my to przez umieszczenie na początku skryptu nastę- Autorzy są entuzjastami Pythona we wszystkich wcie- pujących wierszy leniach (czytaj: implementacjach). Kontakt: [email protected] import sys sys.path.append(r"c:\python24\lib")

30 www.sdjournal.org Software Developer’s Journal 02/2007 .NET + Python = IronPython

Oczywiście, importujemy moduł clr (CLR), a dostęp do potrzeb- danie odtworzenia struktury dokumentu XML się kończy. Chcąc nych modułów .NET zapewniamy sobie dzięki metodzie clr.Ad- umieścić (wyświetlić) naszą kontrolkę w formularzu, w konstruk- dReferenceByPartialName(...). W naszym skrypcie utworzy- torze klasy HelloXML tworzymy instancję klasy xmlTree i wywołu- my 2 klasy – pierwszą o nazwie xmlTree dziedziczącej po kla- jemy metodę PopulateTree(), Teraz wystarczy tylko dodać ją do sie TreeView, drugą zaś (dziedziczącą po klasie Form) nazwie- formularza i w funkcji Main umieścić znaną skądinąd my HelloXML. Klasa xmlTree posiada 2 zmienne: pathToXML (w której przechowywać będziemy ścieżkę do analizowanego pli- Application.Run(HelloXML(nazwa_pliku_XML)) ku) oraz root, która posłużymy się do przechowania treści doku- mentu (XmlDocument). Odtworzenie struktury dokumentu XML i efekt jest widoczny jak przedstawionej ilustracji w Rysunku 1. najłatwiej rozwiązać rekurencyjnie – stąd obecność w ciele kla- Chcąc sprowokować pojawienie się wyjątku, wywołamy skrypt sy kolejnych metod. Pierwsza z nich o nazwie AddNode(...) po- z nazwą nieistniejącego dokumentu – zob. Rysunek 2. rusza się rekurencyjnie po strukturze (drzewiastej) analizowane- go dokumentu, dodając odwiedzane węzły inXMLNode do kolej- Tworzenie rozszerzeń nych węzłów inTreeNode naszej kontrolki. Druga metoda Popula- Tworzenie rozszerzeń .NET dla IronPythona pokażemy na iden- teTree() to sterownik wczytujący żądany dokument do zmiennej tycznym przykładzie jak poprzednio – zob. Listing 2. Nasze za- root i wywołujący rekurencyjną metodę AddNode(...). Na tym za- danie polega na osiągnięciu identycznej funkcjonalności jak

Listing 1. Pierwszy przykład - xmlTree

import sys self.Nodes.Add(TreeNode(System.Convert.ToString( # Dodajemy dostep do standardowych modulow Pythona detail))) sys.path.append("c:\python24\lib") # Zadaniem rekurencyjnej metody AddNode jest dodawanie # Importujemy … # kolejnych wezlow do kontrolki

import clr def AddNode(self, inXmlNode, inTreeNode): # Formularze, kontrolki tNode = TreeNode() clr.AddReferenceByPartialName("System.Windows.Forms") i = 0 clr.AddReferenceByPartialName("System.Drawing") # Gdy rodzic inXmlNode posiada dzieci, # XML # to rekurencyjnie wedrujemy po drzewku

clr.AddReferenceByPartialName("System.Xml") if (inXmlNode.HasChildNodes): # ...oraz inne potrzebne moduły for node in inXmlNode.ChildNodes: import System xNode = inXmlNode.ChildNodes[i] from System.Windows.Forms import * inTreeNode.Nodes.Add(TreeNode(xNode.Name)) from System.Drawing import * tNode = inTreeNode.Nodes[i] from System.Xml import * self.AddNode(xNode, tNode) # Tworzymy kontrolke przegladarki i+=1 # – dziedziczy po klasie TreeView else: class xmlTree(TreeView): inTreeNode.Text = (inXmlNode.OuterXml) def __init__(self): # Domyslny bezparametrowy konstruktor # Klasa HelloXML dziedziczy po klasie Form self.Nodes.Clear() class HelloXML(Form): self.pathToXml = '' # Sciezka do pliku XML # Konstruktor z parametrem w postaci nazwy pliku XML self.root = None def __init__(self, filename): # W metodzie PopulateTree generujemy structure pliku XML self.xmlTree = xmlTree() # Instancja klasy xmlTree ! def PopulateTree(self): self.xmlTree.pathToXml = filename self.root = XmlDocument() # korzen # Zapelniamy drzewko kontrolki … try: # obsluga wyjatkow self.xmlTree.PopulateTree() self.root.Load(self.pathToXml) # Otwieramy plik self.xmlTree.Dock = DockStyle.Fill # Kasujemy wszystkie istniejace wezly # … i dodajemy je do formularza self.Nodes.Clear() self.Controls.Add(self.xmlTree) # Dodajemy pierwszy wezel (korzen) do drzewa self.Size = Size(300,200) self.Nodes.Add(TreeNode(self.root. self.AutoSizeMode= AutoSizeMode.GrowAndShrink DocumentElement.Name)) # Tu metoda Main jest na zewnatrz klasy tNode = TreeNode() def Main(filename): tNode = self.Nodes[0] # Wskazanie na korzen Application.Run(HelloXML(filename)) # i rekurencyjnie zapelniamy cale drzewo # Na koniec: uruchamiamy skrypt z parametrem z postaci pliku self.AddNode(self.root.DocumentElement, tNode) # XML i wywolujemy metode Main except Exception, detail: if __name__=="__main__": # Komunikujemy blad import sys MessageBox.Show(System.Convert.ToString(detail)) Main(sys.argv[1]) self.Nodes.Clear()

Software Developer’s Journal 02/2007 www.sdjournal.org 31 Programowanie .NET

w poprzednim przykładzie. Tym razem jednak, rozpoczniemy struktury pliku XML (pośrednio, poprzez wywołanie w treści wła- od utworzenia kontrolki przeglądarki i wywołania jej w nowym ściwości metody PopulateTree()). skrypcie. Zaczynamy od utworzenia nowego projektu typu Class Tak utworzoną kontrolkę możemy bezproblemowo użyć Library w Visual Studio i utworzenia nowej klasy o nazwie (nie- w naszym skrypcie, co przedstawia Listing 3. Oprócz oma- spodzianka!) xmlTree. Oczywiście, klasa ta powinna dziedziczyć wianej już zawartości w skrypcie pojawia się referencja do po klasie TreeView. W ciele klasy pojawiają się konstruktor z pa- nowej kontrolki przy użyciu metody AddReferenceToFi- rametrem w postaci nazwy pliku XML, którego strukturę odtwa- le modułu clr. i import klasy xmlTree do naszego skryptu. rzamy oraz prywatne metody o znanej już funkcjonalności i na- W tym przypadku zaczynamy od utworzenia nowej klasy zwach: PopulateTree() i AddNode(XmlNode, TreeNode). Zmienna o nazwie xmlViewer dziedziczącej po klasie Form. W kon- m _ directoryPath posłuży nam do przechowywania nazwy od- struktorze tworzymy instancję kontrolki self.xmTree i odtwa- twarzanego pliku. Dodatkowo, ciało klasy uzupełnimy o nową rzamy strukturę pliku XML. Teraz, wystarczy tylko dodać publiczną właściwość o nazwie newFile. Pozwoli ona nam za- kontrolkę do formularza i po jego wywołaniu uzyskujemy równo na odczyt nazwy analizowanego pliku, jak i odtworzenie identyczny efekt, jak w poprzednim przypadku.

Listing 2. Kontrolka xmlTree – C#

using System.Collections; } using System.ComponentModel; // Obsluga wyjatkow using System.Drawing; catch(XmlException xmlEx) { using System.Windows.Forms; MessageBox.Show(xmlEx.Message); using System.Xml; } // Dziedziczymy do klasie TreeView catch(Exception ex) { public class xmlTree : System.Windows.Forms.TreeView{ MessageBox.Show(ex.Message); // skladowe klasy sciezka do pliku XML (z nazwa) } private string m_directoryPath; } // Bezparametrowy konstruktor // Rekurencyjna metoda kopiujaca strukture dokumentu public xmlTree() { InitializeComponent();} private void AddNode(XmlNode inXmlNode, // Przeciazony konstruktor z parametrem w postaci nazwy TreeNode inTreeNode){ // pliku XML XmlNode xNode; // wezel DOM public xmlTree(string file){ TreeNode tNode; // wezel TreeNode InitializeComponent(); // Inicjalizacja komponentu XmlNodeList nodeList; // lista wezlow DOM m_directoryPath = file; int i; // Odtworzenie struktury pliku XML w kontrolce // Wedrowka po wezlach DOM do czasu napotkania PopulateTree(); // “bezdzietnego” wezla } if (inXmlNode.HasChildNodes){ protected override void Dispose( bool disposing ){ nodeList = inXmlNode.ChildNodes; if( disposing ){ for(i = 0; i<=nodeList.Count - 1; i++){ if( components != null ) xNode = inXmlNode.ChildNodes[i]; components.Dispose(); inTreeNode.Nodes.Add(new TreeNode(xNode.Name)); } tNode = inTreeNode.Nodes[i]; base.Dispose( disposing ); AddNode(xNode, tNode); // Rekurencja… } } private void InitializeComponent(){ } else { // Inicjalizacja konstrolki inTreeNode.Text = (inXmlNode.OuterXml).Trim(); } } // Odtwarzanie struktury pliku XML } private void PopulateTree(){ // new File zwraca nazwe dokumentu, ew. wyswietla/ try{ // generuje strukture nowego pliku XmlDocument dom = new XmlDocument(); public XmlDocument newFile { dom.Load(m_directoryPath); // Pobranie pliku get { this.Nodes.Clear(); return directoryPath; // Tworzymy korzen } this.Nodes.Add(new TreeNode(dom.DocumentElement. set { Name)); m_directoryPath = value; TreeNode tNode = new TreeNode(); PopulateTree(); tNode = this.Nodes[0]; } // Rekurencyjnie wypelniamy kontrolke wezlami } // XmlNode } AddNode(dom.DocumentElement, tNode);

32 www.sdjournal.org Software Developer’s Journal 02/2007 .NET + Python = IronPython

Rysunek 4. Irrlicht .NET

dyfikowany już słownik i wyświetlić go w kontrolce listy lvItems Rysunek 2. IronPython w Visual Studio – Solution Explorer naszego formularza jak na Rysunku 4. Hosting Sam skrypt odczytujący słownik przedstawiony jest w Listin- Bardzo często przy projektowaniu aplikacji mamy do czynienia gu 5. Jego treść nie odbiega wiele od tych, które widzieliśmy do z potrzebą zwiększania funkcjonalności aplikacji czy automaty- tej pory. Oprócz znanych nam konstrukcji potrzebne są nam jesz- zacji określonych działań przy użyciu zewnętrznych skryptów cze typy ogólne (słownik), które importujemy instrukcją (w dowolnym języku). Konieczny jest mechanizm interprete- ra udostępniającego określony interface aplikacji na zewnątrz w from System.Collections.Generic import * treści skryptu i umożliwiający pobranie wyniku działania skryptu a sam słownik inicjujemy następująco z powrotem do otoczenia, z którego dane zostały wysłane. Dzia- dict = Dictionary[Int32,String]() łanie interpretera poleceń IronPythona zilustrujemy na przykła- dzie prostej aplikacji wczytującej zewnętrzny słownik (przy uży- Listing 3. Przykład użycia kontrolki - Python ciu zewnętrznego skryptu .py) i wyświetlającej nowe dane w kon- trolce listy (ListView). Takie podejście pozwala w naturalny spo- import sys # Dostep do standardowych modulow Pythona sób oddzielić logikę biznesową aplikacji od warstwy prezentacyj- sys.path.append(r"c:\python24\lib") nej. Kod aplikacji przedstawiono w Listingu 4. Rozpoczynamy od # Tradycyjnie, importujemy CLR i potrzebne biblioteki .NET utworzenia nowego projektu typu Windows Application w Visu- import clr al Studio o przykładowej nazwie frmAppHost. Aby nasza aplika- clr.AddReferenceByPartialName("System.Windows.Forms") cja była w stanie interpretować wyniki działania wczytywanego clr.AddReferenceByPartialName("System.Drawing") skryptu, do projektu dodajemy referencje do nowych modułów: from System.Windows.Forms import * IronPython.Modules i IronPython.Hosting – zob. Rysunek 2. Na- # Tworzymy referencje do utworzonej kontrolki przegladarki sza aplikacja jest prostym słownikiem, więc na początek dekla- clr.AddReferenceToFile("xmltree.dll") # Import kontrolki rujemy zmienną dictionary klasy Dictionary służącą import xmlTree przechowywaniu wczytywanych słów. W dalszej kolejności two- # Klasa xmlViewer to formularz – dziedziczy po klasie Form rzymy interpreter Pythona o nazwie engine i przekierowujemy class xmlViewer(Form): standardowe we/wy interpretera do nowego pliku instrukcjami # Tworzymy kontruktor z parametrem w postaci nazwy # analizowanego pliku engine.SetStandardOutput(FileStreamObject); def __init__(self, filename): engine.SetStandardError(FileStreamObject); # Tworzymy instancje kontrolki i odtwarzamy structure # pliku XML

Potrzebny nam jeszcze nowy moduł em oraz słownik locals self.xmlTree = xmlTree() do przechowywania eksportowanych zmiennych (słownika – self.xmlTree.newFile = filename words (dictionary), nazwy pliku słownika – myDictionaryFile). # Dodajemy kontrolke przegladarki do formularza Teraz wystarczy wystarczy wykonać skrypt instrukcją engine. self.Controls.Add(self.xmlTree) ExecuteFile("getDictionary.py", em, locals); aby odczytać zmo- # …i ustawiamy parametry formularza self.AutoSize = True self.AutoSizeMode= AutoSizeMode.GrowAndShrink # Identycznie jak poprzednio - Metoda Main

def Main(filename): Application.Run(xmlViewer(filename)) if __name__=="__main__": import sys Main(sys.argv[1]) Rysunek 3. Słownik

Software Developer’s Journal 02/2007 www.sdjournal.org 33 Programowanie .NET

Zwróćmy uwagę na sposób deklaracji typów ogólnych w IronPy- thonie. Działanie skryptu rozpoczynamy od sprawdzenia obec- W Sieci ności słownika myDictionaryFile, aby w dalszej kolejności przejść • http://www.python.org do sekwencyjnego odczytu pliku i zapisu słów do słownika. • http://irrlicht.sourceforge.net Zwróćmy uwagę, że końcowe komunikaty pojawią się w żąda- • http://www.jython.org nym logu application-log.txt. • http://www.codeplex.com/Wiki/View.aspx?ProjectName=IronP ython Zastosowanie: Grafika 3D • http://www.asp.net/ironpython W aspekcie użycia IronPythona w rzeczywistych projektach, po- wstaje oczywiste pytanie, jak wygląda współpraca IronPythona z innymi bibliotekami. Okazuje się, że całkiem nieźle. Do demon- Listing 4. Użycie interpretera poleceń Pythona – C# stracji w naszym przypadku posłużyliśmy się w znanym środowi- skiem graficznym Irrlicht w wersji .NET. Napiszemy skrypt przed- ... stawiony w Listingu 6. wyświetlający teksturowaną siatkę tere- using IronPython.Hosting; nu. Jak zwykle, importujemy moduły clr i System, a referencję do using IronPython.Modules; środowiska 3D tworzymy przy użyciu konstrukcji AddReference- ... ToFile(...). W rezultacie, możemy tu już zaimportować samo śro- private void frmAppHost_Load(object sender, EventArgs e){ dowisko i potrzebne nam biblioteki (Video, Core, Scene, GUI). try{ Scenę skonfigurujemy w konstruktorze klasy IrrlichtExample. // Tworzymy slownik do przechowywania pobieranych slow W konstruktorze tworzymy instancję bazowej klasy IrrlichtDevice Dictionary dictionary = self.device. sterownik video self.driver, kamerę self.camera oraz new Dictionary(); // Interpreter Pythona Listing 5. Użycie interpretera poleceń Pythona – skrypt PythonEngine engine = new PythonEngine(); // W jezyku Pythona: os.path.join(“…”) # Jak we wszystkich przypadkach – import modulow i bibliotek engine.AddToPath(Application.StartupPath); import sys // Nowy log aplikacji – zamiast konsoli sys.path.append(r"c:\python24\lib") System.IO.FileStream fs = new System.IO.FileStream( import string "application-log.txt",System.IO.FileMode.Create); import os // Przekierowujemy standardowe we/wy do nowego pliku # Import .NET engine.SetStandardOutput(fs); import clr engine.SetStandardError(fs); from System import * // Tworzymy nowy modul i slownik from System.Collections.Generic import * EngineModule em = engine.CreateModule(); dict = Dictionary[Int32,String]() Dictionary locals = # Na poczatek, sprawdzamy istnienie slownika new Dictionary(); if os.path.exists(myDictionaryFile): locals.Add("words", dictionary); f=open(filename,'r') # Otwarcie pliku locals.Add("myDictionaryFile","dictionary.txt"); str = ‘_’ engine.ExecuteFile("getDictionary.py", em, locals); count = 0; # licznik slow engine.Shutdown(); while str!=: // Odczytujemy z powrotem liste try: dictionary = (Dictionary)locals["words"]; line = f.readline() # Odczyt wiersz po wierszu // Na koniec wypelniamy slownikiem liste if line!='': foreach (KeyValuePair item in # Dzielimy wiersz na czesci dictionary) { (index,word) = line.split(r",") ListViewItem lvItem = new ListViewItem( # … i dodajemy do slownika item.Key.ToString()); dict.Add(Int32(index),String(word)); lvItem.SubItems.Add(item.Value.Trim()); count = count+1 # Zliczamy pozycje lvItems.Items.Add(lvItem); except IOError, (errno,strno): } # Obsluga wyjatku } print "%s in line %s\n",errno, strno catch (IronPython.Runtime.Exceptions. f.close() PythonNameErrorException E){ if (count>1): MessageBox.Show(E.Message); # Uwaga: Wszystkie komunikaty pojawia sie w logu } print 'Loaded data from file -> ', filename catch (Exception E){ print 'There are items->',count MessageBox.Show(E.Message); for item in words: } print item.Key, ":", item.Value } words = dict # koniec!

34 www.sdjournal.org Software Developer’s Journal 02/2007 .NET + Python = IronPython

Listing 6. Przykład użycia środowiska grafiki 3D – Irrlicht .NET

# Tradycyjnie, importujemy potrzebne biblioteki terrain.SetMaterialTexture(0, self.driver. import clr GetTexture("terrain-texture.jpg")); clr.AddReferenceToFile("Irrlicht.NET") # Irrlicht! terrain.SetMaterialTexture(1, self.driver. import Irrlicht GetTexture("detailmap3.jpg")); from Irrlicht import * terrain.ScaleTexture(1.0, 20.0); from Irrlicht.Video import * # Wlaczamy detector kolizji,… from Irrlicht.Core import * selector = smgr.CreateTerrainTriangleSelector( from Irrlicht.Scene import * terrain, 0) from Irrlicht.GUI import * anim = smgr.CreateCollisionResponseAnimator( import System selector, self.camera, Vector3D(60,100,60), # Prosta klasa, przy pomocy ktorej wyswietlimy tworzona Vector3D(0,0,0), Vector3D(0,50,0),0.0005); # scene # …ktory dodajemy do kamery

class IrrlichtExample: self.camera.AddAnimator(anim); def __init__(self): self.driver.SetTextureCreationFlag( try: TextureCreationFlag.CREATE_MIP_MAPS, False); # Tworzymy instancje klasy IrrlichtDevice # Ustawiamy kolejno tekstury otoczenia self.device = IrrlichtDevice(DriverType.DIRECT3D8) # (gora/dol/lewo/prawo/przod/tyl) self.device.ResizeAble = True; smgr.AddSkyBoxSceneNode( self.device.WindowCaption = self.driver.GetTexture("irrlicht2_up.jpg" ), "Iron Python + Irrlicht" self.driver.GetTexture("irrlicht2_dn.jpg"), # Pobieramy sterownik video self.driver.GetTexture("irrlicht2_lf.jpg"), self.driver = self.device.VideoDriver self.driver.GetTexture("irrlicht2_rt.jpg"), # … oraz samo GUI self.driver.GetTexture("irrlicht2_ft.jpg"), self.env = self.device.GUIEnvironment self.driver.GetTexture( self.driver.SetTextureCreationFlag( "irrlicht2_bk.jpg"),None,0); TextureCreationFlag.ALWAYS_32_BIT,True) self.driver.SetTextureCreationFlag( # Menedzer sceny TextureCreationFlag.CREATE_MIP_MAPS, True); smgr = self.device.SceneManager # Wyswietlamy grafike az do zamkniecia aplikacji # Tworzymy kamere i ustawiamy ja na scenie while self.device.Run(): self.camera = smgr.AddCameraSceneNodeFPS() if self.device.WindowActive: self.camera.Position=Vector3D(1900*2,255*2,3700*2) self.device.VideoDriver.BeginScene( self.camera.Target= Vector3D(2397*2,343*2,2700*2) True,True, Color(0,100,100,100)) self.camera.FarValue=12000.0 self.device.SceneManager.DrawAll(); # Odtad kursor bedzie niewidoczny self.device.VideoDriver.EndScene() self.device.CursorControl.Visible=False self.device.CloseDevice() # Generujemy teren except Exception, detail: terrain = smgr.AddTerrainSceneNode( print detail "terrain-heightmap.bmp",None,-1, # Metoda Main(), w ktorej utworzymy instancje naszej Vector3D(),Vector3D(40, 4.4, 40), # klasy Color(255,255,255,255)) def Main(): terrain.SetMaterialFlag( game = IrrlichtExample() # Instancja klasy Irrlicht MaterialFlag.LIGHTING, False); # Gotowe… terrain.SetMaterialType(MaterialType.DETAIL_MAP); if __name__=="__main__": # Ustawiamy tekstury terenu i skalujemy je Main() menedżera sceny (smgr). Ustawiamy kamerę w dogodnym miej- programista .NET dostaje do ręki dynamiczne narzędzie, scu na scenie (self.camera.Position), a teren generujemy techni- mogące łatwo służyć uzupełnieniu lub zwiększeniu funkcjo- ką heightmap. Wynik jak na Rysunku 4. nalności opracowywanych aplikacji. To narzędzie, które mo- że być użyte nie tylko do tworzenia mniej lub bardziej zło- Podsumowanie żonych skryptów, ale także w dużych projektach programi- Zakres i możliwości IronPython (i samego Pythona) są stycznych; pole możliwych zastosowań jest ogromne (apli- ogromne. IronPython pozwala użytkownikowi Pythona po- kacje desktopowe, ASP. NET, Web frameworks hosting, itd). ruszać sie bardzo sprawnie po platformie .NET. Posiada Aktualnie, wadą jego jest brak wsparcia dla serwisów webo- w pełni dynamiczny zestaw prostych i złożonych typów, jak wych (Web Services). Skorzystają na nim wszyscy ci, któ- i mechanizm automatycznego zarządzania pamięcia. Naj- rzy szukając dynamicznej alternatywy (lub uzupełnienia) dla ważniejszy obszar zastosowania Pythona to szeroko poję- statycznego C#. Krótko mówiąc, ograniczeń jest niewiele, ty RAD (Rapid Application Development). Z drugiej strony, a zabawa przednia. n

Software Developer’s Journal 02/2007 www.sdjournal.org 35