11

Web 上の Qt でClojure REPLを使用するための解決策が見つかりませんでした。基本的に問題は、UI を表示するために QApplication/exec を呼び出すとすぐに REPL がハングすることです。Cc Cc を REPL に戻すことはできず、アクティブな Qt ウィンドウを閉じると Clojure プロセス全体が強制終了されるようです。

Qt ウィジェットを作成したのとまったく同じスレッドでエージェントが実行されない限り、エージェント内から QApplication/processEvents を単純に呼び出すことはできません。これを理解するのに2日かかりました.他の人が同じ問題/問題を抱えているのを見てきましたが、解決策はありません. だからここに私のコードがあります:

(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))

基本的に、すべての Qt コードを実行するために ScheduledThreadPoolExecutor クラスを使用します。with-gui-thread マクロを使用すると、スレッド内から関数を簡単に呼び出すことができます。これにより、再コンパイルせずに Qt UI をオンザフライで変更できます。

4

2 に答える 2

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
于 2009-11-07T18:26:40.953 に答える
3

SLIMEを使用してこれを行う方法については、ブログ(ドイツ語)とClojureメーリングリストに書いています。秘訣は、Emacs側で適切な関数を定義し、リクエストを行うときにそれらを使用するようにSLIMEに指示することです。重要なのは、これにより、Qtコードを呼び出すときに特別な呪文を唱える必要がなくなることです。

自分を引用する:

とにかく、ここでLispについて話していることを考えると、解決策は明白であるように思われました。SLIMEをハックしてください。それが私がしたことです。以下のコードは、.emacsにドロップされると(SLIMEがすでに完全にロードされている時点で)、インタラクティブに使用するために3つの新しいEmacs-Lisp関数を登録します。それらを任意のキーにバインドできます。または、アプリケーションの開始後にslime-send-through-qapplication変数をtに設定するだけで、キーのバインドについてまったく心配する必要はありません。どちらも、REPL送信とCMxスタイルのインタラクティブ評価を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))) 
于 2009-11-10T23:05:02.853 に答える