我还没有找到在网络上使用Clojure REPL和Qt的解决方案。 基本上问题是,只要您调用QApplication / exec以便显示UI,REPL就会挂起。你不能将C-c C-c重新放回到REPL中,关闭活动的Qt窗口似乎会杀死整个Clojure进程。
现在只能在代理中调用QApplication / processEvents,除非代理在您创建Qt小部件的完全相同的线程中运行。我花了两天时间来弄明白这一点,我看到其他人有同样的问题/问题,但没有解决方案。 所以这是我的,代码:
(add-classpath "file:///usr/share/java/qtjambi.jar")
(ns qt4-demo
(:import (com.trolltech.qt.gui QApplication QPushButton QFont QFont$Weight)
(com.trolltech.qt.core QCoreApplication)
(java.util Timer TimerTask)
(java.util.concurrent ScheduledThreadPoolExecutor TimeUnit))
(:require swank.core))
(defn init []
(QApplication/initialize (make-array String 0)))
(def *gui-thread* (new java.util.concurrent.ScheduledThreadPoolExecutor 1))
(def *gui-update-task* nil)
(def *app* (ref nil))
(defn update-gui []
(println "Updating GUI")
(QApplication/processEvents))
(defn exec []
(.remove *gui-thread* update-gui)
(def *gui-update-task* (.scheduleAtFixedRate *gui-thread* update-gui 0 150 (. TimeUnit MILLISECONDS))))
(defn stop []
(.remove *gui-thread* update-gui)
(.cancel *gui-update-task*))
(defmacro qt4 [& rest]
`(do
(try (init) (catch RuntimeException e# (println e#)))
~@rest
))
(defmacro with-gui-thread [& body]
`(.get (.schedule *gui-thread* (fn [] (do ~@body)) (long 0) (. TimeUnit MILLISECONDS))))
(defn hello-world []
(with-gui-thread
(qt4
(let [app (QCoreApplication/instance)
button (new QPushButton "Go Clojure Go")]
(dosync (ref-set *app* app))
(doto button
(.resize 250 100)
(.setFont (new QFont "Deja Vu Sans" 18 (.. QFont$Weight Bold value)))
(.setWindowTitle "Go Clojure Go")
(.show)))))
(exec))
基本上它使用ScheduledThreadPoolExecutor类来执行所有Qt代码。您可以使用with-gui-thread宏来更容易地从线程内调用函数。 这样就可以在不重新编译的情况下即时更改Qt UI。
答案 0 :(得分:5)
如果您想要使用REPL中的Qt小部件,QApplication/invokeLater
或QApplication/invokeAndWait
可能就是您想要的。您可以将它们与代理结合使用。鉴于此:
(ns qt4-demo
(:import (com.trolltech.qt.gui QApplication QPushButton)
(com.trolltech.qt.core QCoreApplication)))
(def *app* (ref nil))
(def *button* (ref nil))
(def *runner* (agent nil))
(defn init [] (QApplication/initialize (make-array String 0)))
(defn exec [] (QApplication/exec))
(defn hello-world [a]
(init)
(let [app (QCoreApplication/instance)
button (doto (QPushButton. "Go Clojure Go") (.show))]
(dosync (ref-set *app* app)
(ref-set *button* button)))
(exec))
然后来自REPL:
qt4-demo=> (send-off *runner* hello-world)
#<Agent@38fff7: nil>
;; This fails because we are not in the Qt main thread
qt4-demo=> (.setText @*button* "foo")
QObject used from outside its own thread, object=QPushButton(0x8d0f55f0) , objectThread=Thread[pool-2-thread-1,5,main], currentThread=Thread[main,5,main] (NO_SOURCE_FILE:0)
;; This should work though
qt4-demo=> (QApplication/invokeLater #(.setText @*button* "foo"))
nil
qt4-demo=> (QApplication/invokeAndWait #(.setText @*button* "bar"))
nil
答案 1 :(得分:3)
我已经写过关于如何使用SLIME on my blog(德语)以及on the Clojure mailing-list执行此操作。诀窍是在Emacs端定义适当的功能,并告诉SLIME在发出请求时使用它们。重要的是,这使您无需在调用Qt代码时进行特殊的咒语。
引用自己:
鉴于我们在这里谈论Lisp, 无论如何,解决方案似乎是 显而易见:Hack SLIME!这就是我 没有。删除后的代码如下 进入你的.emacs(在某一点上 SLIME已经满载了, 注册三个新的Emacs-Lisp 交互式使用的功能。您 可以将它们绑定到你的任何键 喜欢,或者你甚至可以设置 煤泥送阅的QApplication 申请后变量为t 已经开始而不用担心关键 绑定。要么应该做 你的REPL提交和C-M-x风格 间接互动评估 通过QCoreApplication / invokeAndWait。
玩得开心!
(defvar slime-send-through-qapplication nil)
(defvar slime-repl-send-string-fn (symbol-function 'slime-repl-send-
string))
(defvar slime-interactive-eval-fn (symbol-function 'slime-interactive-
eval))
(defun qt-appify-form (form)
(concatenate 'string ;'
"(let [return-ref (ref nil)] "
" (com.trolltech.qt.core.QCoreApplication/invokeAndWait "
" (fn [] "
" (let [return-value (do "
form
" )] "
" (dosync (ref-set return-ref return-value))))) "
" (deref return-ref))"))
(defun slime-interactive-eval (string)
(let ((string (if slime-send-through-qapplication
(qt-appify-form string)
string)))
(funcall slime-interactive-eval-fn string)))
(defun slime-repl-send-string (string &optional command-string)
(let ((string (if slime-send-through-qapplication
(qt-appify-form string)
string)))
(funcall slime-repl-send-string-fn string command-string)))
(defun slime-eval-defun-for-qt ()
(interactive)
(let ((slime-send-through-qapplication t))
(slime-eval-defun)))
(defun slime-repl-closing-return-for-qt ()
(interactive)
(let ((slime-send-through-qapplication t))
(slime-repl-closing-return)))
(defun slime-repl-return-for-qt (&optional end-of-input)
(interactive)
(let ((slime-send-through-qapplication t))
(slime-repl-return end-of-input)))