2009-11-07 8 views
11

No he encontrado una solución para usar el ClojureREPL con Qt en la web. Básicamente, el problema es que la REPL se cuelga tan pronto como se llama a QApplication/exec para que se muestre la IU. No se puede volver a C-c C-c en REPL, y cerrar la ventana de Qt activa parece matar todo el proceso de Clojure.¿Cómo puedo usar Clojure REPL junto con Qt Jambi?

Ahora simplemente no se puede llamar a QApplication/processEvents desde un agente, a menos que el agente ejecute exactamente el mismo hilo en el que creó los widgets de Qt. Me tomó dos días resolver esto y he visto a otros tener el mismo problema/problema pero sin una solución. Así que aquí es mío, en código:

(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#))) 
    [email protected] 
    )) 

(defmacro with-gui-thread [& body] 
    `(.get (.schedule *gui-thread* (fn [] (do [email protected])) (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)) 

Básicamente se utiliza la clase ScheduledThreadPoolExecutor con el fin de ejecutar todas Qt-código. Puede usar la macro con hilo gui para facilitar la llamada de funciones desde el hilo. Esto hace posible cambiar la UI de Qt sobre la marcha, sin volver a compilar.

+0

Sí, tuve que hacer lo mismo. – levand

+0

No sé nada sobre QT. Pero, ¿por qué quieres hacer esto? Clojure tiene acceso a Swing, que es un marco de GUI muy poderoso y versátil. ¿Te estás conectando a una GUI de QT que ya está en su lugar? –

+0

QT es sin duda mejor que Swing en muchos aspectos, incluido el rendimiento y la apariencia nativa. – levand

Respuesta

5

Si quiere meterse con los widgets Qt de REPL, QApplication/invokeLater o QApplication/invokeAndWait son probablemente lo que quiere. Puede usarlos junto con agentes. Teniendo en cuenta esto:

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

Luego de un REPL:

qt4-demo=> (send-off *runner* hello-world) 
#<[email protected]: 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 
+0

Muy agradable. Me gusta eso. ¡Gracias! – MHOOO

3

que he escrito acerca de cómo hacer esto con BABA on my blog (alemán), así como on the Clojure mailing-list. El truco consiste en definir las funciones apropiadas en el lado de Emacs y decirle a SLIME que las use al realizar las solicitudes. Es importante destacar que esto te libera de tener que hacer conjuros especiales al invocar el código Qt.

Citando a mí mismo:

Teniendo en cuenta que estamos hablando aquí Lisp, todos modos, la solución parecía ser obvia: Hack BABA! Entonces eso es lo que hice . El código siguiente, cuando se suelta en su .emacs (en un punto en el cual SLIME ya está completamente cargado), registra tres nuevas funciones de Emacs-Lisp para uso interactivo. Usted puede enlazar a cualquier teclas que gusta, o puede que incluso acaba de establecer la limo-enviar-a través-QApplication variable en T después de su aplicación ha comenzado y no preocuparse clave fijaciones en absoluto. O bien debe hacer sus envíos REPL y evaluaciones interactivas C-M-x indirectas a través de QCoreApplication/invokeAndWait.

¡Diviértete!

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