| | 1 | = Divmod : Nevow = |
|---|
| | 2 | |
|---|
| | 3 | == Athena == |
|---|
| | 4 | |
|---|
| | 5 | === !LiveElement Tutorial === |
|---|
| | 6 | |
|---|
| | 7 | Subclass {{{nevow.athena.LiveElement}}} and provide a {{{docFactory}}} which uses the {{{liveElement}}} renderer: |
|---|
| | 8 | |
|---|
| | 9 | {{{ |
|---|
| | 10 | #!python |
|---|
| | 11 | from nevow import athena, loaders, tags as T |
|---|
| | 12 | |
|---|
| | 13 | class MyElement(athena.LiveElement): |
|---|
| | 14 | docFactory = loaders.stan(T.div(render=T.directive('liveElement'))) |
|---|
| | 15 | }}} |
|---|
| | 16 | |
|---|
| | 17 | Put the result onto a {{{nevow.athena.LivePage}}} somehow: |
|---|
| | 18 | |
|---|
| | 19 | {{{ |
|---|
| | 20 | #!python |
|---|
| | 21 | class MyPage(athena.LivePage): |
|---|
| | 22 | docFactory = loaders.stan(T.html[ |
|---|
| | 23 | T.head(render=T.directive('liveglue')), |
|---|
| | 24 | T.body(render=T.directive('myElement'))]) |
|---|
| | 25 | |
|---|
| | 26 | def render_myElement(self, ctx, data): |
|---|
| | 27 | f = MyElement() |
|---|
| | 28 | f.setFragmentParent(self) |
|---|
| | 29 | return ctx.tag[f] |
|---|
| | 30 | |
|---|
| | 31 | def child_(self, ctx): |
|---|
| | 32 | return MyPage() |
|---|
| | 33 | }}} |
|---|
| | 34 | |
|---|
| | 35 | Put the page into a {{{nevow.appserver.NevowSite}}} somehow: |
|---|
| | 36 | |
|---|
| | 37 | {{{ |
|---|
| | 38 | #!python |
|---|
| | 39 | from nevow import appserver |
|---|
| | 40 | site = appserver.NevowSite(MyPage()) |
|---|
| | 41 | }}} |
|---|
| | 42 | |
|---|
| | 43 | Hook the site up to the internet: |
|---|
| | 44 | |
|---|
| | 45 | {{{ |
|---|
| | 46 | #!python |
|---|
| | 47 | from twisted.application import service, internet |
|---|
| | 48 | |
|---|
| | 49 | application = service.Application("Athena Demo") |
|---|
| | 50 | webService = internet.TCPServer(8080, site) |
|---|
| | 51 | webService.setServiceParent(application) |
|---|
| | 52 | }}} |
|---|
| | 53 | |
|---|
| | 54 | Put it all into a {{{.tac}}} file and run it: |
|---|
| | 55 | |
|---|
| | 56 | {{{ |
|---|
| | 57 | #!sh |
|---|
| | 58 | twistd -noy myelement.tac |
|---|
| | 59 | }}} |
|---|
| | 60 | |
|---|
| | 61 | And hit <http://localhost:8080/>. You now have an extremely simple Athena page. |
|---|
| | 62 | |
|---|
| | 63 | ==== Customizing Behavior ==== |
|---|
| | 64 | Add a Twisted plugin which maps your module name onto your JavaScript source file: |
|---|
| | 65 | |
|---|
| | 66 | {{{ |
|---|
| | 67 | #!python |
|---|
| | 68 | from nevow import athena |
|---|
| | 69 | |
|---|
| | 70 | myPackage = athena.JSPackage({ |
|---|
| | 71 | 'MyModule': '/absolute/path/to/mymodule.js', |
|---|
| | 72 | }) |
|---|
| | 73 | }}} |
|---|
| | 74 | |
|---|
| | 75 | Place this Python source file into {{{nevow/plugins/}}} ([http://twistedmatrix.com/projects/core/documentation/howto/plugin.html 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). |
|---|
| | 76 | |
|---|
| | 77 | |
|---|
| | 78 | In the JavaScript source file (in this case, {{{mymodule.js}}}), import {{{Nevow.Athena}}}: |
|---|
| | 79 | |
|---|
| | 80 | {{{ |
|---|
| | 81 | #!js |
|---|
| | 82 | // import Nevow.Athena |
|---|
| | 83 | }}} |
|---|
| | 84 | |
|---|
| | 85 | Next, subclass the JavaScript {{{Nevow.Athena.Widget}}} class (notice the module name that was defined in the plugin file): |
|---|
| | 86 | |
|---|
| | 87 | {{{ |
|---|
| | 88 | #!js |
|---|
| | 89 | MyModule.MyWidget = Nevow.Athena.Widget.subclass('MyModule.MyWidget'); |
|---|
| | 90 | }}} |
|---|
| | 91 | |
|---|
| | 92 | Now, add a method to your newly defined class: |
|---|
| | 93 | |
|---|
| | 94 | {{{ |
|---|
| | 95 | #!js |
|---|
| | 96 | MyModule.MyWidget.methods( |
|---|
| | 97 | function echo(self, argument) { |
|---|
| | 98 | alert("Echoing " + argument); |
|---|
| | 99 | return argument; |
|---|
| | 100 | }); |
|---|
| | 101 | }}} |
|---|
| | 102 | |
|---|
| | 103 | Define the JavaScript class which will correspond to your {{{LiveElement}}} subclass: |
|---|
| | 104 | |
|---|
| | 105 | {{{ |
|---|
| | 106 | #!python |
|---|
| | 107 | from nevow import athena, loaders, tags as T |
|---|
| | 108 | |
|---|
| | 109 | class MyElement(athena.LiveElement): |
|---|
| | 110 | jsClass = u'MyModule.MyWidget' |
|---|
| | 111 | docFactory = loaders.stan(T.div(render=T.directive('liveElement'))) |
|---|
| | 112 | }}} |
|---|
| | 113 | |
|---|
| | 114 | ==== Invoking Code in the Browser ==== |
|---|
| | 115 | |
|---|
| | 116 | 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: |
|---|
| | 117 | |
|---|
| | 118 | {{{ |
|---|
| | 119 | #!python |
|---|
| | 120 | from twisted.internet import reactor |
|---|
| | 121 | |
|---|
| | 122 | from nevow import athena, loaders, tags as T |
|---|
| | 123 | |
|---|
| | 124 | class MyElement(athena.LiveElement): |
|---|
| | 125 | jsClass = u'MyModule.MyWidget' |
|---|
| | 126 | docFactory = loaders.stan(T.div(render=T.directive('liveElement'))) |
|---|
| | 127 | |
|---|
| | 128 | def __init__(self, *a, **kw): |
|---|
| | 129 | super(MyElement, self).__init__(*a, **kw) |
|---|
| | 130 | reactor.callLater(5, self.myEvent) |
|---|
| | 131 | |
|---|
| | 132 | def myEvent(self): |
|---|
| | 133 | print 'My Event Firing' |
|---|
| | 134 | self.callRemote('echo', 12345) |
|---|
| | 135 | }}} |
|---|
| | 136 | |
|---|
| | 137 | Start up the server again and revisit <http://localhost:8080>. |
|---|
| | 138 | |
|---|
| | 139 | ==== Invoking Code on the Server ==== |
|---|
| | 140 | |
|---|
| | 141 | 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: |
|---|
| | 142 | |
|---|
| | 143 | {{{ |
|---|
| | 144 | #!python |
|---|
| | 145 | class MyElement(athena.LiveElement): |
|---|
| | 146 | docFactory = loaders.stan(T.div(render=T.directive('liveElement'))[ |
|---|
| | 147 | T.input(type="submit", value="Push me", onclick='Nevow.Athena.Widget.get(this).clicked()')]) |
|---|
| | 148 | ... |
|---|
| | 149 | }}} |
|---|
| | 150 | |
|---|
| | 151 | Update the JavaScript definition of {{{MyModule.MyWidget}}} to handle this event and actually call the server method: |
|---|
| | 152 | |
|---|
| | 153 | {{{ |
|---|
| | 154 | #!js |
|---|
| | 155 | MyModule.MyWidget.method( |
|---|
| | 156 | 'clicked', |
|---|
| | 157 | function(self) { |
|---|
| | 158 | self.callRemote('echo', 'hello, world'); |
|---|
| | 159 | }); |
|---|
| | 160 | }}} |
|---|
| | 161 | |
|---|
| | 162 | Add a method to {{{MyElement}}} which the browser will call, and expose it to the browser: |
|---|
| | 163 | |
|---|
| | 164 | {{{ |
|---|
| | 165 | #!python |
|---|
| | 166 | class MyElement(athena.LiveElement): |
|---|
| | 167 | ... |
|---|
| | 168 | |
|---|
| | 169 | def echo(self, argument): |
|---|
| | 170 | print 'Echoing', argument |
|---|
| | 171 | return argument |
|---|
| | 172 | athena.expose(echo) |
|---|
| | 173 | }}} |
|---|
| | 174 | |
|---|
| | 175 | Start up the server again and revisit <http://localhost:8080>. |
|---|
| | 176 | |
|---|
| | 177 | === Download the files for this tutorial: === |
|---|
| | 178 | |
|---|
| | 179 | * [http://divmod.org/trac/attachment/wiki/DivmodNevow/Athena/Files/myelement.tac?format=raw myelement.tac] |
|---|
| | 180 | * [http://divmod.org/trac/attachment/wiki/DivmodNevow/Athena/Files/mymodule.js?format=raw mymodule.js] |
|---|
| | 181 | * [http://divmod.org/trac/attachment/wiki/DivmodNevow/Athena/Files/mymodule_pkg.py?format=raw mymodule_pkg.py] |