This probably only counts as trivial, but it's what I've got, so I
hope it helps a little. I wrote it up for a talk I gave, so it can be
run a demo, dropping each top-level form into a REPL one at a time,
watching the GUI change as you go. I hope it helps a little...
I start my REPL like this:
rlwrap java -server -cp
~/build/clojure/clojure.jar:/usr/share/java/postgresql-jdbc3-8.2.jar
-Djdbc.drivers=org.postgresql.Driver clojure.lang.Repl
Then I paste into it each of these forms:
(import '(javax.swing JFrame JPanel JScrollPane JTable SwingUtilities)
'(javax.swing.table AbstractTableModel DefaultTableCellRenderer)
'(java.awt Dimension GridLayout Font Color)
'(java.awt.event MouseAdapter)
'(java.sql DriverManager))
(def table (JTable.))
(. SwingUtilities invokeLater
(fn []
(doto (JFrame. "demo")
(setDefaultCloseOperation (. JFrame EXIT_ON_CLOSE))
(setContentPane
(doto (JPanel. (GridLayout. 1 0))
(setOpaque true)
(add (JScrollPane.
(doto table
(setPreferredScrollableViewportSize
(Dimension. 500 70))
(setFillsViewportHeight true))))))
(pack)
(setVisible true))))
; at this point you should have a window
; modify this line based on your database server, db name, password, etc:
(def conn
(. DriverManager getConnection "jdbc:postgresql:mydb" "postgres" "system"))
(defn query-vec [sql]
(with-open stmt (.createStatement conn)
(vec (resultset-seq (.executeQuery stmt sql)))))
; modify this line based on your table name:
(def rows (query-vec "SELECT * FROM LogB"))
(defn model [rows col-names value-at]
(proxy [AbstractTableModel] []
(getRowCount [] (count rows))
(getColumnCount [] (count col-names))
(getColumnName [c] (nth col-names c))
(getValueAt [r c] (value-at r c))
(isCellEditable [r c] false)))
(.setModel table (model rows
["foo" "bar" "baz"]
(fn [r c] (str r ", " c))))
; at this point you should have a fairly meaningless grid
(.setModel table (model rows
["record"]
(fn [r c] (prn-str (nth rows r)))))
(.setModel table (model rows
(vec (map str (keys (first rows))))
(fn [r c] ((nth rows r) (nth (keys (first rows)) c)))))
; modify this based on the actual columns in your table:
(.setModel table (model rows
["Time" "Source" "Message"]
(fn [r c] (let [row (nth rows r)]
((nth [#(:createtime row)
#(str (:path row) ":" (:line row))
#(:buffer row)] c))))))
(.setDefaultRenderer table Object
(proxy [DefaultTableCellRenderer] []
(getTableCellRendererComponent [tbl obj isSelected hasFocus r c]
(let [{:keys [level]} (nth rows r)]
(doto this
(setForeground (cond (<= level 0) (. Color white)
(<= level 10) (. Color blue)
(<= level 20) (. Color red)
(<= level 30) (. Color magenta)
:else (. Color black)))
(setText (str obj)))))))
(.addMouseListener table
(proxy [MouseAdapter] []
(mouseClicked [e]
(when (== (.getClickCount e) 1)
(let [p (.getPoint e)
r (.rowAtPoint table p)
c (.columnAtPoint table p)]
(prn "click row " r)
(flush))))))
--Chouser
[Note that I don't have the actual code or example that I had working
since that is on my laptop hibernating at home atm.]
; rough example
(make-ui
'(JFrame {:title "The title"}
(JLabel {:id fooLabel :text "Some label"})
(JButton {:id btn1 :label "Click me" :actionPerformed (fn [evt]
(println "clicked")) })
(JButton {:label "Apply" :actionPerformed someFn :enabled false })))
This doesn't address the functional/stateless impedance mismatch, but
would make defining guis simpler. However, the basic idea is to
represent the containment hierarchy inherent in a gui with the nesting
of lisp expressions. The map of attributes would be applied as setters
on each component after it is created. The event handlers such as
":actionPerformed" are harder, but also doable.
The nasty part that I haven't spent any time thinking about is the
LayoutManager gong show in swing. That sounds much harder to solve
declaratively without writing a bunch of LMs that support simplifying
the declarative style.
Michael
--
Tom Emerson
trem...@gmail.com
http://www.dreamersrealm.net/~tree
The nasty part that I haven't spent any time thinking about is the
LayoutManager gong show in swing. That sounds much harder to solve
declaratively without writing a bunch of LMs that support simplifying
the declarative style.
-------------------------clojure.contrib.miglayout/miglayout([container & args])Adds java.awt.Components to a java.awt.Container with constraintsformatted for the MiGLayout layout manager.Arguments: container layout-constraints? [component constraint*]*- container: the container for the specified components, its layoutmanager will be set to a new instance of MigLayout- layout-constraints: an optional map that maps any or all of:layout, :column, and/or :row to a string that specifies thecorresponding constraints for the whole layout- an inline series of components and constraints: each component may befollowed by zero or more component constraintsThe set of constraints for each component is presented to MiGLayout as asingle string with each constraint and its arguments separated from anysubsequent constraint by a comma.Component constraint: string, keyword, vector, or map- A string specifies one or more constraints each with zero or morearguments. If it specifies more than one constraint, the string mustinclude commas to separate them.- A keyword specifies a single constraint without arguments- A vector specifies a single constraint with one or more arguments- A map specifiess one or more constraints as keys, each mapped to asingle argumentEmpty strings, vectors, and maps are accepted but don't affect thelayout.
(the constraints line up in a monospaced font)
(fn test2
[panel]
(miglayout panel
{:column "[right]"}
(JLabel. "General") "split, span"
(JSeparator.) :growx :wrap
(JLabel. "Company") [:gap 10]
(JTextField. "") :span :growx
(JLabel. "Contact") [:gap 10]
(JTextField. "") :span :growx :wrap
(JLabel. "Propeller") :split :span [:gaptop 10]
(JSeparator.) :growx :wrap [:gaptop 10]
(JLabel. "PTI/kW") {:gapx 10 :gapy 15}
(JTextField. 10)
(JLabel. "Power/kW") [:gap 10]
(JTextField. 10) :wrap
(JLabel. "R/mm") [:gap 10]
(JTextField. 10)
(JLabel. "D/mm") [:gap 10]
(JTextField. 10)))
Actually, AWT isn't thread-safe either. Documentation about the fact
seems pretty hard to find. From what I can gather, in Java 1.1, AWT
claimed to be thread-safe, but there were problems with races and
deadlocks, therefore in Java 1.2 and above AWT doesn't claim to be
thread-safe. AWT does seem to mostly work if you call it from multiple
threads.
Some discussion here:
http://www.elharo.com/blog/software-development/java/2006/11/10/awt-on-the-event-dispatch-thread/
A brief official reference here:
http://java.sun.com/j2se/1.5.0/docs/guide/deployment/deployment-guide/applet-compatibility.html
Also here:
There's some more info here about why multi-threaded GUI toolkits
aren't such a good idea:
http://weblogs.java.net/blog/kgh/archive/2004/10/multithreaded_t.html
Restricting GUI objects to only work from one thread is done so that
they don't need to use locking, which makes them faster and less prone
to bugs. Lots of GUI toolkits do this, eg: Win32, .NET, SWT. Allowing
objects to be accessed from multiple threads makes things slightly
easier for clients, but isn't really going to improve performance.
Swing does have some annoying aspects of its design (its fake platform
UI isn't all that convincing), but I don't think that the
single-threaded access model is that bad a thing.
--
Dave
I've been using Flex a lot lately and it is a very nice way to do GUI
programming. Not perfect, but better than other things I've used by
quite a margin. I'll be sure to check out Shoes. Thanks for the tip.
Michael
I've done rather little GUI programming in my day (it doesn't appeal to
me), but I've been hearing about JavaFX lately and just today took a
quick look at what it's all about (someone on one of the IDEA forums
suggested it will ultimately take the place of Swing, and that got me
curious).
So I'm wondering, how does JavaFX factor into matters of GUI programming
and good APIs and frameworks to facilitate it?
> ...
Randall Schulz