Home > clojure, seesaw, swing > Nice Weather for Some Clojure (UIs)

Nice Weather for Some Clojure (UIs)

Summary: I tried build something slightly non-trivial with Seesaw and learned some things.


And so it came to pass that it seemed like I needed to start dog-fooding Seesaw a bit. I’ve been steadily adding features and building tiny toy apps to demonstrate them. Others (and others) have apparently found it useful, but I haven’t tried anything myself.

So, I decided to write Gaidica, a little app that displays weather info for user-selected cities. The whole process wasn’t terrible, but shows that there’s work to do. I’d guess about 4 hours of work including fixing Seesaw bugs as I hit them and at least an hour puzzling over Clojure XML stuff. Here’s a screenshot of the forecasts:

The code is all available on github.

Intro

Just to make sure we’re on the same page, Seesaw is an API/DSL for building user interfaces in Clojure. It happens to be built on Swing as you’ll see below, but please don’t judge it too harshly. I’m working hard to make it so you never need to know that Swing’s even there.

The Data

I decided to use Weather Underground’s XML API for the data. It provides the data in a simple format and as a bonus, includes icons for weather conditions, which saved me some work. Additionally, I got to do some XML wrangling in Clojure. Let’s just say that that deserves a blog post of its own.

In any case, I just point Clojure’s (xml/parse) function at the URL and get back some results. All this is done in a background thread using (future) to keep the UI responsive.

The Good

I mostly want to focus on the bad in this post, but despite any difficulties I had, building this was more enjoyable in Clojure+Seesaw than the equivalent Clojure+Swing or raw Java code. The lack of (proxy) and (reify) usage is testament to that.

Seesaw’s table support, seen in the webcam tab below, really paid off. Compared to the equivalent JTable-wrangling you’d have to do, it was downright dreamy.

Here’s the code to create that table:

(defn make-webcam-table [] 
  (table 
    :id :webcam-table 
    :model 
      [:columns 
        [{:key :handle :text "Name" } 
         :lat :lon 
         {:key :updated :text "Last Updated"}
         {:key :image :text "Image"}]]))

The Bad

I learned a lot during this exercise. One thing I noticed is that Swing is a pain in the ass. I think when you’re in the depths of a Java-based Swing app, you don’t even notice it because you’re already so mired in Java verbosity. It’s a form of Stockholm Syndrome I guess.

Most of the pain centered around, you guessed it, layout. I wanted to do it with the vanilla Swing layouts provided by Seesaw (border-panel, vertical-panel, etc), but after fiddling with component alignments and lame resize problems, I eventually surrendered and ran back to MigLayout. It’s powerful and pretty easy to use, but it’s yet another little language to learn. Luckily Seesaw already has Mig support.

Otherwise, most of the other issues I had were just missing functionality. Swing methods not yet wrapped by Seesaw. The nice thing is that since Seesaw’s a fairly thin wrapper, it’s easy to drop down to raw Swing when needed. Some random functionality that I’m going to address as a result:

  • Manipulation of the widget hierarchy needs to be seamless. This has historically been a problem in Swing (invalidate(); revalidate(); repaint()!!)
  • Widget styling. Sprinkling fonts and colors throughout the code is a hassle. The main question is whether I just add convenience options for this, or go whole hog and try for a CSS-style styling.
  • Saner defaults for “vanilla” layouts. I think I can tune some of the defaults and add some dials to make it less frustrating. It still really bugs me that I had to use Mig for something so simple.

Conclusion

From the perspective of a potential Seesaw user, already suspicious of Swing, all of this is a problem. I’ll take it as a challenge to continue making Seesaw a buffer between them and the occasional craziness of Swing. Feedback welcome!

Categories: clojure, seesaw, swing Tags: , ,
  1. Andreas Liljeqvist
    May 25th, 2011 at 06:01 | #1

    Hi.

    Unfortunately I haven’t had the time to actually try Seesaw.
    But I think it’s sorely needed.
    Great work, hope to see more updates.

    Andreas Liljeqvist

  2. tzach
    May 29th, 2011 at 00:53 | #2

    Nice work!
    The waiting time for data is somewhat frustrating.
    A progress bar or a sand clock pointer might help.

    I’m starting to play around with Seesaw and find it very easy to use.
    I wanted to build a simple form including radio buttons and combo boxs.
    What is the idiomatic (and easiest) way to do so with Seesaw?
    Ideally, I was looking for a seq to combo box box widget function.

    Tzach

  3. May 29th, 2011 at 13:04 | #3

    @tzach
    Thanks! As a matter of fact, progress bar support in Seesaw was added today and cursor support is on the docket. Hopefully I’ll get a chance to update the example this week.

    For your use case, Seesaw has (radio-button) and (combobox) already built in. Your comment reminded me that ButtonGroup isn’t supported yet, so I’ll have to add that shortly. Until then you’d have to just use ButtonGroup directly to ensure the mutual exclusion of radio buttons.

    (combobox) can already select from a seq using the :model option. Something like (combobox :model (range 10)) will give you a combobox with numbers from 0 to 10 to choose from. Use the (selection) function to get the current selection.

    I’m hesitant to say what’s idiomatic in Seesaw at this point since everything’s so new. That will probably shake over time as people use it and figure out what works and what doesn’t.

  4. tzach
    May 30th, 2011 at 01:02 | #4

    @dave
    Thanks Dave!
    From some reason I missed the combo box and radio support in the code, and I was looking for it.
    Thanks again for the grate work!

  5. May 30th, 2011 at 17:41 | #5

    @tzach
    No problem. Button group support’s in there now, with an example: https://github.com/daveray/seesaw/blob/develop/src/seesaw/examples/button_group.clj

  6. June 13th, 2011 at 04:12 | #6

    Do you plan a version without miglayout resp. with a factored out miglayout? I’d prefer forms.

  7. June 13th, 2011 at 06:04 | #7

    @Meikel
    Hey. I’ve considered pushing the Mig support into its own namespace so if you don’t use it you can just omit the jar. Breaking it up into multiple projects/modules like Incanter might be overkill. That said, there’s nothing in Seesaw that requires you use Mig and the jar’s only 200k.

    By “Forms” you mean JGoodies Forms? They look nice and more direct support might be a nice addition to Seesaw, especially if someone would use it :)

  8. June 27th, 2011 at 08:29 | #8

    Great writeup. I did not know of SeeSaw, but it looks nice.

    Just a side note – your GitHub link to Seesaw is broken – should be https://github.com/daveray/seesaw instead of https://https//github.com/daveray/seesaw (Note the double https in the second url).

    Nice writeup. Thanks.

  9. June 27th, 2011 at 08:35 | #9

    @Raju
    Whoops. Thanks for the tip. Fixed.

  1. No trackbacks yet.