2

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 printlns from the main GUI, the text doesn't shown up in the emacs REPL. For example I have some text output printlns 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 printlns 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))
Community
  • 1
  • 1
Sonicsmooth
  • 2,673
  • 2
  • 22
  • 35
  • This has been addressed multiple times. The binding of `*out*` is dynamic, and the root binding in cider is a buffer that you wouldn't normally look at. [Full answer here](http://stackoverflow.com/a/26744113/2258453) – noisesmith Jan 05 '15 at 08:03
  • Thanks for the link, it helped greatly. I can now rebind `*out*` so `println` goes to the main repl from the JavaFX thread. However for some reason, a `defrecord` type which is instantiated in the JavaFX thread after rebinding `*out*` to the repl's stream (ie in the same scope as the correctly printing `println`), and which is receiving the keystroke events from the `TextField` (by implementing `ChangeListener` interface), still prints to the original `*out*` location, ie the `*nrepl-server-toydb*` buffer. – Sonicsmooth Jan 05 '15 at 11:36
  • Thanks for writing such a well thought out question. Dynamic binding covers most of what it seems you are asking for, which is why I marked the question a duplicate. Other code can break your binding in various ways, most directly by not useing *out* and instead printing directly or using things that ignore bindings. If these are the more important parts of this question, I'm happy to remove the duplicate flag. – Arthur Ulfeldt Jan 06 '15 at 02:08
  • Hi, dynamic binding addresses the general question. I have more specific questions which I'll post separately. – Sonicsmooth Jan 07 '15 at 06:16

0 Answers0