2009-11-07 3 views
11

나는 웹상에서 ClojureREPL을 Qt와 함께 사용하는 해결책을 찾지 못했습니다. 기본적으로 UI를 표시하기 위해 QApplication/exec를 호출하자마자 REPL이 멈추는 문제가 있습니다. REPL로 C-c C-c를 되돌릴 수는 없으며, 활성 Qt 윈도우를 닫으면 Clojure 프로세스 전체가 종료됩니다.Qt Jambi와 함께 Clojure REPL을 어떻게 사용할 수 있습니까?

이제 에이전트가 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#))) 
    [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)) 

는 기본적으로 모든 Qt는 코드를 실행하기 위해 스케줄 할 클래스를 사용합니다. with-gui-thread 매크로를 사용하면 스레드 내에서 함수를보다 쉽게 ​​호출 할 수 있습니다. 다시 컴파일하지 않고도 Qt UI를 즉석에서 변경할 수 있습니다.

+0

그래도 나는 똑같은 것을해야했다. – levand

+0

나는 QT에 대해 아무것도 모른다. 그런데 왜 이걸하고 싶니? Clojure는 매우 강력하고 다양한 GUI 프레임 워크 인 Swing에 액세스 할 수 있습니다. 이미 설치되어있는 QT GUI에 연결하고 있습니까? –

+0

QT는 퍼포먼스와 네이티브 룩앤필을 포함한 많은면에서 Swing보다 뛰어나다. – levand

답변

5

REPL에서 Qt 위젯을 엉망으로 만들고 싶다면, QApplication/invokeLater 또는 QApplication/invokeAndWait 일 것입니다. 에이전트와 함께 사용할 수 있습니다. REPL에서 다음

(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

내가 on the Clojure mailing-list뿐만 아니라 점액 on my blog (독일어)와 함께이 작업을 수행하는 방법에 대해 설명했습니다. 그 트릭은 Emacs 측에 적절한 함수를 정의하고 요청할 때 SLIME에 사용하도록 지시하는 것이다. 중요한 것은 Qt 코드를 호출 할 때 특별한 명령을하지 않아도된다는 것입니다.

자신을 인용 : 해킹 점액 : 우리가 리스프 여기에서 얘기하는 것이

감안할 때, 어쨌든,이 솔루션은 분명 듯! 그래서 그게 제가 입니다. 아래의 코드는 을 .emacs에 놓았을 때 ( SLIME이 이미 완전히로드 된 시점에) 은 대화식으로 사용할 수있는 세 가지 새로운 Emacs-Lisp 함수를 등록합니다. 당신은 당신이 좋아하는 어떤 키에 바인딩 할 수 있습니다, 또는 당신도 바로 설정할 수있다 점액 보내-관통의 QApplication에게 변수를 응용 프로그램 이 시작된 후 T와 모든 키 바인딩에 대해 걱정하지 수 있습니다. 어느 쪽도 QCrefApplication/invokeAndWait를 통해 REPL 제출 및 C-M-x- 스타일 대화 형 평가를 간접적 인 으로 만들어야합니다.

재미있게 보내세요!

(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))) 
관련 문제