1

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))
Community
  • 1
  • 1
Sonicsmooth
  • 2,673
  • 2
  • 22
  • 35

1 Answers1

1

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.

Symfrog
  • 3,398
  • 1
  • 17
  • 13
  • Actually, it worked for the first text field `alter`ing `*out*` to `(constantly *out*)` or to another `PrintWriter`, but for the second text field it only worked for `alter`ing `*out*` to `(constantly *out*`)` but not to another PW, presumably having to do with what thread the PW was created in. I'm just kind of shooting in the dark here. I need to study this better. thanks. – Sonicsmooth Jan 07 '15 at 15:24
  • I have updated my answer with a short description of why this works. You only have to alter the root binding once and do not have to call it in every thread that you instantiate. I can not see the modifications that you made in your code after adding alter-var-root, but I would guess that you are attempting to invoke it in every thread rather than just binding it once to the desired value. – Symfrog Jan 07 '15 at 15:36