【问题标题】:tracking down AWT exception in Clojure UI app under Leiningen在 Leiningen 下跟踪 Clojure UI 应用程序中的 AWT 异常
【发布时间】:2013-03-24 14:53:20
【问题描述】:

参考这个 SO question on a first UI program in Clojure,我创建了一个新的Leiningen 应用项目:

lein new app a-ui-app

将源代码复制到 leiningen 生成的 core.clj 并修改 -main 例程以调用它

(defn -main
  "See https://stackoverflow.com/questions/2792451/improving-my-first-clojure-program?rq=1."
  [& args]
  ;; work around dangerous default behaviour in Clojure
  (alter-var-root #'*read-eval* (constantly false))

  (doto panel
        (.setFocusable true)
        (.addKeyListener panel))

  (doto frame
        (.add panel)
        (.pack)
        (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
        (.setVisible true))

  (loop []
        (draw-rectangle panel @x @y)
        (Thread/sleep 10)
        (recur))
  )

然后我通过任一方式运行它

lein run

lein uberjar
java -jar ./target/a-ui-app-0.1.0-SNAPSHOT-standalone.jar 

在这两种情况下,应用程序都运行良好,但在我用来启动它的终端中,在随机延迟几秒钟后出现异常:

线程“AWT-EventQueue-0”中的异常 java.lang.IllegalArgumentException:没有匹配的子句:157 at a_ui_app.core$fn__16$fn__21$fn__22.invoke(core.clj:19) 在 clojure.lang.AFn.call(AFn.java:18) 在 clojure.lang.LockingTransaction.run(LockingTransaction.java:263) 在 clojure.lang.LockingTransaction.runInTransaction(LockingTransaction.java:231) 在 a_ui_app.core$fn__16$fn__21.invoke(core.clj:17) 在 a_ui_app.core.proxy$javax.swing.JPanel$KeyListener$6c415903.keyPressed(未知 源)在 java.awt.Component.processKeyEvent(Component.java:6340) 在 javax.swing.JComponent.processKeyEvent(JComponent.java:2809) 在 a_ui_app.core.proxy$javax.swing.JPanel$KeyListener$6c415903.processKeyEvent(未知 源)在 java.awt.Component.processEvent(Component.java:6159) 在 java.awt.Container.processEvent(Container.java:2083) 更多 线条...

我没有对project.clj 进行任何更改——只是使用了 leiningen 生成的那个。

我想了解发生了什么。我对 Java 线程一无所知。问题是否与 leiningen 启动应用程序的 Java 线程的方式有关?这是不可避免的吗?如果没有,我该如何修复它,无论是对于这个小示例程序还是未来,作为使用 UI 线程(我认为是 AWT-EventQueue-0)的未来项目的项目模式。

【问题讨论】:

  • 附带说明,有谁知道第一行(alter-var-root 等)的效果是什么?它由 lein new app 生成_

标签: clojure awt leiningen


【解决方案1】:

我不知道你为什么会得到这个错误,但我想说你做错了几件事: Swing 是一个复杂的野兽; )

Swing 不是线程安全的。关于在 EDT(事件调度线程/UI 线程)上可以做什么和不可以做什么的“规则”随着时间的推移而改变……在某个时候,Sun 决定所有修改 Swing 组件的事情都应该在 EDT 上完成。

因此,从另一个忙于旋转循环的线程中绘制矩形是一个很大的禁忌。此外,您绘制的方式也不正确:您不应该直接获取 Graphics 对象并从其他线程修改它(这是一种超级黑客,应该会触发疯狂的闪烁)。一种“正确”的 Swing 方法是覆盖 paintComponent(Graphics g) Java 方法并在那里进行绘图:所以每次需要重绘该组件时,都会正确地重绘它。

这是使用 paintComponent 绘制矩形的代码的修改版本(我没有修复嵌套的 if 语句,应该是 case 等):

(import java.awt.Color)
(import java.awt.Dimension)
(import java.awt.event.KeyListener)
(import javax.swing.JFrame)
(import javax.swing.JPanel)

(def x (ref 0))
(def y (ref 0))

(def panel
  (proxy [JPanel KeyListener] []
    (paintComponent [g]
      (proxy-super paintComponent g)
      (doto g
        (.setColor (java.awt.Color/WHITE))
        (.fillRect 0 0 100 100)
        (.setColor (java.awt.Color/BLUE))
        (.fillRect (* 10 @x) (* 10 @y) 10 10)))
    (getPreferredSize [] (Dimension. 100 100))
    (keyPressed [e]
      (let [keyCode (.getKeyCode e)]
        (if (== 37 keyCode) (dosync (alter x dec))
        (if (== 38 keyCode) (dosync (alter y dec))
        (if (== 39 keyCode) (dosync (alter x inc))
        (if (== 40 keyCode) (dosync (alter y inc))))))
        (.repaint this)
        ))
    (keyReleased [e])
    (keyTyped [e])))

(def frame (JFrame. "Test"))

(defn -main [& args]

  (doto panel
        (.setFocusable true)
        (.addKeyListener panel))

  (doto frame
        (.add panel)
        (.pack)
        (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
        (.setVisible true)))

我不是 deref'ing x 和 y 的忠实粉丝:但 AFAICT paintComponent 和关键侦听器触发器都保证在 EDT 上发生,因此您的案例中的读数应该是一致的。如果我是你,我仍然会使用单个 xy def。

我也有点困惑 JPanel 也是 KeyListener 和 -main 函数,然后将 KeyListener 添加到自己:(doto panel (.addKeyListener panel)) 感觉很奇怪。可能没问题,我不知道:只是感觉很奇怪:)

现在关于您的异常,我不知道,但 Swing EDT 实际上偶尔会抛出异常,因为 Swing 有很多错误 并且 因为 Swing 使用起来非常复杂,以至于程序倾向于犯了很多诚实的错误。根据平台/JVM,异常被捕获并且 EDT 继续运行自动启动新的 EDT。通常,您不应该能够“崩溃” EDT,因为如果 EDT 崩溃,它应该会自动重新启动。这就是为什么您看到异常但您说您的程序仍然“运行良好”的原因。

我会说异常和神秘的堆栈跟踪与 Swing 不是线程安全的并且你做奇怪的事情有关:旋转循环获取面板的底层 Graphics 对象并弄乱它,但我真的不确定。

经过上述修改的代码似乎正在做你想做的事,而且没有眨眼。

希望对你有帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多