Backspaces

October 29, 2006

Processing: .. with Jython

Filed under: Hacks — backspaces @ 12:08 pm

Last article showed how we could use Processing with Eclipse and Java 1.5. Heady with success, I decided to try to convert the near-trivial RandBoxes Processing demo to Jython.

Why?? Well, our group uses Blender quite a bit, and Blender uses Python as its scripting language. More generally, Python is quite popular within our scientific community.

Jython, a Python implementation using the JVM, has been surprisingly successful, having most of what we like about CPython. And its Java integration made it a natural for Processing. This presentation is a good overview.

Java

Here’s the Java 1.4 code we’re starting with, the Processing .java file created when creating the applet above. A few notes:

  1. The two methods setup() and draw() are required by PApplet.
  2. Class Box is an inner class, as is usual for Processing, because:
  3. Several utilities are in PApplet like color(), random() and all drawing methods. Use of inner classes lets you “inherit” these.
public class RandBoxes extends PApplet {
  int numBoxes = 1000;
  ArrayList boxes = new ArrayList();

  public void setup() {
    size(600, 500, JAVA2D);
    while(boxes.size() < numBoxes)
      boxes.add(new Box(random(width), random(height)));
    println("number of boxes = "+boxes.size());
  }
  public void draw() {
    background(128);
    for (int i = 0; i < boxes.size(); i++)
      ((Box) boxes.get(i)).step();
    if (frameCount%10==0)
      println("frameRate="+frameRate+" frameCount="+frameCount);
  }

  class Box {
    int c = color(255, 255, 0);
    float x, y;
    Box(float x, float y) {
      this.x = x;
      this.y = y;
    }
    public void step() {
      x = max(min(x+random(-1, 1), width), 0);
      y = max(min(y+random(-1, 1), height), 0);
      paint();
    }
    public void paint() {
      noStroke();
      fill(c);
      rect(x-2, y-2, 5, 5);
    }
  }
}

Jython

Here’s the Jython I eventually came up with. Note this is not “pythonic” yet, but at least runs. Goals:

  1. Be able to run as an applet if possible
  2. Avoid a “proxy class” subclass of PApplet even though that’s a common stunt to make Jython usage simpler .. i.e. hide any Processing or Jython “warts”! See the Thoughts for an example.
  3. Run easily in the standard Jython environment

To run,

export CLASSPATH=~/local/processing/lib/core.jar:.

then put this in any file other than RandBoxes.py (see notes). I use RandBoxesTest.py :

from javax.swing import JFrame
from processing.core import PApplet
from random import uniform

winWidth=600
winHeight=500
numBoxes = 1000
boxes = []

class RandBoxes (PApplet):
  def __init__(self):
    pass
  def setup(self):
    self.size(winWidth, winHeight, self.JAVA2D)
    while len(boxes)<numBoxes:
      boxes.append(
        Box(self, uniform(0,winWidth),uniform(0,winHeight)))
    print "number of boxes = %d" % len(boxes)
  # rqd due to PApplet's using frameRate and frameRate(n) etc.
  def getField(self, name):
    return self.class.superclass.getDeclaredField(name).get(self)
  def draw(self):
    self.background(128)
    for b in boxes: b.step()
    if self.frameCount % 10 == 0:
      print "frameRate=%f frameCount=%i" %
      (self.getField('frameRate'), self.frameCount)

class Box(object):
  def __init__(self, p, x, y):
    self.p = p
    self.x = x
    self.y = y
    self.c = p.color(255,255,0)
  def step(self):
    self.x = max(min(self.x+uniform(-1,1),winWidth),0)
    self.y = max(min(self.y+uniform(-1,1),winHeight),0)
    self.paint()
  def paint(self):
    self.p.noStroke()
    self.p.fill(self.c)
    self.p.rect(self.x-2,self.y-2,5,5)

if __name__ == '__main__':
  frame = JFrame(title="Processing",
    resizable = 0,
    defaultCloseOperation=JFrame.EXIT_ON_CLOSE)
  panel = RandBoxes()
  frame.add(panel)
  panel.init()
  while panel.defaultSize and not panel.finished:
    pass
  frame.pack()
  frame.visible = 1

Notes:

  • My first try did not use the 4 “global” variables: winWidth, winHeight, .. but it was pointed out to me that its more pythonic to use globals like this, and avoids overuse of “self”.
  • Similarly, to avoid way too many uses of “self.random()” and so on, I migrated to using Python utilities for max, min, random.uniform, and so on. You can tell I was beset with way, way too many uses of self.foo()!
  • I tried using inner-classes within Jython, but apparently that doesn’t work, even with __future__ nested_scope. The ASPN Cookbook discusses solutions, but I decided simply to pass in the PApplet instance to the Box constructor. Not sure what the most pythonic solution would be, but being explicit doesn’t hurt.
  • I chose to use JFrame for putting my PApplet subclass in an application even though PApplet has a main() that I could call. (PApplet, like many Java Applets, has a main for running as an application). I did this for three reasons: to learn Jython’s Java integration, to use the Python “if __name__” cliche, and to avoid having to use jythonc to create a .class file everytime I made a change. The latter was a subtle point caused by the main() in PApplet actually running your class file.
  • The getField() utility was needed to disambiguate frameRate, a variable, and frameRate(), a method, both in PApplet. This was a gift from Jeff Emanuel on the jython-users list
  • I did succeed in building an Applet, see below. This used jythonc to build a true Java .class file, and has facilities for including the Jython core libraries as well your Jython source.
  • The .py file cannot be RandBoxes.py due to a jythonc convention which creates a main() for the outermost Jython code. This main throws an exception, which does not match the standard Java main() signature. Because PApplet has its own standard Java main(), jython flags an error and stops. And, yes, you can bet this took a long time to fine!

Applet

To build the Applet, I used this jythonc command:

jythonc -J-g:none --core --deep --jar RandBoxes.jar RandBoxesTest.py

The applet tag looks like:

  <applet code="RandBoxesTest$RandBoxes"
         archive="RandBoxes.jar,core.jar"
         width=600 height=500>
  </applet>

The Applet, and similar links, are here:

Thoughts

  • Jython is for real: It interoperates well with Java, implements a recent enough version of the Python language, has a good support forum and documentation.
  • This wasn’t easy, but was made possible due to the Jython community and website, and the Jython books (I used Jython for Java Programmers)
  • There is friction between Jython and Java, and is likely to remain there. This will require Jython users trying to integrate with Java to smooth the way somehow. Reflection, like in getField(), can help. Also, Proxy classes are a very reasonable approach. I used this initially, and then had the Jython code subclass PAppletProxy rather than PApplet :
        import processing.core.*;
        public class PAppletProxy extends PApplet {
          public float getFrameRate() {
            return frameRate;
          }
        }
  • Python: well, there’s good and bad. Its a well crafted language, but I certainly made all the blunders newbies make: I got my indentation wrong, found myself using self.foo() way, way too much, and wished for standard lexical scoping. But boy is the code clean.
  • Performance: Java’s RandBoxes runs at about 40fps, while the Jython version runs at about 6-7fps, around 7 times slower, give or take. This is really unfair in a way, I’m such a Jython newbie, and this is definitely a difficult task. And for rapid prototyping, that sort of decrease may easily be acceptable. I think if it were more like 2-3 times slower, that would be fine. Any suggestions welcome!

6 Comments »

  1. [...] Processing from Jython is a promising idea, so I took the base from this post on backspaces.net where they explained how to use Jython and built on it a [...]

    Pingback by Processing with Jython and Nodebox/Shoebot libraries » /dev/stu — February 10, 2010 @ 4:44 pm

  2. Hi,
    I’ve been playing with your code a bit more and Jython seems to be faster these days. Also, it’s possible to install the Shoebot/Nodebox libraries in Jython.

    I did however run into some problems… heres how far I got:

    http://www.stuartaxon.com/2010/02/10/processing-with-jython-and-nodeboxshoebot-libraries/

    Comment by Stu — February 10, 2010 @ 4:46 pm

  3. I studied the css tutorial and I really learned a lot. I think my next homepage will be a lot better than my first one.

    Comment by eigene homepage erstellen — April 7, 2010 @ 8:23 am

  4. [...] first effort was a straight port into Jython, the Java-based implementation of Python. I used the framework provided by backspaces.net and it proved easy enough to complete and the full source code is available here: time series [...]

    Pingback by Processing with Python: A first effort | Hacking Public Health — July 14, 2011 @ 2:39 am

  5. Best Blogs 2011……

    [...] the best blogs will be mentioned below [...]…

    Trackback by jogos do ben 10 — October 18, 2011 @ 5:49 pm

  6. Thanks for using some time to compose “Processing: .
    . with Jython

    Comment by Mahalia — February 18, 2013 @ 9:00 pm

RSS feed for comments on this post. TrackBack URL

Leave a comment

Powered by WordPress