Archive for November, 2009

Convert your Python to JavaScript with Pyjamas

The problem

I faced an irritating situation last year in a project I was working on. I was creating a screen that pulled in a very large set of data from the database then did various calculations on the data based on input in a form. The results were shown in several grids and a flash chart all on the same page. These exact same calculations also needed to be run to generate a PDF on the server. I didn’t want to go back to the server for the calculation because the amount of data that needed to go back and forth was very large. At the same time, I didn’t want to write and maintain a piece of code that involved 30+ math operations in both JavaScript and Python.

So I discovered Pyjamas!

A little background: the Pyjamas project was started to be the Python answer to GWT. It’s a framework that allows you to define an entire UI in Python which is then converted to JavaScript and HTML. What I found though was that it’s possible to use the Python to JavaScript converter in the project to compile your own Python to JavaScript even if you don’t need the UI framework Pyjamas provides. This was a perfect solution to my problem!

Unfortunately, Pyjamas was still in its infancy at that time, so I had to use a much stripped down subset of Python to get it to build JavaScript successfully. Once I figured out the tricks, it was a great way to avoid a painful round trip to the server and yet also avoid maintaining the same logic in two languages.

Fast forward to today. The Pyjamas project has a greatly improved Python to JavaScript compiler. You can now import multiple modules through the usual Python import functionality. The painful things I had to do before (like not use the dict constructor!) are no longer necessary. There’s even a way to include external JavaScript files from Python for use in your final generated script.

How do I use this?

It looks like at this very moment (mid-November 2009) the compiler inside of Pyjamas is in the process of being broken out into a module called pyjampiler that makes it a little easier to use it independently of the larger project. This is currently getting moved into the main code, so look for it inside of the next release. For now you can download it from the link above. Playing around with pyjampiler is this easy:

pyjampiler.py --output=my_javascript.js --entry=my_python

Really! Your python imports will be followed, converted, and linked together. You’ll get JavaScript that behaves like your Python.

Tips for success

As cool as this is, there are some limits. You can’t import whatever you like from the Python standard library. The JavaScript generated is going to be much more verbose than something you would have written yourself. You’ll need to include a helper .js file which is not one of the frameworks you’re already using (supposedly it’ll play nice).

I think this technique is best limited to functionality like I described in the beginning of this post. I implemented my logic as a function that took in two dicts and returned another dict of results. The only thing handled by the piece that was converted was the arithmetic required to drive the calculator. I wrote JavaScript to handle the DOM manipulations required to display the results on the client and Python to turn the results into a PDF on the server.

What do you think? Is this a reasonable way to avoid maintaining the same logic in two places? Have a better solution?

 

position: absolute inside position: relative

This is one of those CSS tricks I find myself using frequently that doesn’t seem to be well documented. If you put an absolutely positioned element inside a relatively positioned one, it gets positioned relative to it. Wait, WHAT?! It’s hard to explain, so let’s see some code:

<div style="position: relative; height: 100px; width: 100px; border: 1px solid #000">
    <div style="position: absolute; right: 0; top: 0;">X</div>
    <div style="position: absolute; left: 20px; bottom: 20px;">Y</div>
</div>

And here it is in action:

X
Y

This is incredibly useful. Try it out for things like positioning text in tabs or putting a close button in the corner of a div.

 

dojo.dnd.Source fires onDndDrop before it has actually moved the node?

Lately I’ve been working with drag and drop on a project. I’m dragging controls between two containers that are set to be dojo.dnd.Source. When a control is dropped on a target, I need to get a list of all the controls in each Source involved. I would really like my onDndDrop handler to look something like this:

function(source, nodes, copy, target){
        if(source.node==this.top_node || target.node==this.top_node){
            var childNodes = dojo.query('#' + this.top_node.id + ' > .custom-control');
            var newOrder = dojo.map(childNodes, function(childNode){
                return childNode.id;
            });
            console.debug(newOrder)
        }
    }

Unfortunately, I came across some annoying behavior. If the source node is located before the target node in the DOM, the onDndDrop event is firing on it before the move actually takes place! My function to calculate what controls are in each Source is therefore getting incorrect results and says the moved control exists in both the source and target Sources. The solution was to add an additional filter for the nodes:

function(source, nodes, copy, target){
        var isSource = source.node==this.top_node;
        var isTarget = target.node==this.top_node;
        if(isSource || isTarget){
            var childNodes = dojo.query('#' + this.top_node.id + ' > .custom-control');
            var newOrder = dojo.map(childNodes, function(childNode){
                return childNode.id;
            });
            // make sure that if the node should be moved out of the Source it really is gone
            // sometimes dojo calls this code for the Source before the node has ACTUALLY been moved
            if(isSource && source.node != target.node){
                newOrder = dojo.filter(newOrder, function(childName){
                    return !dojo.filter(nodes, function(movedNode){
                        return movedNode.id == childName;
                    }).length;
                });
            }
            console.debug(newOrder)
        }
    }

As a side note, it works as expected if the target node is before the source in the DOM. I wouldn’t mind this behavior if it was at least consistent.