Thursday, May 10, 2007

Creating Rich Internet Applications with Python

I have been looking for the ideal tool for creating Rich Internet Applications (RIA) with Python. Although there is a ton of web frameworks out there, my favorite being Turbogears, all of them have so far failed to jump in the RIA bandwagon. I have been playing with OpenLaszlo, which although quite visually stunning, is a bit hard to integrate with a Python backend (but I am working on it, stay tuned!).

So yesterday I came across this other framework called Rialto which is marketed as language agnostic. They directly support Python (among other dynamic languages). They also say that their goal is to enable people to create RIAs without having to write DHTML, javascript or understanding DOM concepts.

They have a tutorial integrating their toolkit with CherryPy, so to teach my self Rialto, I decided to port their tutorial to TurboGears . Read along for how to make a simple app looking like the figure below.

I assume readers of this howto will be somewhat familiar with TurboGears, otherwise what's the point? So the first step is to generate a bare-bones TurboGears app with "tg-admin quickstart". Then Download and install rialtoPython from the Rialto website.

Then go to you controllers.py and first add the required import line:
from rialtoPython import *

Then, in the class root, empty the guts of the index method. Change the decorator of this method to:
@expose
def index(self):

Now we can proceed to insert the Rialto content inside the method. RialtoPython works by assembling the HTML page as a big string using python objects to generate the javascript parts. It's easy for a simple example like this but might get out of hand quickly for larger projects. I believe the python calls made within the index method could be done inside a Kid template, separating the view from the controller part as it is expected in a Turbogears application.

Here is the contents of the index Method:

@expose()
def index(self):
rp = rialtoPython.RialtoPython()
out = []
DIV = "document.body"
LAYOUT = "ihmDemo"
WINDOW = "myWindow"
WINDOW_TITLE = "Rialto-Turbogears Integration Example"
FRAME = "boxDemo"
FORM = "myForm"
URL = "do"
# assembling the page
out.append(rp.addImport(devMode=False))
out.append('')
out.append('\trialto/I18N.setLanguage("en");\n')
#layout
out.append(rp.openLayout(LAYOUT))
#app components
out.append(rp.windowTag(WINDOW, WINDOW_TITLE, DIV))
out.append(rp.frameTag(FRAME, '30', '30', '300', '120', 'My 1st python Box', 'true', 'false', 'relative', 'false', WINDOW))
out.append(rp.formTag(FORM,URL,FRAME))
out.append(rp.labelTag('label1', '25', '10', 'Spam', FORM))
out.append(rp.textTag('login', '25', '90', '200', 'A', FORM, 'true', 'true', 'Eggs'))

out.append(rp.buttonTag('doLogon', 'Ni!', '70', '10', 'And now to something completely different', ['FORM', FORM], FORM))
out.append(rp.buttonTag('default', 'Camelot!', '70', '100', 'The television reception isnt good here anyway', ['FORM', FORM], FORM))
# close the layout Tag
out.append(rp.closeLayout(LAYOUT))
out.append(rp.init(LAYOUT))
# Completing the Html code
out.append('\n')
out.append('\n\n')
return " ".join(out)
Let's now go over how it works, in the beginning of the index method, the RialtoPython is instantiated and some variables are defined in order to make the following calls a little more readable.

All "rp" methods called, return strings that are stored sequentially in the "out" list and the joined into a single string which contains the complete code of the application page. The App contains a frame with a form in it containing a text lable, a text input box, and a couple of buttons, when we call the method that returns the form code, a URL is specified to handle the the data coming from the form. On Turbogears, the URLs are exposed methods of the Root class. So let us write this method:

@expose()
def do(self,login,action):
res=rialtoPython.RialtoPython()
out=[]
error_msg=[]
if action=='doLogon':
if login=="":
error_msg.append("Empty Spam")
elif login!="Eggs":
error_msg.append("Unknown Spam")
if len(error_msg)>0:
out.append(res.showAlert('myAlert', ','.join(error_msg)))

else:
out.append(res.showAlert('myAlert', 'This Spam was good!'))
elif action == 'default':
out.append(res.showAlert('myAlert', 'Are you sure, you want to go to camelot?'))

else:
out.append(res.showAlert('myAlert', 'I dont know anything about the action %s'% action))

return ''.join(out)


The method "do" shows a floating alert window in response to pressing a button in the form. The method takes two arguments: "login", which is the contents of the input text box of the same name. Action is generated by the rialto API and contains the name of the button pressed.

There is only a couple of other things left for us to do before our app is ready:
  1. You have to download the Rialto javascript API source distribution, and unzip it in the same directory we have our controllers.py. After you do that, you should have a "rialtoEngine" directory in it.
  2. Now you have to add the following code to your app.cfg in order to let Turbogears find the Rialto code:
[/rialtoEngine]
static_filter.on=True
static_filter.dir="%(top_level_dir)s/rialtoEngine"

[/config.js]
static_filter.on=True
static_filter.file="%(top_level_dir)s/rialtoEngine/config.js"

[/javascript]
static_filter.on=True
static_filter.dir="%(top_level_dir)s/rialtoEngine/javascript"

[/rialto.js]
static_filter.on=True
static_filter.file="%(top_level_dir)s/rialtoEngine/javascript/rialto.js"

[/images]
static_filter.on=True
staticFilter.dir="%(top_level_dir)s/rialtoEngine/images"

[/style]
static_filter.on=True
static_filter.dir="%(top_level_dir)s/rialtoEngine/style"
Now just start your application and point your browser to http://localhost:8080 to see your first rich internet application, written entirely in Python, running.

Tips: This code may require the latest version of RialtoPython released in may 2007.

10 comments:

Andrew Dalke said...

Instead of appending each string to a list then doing a "".join() you can "yield" each string and let TurboGears do the join for you. In essence, 'yield' acts like a print statement.

usagi said...

Interesting, I didn't know Turbogears could do that...

Bill Mill said...

The yield behavior comes from cherrypy.

Calvin Spealman said...

I have to say that right now, the best answer for Python RIAs is looking to be coming from the unlikely place: Microsoft and Silverlight. You get to write Python, it runs on IE and Firefox in Windows, Mac OS X, and Linux support is very well underway. I imagine this kind of multi language support is on the way from other providers, but right now you should take advantage of what is here today.

usagi said...

Hi Calvin,

I looked at Silverlight tutorial video, and got the impression that only if you work from within visual Studio you will get all the productivity boost attributed to SilverLight...

Salvatore said...

That is the tutoril I was waiting for.
I can't wait your tutorial on OpenLaszlo and Python

Thank you again

Salvatore said...

That is the tutoril I was waiting for.
I can't wait your tutorial on OpenLaszlo and Python

Thank you again

Salvatore said...

That is the tutorial I was waiting for.
I can't wait your tutorial on OpenLaszlo and Python

Thank you again

ajvogel said...

OpenLaszlo and Python!!! Can hardly wait!

Chris said...

Your link to Rialto's homepage is malformed. It should be http://rialto.application-servers.com/wiki/

ccp

Amazon