If you really want to get into Swing I suggest taking a look at the
following book:

http://oreilly.com/catalog/9780596009076/

It's got tons of "I didn't knnow you could do that..." ideas

On Aug 5, 9:01 am, John Harrop <jharrop...@gmail.com> wrote:
> On Tue, Aug 4, 2009 at 10:33 PM, Joe Van Dyk <joevan...@gmail.com> wrote:
>
>
>
>
>
> > Hey,
>
> > New to Java and Clojure.  My possibly relevant experience is with Gtk
> > and Ruby and C++ programming.
>
> > I'd like to develop a GUI in Clojure.  I'm guessing I want to use
> > Swing.  This application will be targeted towards scientists who are
> > used to working with the most horribly-designed Tk UI known to man, so
> > I'm sure they will be fine with Swing.
>
> > So, where's the best place to start?
>
> > What I've been doing:
>
> > - Watched the peepcode
> > - Working my way through Stuart's book
> > - Playing with netbean's GUI designer
>
> > Is it possible to use the netbean designer and clojure at the same
> > time?  What's the best way of doing that?  I'm used to writing GUIs in
> > C++, would Clojure have a drastically different approach (as far as
> > application logic and event handling)?
>
> I've not done much GUI work in Clojure yet, but what I have done has not
> involved any GUI builders. Just Clojure functions, some calling Swing
> methods.
>
> A lot simplifies once you have some useful utility functions and macros. For
> example:
>
> (defmacro jbutton
>   "Creates a JButton with the specified text, which when clicked executes
> the body with an ActionEvent bound to event."
>   [text [event] & body]
>   `(let [b# (JButton. ~text)]
>      (.addActionListener b#
>        (proxy [ActionListener] []
>          (actionPerformed [~event] ~...@body)))))
>
> (defn add-all
>   "Adds the specified things, in order to obj. Useful for obj = a Swing
> JPanel with a BoxLayout or a FlowLayout and things = Swing components."
>   [obj & things]
>   (doseq [thing things] (.add obj thing)))
>
> (defn box-layout-panel
>   "Given a JPanel or other component that can have a layout, gives it a
> BoxLayout with the specified orientation."
>   [panel orientation]
>   (.setLayout panel (BoxLayout. panel orientation)))
>
> (defn horizontal-box-layout-panel
>   "Given a JPanel or other component that can have a layout, gives it a
> BoxLayout with horizontal orientation."
>   ([panel]
>     (box-layout-panel panel BoxLayout/LINE_AXIS)
>     panel)
>   ([]
>     (horizontal-box-layout-panel (JPanel.))))
>
> (defn horizontal-glue
>   []
>   (Box/createHorizontalGlue))
>
> (defn left-component
>   "Given a JComponent, returns a component that will try to display it at
> its
> preferred size on the left side of its container."
>   [component]
>   (let [result (horizontal-box-layout-panel)]
>     (.add result component)
>     (.add result (horizontal-glue))
>     result))
>
> (defn combine-borders
>   "Combines multiple borders into one. The first argument will be the
> outermost,
> followed by the next argument, and the next; the last argument will be the
> innermost."
>   ([border]
>     border)
>   ([outer inner]
>     (BorderFactory/createCompoundBorder outer inner))
>   ([outermost nxt & more]
>     (reduce combine-borders (cons outermost (cons nxt more)))))
>
> and so forth.
>
> I've even got:
>
> (def *instruction-columns* 60)
>
> (def *instruction-inset* 10)
>
> (defn instructions
>   "Turns strings, or other objects, using str, into instruction text and
> returns
> a component suitable for displaying these in a Swing UI."
>   [& strings]
>   (doto (JTextArea. (apply str strings))
>     (.setEditable false)
>     (.setLineWrap true)
>     (.setWrapStyleWord true)
>     (.setColumns *instruction-columns*)
>     (.setOpaque false)
>     (.setBorder (combine-borders
>                   (lowered-bevel-border)
>                   (empty-border *instruction-inset*)))))
>
> which produces a component that displays essentially a multi-line label with
> a 10-pixel space around its perimeter; as the name says, useful for putting
> instruction strings in a dialog box or whatever without worrying about where
> to put manual line breaks. I've used this in wizards, which often have large
> chunks of textual instructions as well as a few form fields and back, next,
> cancel buttons.
>
> Naturally, other code does things like take a passed-in JPanel and pop up a
> JDialog with a specified title, the JPanel front and center, and OK and
> Cancel buttons or similar at the bottom, with a supplied block of code to
> execute when OK is pressed and the behavior that both Cancel and the close
> button close and dispose the JDialog without doing anything; the macro for
> this expands into code that evaluates to nil on cancel and whatever the OK
> code returns on OK.
>
> It can take as little as ten minutes to slap something like one of these
> things together and another ten to test it. With these kinds of reusable UI
> fragments, both "framed" elements (like jbutton and instructions generate)
> and "framing" ones (like the JDialog generator macro), slapping together a
> GUI in code becomes easy.
>
> You might have noticed indications of my preference for using nested
> BoxLayouts to build things. I like this because BoxLayout respects a
> component's preferred size and nested BoxLayouts tend to be well behaved
> when subjected to window or component resizings. GridBagLayout would be the
> only other contender for flexibility and size-control of components, but
> BoxLayout nesting lends itself much better to a compositional style of
> assembly of the GUI component tree, and thus to a functional style of code
> and to use of reusable fragments like the above examples.
>
> Last but not least, do keep in mind the need for Swing actions to take place
> on the EDT. SwingWorker macro in the works, and someone else posted code
> here a while ago that I shamelessly copied that invokes actions on the EDT.
> I may have made some modifications:
>
> (defmacro do-on-edt
>   "Evaluates body on the event dispatch thread. Does not block. Returns
> nil."
>   [& body]
>   `(SwingUtilities/invokeLater (fn [] ~...@body)))
>
> (defmacro get-on-edt
>   "Evaluates body on the event dispatch thread, and returns whatever body
> evaluates to. Blocks until body has been evaluated."
>   [& body]
>   `(let [ret# (LinkedBlockingDeque.)]
>      (SwingUtilities/invokeLater (fn [] (.put ret# (do ~...@body))))
>      (.take ret#)))
>
> (defmacro future-on-edt
>   "Evaluates body on the event dispatch thead and returns a future that will
> eventually hold the result of evaluating that body."
>   [& body]
>   `(future (get-on-edt ~...@body)))
>
> The REPL does NOT run on the EDT, so GUI code tests from the REPL should be
> wrapped in do-on-edt. That returns nil; the other two furnish the result of
> evaluating the body code back to the calling thread. All but get-on-edt can
> be invoked from the EDT safely; get-on-edt will deadlock if called on the
> EDT, since invokeLater won't do anything until pending events are processed,
> but one of those pending events will be the code that's blocking on the
> LinkedBlockingDeque waiting for the invokeLater. It can be called from
> SwingWorkers, futures, and agents safely though, and directly at the REPL. I
> should make get-on-edt smarter actually:
>
> (defmacro get-on-edt
>   "Evaluates body on the event dispatch thread, and returns whatever body
> evaluates to. Blocks until body has been evaluated."
>   [& body]
>   `(if (SwingUtilities/isEventDispatchThread)
>      (do ~...@body)
>      (let [ret# (LinkedBlockingDeque.)]
>        (SwingUtilities/invokeLater (fn [] (.put ret# (do ~...@body))))
>        (.take ret#))))
>
> There, fixx0red.
>
> I have a .clj file full of the most reusable bits of Swing interop code like
> these. The (ns ...) at the top is chock full of Swing classes. The code that
> uses this .clj file tends to only need to import one or two, on top of
> :use-ing that .clj file, so building up a load of little utilities like
> these in a file even saves on big complex (ns ...) work at the top of your
> other UI-related source files.
>
> By the way, feel free to use all of the above as public domain code. Well,
> unless the earlier posted of the foo-on-edt functions complains anyway, but
> I think he offered them in the same spirit, and I'm not sure code snippets
> that short are copyrightable anyway.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to