My setup: Windows 7 x64, Clojure 1.6, Java 1.7, cider 0.9, Emacs 24.3.1
I'm using the run-later functionality of the JavaFX platform to enable running a JavaFX program from Clojure from Emacs. With this I'm able to start the program multiple times (or even multiple instances) without having to restart the session, ie cider-restart
. I can just close the window, then say (main)
again from the emacs/cider/clojure REPL and it works fine.
The problem is if there are any runtime errors or println
s from the main GUI, the text doesn't shown up in the emacs REPL. For example I have some text output println
s in the callback ChangeListener
of a TextField
. If run from a dos box via lein run
, etc., then the debug text shows up in the dos box fine when I edit the TextField
, but not in the Emacs REPL when run from there. Likewise, any runtime errors/stacktraces don't show up in the emacs REPL, but they show up fine when run from command line.
I've tried clojurifying stuff such as from Redirecting System.out to a TextArea in JavaFX and another post which dynamically rebinds *out*
, but I'm not sure this is the right approach, as I fundamentally don't understand all the threads going on, between Emacs, cider thread, the main gui thread, the run-later
stuff, and how all this interacts with the stdout, *out*
, dynamic binding, etc. I guess I'm not in Kansas any more.
Here is my code (much of it stolen/borrowed from other SO posts and peoples' blogs, etc.)...
How do I get println
s and stack traces to show up either in a JavaFX text box, or in the emacs REPL?
(ns toydb.core
(:gen-class )
(:use jfxutils.core)
(:use (clojure repl reflect pprint))
(:import (java.util List)
(javafx.beans.property SimpleStringProperty SimpleBooleanProperty)
(javafx.beans.value ObservableValue ChangeListener)
(javafx.stage Stage)
(javafx.scene Scene SceneBuilder Group Node)
(javafx.scene.control TableView TableViewBuilder
TableColumn Button
TextArea TextField TableCell
LabelBuilder ComboBox
MenuButton MenuItem
Label)
(javafx.scene.layout HBoxBuilder
VBoxBuilder
VBox)
(java.io PrintStream OutputStream Console )))
;; Utility/setup stuff, normally in utils file
(javafx.application.Platform/setImplicitExit false)
(defn run-later* [f]
(javafx.application.Platform/runLater f))
(defmacro run-later [& body]
`(run-later* (fn [] ~@body)))
(defn run-now* [f]
(let [result (promise)]
(run-later
(deliver result (try (f) (catch Throwable e e))))
@result))
(defmacro run-now [& body]
`(run-now* (fn [] ~@body)))
(defmacro eventhandler
"Reifies the eventhandler interface.
args must be args list, eg [arg1 arg2], and body is like
any other function. \"this\" argument is silently inserted
and made available to the function, but I don't think this works"
[args & body]
`(reify javafx.event.EventHandler
(~'handle [this# ~@args]
~@body)))
(defn add-to-children [container & args]
"Adds items to container's children"
(let [children_list (.getChildren container)]
(.addAll children_list (to-array args ))))
;; Problems start below
(defn make-console-scene [ init_text]
(let [ta (TextArea. init_text )
ps (PrintStream. console true)
group (Group. )
scene (Scene. group)]
(add-to-children group ta)
(System/setOut ps)
(System/setErr ps)
scene))
(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))
(def text1 (TextField. "hi"))
(.setOnAction text1 (eventhandler [evt] (println "in event handler"))) ;;<<- never shows up in Emacs REPL
(defn -start []
(let [vbscene1 (make-vbox-scene text1)
window1 (make-window vbscene1 )
console-scene (make-console-scene)
debugwin (make-window console-scene 400 300)]
(.show window1)
(.show debugwin)))
(defn main []
(run-now (-start)))
(defn -main []
(javafx.application.Platform/setImplicitExit true)
(main))