Nagare benötigt Stackless-Python, welches man sich außer unter Windows selbst kompilieren muss. Ich beschreibe hier, wie es unter Ubuntu 8.04 funktioniert.
Ich musste (glaube ich) fünf Pakete nachinstallieren: libc6-dev mit linux-libc-dev, libzlib1g-dev, libxml2-dev und libxslt1-dev. Ohne die ersten beiden Pakete geht gar nichts. Das nächste wird für die Installation von "setuptools" benötigt. Die letzten beiden werden später benötigt, um "lxml" zu kompilieren und zu installieren.
Die Pakete installiert man (gefahrlos, falls sie schon da sind) so:
Code: Alles auswählen
$ sudo apt-get install libc6-dev linux-libc-dev zlib1g-dev libxml2-dev libxslt1-dev
Code: Alles auswählen
$ cd $HOME
$ wget http://www.stackless.com/binaries/stackless-252-export.tar.bz2
$ tar xfj stackless-252-export.tar.bz2
$ cd stackless-2.5.2-*
$ ./configure --prefix=$HOME/stackless
$ make
$ make install
Code: Alles auswählen
$ export PATH=$HOME/stackless/bin:$PATH
Code: Alles auswählen
$ python
Python 2.5.2 Stackless 3.1b3 060516 (release25-maint, Sep 28 2008, 17:22:40)
[GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
Lade nun setuptools-0.6c9-py2.5.egg und installiere es, um das "easy_install"-Kommando zu bekommen:
Code: Alles auswählen
$ cd $HOME
$ wget http://pypi.python.org/packages/2.5/s/setuptools/setuptools-0.6c9-py2.5.egg#md5=fe67c3e5a17b12c0e7c541b7ea43a8e6
$ sh setuptools-0.6c9-py2.5.egg
Optional kann man jetzt "virtualenv" installieren. Mir scheint es nicht ganz so wichtig, weil man ja eh mit einer eigenen Version von Python unterwegs ist, aber andererseits schadet es auch nicht:
Code: Alles auswählen
$ cd $HOME
$ easy_install virtualenv
$ virtualenv nagare
$ cd nagare
$ source bin/activate
Code: Alles auswählen
$ easy_install nagare[full]
Teil 2 - Die erste Anwendung
Erzeuge nun eine neue Nagare-Anwendung:
Code: Alles auswählen
$ cd $HOME
$ nagare-admin create-app myapp
$ cd myapp
$ python setup.py develop
Code: Alles auswählen
$ nagare-admin serve --debug --reload myapp
Der Programmcode der angezeigten Seite befindet sich im Verzeichnis "myapp" in der gleichnamigen Datei "myapp.py", in der eine Klasse "Myapp" definiert ist. Die Variable "app" enthält diese Klasse und ist die Verbindung nach außen, der "Entrypoint". In "setup.py" ist festgelegt, dass "myapp.myapp::app" die Stelle ist, wo Nagare die Anwendung findet. Eigentlich müsste man dort auch direkt Myapp eintragen können und kann sich dann die Variable sparen.
Die Beispielanwendung besteht neben der leeren Klasse "Myapp" nur noch aus einer "render"-Methode, die über "@presentation.render_for(Myapp)" mit "Myapp" verknüpft wird.
Bauen wir diese Anwendung nun zu einer Anwendung um, die nacheinander zwei Zahlen erfragt und dann die Summe dieser Zahlen ausgibt. Dazu benötigen wir eine Komponente, die mit einem Eingabefeld nach der Zahl fragt und eine Schaltfläche "Weiter" enthält.
Dies ist das Modell für meinen Dialog. Es speichert einen Fragetext und den anzuzeigenden Wert:
Code: Alles auswählen
class Input(object):
def __init__(self, prompt, value=None):
self.prompt, self.value = prompt, value
def set_value(self, value):
self.value = value
Code: Alles auswählen
@presentation.render_for(Input)
def render(self, h, comp, *args):
with h.form:
h << self.prompt
h << h.input(value=self.value).action(self.set_value)
h << h.input(type='submit', value='Weiter')
return h.root
Code: Alles auswählen
...
h << h.h1('Your application is running')
h << component.Component(Input("Erste Zahl:", 3))
h << component.Component(Input("Zweite Zahl:", 4))
Hier ist eine zweite Komponente, die etwas anzeigen kann:
Code: Alles auswählen
class Display(object):
def __init__(self, value):
self.value = value
@presentation.render_for(Display)
def render(self, h, comp, *args):
with h.p:
h << self.value
return h.root
Ich möchte nun aber nicht, dass beide Eingabefelder gleichzeitig angezeigt werden, sondern dass dies nacheinander geschieht. Dazu braucht man einen "Task" mit einer go()-Methode:
Code: Alles auswählen
class Add(component.Task):
def go(self, comp):
comp.call(Input("Erste Zahl:", 3))
comp.call(Input("Zweite Zahl:", 4))
comp.call(Display(7))
Code: Alles auswählen
class Myapp(object);
def __init__(self):
self.add = component.Component(Add())
...
h << h.h1('Your application is running')
h << self.add
Code: Alles auswählen
class Input...
...input(type='submit', value='Weiter').action(lambda: comp.answer(self.value))
class Add(component.Task):
def go(self, comp):
a = int(comp.call(Input("Erste Zahl:", 3)))
b = int(comp.call(Input("Zweite Zahl:", 4)))
comp.call(Display(a + b))
Stefan