[Twisted-Python] timeout using LivePage

Sean Gillies sgillies at frii.com
Tue Aug 26 09:48:12 EDT 2003

On Monday, August 25, 2003, at 05:45  PM, Donovan Preston wrote:

> On Monday, August 25, 2003, at 1:30 PM, Sean Gillies wrote:
>> Hi,
>> Am very new to Twisted but am quickly finding that I like
>> it.  All my questions and comments involve Twisted 1.0.6
>> in combo with Python 2.3 on OS X.
> Great! Always glad to have more users giving me feedback.
> If you are going to be using LivePage, you should probably
> track CVS instead of using a release, since LivePage is the
> one major area of woven I am still doing major work on.
> First, I want to clarify some terminology. LivePage is designed
> to allow two things:
> Client-To-Server Events: A JavaScript event handler in the
> browser is routed to the Twisted server and causes some code
> to run. This uses a channel I am calling the "InputConduit"
> Server-To-Client Events: Some event happened on the server
> (For example, new mail arrived, another player entered the room)
> and the server wants to "Push" some new HTML to the client.
> This uses a channel I am calling the "OutputConduit"
>> I've run into a problem while making a rough .rpy prototype of
>> a mapping application (ala MapQuest) using the U of Minnesota
>> MapServer's Python interface.
>> I will need to have server-side handlers for javascript events
>> to allow map zooming, panning, &c and so have been experimenting
>> with LivePage.  I intend that the code should render a map image,
>> and upon clicking the image, we zoom into the map (changing model
>> data), and redraw the map image.  Unfortunately, browsers are
>> unable to load the
>> mapserv.rpy/?woven_hookupOutputConduitToThisFrame=1
>> URL that is generated by my instance of LivePage, and so the map
>> is never redrawn as it should.
> There are actually two unrelated issues here. The first is that the
> current implementation of LivePage in the Twisted CVS is geared
> towards the NewReality web client (NewReality is a multiuser text
> environment (game) written in Twisted) and thus the ability for
> the server to send events to the client is pretty important.
> You are connected and logged in using the web client, and
> someone else enters the room -- you want the web browser to
> display a notice about someone entering the room without having
> to refresh the browser manually or set up continual reloading.
> The output conduit is implemented in certain browsers (those
> lacking Flash and LiveConnect) by embedding an <iframe>
> tag whose src="?woven_hookupOutputConduitToThisFrame=1"
> in the page. When the browser attempts to load this iframe, it
> makes a request to the server, and twisted notices that the
> browser "wants to hook up the output conduit to this frame",
> and *never finishes sending this page to the client*. The
> Twisted server just holds this connection open forever, so
> that it can write data to the client on demand in response
> to events on the server, and this is why it appears your
> browser is unable to load this URL.
> Since with your application it sounds like you only want
> client-to-server events, you need to do a little bit of
> hacking to prevent Woven from including the output
> conduit HTML in your pages. This will get easier in future
> releases of Twisted; I just haven't had time to enumerate
> the possible ways people will want to use LivePage and
> come up with an easy way for the programmer to specify
> the features they want.
> If you look at the HTML fragment woven includes in your
> page when you specify the view directive "webConduitGlue"
> you will see it includes the following parts:
>   <div>
>    <script src="WebConduit2_js" language="javascript"></script>
>    <iframe src="input_html"
>         style="width: 0; height: 0" id="woven_inputConduit"></iframe>
>    <iframe
>         src="?woven_hookupOutputConduitToThisFrame=1"
>         style="width: 100%" id="woven_outputConduit"></iframe>
>   </div>
> Simply replace the view="webConduitGlue" node with the above
> fragment, sans the output conduit frame:
>   <div>
>    <script src="WebConduit2_js" language="javascript"></script>
>    <iframe src="input_html"
>         style="width: 0; height: 0" id="woven_inputConduit"></iframe>
>   </div>
> After you have done this, your pages will actually appear to fully
> load and you should feel happier.
>> The model data does get changed
>> successfully, and when I manually reload the page, I do see
>> the zoomed map image as I expect.
> The second issue is that you are not notifying your models
> properly to tell them that they have changed. When a
> client-to-server event is sent from the browser to the server,
> the current version of Woven only sends the portions of the
> page which have actually changed, not the entire page,
> back to the browser. Again, this is something I would like
> the programmer to be able to control, because sometimes
> the changes are extensive enough that it makes more sense
> to send the entire page back.
> Your redirect hack is not a terrible solution, actually. I have
> used it before.
> See below for an example of how you can make your current
> controller code notify the model properly.
>> What is this 'woven_hookupOutputConduitToThisFrame' all about?
>> Do I need to set up another method to handle this URL?
>> I know that it's not recommended to put so much logic in the .rpy
>> file, but as I said, a quick and dirty prototype is what I'm after.
>> Am attaching the source of mapserv.rpy here:
>> --------------------------------------------------------------------
>> import os
>> import time
>> from twisted.web.woven import page, widgets, controller, model
>> from mapscript import *
>> base_mapfile = 'navtech_std.map'
>> # Get map object from the session data if we can
>> class MapModel(model.MethodModel):
>>     def wmfactory_mapobj(self, request):
>>         session_obj = request.getSession()
>>         try:
>>             session_mapobj = session_obj.mapobj
>>         except AttributeError:
>>             session_mapobj = mapObj(base_mapfile)
>>             session_obj.mapobj = session_mapobj
>>         return session_mapobj
>> # Widget for creating a temp map image and a tag linking to it
>> class MapImage(widgets.Widget):
>>     def setUp(self, request, node, data):
>>         mapobj = data
>>         node.tagName = "img"
>>         imgobj = mapobj.draw()
>>         tmp_file = '%s_%d_%d.%s' \
>>                  % (mapobj.name, time.time(), os.getpid(),
>>                     imgobj.format.extension )
>>         imgobj.save(os.path.join('/tmp', tmp_file))
>>         map_url = os.path.join('/tmp', tmp_file)
>>         node.setAttribute('src', map_url)
>>         node.setAttribute('height', str(mapobj.height))
>>         node.setAttribute('width', str(mapobj.width))
>>         node.setAttribute('border', '1')
>> # Widget for displaying the current scale of the map
>> class MapScale(widgets.Widget):
>>     def setUp(self, request, node, data):
>>         text = 'Scale: 1:%d' % (data.scale)
>>         newNode = request.d.createTextNode(text)
>>         node.appendChild(newNode)
>> # Template, using the web conduit glue
>> template = """<html>
>>   <body>
>>     <img model="mapobj" view="map_image" controller="map_ctrl"/>
>>     <p model="mapobj" view="map_scale" />
>>     <div view="webConduitGlue" />
>>   </body>
>> </html>
>> """
>> # Zoom into the map in the event of a javascript onclick
>> class MyEventHandler(controller.Controller):
>>     def handle(self, request):
>>         self.view.addEventHandler("onclick", self.onClick)
>>     def onClick(self, request, widget):
>>         # Get session map object (layers and spatial extents)
>>         session_obj = request.getSession()
>>         try:
>>             session_mapobj = session_obj.mapobj
>>         except AttributeError:
>>             session_mapobj = mapObj(base_mapfile)
>>             session_obj.mapobj = session_mapob
>>         # zoom in on the map
>>         w = session_mapobj.width
>>         h = session_mapobj.height
>>         pt = pointObj()
>>         pt.x, pt.y = w/2, h/2
>>         session_mapobj.zoomPoint(2, pt, w, h, session_mapobj.extent,  
>> None)
>>         print session_mapobj.extent.minx, session_mapobj.extent.maxx
>>         print self, "Zoomed!!!"
>            # Tell the browser that the model has changed, and
>            # any widgets which rely on this model will have to be
>            # rerendered, and the HTML sent to the browser
> 	 widget.model.notify({'request': request})
>            # Delete the next three lines
>>         # There is a better way to redraw the map, certainly
>>         request.redirect('http://localhost:8084/mapserv.rpy/')
>>         request.finish()
>> # Page class
>> class MyPage(page.LivePage):
>>     def wcfactory_map_ctrl(self, request, node, model):
>>         return MyEventHandler(model)
>> # Resource
>> resource = MyPage(MapModel(), template=template)
>> resource.setSubviewFactory("map_image", MapImage)
>> resource.setSubviewFactory("map_scale", MapScale)
>> ---------------------------------------------------------------------- 
>> -
>> looking forward to any insights into LivePage and also looking forward
>> to getting enough experience with Twisted that I can begin to  
>> contribute
>> to the list discussions.
> By the way, addEventHandler takes additional arguments
> which are javascript strings which will be evaluated within the
> context of the browser, and the results will be sent as additional
> arguments to the server-side event handler.
> To take advantage of this, you have to know a bit about
> JavaScript, but for example, if you know about the IE global
> "event" object, you could use it to pass the current x, y
> position of the mouse to the server with something like
> this:
> self.view.addEventHandler("onclick", self.onClick, 'event.x',  
> 'event.y')
> Then defining the event handler like this:
> def addEventHandler(self, request, widget, x, y):
> 	print "x, y", x, y
> Of course, doing this means knowing enough about javascript to do
> this properly, but those are problems with JavaScript and not Woven.
> I think Mozilla has different semantics for getting at the event object
> which may require changes in WebConduit2_js. I would consider it a
> bug if changes are required.
> I hope this helps!
> Donovan

Hi Donovan,

Thanks for the reply.  I can't wait to try out your recommendations.

I had read in the docs about the extra args to addEventHandler, but
appreciate seeing an example that's so close to my particular need.


Sean Gillies
sgillies at frii dot com

More information about the Twisted-Python mailing list