[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.

cheers,
Sean

--
Sean Gillies
sgillies at frii dot com
http://www.frii.com/~sgillies





More information about the Twisted-Python mailing list