Mutliple Events with Jython Event Properties


At first glance, Jython event properties seem magical–almost too magical. They allow you to bypass the massive Java boilerplate required to set up event listeners. What is normally something along these lines:

public class Test extends JFrame {
    private JButton button;

    public Test() {
         super("A Frame");
         setSize(200, 200);
         setupEvents();
         setVisible(true);
    }

    public void setupEvents() {
          button = new JButton("Click me");
          button.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                      System.out.println("Button pressed");
                }
          });

         this.add(button);
    }
}

Instead becomes something like this, when using Jython:

class Test(JFrame):
    def __init__(self):
        JFrame.__init__(self, "A Frame")
        button = JButton("Hello", actionPerformed=self.buttonPress)

        self.add(button)
        self.setSize(200, 200)
        self.visible = true

    def buttonPress(self, event):
        print "Button pressed"

if __name__=="__main__":
    Test()

The Python version of this is far briefer and much more compact than its Java counterpart. That’s all well and good, you say, but what if you want to add multiple event handlers? At first glance, it does not look like Jython’s event properties would allow for such a thing. Luckily, it is possible. However, it is not documented. Indeed, documentation of Jython’s Java API and specialized features is a bit … lacking.

This undocumented feature was discovered after some chatting in the Jython IRC channel, and a bit of experimentation. It turns out that the event properties are actually an object type called CompoundCallable. These objects, as their name implies, store a set of functions that are executed one after another in sequence when called. Observe:

class Test(JFrame):
    def __init__(self):
        JFrame.__init__(self, "Hello Jython")
        button = JButton("Hello")
        button.actionPerformed = self.hello
        button.actionPerformed.append(self.otherEvent)
        self.add(button)

        self.setSize(200, 200)
        self.visible = true

    def hello(self, event):
        print "Hello, world!"

    def otherEvent(self, event):
        print "this is the other event"

if __name__=="__main__":
      Test()

When the button is clicked, both the “hello” and “otherEvent” functions will execute. Any Jython event property (including ones you create yourself) has two useful methods: append() and clear(). They do what their names say. The append method takes a function as a parameter, and adds it to the list of functions to be executed. The clear() methods clears all the functions out of the property.

Unfortunately, there do not appear to be ways to iterate over the functions contained within the CompoundCallable, or access specific event handlers directly. In a similar vein, you cannot delete individual event handlers out of the CompoundCallable. Your only option is the clear() method.

The source code for the PyCompoundCallable object is very simple. It doesn’t look like it would be too hard to add some more methods for getting and deleting specific event handlers. (Scroll way down to the bottom of that page to view the code.)