1

In a source file called gui.clj, I define a frame, fr, that holds the window for my application, like this:

(def fr (frame ...))

and a run function that sets up fr and causes it to repaint when data changes, something like this (modeled on scribble.clj:

(defn run []
  (-> fr add-behaviors pack! show!)
  (when-data-changes
    (swap! state assoc :shapes (dot/g->seesaw t/ws))
    (repaint! fr)))

As I'm messing around in the REPL, I often modify a source file and then call c.t.n.repl/refresh. When I run run again, it puts up a new window, leaving the old window on the screen. How can I make my (newly updated) code operate on the same window even after a refresh?

Ben Kovitz
  • 4,920
  • 1
  • 22
  • 50
  • I've not used seesaw, but does using `(defonce fr (frame ...))` instead of `(def fr ...)` help? – Joost Diepenmaat Oct 25 '17 at 13:03
  • @JoostDiepenmaat Ah, maybe this is what `defonce` is for! … Just tried it. Nope, it doesn't seem to have any effect. However, `state` seems to refer to the same atom even after a refresh. – Ben Kovitz Oct 25 '17 at 14:59

2 Answers2

1

You can put your application's state (containing the window object) into a defonce in a separate namespace and call disable-reload on the namespace. This will prevent the reloading of the namespace when (refresh) is called thus keeping the original state (containing the original window object).

In practice, however, it is usually better to clean up and restart the application on reloads. It can be dangerous to hold to obsolete objects from the previous state of some namespace. Use component or mount to manage the application state.

erdos
  • 3,135
  • 2
  • 16
  • 27
  • 1
    I put `(defonce state (atom ...))` in a namespace called `no-reload`, followed by `(clojure.tools.namespace.repl/disable-reload!)`. After a `(refresh)`, when the code accesses the data displayed in the window, I get an error message that my records don't satisfy the protocols that I extended them to. Apparently [reloading protocols invalidates already-existing records](https://github.com/clojure/tools.namespace). I found an odd but OK workaround, though: `:repl-options {:init-ns fargish.user :init (refresh)}` in project.clj—to refresh everything _before_ I start messing around. – Ben Kovitz Oct 26 '17 at 22:42
0

It sounds like you want to have a bit of a "lifecycle" for the stateful parts of your program, somewhat like

  1. make it exist if it does not
  2. let it run
  3. clean it up

and would like this to happen when you reload. You can have the same window continue to exist and get new contents by adding code to your clean-it-up function that clears the window, or you can close the window and create a new one for each cycle.

I have used the component library for larger projects using this style and it was very effective, though it's a bit of a lifestyle change to get used to it. for your case you may just want to initialize an atom fro store the active window then define the three basic lifecycle functions that opperate on that atom's contents. (and put the actual atom in a defonce)

Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284
  • Actually, I don't even want to clean up or close the window. I just want to hit `(refresh)` and have the newly loaded code operate on the same window—ideally, with the same data model that the window displays. IOW, I'm modifying the code that modifies the data model all the time. I'd like to be able to easily modify that code and then just continue rather than starting everything from scratch. – Ben Kovitz Oct 25 '17 at 23:29