Posted by stu at August 21st, 2009

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

gsimple

Now we’ll run through the code, it’s important to understand what it does before we port it…

package com.zetcode;

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;

/**
 * ZetCode Java Gnome tutorial
 *
 * This program is a simple Java Gnome
 * application.
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class GSimple extends Window  {

    public GSimple() {

        setTitle("Simple");

        connect(new Window.DeleteEvent() {
            public boolean onDeleteEvent(Widget source, Event event) {
                Gtk.mainQuit();
                return false;
            }
        });

        setDefaultSize(250, 150);
        setPosition(WindowPosition.CENTER);
        show();
    }

    public static void main(String[] args) {
        Gtk.init(args);
        new GSimple();
        Gtk.main();
    }
}
Code Runthrough
  • 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.

    # j2py -i GSimple.java > gsimple.py

    Output [gsimple.py]:

    #!/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:

    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:

            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

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

    # 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:

    import gtk
    help(gtk.Window)

    …[output snipped]…

     |  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

    import gtk

    To the top of the code, and change all instances of the gtk classes to ‘gtk.ClassName’, e.g. gtk.Window:

    #!/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:

    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:

    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.

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

    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:

    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:

    # 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:

    #!/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… 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 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:

    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:

    self.set_position(gtk.WIN_POS_CENTER)
    

    And the output…
    gsimple

    Huzzah! Success!

    Final Source code

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    import gtk
    
    class GSimple(gtk.Window):
    
        def __init__(self):
    	gtk.Window.__init__(self)
            self.set_title("Simple")
    
            self.connect('delete_event', self.delete_event)
            self.set_default_size(250, 150)
            self.set_position(gtk.WIN_POS_CENTER)
            self.show()
    
        def delete_event(self, source, event):
    	gtk.main_quit()
    	return False
    
    if __name__ == '__main__':
        import sys
        GSimple()
        gtk.main()
    

    Conclusions

    Although Cairo APIs are available for Java and Python they are of course quite different. One implication of this is that if you wanted to use Cairo from a Jython java applet (via native methods) it couldn’t have code 100% the same as a normal python app.
    java2python is good, but you still need to put the work in.
    Working in this way is also a good way to find differences within implementations of an API.


    Posted in projects, Uncategorized| 

Commentary

  1. 1. November 18th, 2009 13:25

    Hi,
    After follow all steps with succes, I have a problem at the moment to execute this command
    $ j2py -i
    I get this:
    Traceback (most recent call last):
    File “/usr/local/bin/j2py”, line 9, in
    from java2python.lib.lexer import Lexer
    File “/usr/local/lib/python2.6/dist-packages/java2python/lib/lexer.py”, line 4, in
    import antlr
    ImportError: No module named antlr
    I have antlr installed, so what else can I do?
    Thanks for help.


    Oscar

  2. 2. November 18th, 2009 13:46

    Well, problem solved, it’s maybe a conflict with python 2.6.x and 2.5.x,
    I installed antlr 2.7.x from synaptic and all is done.


    Oscar

  3. 3. November 18th, 2009 14:20

    Cool, I thought I was the only one crazy to try this :)

    Keep us posted if you do anything with gtk and jython !


    stu

  4. 4. November 20th, 2009 13:55

    Hi again, this was helpful, I’m trying to convert java regex into python re, just for a homework, but this is a good tool, I’d do the example but when I type this command ‘$ javac GSimple.java’ show a lot of compilation bugs. In some aplications the .py file shows and and exception with character ‘@’, maybe cannot handle actions commands with interface like ‘@Action’.

    Well i’ll show what I’d do for installation:

    1. $ sudo apt-get install antlr python2.5 sun-java6-bin libjava-gnome-java
    2. $ sudo easy_install-2.5 java2python Don’t work at the beggining but I tried with:
    $ sudo apt-get install python-setuptools and again with 2.
    3. But the command $ j2py -i don’t work, and then run $ antlr, the system show me try with ‘apt-get install pccts’ I’d do and try again with j2py -i, but nothing.
    4. $ sudo synaptic, then search and check all antlr 2.7.7 related packages and then all works without problems.


    Oscar

  5. 5. November 20th, 2009 13:58

    Oops I forgot that, maybe the ‘python-antlr’ package found in synaptic fix my problem.

    Pd: sorry for my bad english (just in case)


    Oscar

  6. 6. November 20th, 2009 14:17

    Cool, so python-antlr fixed it ?

    If your having trouble compiling GSimple.java it could be a problem with java-gnome, post the errors here if it’s not working.

    I should probably update the article to python 2.6 at some point.


    stu

  7. 7. November 26th, 2009 19:20

    Hi again,
    there are my errors:
    javac Gsimple.java
    Gsimple.java:21: class GSimple is public, should be declared in a file named GSimple.java
    public class GSimple extends Window {
    ^
    Gsimple.java:3: package org.gnome.gdk does not exist
    import org.gnome.gdk.Event;
    ^
    Gsimple.java:4: package org.gnome.gtk does not exist
    import org.gnome.gtk.Gtk;
    ^
    Gsimple.java:5: package org.gnome.gtk does not exist
    import org.gnome.gtk.Widget;
    ^
    Gsimple.java:6: package org.gnome.gtk does not exist
    import org.gnome.gtk.Window;
    ^
    Gsimple.java:7: package org.gnome.gtk does not exist
    import org.gnome.gtk.WindowPosition;
    ^
    Gsimple.java:21: cannot find symbol
    symbol: class Window
    public class GSimple extends Window {
    ^
    Gsimple.java:25: cannot find symbol
    symbol : method setTitle(java.lang.String)
    location: class com.zetcode.GSimple
    setTitle(“Simple”);
    ^
    Gsimple.java:27: package Window does not exist
    connect(new Window.DeleteEvent() {
    ^
    Gsimple.java:34: cannot find symbol
    symbol : method setDefaultSize(int,int)
    location: class com.zetcode.GSimple
    setDefaultSize(250, 150);
    ^
    Gsimple.java:35: cannot find symbol
    symbol : variable WindowPosition
    location: class com.zetcode.GSimple
    setPosition(WindowPosition.CENTER);
    ^
    Gsimple.java:36: cannot find symbol
    symbol : method show()
    location: class com.zetcode.GSimple
    show();
    ^
    Gsimple.java:40: cannot find symbol
    symbol : variable Gtk
    location: class com.zetcode.GSimple
    Gtk.init(args);
    ^
    Gsimple.java:42: cannot find symbol
    symbol : variable Gtk
    location: class com.zetcode.GSimple
    Gtk.main();
    ^
    14 errors

    I have Xubuntu 9.10 and I installed the libjava-gnome-java package.
    which more dependencies I need?


    Oscar

  8. 8. November 26th, 2009 20:52

    Hi,
    The first problem is because the file is called Gsimple.java, not GSimple.java (notice the second ‘s’ should be uppercase).

    The other errors look like java gnome is not in the classpath.


    stu

  9. 9. November 30th, 2009 05:12

    I had a bit of a go at this on the weekend.
    The current version of the java-gnome bindings is 4.x, for a while ubuntu had the old bindings installed (my test vm seems to).

    To check which version you have look in
    /usr/share/java
    gtk-4.0.jar Is the correct version
    gtk2.10 Is the old version

    I managed dist-upgrade my ubuntu to karmic koala, but

    $ sudo apt-get upgrade libjava-gnome-java

    Was still not enough… however:

    $ sudo apt-get remove libjava-gnome-java
    $ sudo apt-get install libjava-gnome-java

    DID install gtk-4.0.jar.

    You may need to manually set the classpath:

    $ export CLASSPATH=/usr/share/java/gtk-4.0.jar

    Unfortunately I don’t have the vm I used to write this tutorial with at the moment, so it’s a little tricky to test stuff – if I get time I’ll try and get everything working again and fix any gaps in the tutorial.


    stu

Trackbacks

    Leave a comment, a trackback from your own site or subscribe to an RSS feed for this entry.

Leave a response

 

leave url