My hobbyist coding updates and releases as the mysterious "Mr. Tines"

Saturday 29 November 2008

Python Swings; Ruby still doesn't

Here's a proof-of-concept of running a Swing UI from Python on the CLR and the JVM, following up on earlier experiments (which I had forgotten about).

try:  
  # IronPython must load the J# redistributable libraries  
  # Using the FullName of the assemblies is probably overkill  
  import clr  
  clr.AddReference( 'vjslib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' )  
  clr.AddReference( 'vjssupuilib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' )  
except ImportError:  
  pass  
     
import javax.swing  
import java.awt.event  
import java.lang  
  
class Exit(java.awt.event.WindowAdapter):  
  def windowClosing(self, evt):  
    evt.getWindow().setVisible(False)
    evt.getWindow().dispose()
  def windowClosed(self, evt):  
    java.lang.System.exit(0) 
    
# With the 'import's above, we can now refer to things that are part of the  
# standard Java platform via their full paths.  
frame = javax.swing.JFrame("Window") # Creating a Java JFrame.  
label = javax.swing.JLabel("Hello")  
     
# We can transparently call Java methods on Java objects, just as if they were defined in Python.  
frame.getContentPane().add(label)  # Invoking the Java method 'getContentPane'.  
frame.addWindowListener(Exit()) # Need to do this for IronPython/J# which is barely JDK 1.2 level  
frame.pack()  
frame.setVisible(True)

which works happily with java -jar jython-complete.jar HelloSwing.py using Jython 2.5β0 and with ipy.exe HelloSwing.py for IronPython2.0RC2 as well.

I was briefly conflicted earlier this week when I saw that IronRuby had reached α2, and wondered about a JRuby/IronRuby UI, but the nigh-equivalent

begin  
  require 'vjslib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'  
  require 'vjssupuilib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'  
rescue LoadError  
  # This is the 'magical Java require line'.  
  require 'java'  
end    
        
class Exit < java.awt.event.WindowAdapter
  def windowClosing evt
    evt.getWindow.setVisible(false)
    evt.getWindow.dispose # make Closed
  end
  def windowClosed evt
    java.lang.System.exit 0 
  end
end

# With the 'require' above, we can now refer to things that are part of the  
# standard Java platform via their full paths.  
frame = javax.swing.JFrame.new("Window") # Creating a Java JFrame.  
label = javax.swing.JLabel.new("Hello")  
     
# We can transparently call Java methods on Java objects, just as if they were defined in Ruby.  
frame.getContentPane.add(label)  # Invoking the Java method 'getContentPane'.  
frame.addWindowListener(Exit.new()) # Need to do this for IronRuby/J# which is barely JDK 1.2 level  
frame.pack  
frame.setVisible(true)  

which works on JRuby 1.1.5, fails in IronRuby α2 when trying to load Swing classes, and I've not found a dodge that will get me at even class metadata for the Swing files. Ruby's casing rules may make it impossible for IronRuby to recognise the lower-case Java namespaces (I had fun with that earlier with Ruby.Net).

LATER: I could do something VM dependent that takes the class name and argument list for construction in {Iron|J}Ruby. What is more tricky is inheritance like

class Exit < java.awt.event.WindowAdapter

which is a major use-case. I wonder if there might be a metaprogramming technique I can use for this...

LATER YET: The problem is that there is no exposed mechanism to wrap a System.Type as a Ruby Class; I could manually subclass everything that is not final that I want in a C# assembly with a My.JavaNamespace and emulate the way that JRuby maps namespace Java::JavaAwtEvent to a value named java.awt.event, but the nearest to automation I can see is to Reflection.Emit a whole ton of stuff.

So, for the moment, consider it a dead end.