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。
我试图让 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。