2009-11-07 5 views
11

Я не нашел решение использовать ClojureREPL с Qt в Интернете. В основном проблема заключается в том, что REPL зависает, как только вы вызываете QApplication/exec, чтобы отобразить пользовательский интерфейс. Вы не можете C-c C-c вернуться в REPL, и закрытие активного окна Qt, кажется, убивает весь процесс Clojure.Как я могу использовать Clojure REPL вместе с Qt Jambi?

Теперь просто вызов 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#))) 
    [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)) 

В основном он использует класс ScheduledThreadPoolExecutor для того, чтобы выполнить все Qt-код. Вы можете использовать макрос с-gui-thread, чтобы упростить вызов функций из потока. Это позволяет изменять Qt UI на лету без перекомпиляции.

+0

Да, я должен был сделать то же самое. – levand

+0

Я ничего не знаю о QT. Но почему вы хотите это сделать? Clojure имеет доступ к Swing, который является очень мощным и универсальным графическим интерфейсом. Вы подключаетесь к графическому интерфейсу QT, который уже существует? –

+0

QT, возможно, лучше, чем Swing во многих отношениях, включая производительность и собственный внешний вид. – levand

ответ

5

Если вы хотите общаться с виджетами Qt от REPL, 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)) 

Тогда из РЕПЛ:

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

Очень приятно. Мне нравится это. Благодаря! – MHOOO

3

Я написал о том, как сделать это с SLIME on my blog (немецкий), а также on the Clojure mailing-list. Трюк состоит в том, чтобы определить соответствующие функции на стороне Emacs и сообщить SLIME использовать их при выполнении запросов. Важно отметить, что это освобождает вас от необходимости делать специальные заклинания при вызове кода Qt.

Цитируя себя:

Учитывая, что мы говорим здесь Лисп, в любом случае, решение, казалось бы очевидна: Hack SLIME! Так вот что я сделал . Код ниже, когда упал в ваш .emacs (в момент, когда SLIME уже полностью загружен), регистрирует три новые функции Emacs-Lisp для интерактивного использования. Вы можете привязать их к тому, что ключам вы нравятся, или вы даже можете просто установить шламовых отправить-Through-QApplication переменную т после того, как приложение начала и не беспокоиться о ключевых привязок на всех. Либо должны сделать ваши заявки REPL и C-M-x-style интерактивные оценки косвенные через 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))) 
Смежные вопросы