`Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "clojure.lang.Var.get()" because "x" is null`
rgkirch opened this issue · 3 comments
Hey!
I have some symbols in a map somewhere. I want to lookup a symbol out of the map, resolve the symbol to a function in my namespace, call it to get a ui component, and then use that as the ui component. I'm getting an exception.
(ns examples.error
(:require
[membrane.ui
:as
ui
:refer
[horizontal-layout
label
vertical-layout]]
[membrane.java2d :as backend]))
(defn help []
(ui/label "help"))
(defn what
[]
(let [component (var-get (resolve 'help))]
(ui/vertical-layout
(component)
(ui/label "me"))))
(backend/run
(var what)
{:window-start-width 400
:window-start-height 400})
produces
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "clojure.lang.Var.get()" because "x" is null
at clojure.core$var_get.invokeStatic(core.clj:4319)
at clojure.core$var_get.invoke(core.clj:4315)
at examples.error$what.invokeStatic(error.clj:17)
at examples.error$what.invoke(error.clj:15)
at clojure.lang.Var.invoke(Var.java:380)
at membrane.java2d$make_panel$fn__17653$fn__17654.invoke(java2d.clj:858)
at clojure.lang.Atom.swap(Atom.java:37)
at clojure.core$swap_BANG_.invokeStatic(core.clj:2356)
at clojure.core$swap_BANG_.invoke(core.clj:2349)
at membrane.java2d$make_panel$fn__17653.invoke(java2d.clj:856)
at membrane.java2d.proxy$java.awt.Component$ff19274a.paint(Unknown Source)
at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:966)
at java.desktop/javax.swing.JComponent.paint(JComponent.java:1128)
at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:952)
at java.desktop/javax.swing.JComponent.paint(JComponent.java:1128)
at java.desktop/javax.swing.JLayeredPane.paint(JLayeredPane.java:586)
at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:952)
at java.desktop/javax.swing.JComponent.paintToOffscreen(JComponent.java:5318)
at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBufferedImpl(RepaintManager.java:1644)
at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1619)
at java.desktop/javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1557)
at java.desktop/javax.swing.RepaintManager.paint(RepaintManager.java:1324)
at java.desktop/javax.swing.JComponent.paint(JComponent.java:1105)
at java.desktop/java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:39)
at java.desktop/sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:75)
at java.desktop/sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:112)
at java.desktop/java.awt.Container.paint(Container.java:2006)
at java.desktop/java.awt.Window.paint(Window.java:3954)
at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:877)
at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:849)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:849)
at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:824)
at java.desktop/javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:773)
at java.desktop/javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1885)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
calling (what)
produces:
[#membrane.ui.Label{:text "help", :font #membrane.ui.Font{:name nil, :size 14, :weight nil, :width nil, :slant nil}} #membrane.ui.Translate{:x 0, :y 17.609375, :drawable #membrane.ui.Label{:text "me", :font #membrane.ui.Font{:name nil, :size 14, :weight nil, :width nil, :slant nil}}}]
Thanks!
@rgkirch , I made it work with the following updated version. The issue is that the view is running in another thread and the *ns*
binding is different than the examples.error
namespace. Generally, you can't assume that *ns*
matches the current namespace when a function is called (which is something to watch out for regardless of membrane). The fix is to use ns-resolve
to explicitly lookup help
in the right namespace.
(ns example.error
(:require
[membrane.ui
:as
ui
:refer
[horizontal-layout
label
vertical-layout]]
[membrane.java2d :as backend]))
(def my-ns *ns*)
(defn help []
(ui/label "help"))
(defn what
[]
(let [component (var-get (ns-resolve my-ns 'help))]
(ui/vertical-layout
(component)
(ui/label "me"))))
(comment
(backend/run
#'what
{:window-start-width 400
:window-start-height 400})
,)
Thank you! I was guessing it had something to do with resolve
not working on another thread.
(defn what
[]
(let [component (var-get (resolve 'help))]
(fn []
(ui/vertical-layout
(component)
(ui/label "me")))))
I tried to close over the result of var-get
in a new lambda and hoped that the lambda would run in the ui thread but the var-get
would run in the "main" thread. That ... didn't work before but it's working now... Ok, the minimal example seems to work but my actual code doesn't work.
Actual code:
(defn make-component [tree]
(let [component (var-get (resolve (:component/component tree)))
incidental-state (:component/state tree)
subcomponents (:component/subcomponents tree)]
(fn [state]
(apply
component
(merge incidental-state state)
(map make-component subcomponents)))))
I don't know why one example works and the other doesn't but it's not a big deal; that code is not as clear as your solution with ns-resolve
.
Thank you!
@rgkirch , Anything inside a function isn't guaranteed to have ns set to the enclosing namespace when the function is called. You would have to call resolve from a top level statement.