I recently came across Java2Python. As I'm interested in Cairo I thought it would be interesting to try porting one of the example tutorials from ZetCode.
I'll run through the steps involved in porting then try and reach some conclusions at the end :).
1. Get setup
This is easiest in Linux, I'm running Ubuntu (in vmware), and installed
antlr 2.x
python2.5.x
sun java6
pygtk
java-gnome
You can install them like this:
```
sudo apt-get install antlr python2.5 sun-java6-bin libjava-gnome-java
```
Then install Python2Java with easy_install
```
sudo easy_install-2.5 java2python
```
To test if it's working run j2py -i. It should complain there is no file:
```
j2py -i
Usage: j2py [options]
j2py: error: -i option requires an argument ```
If you get any other errors your missing some packages.
2. Get the Java Code from the Simple Example.
Save the 'simple.java' example as 'GSimple.java'
If java-gnome is running ok, compiling and running it you should see a window:
# javac GSimple.java
# java GSimple
{: .alignnone
.size-full .wp-image-171 width="258" height="178"}
- Import java-gnome bindings
- Create a class that extends org.gnome.gtk.Window
- Set the size of the window
- Connect the 'delete' event to an InnerClass. When a delete event arrives, 'onDeleteEvent' is called which in turn calls Gtk.mainQuit(). > Gtk.mainQuit() will quit program and cleanup gtk.
- set window size
- show the window #### 3. Use j2py to create the initial python code. The first stage is to generate the python. ``` {: .brush:shell} # j2py -i GSimple.java > gsimple.py ``` Output \[gsimple.py\]: ``` {: .brush:python} #!/usr/bin/env python # -*- coding: utf-8 -*- class GSimple(Window): """ generated source for GSimple """ def __init__(self): setTitle("Simple") def onDeleteEvent(self, source, event): Gtk.mainQuit() return False connect(Window.DeleteEvent()) setDefaultSize(250, 150) setPosition(WindowPosition.CENTER) show() @classmethod def main(cls, args): Gtk.init(args) GSimple() Gtk.cls.main() if __name__ == '__main__': import sys GSimple.main(sys.argv) ``` #### 5. Analyze Unfortunately j2py is not magic, it's time to have a look at the code.. Things are missing and others look wrong, heres an initial list of problems: - no imports - 'self' not specified in class constructor - CamelCase used - this is suspicious, it's unlikely the cairo bindings work like this - @classmethod is probably not nessacary here - Gtk.cls.main() - This looks particularly suspicious Indeed - If you try and run it, the missing imports become apparent: ``` {: .brush:shell} bagside@bagvapp:~/jythongnome$ python gsimple.py Traceback (most recent call last): File "gsimple.py", line 5, in class GSimple(Window): NameError: name 'Window' is not defined ``` #### 6. Fixing the initial Problems Some problems are easy to fix, others involve learning the differences between the java and python APIs. Starting with the easiest... ##### 'self' not specified in class constructor This looks easiest so we'll tackle it first, heres the new constructor: ``` {: .brush:python} self.setTitle("Simple") def onDeleteEvent(self, source, event): Gtk.mainQuit() return False self.connect(Window.DeleteEvent()) self.setDefaultSize(250, 150) self.setPosition(WindowPosition.CENTER) self.show() ``` Doing this does emphasize how wrong the CamelCase looks, also the onDeleteEvent being a function in the constructor doesn't look right, we could probably move it up a level. ##### No imports In the java code there is ``` {: .brush:java} import org.gnome.gdk.Event; import org.gnome.gtk.Gtk; import org.gnome.gtk.Widget; import org.gnome.gtk.Window; import org.gnome.gtk.WindowPosition; ``` Googling for **Window module** and **gtk** you'll end up at the [pygtk site](http://library.gnome.org/devel/pygtk/stable/class-gtkwindow.html). The documentation shows the Window class is in the gtk module. Further research on the site or in the python prompt shows most of the other classes are in the same module too. ``` {: .brush:python} # To do research in the python prompt, simply start python and try import gtk dir(gtk) help(gtk) help(gtk.Window) ``` It's worth having a glance at the python help to see if the bindings look the same, start python and try: ``` {: .brush:python} import gtk help(gtk.Window) ``` ...\[output snipped\]... ``` {: .brush:python} | set_skip_taskbar_hint(...) | | set_startup_id(...) | | set_title(...) | | set_transient_for(...) | | set_type_hint(...) | | set_urgency_hint(...) ``` It's fairly obvious that CamelCase is not used here, as suspected (remember to change this later...) As "Window" is quite generic, I'll move everything into the 'gtk' namespace, add ``` {: .brush:python} import gtk ``` To the top of the code, and change all instances of the gtk classes to 'gtk.ClassName', e.g. gtk.Window: ``` {: .brush:python} #!/usr/bin/env python # -*- coding: utf-8 -*- import gtk class GSimple(gtk.Window): """ generated source for GSimple """ def __init__(self): self.setTitle("Simple") def onDeleteEvent(self, source, event): Gtk.mainQuit() return False self.connect(gtk.Window.DeleteEvent()) self.setDefaultSize(250, 150) self.setPosition(WindowPosition.CENTER) self.show() @classmethod def main(cls, args): Gtk.init(args) GSimple() Gtk.cls.main() if __name__ == '__main__': import sys GSimple.main(sys.argv) ``` And try running it: ``` {: .brush:shell} Traceback (most recent call last): File "gsimple.py", line 31, in GSimple.main(sys.argv) File "gsimple.py", line 25, in main Gtk.init(args) NameError: global name 'Gtk' is not defined ``` It's complaining about the 'main' class method on GSimple, we can just remove it and move everything into the main method of the module: ``` {: .brush:python} if __name__ == '__main__': import sys Gtk.init(args) GSimple() Gtk.cls.main() ``` This still won't work for sure as 'Gtk' is not in the namespace. The first step is to work out whats going on, consult the java code then the relevant documentation. ``` {: .brush:java} [GSimple.java] ``` public static void main(String\[\] args) { Gtk.init(args); new GSimple(); Gtk.main(); } OK, the code is the same, what does the java gnome documentation say about Gtk.init ? > init > > public static void init(String\[\] args) > > Initialize the GTK libraries. This must be called before any other > org.gnome.\* classes are used. > > Parameters: > args - The command line arguments array. This is passed to the > underlying library to allowing user (or window manager) to alter GTK's > behaviour. > Since: > 4.0.0 Looking in pygtk there isn't 'init' (it would be confusing having this and \_\_init\_\_ anyway). Looking in the pygtk documentation there is no 'init' method (which makes sense as it could be confused with '\_\_init\_\_'. There is however [gtk.main](http://www.pygtk.org/docs/pygtk/gtk-functions.html#function-gtk--main): > gtk.main > > def gtk.main() > > The gtk.main() function runs the main loop until the gtk.main\_quit() > function is called. You can nest calls to gtk.main(). In that case the > call to the gtk.main\_quit() function will make the innermost > invocation of the main loop return. Here I confess some prior knowledge, I already had a vague idea the main loop might be involved as I'd worked with gtk before. So we'll try gtk.main, and commenting out the line calling main on the class as I'm not entirely sure what it does: ``` {: .brush:python} if __name__ == '__main__': import sys gtk.main() # Note gtk.main takes no parameters GSimple() # Gtk.cls.main() ``` ##### Try running And... nothing happens - you have to quit with CTRL-C! It looks like it's not even getting to the constructor (as there should be errors here, maybe gtk.main() needs to run \*after\* the GSimple() constructor.. Sure enough: ``` {: .brush:shell} # python gsimple.py Traceback (most recent call last): File "gsimple.py", line 30, in GSimple() File "gsimple.py", line 13, in __init__ self.setTitle("Simple") AttributeError: 'GSimple' object has no attribute 'setTitle' ``` ##### Fixing the CamelCase The pygtk documentation seemed all be underscored\_lowercase, it's probably safe to speculatively change everything to this then fix any errors: ``` {: .brush:python} #!/usr/bin/env python # -*- coding: utf-8 -*- import gtk class GSimple(gtk.Window): """ generated source for GSimple """ def __init__(self): self.set_title("Simple") def on_delete_event(self, source, event): gtk.main_quit() return False self.connect(gtk.Window.DeleteEvent()) self.set_default_size(250, 150) self.set_position(WindowPosition.CENTER) self.show() if __name__ == '__main__': import sys GSimple() gtk.main() # Note gtk.main takes no parameters # Gtk.cls.main() ``` Output: python gsimple.py gsimple.py:12: GtkWarning: gtk_window_set_title: assertion `GTK_IS_WINDOW (window)' failed self.set_title("Simple") Traceback (most recent call last): File "gsimple.py", line 28, in GSimple() File "gsimple.py", line 18, in __init__ self.connect(gtk.Window.DeleteEvent()) AttributeError: type object 'gtk.Window' has no attribute 'DeleteEvent' Thats progress! It means the self.set\_title call probably works, and there is a self.connect call too. There is no DeleteEvent in the [pygtk documentation](http://library.gnome.org/devel/pygtk/stable/)... which is not suprising as it looks more java than python. Better to step back and have a look at self.connect, in the end I googled gtk connect delete event And got to a [getting started with pygtk](http://www.pygtk.org/pygtk2tutorial/ch-GettingStarted.html) page: > 36 # When the window is given the "delete_event" signal (this is given > 37 # by the window manager, usually by the "close" option, or on the > 38 # titlebar), we ask it to call the delete_event () function > 39 # as defined above. The data passed to the callback > 40 # function is NULL and is ignored in the callback function. > 41 self.window.connect("delete_event", self.delete_event) Note here they have 'window' as their delegating, our generated code extends this class. The signature is slightly different, also instead of 'on\_delete\_event' the function is just called 'delete\_event', this seems more pythonic so we'll do the same. This would also be a good time to move the function into the main class, out of the constructor: ``` {: .brush:python} class GSimple(gtk.Window): """ generated source for GSimple """ def __init__(self): self.set_title("Simple") self.connect('delete_event', delete_event) self.set_default_size(250, 150) self.set_position(WindowPosition.CENTER) self.show() def delete_event(self, source, event): gtk.main_quit() return False ``` And the output: gsimple.py:12: GtkWarning: gtk_window_set_title: assertion `GTK_IS_WINDOW (window)' failed self.set_title("Simple") Traceback (most recent call last): File "gsimple.py", line 29, in GSimple() File "gsimple.py", line 14, in __init__ self.connect('delete_event', delete_event) NameError: global name 'delete_event' is not defined The first line assertion *\`GTK\_IS\_WINDOW (window)' failed* is the important one here; it makes sense, the constructor of the gtk.Window has not been called yet. Add gtk.Window.__init__(self) to the top of the constructor and run. Traceback (most recent call last): File "gsimple.py", line 30, in GSimple() File "gsimple.py", line 17, in __init__ self.set_position(WindowPosition.CENTER) NameError: global name 'WindowPosition' is not defined ###### And again... Repeating the same process for WindowPosition and set\_position, you'll find that the set\_position line should look like this: ``` {: .brush:python} self.set_position(gtk.WIN_POS_CENTER) ``` And the output...  {: .alignnone .size-full .wp-image-171 width="258" height="178"}