Created
September 22, 2015 09:41
-
-
Save arsane/fabb2f20155d04ec8505 to your computer and use it in GitHub Desktop.
gui with clojure: http://stuartsierra.com/2010/01/05/taming-the-gridbaglayout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;;; http://stuartsierra.com/2010/01/05/taming-the-gridbaglayout | |
(import '(javax.swing JFrame JPanel JButton JOptionPane JLabel)) | |
(import 'java.awt.event.ActionListener) | |
(import '(java.awt GridBagConstraints GridBagLayout Insets)) | |
;;; | |
(comment | |
(def button (JButton. "Click Me!")) | |
(def panel (doto (JPanel.) | |
(.add button))) | |
(def frame (doto (JFrame. "Hello Frame") | |
(.setSize 200 200) | |
(.setContentPane panel) | |
(.setVisible true))) | |
(defn say-hello [] | |
(JOptionPane/showMessageDialog | |
nil "Hello, World!" "Greeting" | |
JOptionPane/INFORMATION_MESSAGE)) | |
(def act (proxy [ActionListener] [] | |
(actionPerformed [event] (say-hello)))) | |
;(.addActionListener button act) | |
(.addActionListener (doto button | |
(on-action event | |
(say-hello)))) | |
) | |
;;; define on-action macro | |
(defmacro on-action [component event & body] | |
`(. ~component addActionListener | |
(proxy [java.awt.event.ActionListener] [] | |
(actionPerformed [~event] ~@body)))) | |
;;; imperative version | |
(defn counter-app1 [] | |
(let [label (JLabel. "Counter: 0") | |
button (JButton. "Add 1") | |
panel (JPanel.) | |
frame (JFrame. "Counter App")] | |
(.setOpaque panel true) | |
(.add panel label) | |
(.add panel button) | |
(.setContentPane frame panel) | |
(.setSize frame 300 100) | |
(.setVisible frame true))) | |
;;; better version. | |
(defn counter-app2 [] | |
(let [label (JLabel. "Counter: 0") | |
button (JButton. "Add 1") | |
panel (doto (JPanel.) | |
(.setOpaque true) | |
(.add label) | |
(.add button))] | |
(doto (JFrame. "Counter App") | |
(.setContentPane panel) | |
(.setSize 300 100) | |
(.setVisible true)))) | |
;;; with macro | |
(defn counter-app [] | |
(let [counter (atom 0) | |
label (JLabel. "Counter: 0") | |
button (doto (JButton. "Add 1") | |
(on-action evnt ;; evnt is not used | |
(.setText label | |
(str "Counter: " (swap! counter inc))))) | |
panel (doto (JPanel.) | |
(.setOpaque true) | |
(.add label) | |
(.add button))] | |
(doto (JFrame. "Counter App") | |
(.setContentPane panel) | |
(.setSize 300 100) | |
(.setVisible true)))) | |
;;; Without set-grid! macro: | |
;;; | |
;;; (def c (GridBagConstraints.)) | |
;;; (set! (. c gridx) 1) | |
;;; (set! (. c gridy) GridBagConstraints/RELATIVE) | |
;;; | |
;;; if the value of a field is a keyword, it gets used as the name | |
;;; of a static field (constant) in GridBagConstraints. | |
;;; | |
;;; With set-grid! macro: | |
;;; | |
;;; (set-grid! c :gridx 1) | |
;;; (set-grid! c :gridy :RELATIVE) | |
;;; | |
(defmacro set-grid! [constraints field value] | |
`(set! (. ~constraints ~(symbol (name field))) | |
~(if (keyword? value) | |
`(. java.awt.GridBagConstraints | |
~(symbol (name value))) | |
value))) | |
;;; | |
;;; (grid-bag-layout container | |
;;; :gridx 0, :gridy 0 | |
;;; component-one | |
;;; :gridx :RELATIVE, :gridwidth 2 | |
;;; component-two | |
;;; ;; ... more components & constraints ... | |
;;; ) | |
(defmacro grid-bag-layout [container & body] | |
(let [c (gensym "c") | |
cntr (gensym "cntr")] | |
`(let [~c (new java.awt.GridBagConstraints) | |
~cntr ~container] | |
~@(loop [result '() body body] | |
(if (empty? body) | |
(reverse result) | |
(let [expr (first body)] | |
(if (keyword? expr) | |
(recur (cons `(set-grid! ~c ~expr | |
~(second body)) | |
result) | |
(next (next body))) | |
(recur (cons `(.add ~cntr ~expr ~c) | |
result) | |
(next body))))))))) | |
;;; With layout manager | |
(defn counter-app3 [] | |
(def panel | |
(doto (JPanel. (GridBagLayout.)) | |
(grid-bag-layout | |
:fill :BOTH, :insets (Insets. 5 5 5 5) | |
:gridx 0, :gridy 0 | |
(JButton. "One") | |
:gridy 1 | |
(JButton. "Two") | |
:gridx 1, :gridy 0, :gridheight 2 | |
(JButton. "Three")))) | |
(def frame | |
(doto (JFrame. "GridBagLayout Test") | |
(.setContentPane panel) | |
(.pack) | |
(.setVisible true)))) | |
;;; program | |
(counter-app) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;;; | |
(import '(javax.swing JLabel JPanel JFrame JTextField) | |
'(javax.swing.event DocumentListener) | |
'(java.awt GridBagLayout GridBagConstraints Insets)) | |
(defmacro set-grid! [constraints field value] | |
`(set! (. ~constraints ~(symbol (name field))) | |
~(if (keyword? value) | |
`(. GridBagConstraints ~(symbol (name value))) | |
value))) | |
(defmacro grid-bag-layout [container & body] | |
(let [c (gensym "c") | |
cntr (gensym "cntr")] | |
`(let [~c (new java.awt.GridBagConstraints) | |
~cntr ~container] | |
~@(loop [result '() body body] | |
(if (empty? body) | |
(reverse result) | |
(let [expr (first body)] | |
(if (keyword? expr) | |
(recur (cons `(set-grid! ~c ~expr | |
~(second body)) | |
result) | |
(next (next body))) | |
(recur (cons `(.add ~cntr ~expr ~c) | |
result) | |
(next body))))))))) | |
(defn f-to-c [f] | |
(* (- f 32) 5/9)) | |
(defn c-to-f [c] | |
(+ (* c 9/5) 32)) | |
;;; take a String typed by the user and convert it to a number | |
(defn parse [s] | |
(try (Double/parseDouble (.trim s)) | |
(catch NumberFormatException e nil))) | |
;;; takes a number, rounds it to the nearest Integer, and | |
;;; return a String. | |
(defn display [n] | |
(str (Math/round (float n)))) | |
;;; update target's temp | |
(defn update-temp [source target convert] | |
(when (.isFocusOwner source) | |
(if-let [n (parse (.getText source))] | |
(.setText target (display (convert n))) | |
(.setText target "")))) | |
(defn listen-temp [source target f] | |
(.. source getDocument | |
(addDocumentListener | |
(proxy [DocumentListener] [] | |
(insertUpdate [e] (update-temp source target f)) | |
(removeUpdate [e] (update-temp source target f)) | |
(changedUpdate [e] ))))) | |
(defn temp-app [] | |
(let [celsius (JTextField. 3) | |
fahrenheit (JTextField. 3) | |
panel (doto (JPanel. (GridBagLayout.)) | |
(grid-bag-layout | |
:gridx 0, :gridy 0, :anchor :LINE_END | |
:insets (Insets. 5 5 5 5) | |
(JLabel. "Degrees Celsius:") | |
:gridy 1 | |
(JLabel. "Degrees Fahrenheit:") | |
:gridx 1, :gridy 0, :anchor :LINE_START | |
celsius | |
:gridy 1 | |
fahrenheit))] | |
(listen-temp celsius fahrenheit c-to-f) | |
(listen-temp fahrenheit celsius f-to-c) | |
(doto (JFrame. "Temperature Converter") | |
(.setContentPane panel) | |
(.pack) | |
(.setVisible true)))) | |
(temp-app) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(import '(java.awt GridBagLayout Insets)) | |
(import '(javax.swing JPanel JFrame JButton JTextField | |
JLabel Timer SwingUtilities)) | |
;;; create a flipper agent | |
(defn new-flipper [] | |
(agent {:total 0, :heads 0, | |
:running false, | |
:random (java.util.Random.)})) | |
;;; - call random number generator and update counters | |
;;; - send itself to the agent again, creating a loop | |
(defn calculate [state] | |
(if (:running state) | |
(do (send *agent* calculate) | |
(assoc state | |
:total (inc (:total state)) | |
:heads (if (.nextBoolean (:random state)) | |
(inc (:heads state)) | |
(:heads state)))) | |
state)) | |
;;; start agent | |
(defn start [state] | |
(send *agent* calculate) | |
(assoc state :running true)) | |
;;; stop agent | |
(defn stop [state] | |
(assoc state :running false)) | |
;;; how far from 0.5 | |
(defn error [state] | |
(if (zero? (:total state)) 0.0 | |
(- (/ (double (:heads state)) | |
(:total state)) | |
0.5))) | |
(defn text-field [value] | |
(doto (JTextField. value 15) | |
(.setEnabled false) | |
(.setHorizontalAlignment JTextField/RIGHT))) | |
(defmacro with-action [component event & body] | |
`(. ~component addActionListener | |
(proxy [java.awt.event.ActionListener] [] | |
(actionPerformed [~event] ~@body)))) | |
(defmacro set-grid! [constraints field value] | |
`(set! (. ~constraints ~(symbol (name field))) | |
~(if (keyword? value) | |
`(. java.awt.GridBagConstraints | |
~(symbol (name value))) | |
value))) | |
(defmacro grid-bag-layout [container & body] | |
(let [c (gensym "c") | |
cntr (gensym "cntr")] | |
`(let [~c (new java.awt.GridBagConstraints) | |
~cntr ~container] | |
~@(loop [result '() body body] | |
(if (empty? body) | |
(reverse result) | |
(let [expr (first body)] | |
(if (keyword? expr) | |
(recur (cons `(set-grid! ~c ~expr | |
~(second body)) | |
result) | |
(next (next body))) | |
(recur (cons `(.add ~cntr ~expr ~c) | |
result) | |
(next body))))))))) | |
(defn flipper-app [] | |
;; Construct components: | |
(let [flipper (new-flipper) | |
b-start (JButton. "Start") | |
b-stop (doto (JButton. "Stop") | |
(.setEnabled false)) | |
total (text-field "0") | |
heads (text-field "0") | |
t-error (text-field "0.0") | |
timer (Timer. 100 nil)] | |
;; Setup actions: | |
(with-action timer e | |
(let [state @flipper] | |
(.setText total (str (:total state))) | |
(.setText heads (str (:heads state))) | |
(.setText t-error (format "%.10g" (error state))))) | |
(with-action b-start e | |
(send flipper start) | |
(.setEnabled b-stop true) | |
(.setEnabled b-start false) | |
(.start timer)) | |
(with-action b-stop e | |
(send flipper stop) | |
(.setEnabled b-stop false) | |
(.setEnabled b-start true) | |
(.stop timer)) | |
;; Create window and layout: | |
(doto (JFrame. "Flipper") | |
(.setContentPane | |
(doto (JPanel.) | |
(.add (JLabel. "Total:")) | |
(.add total) | |
(.add (JLabel. "Heads:")) | |
(.add heads) | |
(.add (JLabel. "Error:")) | |
(.add t-error) | |
(.add b-start) | |
(.add b-stop))) | |
(.addWindowListener | |
(proxy [java.awt.event.WindowListener] [] | |
(windowActivated [e]) | |
(windowClosed [e]) | |
(windowClosing [e] | |
(send flipper stop) | |
(.stop timer)) | |
(windowDeactivated [e]) | |
(windowDeiconified [e]) | |
(windowIconified [e]) | |
(windowOpened [e]))) | |
(.pack) | |
(.setVisible true)))) | |
(defn flipper-app2 [] | |
;; Construct components: | |
(let [flipper (new-flipper) | |
b-start (JButton. "Start") | |
b-stop (doto (JButton. "Stop") | |
(.setEnabled false)) | |
total (text-field "0") | |
heads (text-field "0") | |
t-error (text-field "0.0") | |
timer (Timer. 100 nil)] | |
;; Setup actions: | |
(with-action timer e | |
(let [state @flipper] | |
(.setText total (str (:total state))) | |
(.setText heads (str (:heads state))) | |
(.setText t-error (format "%.10g" (error state))))) | |
(with-action b-start e | |
(send flipper start) | |
(.setEnabled b-stop true) | |
(.setEnabled b-start false) | |
(.start timer)) | |
(with-action b-stop e | |
(send flipper stop) | |
(.setEnabled b-stop false) | |
(.setEnabled b-start true) | |
(.stop timer)) | |
;; Create window and layout: | |
(doto (JFrame. "Flipper") | |
(.setContentPane | |
(doto (JPanel. (GridBagLayout.)) | |
(grid-bag-layout | |
:insets (Insets. 5 5 5 5) | |
:gridx 0, :anchor :LINE_END | |
:gridy 0, (JLabel. "Total:") | |
:gridy 1, (JLabel. "Heads:") | |
:gridy 2, (JLabel. "Error:") | |
:gridx 1, :anchor :LINE_START | |
:gridy 0, total | |
:gridy 1, heads | |
:gridy 2, t-error | |
:gridx 0, :gridy 3, :gridwidth 2, :anchor :CENTER | |
(doto (JPanel.) | |
(.add b-start) | |
(.add b-stop))))) | |
(.pack) | |
(.setVisible true)))) | |
(SwingUtilities/invokeLater flipper-app2) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment