2009-11-07 39 views
11

Tôi chưa tìm thấy giải pháp để sử dụng ClojureREPL với Qt trên web. Về cơ bản, vấn đề là REPL bị treo ngay sau khi bạn gọi QApplication/exec để hiển thị giao diện người dùng. Bạn không thể C-c C-c trở lại REPL, và đóng cửa sổ Qt đang hoạt động có vẻ như giết toàn bộ quá trình Clojure.Tôi có thể sử dụng Clojure REPL cùng với Qt Jambi bằng cách nào?

Bây giờ chỉ cần gọi QApplication/processEvents từ bên trong một tác nhân là không thể, trừ khi tác nhân chạy chính xác cùng một chuỗi mà bạn đã tạo các tiện ích Qt của mình. Tôi mất hai ngày để tìm ra điều này và tôi đã thấy những người khác có cùng vấn đề/vấn đề nhưng không có giải pháp. Vì vậy, đây là của tôi, trong mã:

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

Về cơ bản nó sử dụng lớp ScheduledThreadPoolExecutor để thực hiện tất cả Qt-mã. Bạn có thể sử dụng macro có gui-thread để dễ dàng gọi các hàm từ bên trong chuỗi. Điều này giúp bạn có thể thay đổi giao diện người dùng Qt khi đang di chuyển mà không cần biên dịch lại.

+0

Vâng, tôi phải làm tương tự. – levand

+0

Tôi không biết gì về QT. Nhưng tại sao bạn muốn làm điều này? Clojure có quyền truy cập vào Swing, một khung công tác GUI rất mạnh mẽ và linh hoạt. Bạn đang kết nối với một GUI QT đã có sẵn? –

+0

QT được cho là tốt hơn Swing theo nhiều cách, bao gồm hiệu suất và giao diện bản địa. – levand

Trả lời

5

Nếu bạn muốn gây rối với tiện ích Qt từ REPL, QApplication/invokeLater hoặc QApplication/invokeAndWait có lẽ là những gì bạn muốn. Bạn có thể sử dụng chúng cùng với các đại lý. Với này:

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

Sau đó, từ một 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

Rất đẹp. Tôi thích nó. Cảm ơn! – MHOOO

3

Tôi đã viết về làm thế nào để làm điều này với SLIME on my blog (Đức) cũng như on the Clojure mailing-list. Bí quyết là xác định các chức năng thích hợp ở phía Emacs và yêu cầu SLIME sử dụng các chức năng đó khi đưa ra yêu cầu. Quan trọng hơn, điều này giải phóng bạn khỏi phải làm những câu thần chú đặc biệt khi gọi mã Qt.

Trích dẫn bản thân mình:

Cho rằng chúng ta đang nói Lisp đây, dù sao, giải pháp dường như là rõ ràng: Hack SLIME! Vì vậy, đó là những gì tôi đã làm. Đoạn mã bên dưới, khi bạn bỏ vào .emacs của bạn (tại thời điểm SLIME đã được tải đầy đủ), đăng ký ba hàm Emacs-Lisp mới để sử dụng tương tác. Bạn có thể liên kết chúng với bất kỳ phím nào mà bạn thích hoặc thậm chí bạn chỉ cần đặt biến số slime-send-through-qapplication sau khi ứng dụng của bạn đã bắt đầu và không lo lắng về việc ràng buộc khóa . Hoặc phải thực hiện các lần gửi REPL của bạn và đánh giá tương tác C-M-x theo kiểu gián tiếp thông qua QCoreApplication/invokeAndWait.

Hãy vui vẻ!

(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))) 
Các vấn đề liên quan