2009-11-07 6 views
11

Nie znalazłem rozwiązania do korzystania z ClojureREPL z Qt w Internecie. Zasadniczo problem polega na tym, że REPL zawiesza się, gdy tylko wywołasz QApplication/exec w celu wyświetlenia interfejsu użytkownika. Nie można cofnąć C-c C-c do REPL, a zamknięcie aktywnego okna Qt wydaje się zabijać cały proces Clojure.Jak mogę wykorzystać Clojure REPL razem z Qt Jambi?

Teraz po prostu wywołanie QApplication/processEvents z poziomu agenta nie jest możliwe, chyba że agent działa dokładnie w tym samym wątku, w którym utworzyłeś widżety Qt. Zajęło mi to dwa dni, aby to zrozumieć i widziałem, że inni mają ten sam problem/problem, ale bez rozwiązania. Więc tutaj jest moje, w kodzie:

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

Zasadniczo używa klasy ScheduledThreadPoolExecutor w celu realizacji wszystkich QT-kod. Możesz użyć makra z-gui-thread, aby ułatwić wywoływanie funkcji z wątku. Umożliwia to zmianę interfejsu Qt w locie, bez rekompilacji.

+0

Tak, musiałem zrobić to samo. – levand

+0

Nie wiem nic o QT. Ale dlaczego chcesz to zrobić? Clojure ma dostęp do Swing, który jest bardzo potężnym i wszechstronnym środowiskiem GUI. Czy łączysz się z GUI QT, który już istnieje? –

+0

QT jest prawdopodobnie lepszy niż Swing na wiele sposobów, w tym wydajność i natywny wygląd. – levand

Odpowiedz

5

Jeśli chcesz zadzwonić z widgetami Qt z REPL, QApplication/invokeLater lub QApplication/invokeAndWait są prawdopodobnie tym, czego potrzebujesz. Możesz ich używać w połączeniu z agentami. Biorąc pod uwagę to:

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

Następnie z 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

Bardzo ładne. Lubię to. Dzięki! – MHOOO

3

Pisałem o tym, jak to zrobić z szlam on my blog (Niemcy), a także on the Clojure mailing-list. Sztuką jest zdefiniowanie odpowiednich funkcji po stronie Emacsa i poinformowanie SLIME, aby używał ich podczas wysyłania żądań. Co ważne, uwalnia cię to od konieczności wykonywania specjalnych inkantacji przy przywoływaniu kodu Qt.

Cytując sobie:

Biorąc pod uwagę, że mówimy tu Lisp, tak, rozwiązanie wydawało się oczywiste: Hack śluzu! I tak zrobiłem to . Poniższy kod po upuszczeniu do pliku .emacs (w punkcie, w którym SLIME jest już w pełni załadowany), rejestruje trzy nowe funkcje Emacs-Lisp do użytku interaktywnego. Ci może wiązać je na cokolwiek klucze chcesz, lub może nawet po prostu ustawić szlam-send-Through-QApplication zmiennej t po aplikacji rozpoczęła i nie martwić się o kluczowych wiązań w ogóle. Powinieneś dokonać swoich zgłoszeń REPL i interaktywnych ocen w stylu C-M-x pośrednich poprzez QCoreApplication/invokeAndWait.

Miłej zabawy!

(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))) 
Powiązane problemy