Clojure 为 JavaFX UI 元素重新绑定 *out*

Clojure rebinding *out* for JavaFX UI elements

我试图让 println 输出到任意位置,在 emacs-cider repl 或 JavaFX TextArea 上。我已经引用了 Why can't I print from background threads,这允许我通过将 *out* 重新绑定到起始线程的线程,从 JavaFX 线程中执行 println

然而,当从 ChangeListener 调用 println 时,无论是在 JFX 线程外还是在 JFX 线程内创建,这都不起作用。

在下面的文本中,我有两个 TextField,一个在主线程中创建,一个在 JFX 线程中创建,以及两个 ChangeListener 实例,一个作为 defstruct实现接口,另一个作为 reify,均从 JFX 线程内实例化。

每次我在两个 TextField 中的任何一个中键入一个字符时,响应文本都会显示在原始线程的 *out* 流中。

我该如何解决这个问题,以便所有 println 都能按预期找到正确的 *out*
谢谢

(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))

您是否尝试过更改 out 的 var root?例如

(alter-var-root #'*out* (constantly *out*))

使用binding 将仅为当前线程将 var 解析为绑定值。

其他线程将继续看到 var 的根值,除非在这些线程中也使用了 binding

回调(例如 ChangeListener)是从不受您控制的线程调用的,因此没有每线程绑定。他们将解析 var 的根值。

因此,您必须更改 out 的根值,以便这些线程解析您希望的值。

有更多关于 var 绑定的信息 here