use JavaFX from Jython

I love Python, even on JVM! So, I decided to use JavaFX from Jython.

This article is a 20th day of JavaFX Advent Calendar 2012

I started translating Java code on this article into Jython.

Setting up Environment

We need JavaFX SDK and Jython which can use it. A following sequence is actual processes I went through.

To obtain JavaFX developing environment, I installed JavaFX for Java SE 7.

Following this instruction, I made Java 7 as a default JDK.

$ sudo mv /System/Library/Java/JavaVirtualMachines/1.6.0.jdk /System/Library/Java/JavaVirtualMachines/1.6.0.jdk.bak
$ sudo ln -s /Library/Java/JavaVirtualMachines/1.7.0.jdk /System/Library/Java/JavaVirtualMachines/1.6.0.jdk
$ java -version
java version "1.7.0_04"
Java(TM) SE Runtime Environment (build 1.7.0_04-b21)
Java HotSpot(TM) 64-Bit Server VM (build 23.0-b21, mixed mode)

Next I compiled Jython with Java 7.

$ hg clone https://bitbucket.org/jython/jython
$ cd jython
$ ant
$ ls dist/bin/jython

The result jython executable exists at dist/bin/jython. When I compiled, the Mercurial changeset ID is 6898:2ccd73a00a86 and Jython version is 2.7.0a2+.

Now, all had been set up.

Hello, World!

Since Jython treat Java class as Jython class, any Java code may be translated into Jython code. Translation is not so hard. Actually, Jython (hence Python) and Java have similar syntax and semantics, such as class-based OOP, member access throw dot "." and method call with parenthesis (Of course, they also have many distinctions).

This Jython code translated from Java code on this page is following:

# hello.py
from javafx.application import Application
from javafx.scene import Scene
from javafx.scene.control import Label
from javafx.scene.layout import AnchorPane


class Hello(Application):
    def start(self, stage):
        stage.setTitle("Hello, World!")

        root = AnchorPane()
        label = Label("Hello, World!")
        root.getChildren().add(label)

        scene = Scene(root, 100, 40)
        stage.setScene(scene)

        stage.show()


if __name__ == '__main__':
    Application.launch(Hello().class, [])

Doesn't this code similar to Java code?

Let's run this script:

$ dist/bin/jython -Dpython.path=/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home/jre/lib/jfxrt.jar ../hello.py
Glass detected outstanding Java exception at +[GlassHelper ApplicationClass]:src/com/sun/mat/ui/GlassHelper.m:122
Exception in thread "JavaFX Application Thread" java.lang.ClassNotFoundException: com.sun.glass.ui.Application
        at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:264)

A path to jfxrt.jar is added to python.path, which corresponds with classpath in Java.

Regardless ClassNotFoundException, my first JavaFX application is running. A window displaying a sentence "Hello, World!" will appears on our screen.

A little Improvement

Here are Jython code which behavior is same as above, but which has less local variables.

from javafx.application import Application
from javafx.scene import Scene
from javafx.scene.control import Label
from javafx.scene.layout import AnchorPane


def scene(root, width, height):
    return Scene(root, width, height)


def anchorPane(*children):
    result = AnchorPane()
    result.getChildren().addAll(children)
    return result


def label(text=''):
    return Label(text)


def launch_stage(stage, scene, title=''):
    stage.setTitle(title)
    stage.setScene(scene)
    stage.show()


class Hello(Application):
    def start(self, stage):
        launch_stage(
            stage=stage,
            scene=scene(
                root=anchorPane(
                    label('Hello, World!')),
                width=100,
                height=40),
            title='Hello, World!')


if __name__ == '__main__':
    Application.launch(Hello().class, [])

Indeed these helper functions are just toys and very naive, but replacing grave constructor calls with light function calls using keyword parameters may lead us easier reading of source codes.

trying to use FXML

Along the article I tried to use FXML, but following code did not work:

from javafx.application import Application
from javafx.fxml import FXMLLoader
from javafx.scene import Scene


class Hello(Application):
    def start(self, stage):
        root = FXMLLoader.load(self.getClass().getResource('Hello.fxml'))

        scene = Scene(root)

        stage.setScene(scene)
        stage.show()


if __name__ == '__main__':
    Application.launch(Hello().class, [])
Glass detected outstanding Java exception at +[GlassHelper ApplicationClass]:src/com/sun/mat/ui/GlassHelper.m:122
Exception in thread "JavaFX Application Thread" java.lang.ClassNotFoundException: com.sun.glass.ui.Application
        at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:264)
<type 'org.python.proxies.__main__$Hello$0'>
None
Exception in Application start method
Traceback (most recent call last):
  File "../hello3.py", line 19, in <module>
    Application.launch(Hello().class, [])
        at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:399)
        at com.sun.javafx.application.LauncherImpl.access$000(LauncherImpl.java:47)
        at com.sun.javafx.application.LauncherImpl$1.run(LauncherImpl.java:115)
        at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.NullPointerException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)

java.lang.RuntimeException: java.lang.RuntimeException: Exception in Application start method

That reason was a failure on loading resource Hello.fxml. Hmm, then I specified a path to Hello.fxml explicitly:

from javafx.application import Application
from javafx.fxml import FXMLLoader
from javafx.scene import Scene
from java.net import URL


class Hello(Application):
    def start(self, stage):
        root = FXMLLoader.load(URL('file:///Users/tomohiko/MyWorks/Jython/Hello.fxml'))

        scene = Scene(root)

        stage.setScene(scene)
        stage.show()


if __name__ == '__main__':
    Application.launch(Hello().class, [])

but did not work also.

Glass detected outstanding Java exception at +[GlassHelper ApplicationClass]:src/com/sun/mat/ui/GlassHelper.m:122
Exception in thread "JavaFX Application Thread" java.lang.ClassNotFoundException: com.sun.glass.ui.Application
        at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:264)
12 20, 2012 11:45:14 午後 javafx.fxml.FXMLLoader logException
SEVERE: java.lang.ClassNotFoundException: javafx.scene.control.Label
/Users/tomohiko/MyWorks/Jython/Hello.fxml:3
  at javafx.fxml.FXMLLoader.importClass(FXMLLoader.java:2268)
  at javafx.fxml.FXMLLoader.processImport(FXMLLoader.java:2122)
  at javafx.fxml.FXMLLoader.processProcessingInstruction(FXMLLoader.java:2090)
  at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2017)
  at javafx.fxml.FXMLLoader.load(FXMLLoader.java:1900)
  at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2486)
  at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2478)
  at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2472)
  at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2466)
  at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2461)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:601)
  at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:186)
  at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:204)
  at org.python.core.PyObject.__call__(PyObject.java:403)
  at org.python.core.PyObject.__call__(PyObject.java:407)
  at org.python.pycode._pyx0.start$2(../hello3.py:14)
  at org.python.pycode._pyx0.call_function(../hello3.py)
  at org.python.core.PyTableCode.call(PyTableCode.java:165)
  at org.python.core.PyBaseCode.call(PyBaseCode.java:301)
  at org.python.core.PyBaseCode.call(PyBaseCode.java:194)
  at org.python.core.PyFunction.__call__(PyFunction.java:417)
  at org.python.core.PyMethod.instancemethod___call__(PyMethod.java:232)
  at org.python.core.PyMethod.__call__(PyMethod.java:223)
  at org.python.core.PyMethod.__call__(PyMethod.java:213)
  at org.python.core.PyMethod.__call__(PyMethod.java:208)
  at org.python.core.PyObject._jcallexc(PyObject.java:3555)
  at org.python.proxies.__main__$Hello$0.start(Unknown Source)
  at com.sun.javafx.application.LauncherImpl$5.run(LauncherImpl.java:315)
  at com.sun.javafx.application.PlatformImpl$4.run(PlatformImpl.java:174)
  at com.sun.javafx.application.PlatformImpl$3.run(PlatformImpl.java:141)

Exception in Application start method
Traceback (most recent call last):
  File "../hello3.py", line 18, in <module>
    Application.launch(Hello().class, [])
        at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:399)
        at com.sun.javafx.application.LauncherImpl.access$000(LauncherImpl.java:47)
        at com.sun.javafx.application.LauncherImpl$1.run(LauncherImpl.java:115)
        at java.lang.Thread.run(Thread.java:722)
Caused by: javafx.fxml.LoadException: java.lang.ClassNotFoundException: javafx.scene.control.Label
        at javafx.fxml.FXMLLoader.importClass(FXMLLoader.java:2268)
        at javafx.fxml.FXMLLoader.processImport(FXMLLoader.java:2122)
        at javafx.fxml.FXMLLoader.processProcessingInstruction(FXMLLoader.java:2090)
        at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2017)
        at javafx.fxml.FXMLLoader.load(FXMLLoader.java:1900)
        at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2486)
        at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2478)
        at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2472)
        at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2466)
        at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2461)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)

java.lang.RuntimeException: java.lang.RuntimeException: Exception in Application start method

A cause of failure was changed, but I do not know how I can break this ClassNotFoundException.

Even now I cannot run this Jython code. Would someone help me?

Conclusion

JavaFX is available from Jython and flexibility of Jython helps us to write GUI code. There are some problems, but challenging to use JavaFX from other JVM languages may be an interesting experiment.

Enjoy Jythoning and JavaFXing!!

appended in 2012/12/26

Thanks for 欽ちゃん1号.

Modified source code as following:

# hello3.py
from javafx.application import Application
from javafx.fxml import FXMLLoader
from javafx.scene import Scene
from java.net import URL


class Hello(Application):
    def start(self, stage):
        # root = FXMLLoader.load(URL('file:///Users/tomohiko/MyWorks/Jython/Hello.fxml'))
        root = FXMLLoader.load(self.getClass().getResource('/Hello.fxml'))

        scene = Scene(root)

        stage.setScene(scene)
        stage.show()


if __name__ == '__main__':
    Application.launch(Hello().class, [])

and launched with following command:

$ ls dist/bin/jython
dist/bin/jython
$ ls ../hello3.py
../hello3.py
$ ls ../Hello.fxml
../Hello.fxml
$ export CLASSPATH=../:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/jre/lib/jfxrt.jar:${CLASSPATH}
$ echo $CLASSPATH
../:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/jre/lib/jfxrt.jar:...

$ dist/bin/jython ../hello3.py

the stacktraces became not to appear and seemed to run normally.

NOW, we can run JavaFX program with FXML from Jython!!

Comments

blog comments powered by Disqus

Licenses