Divmod : Nevow

Athena

LiveElement Tutorial

Subclass nevow.athena.LiveElement and provide a docFactory which uses the liveElement renderer:

from nevow import athena, loaders, tags as T

class MyElement(athena.LiveElement):
    docFactory = loaders.stan(T.div(render=T.directive('liveElement')))

Put the result onto a nevow.athena.LivePage somehow:

class MyPage(athena.LivePage):
    docFactory = loaders.stan(T.html[
        T.head(render=T.directive('liveglue')),
        T.body(render=T.directive('myElement'))])

    def render_myElement(self, ctx, data):
        f = MyElement()
        f.setFragmentParent(self)
        return ctx.tag[f]

    def child_(self, ctx):
        return MyPage()

Put the page into a nevow.appserver.NevowSite somehow:

from nevow import appserver
site = appserver.NevowSite(MyPage())

Hook the site up to the internet:

from twisted.application import service, internet

application = service.Application("Athena Demo")
webService = internet.TCPServer(8080, site)
webService.setServiceParent(application)

Put it all into a .tac file and run it:

twistd -noy myelement.tac

And hit <http://localhost:8080/>. You now have an extremely simple Athena page.

Customizing Behavior

Add a Twisted plugin which maps your module name onto your JavaScript source file:

from nevow import athena

myPackage = athena.JSPackage({
    'MyModule': '/absolute/path/to/mymodule.js',
    })

Place this Python source file into nevow/plugins/ (the Twisted plugin documentation describes where else you can put it, with the exception that Nevow plugins should be placed beneath a nevow directory as opposed to a twisted directory).

In the JavaScript source file (in this case, mymodule.js), import Nevow.Athena:

// import Nevow.Athena

Next, subclass the JavaScript Nevow.Athena.Widget class (notice the module name that was defined in the plugin file):

MyModule.MyWidget = Nevow.Athena.Widget.subclass('MyModule.MyWidget');

Now, add a method to your newly defined class:

MyModule.MyWidget.methods(
    function echo(self, argument) {
        alert("Echoing " + argument);
        return argument;
    });

Define the JavaScript class which will correspond to your LiveElement subclass:

from nevow import athena, loaders, tags as T

class MyElement(athena.LiveElement):
    jsClass = u'MyModule.MyWidget'
    docFactory = loaders.stan(T.div(render=T.directive('liveElement')))

Invoking Code in the Browser

Add some kind of event source (in this case, a timer, but this is incidental) which will cause the server to call a method in the browser:

from twisted.internet import reactor

from nevow import athena, loaders, tags as T

class MyElement(athena.LiveElement):
    jsClass = u'MyModule.MyWidget'
    docFactory = loaders.stan(T.div(render=T.directive('liveElement')))

    def __init__(self, *a, **kw):
        super(MyElement, self).__init__(*a, **kw)
        reactor.callLater(5, self.myEvent)

    def myEvent(self):
        print 'My Event Firing'
        self.callRemote('echo', 12345)

Start up the server again and revisit <http://localhost:8080>.

Invoking Code on the Server

Add an event source (in this case, a user-interface element, but this is incidental) which will cause the browser to call a method on the server:

class MyElement(athena.LiveElement):
    docFactory = loaders.stan(T.div(render=T.directive('liveElement'))[
        T.input(type="submit", value="Push me", onclick='Nevow.Athena.Widget.get(this).clicked()')])
    ...

Update the JavaScript definition of MyModule.MyWidget to handle this event and actually call the server method:

MyModule.MyWidget.method(
    'clicked',
    function(self) {
        self.callRemote('echo', 'hello, world');
    });

Add a method to MyElement which the browser will call, and expose it to the browser:

class MyElement(athena.LiveElement):
    ...

    def echo(self, argument):
        print 'Echoing', argument
        return argument
    athena.expose(echo)

Start up the server again and revisit <http://localhost:8080>.

Download the files for this tutorial:

jethro@divmod.org