Clojure Oz/View! Not Connecting to Browser

Viewed 131

I'm trying to do some (simple) plots in Clojure and it seems like Oz would be a great long term solution. However, I'm running into problems just trying to get the example code to run. PS I'm completely new to ClojureScript / Reagent / Hiccup ...

On the website it shows the following example code:

(defn play-data [& names]
  (for [n names
        i (range 20)]
    {:time i :item n :quantity (+ (Math/pow (* i (count n)) 0.8) (rand-int (count n)))}))

(def line-plot
  {:data {:values (play-data "monkey" "slipper" "broom")}
   :encoding {:x {:field "time" :type "quantitative"}
              :y {:field "quantity" :type "quantitative"}
              :color {:field "item" :type "nominal"}}
   :mark "line"})

;; Render the plot
(oz/view! line-plot)

For me, (oz/view! ...) creates a blank page in my browser but no plots actually get outputted. Can someone help me figure out what's going on? How I do check that Oz even connected to the browser directly? How did Oz figure out which browser to use (I'm currently using Brave)?

2 Answers

Try something like the following, which uses my favorite template project.

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require
    [oz.core :as oz]
    ))

(defn play-data [& names]
  (for [n names
        i (range 20)]
    {:time i :item n :quantity (+ (Math/pow (* i (count n)) 0.8) (rand-int (count n)))}))

(defn line-plot []
  {:data     {:values (play-data "monkey" "slipper" "broom")}
   :encoding {:x     {:field "time" :type "quantitative"}
              :y     {:field "quantity" :type "quantitative"}
              :color {:field "item" :type "nominal"}}
   :mark     "line"})

(dotest
  ; Render the plot
  (println \newline "calling (oz/start-server!)")
  (oz/start-server!) ; this is optional and is implied by oz/view!

  (println \newline "calling (oz/view! (line-plot))")
  (oz/view! (line-plot))

  (println \newline "sleeping")
  (Thread/sleep 5000)
)

with result:

 calling (oz/start-server!)
21-04-14 22:21:15 brandy INFO [oz.server:142] - Web server is running at `http://localhost:10666/`
Opening in existing browser session.
21-04-14 22:21:16 brandy INFO [oz.server:50] - Connected uids change: {:ws #{"b75d3026-919f-4927-a43c-ce678167348d"}, :ajax #{}, :any #{"b75d3026-919f-4927-a43c-ce678167348d"}}

 calling (oz/view! (line-plot))

 sleeping

You need to either sleep a few seconds, read input, or otherwise keep the thread alive. If not, the thread seems to exit before the info is transmitted from the JVM to the browser.

The result looks like this (using Chrome):

enter image description here

Internally, Oz uses clojure.java.browse, which internally uses java.awt.Desktop to open the default browser.

It seems like Alan was able to help you get this working, but to clarify a bit:

if you're running as a script or program (as apposed to from a continuously running REPL process), you will indeed need to make sure you wait a bit for the plot message to actually get sent to the front end (with a sleep or something).

This delay is probably unavoidable, and is a result of using Sente for asynchronous websocket communications (though it's possible using any websocket lib would have a similar issue). With Sente the issue is somewhat compounded, because its a little weird how the connection is established, and it's possible for the first message to get lost in the ether if you don't wait till fully established. The easiest thing to do was just wait a few seconds, but it's on my radar to come back and try to clean this up so that the client sends a message as soon as connection is established, and the plot message wouldn't send until that message is received. This could potentially cut down the wait time a bit, but has just been low priority for me. For typical usage, the first plot is the only one that takes a while to load; all other should load up very quickly (unless they're particularly large). Like I said, fixing this is on my radar, but feel free to submit a GitHub issue about it if you're inspired.

Please note that if all of this seems rather annoying, you can also just use the static output functionality of Oz (oz/export! or oz/compile) to spit out an html file or static image. That will block till complete, and so won't have the same issue with the program halting prior to a websocket message being sent.

Regarding how Oz knows which browser to open, the JVM is aware of default browser settings, and so Oz can just use this to open in whatever browser you have set as default. The only time I've had problems with this is when running from within Docker, since you generally wouldn't be able to run a browser from a container (even if you're running your code there, e.g. to take advantage of a pre-baked python+clj environment). But that's a pretty niche edge case, and from what I've gathered it works pretty well aside from that.

Thanks for trying out Oz!

Related