While preparing an answer to another question, I created one for myself. Consider the following short program.
(ns net.dneclark.JFrameAndTimerDemo
(:import (javax.swing JLabel JButton JPanel JFrame Timer))
(:gen-class))
(defn timer-action [label counter]
(proxy [java.awt.event.ActionListener] []
(actionPerformed
[e]
(.setText label (str "Counter: " (swap! counter inc))))))
(defn timer-fn []
(let [counter (atom 0)
label (JLabel. "Counter: 0")
timer (Timer. 1000 (timer-action label counter))
panel (doto (JPanel.)
(.add label))]
(.start timer)
(doto (JFrame. "Timer App")
(.setContentPane panel)
(.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
(.setLocation 300 300)
(.setSize 200 200)
(.setVisible true))))
(defn -main []
(timer-fn))
In the action listener, 'timer-action', the value of the 'counter' argument is altered. The actual variable is declared in the 'timer-fn' function, but is altered in the listener. In my earlier experience with languages like Pascal, I would have considered 'counter' to be passed by reference. Is that the case here or is this an example of a closure? Something else?
Thanks for the help.
Yes, it's a closure. The lexical context of the handler function definition is preserved, and when it is later invoked it can access and update variables that "live" there.
I'm not sure how to answer the question, "why?" other than to point out that it's simply the way the language is defined to work.
Related
I am trying to find out how in Reagent with Hiccup make an element with takes all available space. So an resize parent I will get :component-did-mount call back.
(defn chart [id col-width row-height]
(let [dimensions (atom {})]
(reagent/create-class
{:component-did-mount
(fn [e]
(let [thisComponent (aget (js/document.querySelector ".app") "parentNode")
width (aget thisComponent "offsetWidth")
height (aget thisComponent "offsetHeight")]
(swap! dimensions {:width width :height height})
(println "----did mountwdth" width "--" height col-width row-height)
(.log js/console thisComponent)))
:reagent-render
(fn [id col-width row-height]
[:div
[:div {:style {:background "gray"}} "--drag handle--"]
[:div.non-dragable
[simple-bar id]
[tchart id col-width (int (- row-height controls-height))]]])})))
I want the chart element to take all the space available.
React lifecycle callbacks like ComponentDidMount does not react to component size changes.
If you want to fire a callback whenever the component size changes - you'll need to use some third-party React libraries like react-measure or react-sizeme
The other strategy is to add an event listener on window resize and get your component's parent size from there.
I use React Virtualized's AutoSizer for this. Example of integration with Reagent:
(ns example
(:require
[cljsjs.react]
[cljsjs.react-virtualized]
[goog.object :as gobject]
[reagent.core :as r]))
(defn autosizer-example
[]
(r/with-let [width (r/atom 500)
_ (js/setTimeout #(reset! width 1000)
1000)]
[:div {:style {:width (str #width "px")}}
[:> js/ReactVirtualized.AutoSizer
{:disableHeight true
:disableWidth true}
(fn [props]
(let [width (gobject/get props "width")]
(r/as-element
[:div
"Width of parent: " width])))]]))
Docs: https://github.com/bvaughn/react-virtualized/blob/master/docs/AutoSizer.md
Basically, what I want is to implement this piece of code in ClojureScript:
var win = window.open('foo.html', 'windowName');
var timer = setInterval(function() {
if(win.closed) {
clearInterval(timer);
alert('closed');
}
}, 1000);
I tried this:
(let [popup (.open js/window "foo.html" "windowName")
interval (.setInterval
js/window
(fn []
(when (.-closed popup)
(do
;; 'interval' is undefined at this point
(.clearInterval js/window interval)
(.alert js/window 'closed')))
1000)]
...)
but CLJS compiler gives me a warning that interval is not defined.
Any ideas?
The issue is that you access interval local binding in your anonymous function before that binding has been defined (right hand side has to be evaluated first before it gets bound to interval symbol and until then interval is not defined.
You might workaround it by defining an atom storing your interval and access it from your callback function:
(let [popup (.open js/window 'foo.html', 'windowName')
interval (atom nil)]
(reset! interval (.setInterval
js/window
(fn []
(when (.-closed popup)
(do
(.clearInterval js/window #interval)
(.alert js/window "Closed")))))))
I am not sure if there is a more elegant way to achieve it using your approach with an interval callback.
Use of an atom
is easy
and is included within ClojureScript's core functionality
however interval being an atom may not clearly convey its actual nature and intent.
The original issue was the compiler's complaint that interval was not defined. So what is needed is a value that:
can be defined first and
resolved (delivered) later
That sounds like a promise to me. As of ClojureScript 1.8.34 Clojure promises aren't supported yet. Because of the existence of core.async there are indications that support of Clojure's promise isn't an urgent priority in ClojureScript - especially as core.async contains promise-chan. With promise-chan the code could be written as follows
(ns test.core
(:require-macros [cljs.core.async.macros :refer [go]])
(:require [cljs.core.async :refer [<! close! promise-chan put!]]))
(let [interval (promise-chan)
fooWin (.open js/window "./foo.html", "windowName")
checkOnFooWin
(fn []
(when (.-closed fooWin) ;; when has an implicit do
(go
(.clearInterval js/window (<! interval)))
(.alert js/window "Closed")))]
(put! interval (.setInterval js/window checkOnFooWin 500))
(close! interval))
This code is neither easier nor more elegant than the atom version - however it does have the advantage that
interval can only be resolved once
each and every value taken from interval will always be the initially delivered value.
Another way is to use direct js interop:
(let [popup (.open js/window "foo.html" "windowName")]
(js* "var interval = setInterval(function() {
if (popup.closed) {
clearInterval(interval);
alert('close');
}
}, 500);")
...)
I'm trying to get println to output to an arbitrary location, either on the emacs-cider repl or to a JavaFX TextArea. I've referenced Why can't I print from background threads and this has allowed me to do println from within the JavaFX thread by rebinding *out* to that of the starting thread.
However this doesn't work when the println is called from a ChangeListener, whether created outside or inside the JFX thread.
In the text below I have two TextFields, one created in the main thread, one created in the JFX Thread, as well as two instances of ChangeListener, one as a defstruct which implements the interface, and another as a reify, both instantiated from within the JFX thread.
Each time I type a character in either of the two TextFields, the responding text shows up in the original thread's *out* stream.
How do I fix this so all printlns find the correct *out* as intended?
thanks
(ns junk.core
(:gen-class )
(:use jfxutils.core)
(:import (javafx.beans.value ObservableValue ChangeListener)
(javafx.stage Stage)
(javafx.scene Scene)
(javafx.scene.control TextArea TextField)
(javafx.scene.layout VBox)
(java.io PrintStream PrintWriter)))
(defn make-vbox-scene [& items]
(let [vb (VBox.)
scene (Scene. vb)]
(apply add-to-children vb items)
scene))
(defn make-window [scene & [width height]]
(let [stage (Stage.)]
(.setScene stage scene)
(when width (.setWidth stage width))
(when height (.setHeight stage height))
stage))
(defrecord myobservable []
ChangeListener
(^void changed [this ^ObservableValue obsval oldval newval]
;; This println goes to the original *out* stream
(println "Myobservable thread" (Thread/currentThread) ", *out* is " *out*)))
(def text1 (TextField. "hi")) ;; Created in main thread
(defn -start [myout]
;; Everything here is in JFX thread
(println "JFX thread is " (Thread/currentThread) ",JFX *out* is " *out*)
(binding [*out* myout] ;; only works for obvious/direct println, not from UI
(let [myobs (myobservable.) ;; Created in JFX thread
text2 (TextField. "bye") ;; Created in JFX thread
vbscene1 (make-vbox-scene text1 text2)
window1 (make-window vbscene1)]
;; This println works! Output goes to cider-repl or other PrintWriter pointed to by myout.
(println "After rebinding out, thread is " (Thread/currentThread) "*out* is " *out*)
;; These printlns go to the original *out* stream in the *nrepl-server junk* buffer, not cider
;; This means I also can't reassign the myout arg to some other PrintWriter
(-> text1 .textProperty (.addListener myobs))
(-> text1 .textProperty (.addListener (reify ChangeListener
(changed [this obsval oldval newval]
(println "Anonymous listener 1, thread is " (Thread/currentThread) "*out* is " *out*)))))
(-> text2 .textProperty (.addListener myobs))
(-> text2 .textProperty (.addListener (reify ChangeListener
(changed [this obsval oldval newval]
(println "Anonymous listener 2, thread is " (Thread/currentThread) "*out* is " *out*)))))
(.show window1))))
(defn main []
(println "main thread is " (Thread/currentThread) ", *out* is " *out*)
(run-now (-start *out*)))
(defn -main []
(javafx.application.Platform/setImplicitExit true) ;; Undoes false from jfxutils
(main))
Have you tried altering the var root of out? e.g.
(alter-var-root #'*out* (constantly *out*))
using binding will resolve the var to the bound value only for the current thread.
Other threads will continue to see the root value of the var, unless binding is used within those threads as well.
The callbacks (e.g. ChangeListener) are invoked from threads that are not under your control and therefore have no per-thread bindings. They will resolve the root value of the var.
You have to therefore alter the root value of out in order for those threads to resolve the value that you wish.
There is more information available on var bindings here.
I've used lein-fruit to generate a basic Clojure project that targets iOS through RoboVM. I've introduced core.async to pass button taps along a channel, but mutating the button in the go block doesn't seem to have an effect.
Is there reason to believe the Java implementation of core.async doesn't work under RoboVM?
Here's my code, slightly modified from the basic lein-fruit template.
(ns core-async-demo.core
(:require [core-async-demo.core-utils :as u]
[clojure.core.async :refer [go chan put! <!]]))
(def window (atom nil))
(def taps (chan))
(defn init
[]
(let [main-screen (u/static-method :uikit.UIScreen :getMainScreen)
button-type (u/static-field :uikit.UIButtonType :RoundedRect)
button (u/static-method :uikit.UIButton :fromType button-type)
normal-state (u/static-field :uikit.UIControlState :Normal)
click-count (atom 0)]
(doto button
(.setFrame (u/init-class :coregraphics.CGRect 115 121 91 37))
(.setTitle "Click me!" normal-state)
(.addOnTouchUpInsideListener
(proxy [org.robovm.cocoatouch.uikit.UIControl$OnTouchUpInsideListener] []
(onTouchUpInside [control event]
(put! taps true)))))
(reset! window (u/init-class :uikit.UIWindow (.getBounds main-screen)))
(go
(loop [_ (<! taps)]
(.setTitle button (str "Click #" (swap! click-count inc)) normal-state)
(recur (<! taps))))
(doto #window
(.setBackgroundColor (u/static-method :uikit.UIColor :lightGrayColor))
(.addSubview button)
.makeKeyAndVisible)))
I don't know about core.async on RoboVM specifically, but background threads in general are not supposed to interact with UIKit. It's a documented limitation of the framework. I would try something simpler to test out core.async on RoboVM and, if it works, you should be able to use Grand Central Dispatch to run your code on the main queue.
I have a couple of questions about the following code:
(import
'(java.awt Color Graphics Dimension)
'(java.awt.image BufferedImage)
'(javax.swing JPanel JFrame))
(def width 900)
(def height 600)
(defn render
[g]
(let [img (new BufferedImage width height
(. BufferedImage TYPE_INT_ARGB))
bg (. img (getGraphics))]
(doto bg
(.setColor (. Color white))
(.fillRect 0 0 (. img (getWidth)) (. img (getHeight)))
(.setColor (. Color red))
(.drawOval 200 200 (rand-int 100) (rand-int 50)))
(. g (drawImage img 0 0 nil))
(. bg (dispose))
))
(def panel (doto (proxy [JPanel] []
(paint [g] (render g)))
(.setPreferredSize (new Dimension
width
height))))
(def frame (doto (new JFrame) (.add panel) .pack .show))
(def animator (agent nil))
(defn animation
[x]
(send-off *agent* #'animation)
(. panel (repaint))
(. Thread (sleep 100)))
(send-off animator animation)
In the animation function - why is #' used before animation in send-off?
Why does send-off at the start of animation function work? Shouldn't it just go the start of animation function again and never execute the repaint and sleep methods?
Is there any disadvantage, as compared to the original, in writing the animation function as:
(defn animation
[x]
(. panel (repaint))
(. Thread (sleep 100))
(send-off *agent* animation))
In the animation function - why is #' used before animation in send-off?
To demonstrate Clojure's dynamic nature.
The form #'animation is a Var, one of Clojure's mutable reference types. The defn macro creates a Var. For convenience, invoking a Var which refers to a function is the same as invoking the function itself. But a Var, unlike a function, can change! We could redefine #'animation at the Clojure REPL and immediately see the effects.
Using (send-off *agent* #'animation) forces Clojure to look up of the current value of the #'animation Var every time. If the code had used (send-off *agent* animation) instead, then Clojure would look up the value only once, and it would not be possible to change the animation function without stopping the loop.
1. This is a little unclear to me as well but seems to be a design decision by Rich. If you notice:
user=> (defn x [y] (+ y 2))
#'user/x
user=> ((var x) 3)
5
If a var is in the function/macro location, it will eventually resolve to the function or macro.
2. One important thing to understand here is the agent model. Agents can be thought of as a worker that operates on a single mutable cell. There is a queue of work (a queue of functions) for that agent to do. send-off and send add work to that queue. Since send-off is only adding work to the queue, it immediately returns. Since the agent only executes the functions serially, the first animation call must finish before executing the next one. Therefore, you achieve basically the same thing regardless of putting send-off first or last.
3. There should be no noticeable difference between the two.