clojure GUI編程-2

clojure GUI編程-2

1 簡介

接上一篇GUI開發,每次手寫GUI佈局代碼比較不方便,能夠使用netbeans的form designer設計好界面,而後從clojure中加載界面,綁定事件來進行GUI設計。 css

2 實現過程

因爲要編譯java代碼,使用leiningen進行項目管理比較方便。先建立一個空項目, lein new okex 建立項目。 html

2.1 添加依賴

修改項目文件夾下的project.clj以下 java

 1: (defproject okex "0.1.0-SNAPSHOT"
 2:   :description "FIXME: write description"
 3:   :url "http://example.com/FIXME"
 4:   :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
 5:             :url "https://www.eclipse.org/legal/epl-2.0/"}
 6: 
 7:   ;; 使用utf-8編碼編譯java代碼,默認會使用windows系統的默認編碼gbk
 8:   :javac-options ["-encoding" "utf-8"]
 9:   :java-source-paths ["src"]
10: 
11:   :dependencies [[org.clojure/clojure "1.10.0"]
12:                  [com.cemerick/url "0.1.1"] ;; uri處理
13:                  [slingshot "0.12.2"] ;; try+ catch+
14:                  [com.taoensso/timbre "4.10.0"] ;; logging
15:                  [cheshire/cheshire "5.8.1"] ;; json處理
16:                  [clj-http "3.9.1"] ;; http client
17:                  [com.rpl/specter "1.1.2"] ;; map數據結構查詢
18:                  [camel-snake-kebab/camel-snake-kebab "0.4.0"] ;; 命名轉換
19:                  [seesaw "1.5.0"] ;; GUI框架
20:                  ]
21:   :main ^:skip-aot okex.core
22:   :aot :all
23:   :target-path "target/%s"
24:   :repl-options {:init-ns okex.core})

2.2 複製文件

把上一篇建立的core2.clj和api.clj複製到src/okex文件夾下,更名core2.clj爲core.clj。 並修改命名空間與文件名對應。 python

2.3 設計gui界面

使用netbeans新建JFrame form,並設計窗體,修改要用到的widget的name屬性爲對應的swing id名。 git

而後保存這個文件到src/okex文件夾下,注意包名要用okex。窗體設計器自動生成的DepthWindow.javagithub

2.4 clojure中加載java gui代碼

修改core.clj,導入gui界面的類,並加載,代碼以下: sql

  1: (ns okex.core
  2:   (:require [seesaw.core :as gui]
  3:             [seesaw.table :as table]
  4:             [seesaw.bind :as bind]
  5:             [seesaw.selector :as selector]
  6:             [seesaw.table :refer [table-model]]
  7:             [okex.api :as api]
  8:             [taoensso.timbre :as log])
  9:   (:use com.rpl.specter)
 10:   (:gen-class)
 11:   (:import okex.DepthWindow))
 12: 
 13: 
 14: ;;;;;;;;;;;;;;;;;;;;; Window-Builder binding
 15: 
 16: (defn identify
 17:   "設置root下全部控件的seesaw :id
 18:   只要有name屬性的,所有綁定到id"
 19:   [root]
 20:   (doseq [w (gui/select root [:*])]
 21:     (if-let [n (.getName w)]
 22:       (selector/id-of! w (keyword n))))
 23:   root)
 24: 
 25: ;;;;;;;;;;;;;;;;;;;;;; 初始化值
 26: 
 27: (def coin-pairs "全部交易對信息" (api/get-instruments))
 28: (def base-coins "全部基準貨幣"
 29:   (-> (select [ALL :base-currency] coin-pairs)
 30:       set
 31:       sort))
 32: 
 33: (defn get-quote-coins
 34:   "獲取基準貨幣支持的計價貨幣"
 35:   [base-coin]
 36:   (select [ALL #(= (:base-currency %) base-coin) :quote-currency] coin-pairs))
 37: 
 38: (defn get-instrument-id
 39:   "根據基準貨幣和計價貨幣得到幣對名稱"
 40:   [base-coin quote-coin]
 41:   (select-one [ALL
 42:                #(and (= (:base-currency %) base-coin)
 43:                      (= (:quote-currency %) quote-coin))
 44:                :instrument-id]
 45:               coin-pairs))
 46: 
 47: ;; 設置form的默認值
 48: (let [first-base (first base-coins)]
 49:   (def coin-pair-data (atom {:base-coin first-base
 50:                              :quote-coin (-> (get-quote-coins first-base)
 51:                                              first)})))
 52: 
 53: ;;;;;;;;;;;;;;;;;;;;;; 服務
 54: (def instruments-info "交易對的深度數據"(atom {}))
 55: 
 56: (defn run-get-instrument-services!
 57:   "啓動獲取交易對深度信息的服務
 58:   沒有提供中止功能"
 59:   [instrument-id]
 60:   (when (and instrument-id
 61:              (not (contains? @instruments-info instrument-id)))
 62:     (future (loop []
 63:               (let [data (api/get-spot-instrument-book instrument-id)]
 64:                 (setval [ATOM instrument-id] data instruments-info))
 65:               (Thread/sleep 200)
 66:               (recur)))))
 67: 
 68: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 輔助函數
 69: 
 70: (defn depth-data-model
 71:   "深度數據table模型"
 72:   [data]
 73:   (table-model :columns [{:key :pos :text "價位"}
 74:                          {:key :price :text "價格"}
 75:                          {:key :amount :text "數量"}
 76:                          {:key :order-count :text "訂單數"}]
 77:                :rows data))
 78: 
 79: (defn update-quote-coin-model!
 80:   "更新計價貨幣的模型"
 81:   [f model]
 82:   (let [quote-coin (gui/select f [:#quote-coin])]
 83:     (gui/config! quote-coin :model model)))
 84: 
 85: (defn get-current-instrument-id
 86:   "獲取當前幣對的id"
 87:   []
 88:   (let [coin-p @coin-pair-data]
 89:     (get-instrument-id (:base-coin coin-p)
 90:                        (:quote-coin coin-p))))
 91: 
 92: (defn bind-transfrom-set-model
 93:   [trans-fn frame id]
 94:   (bind/bind
 95:    (bind/transform #(trans-fn %))
 96:    (bind/property (gui/select frame [id]) :model)))
 97: 
 98: (defn add-behaviors
 99:   "添加事件處理"
100:   [root]
101:   (let [base-coin (gui/select root [:#base-coin])
102:         quote-coin (gui/select root [:#quote-coin])]
103:     ;; 基準貨幣選擇事件綁定
104:     (bind/bind
105:      (bind/selection base-coin)
106:      (bind/transform get-quote-coins)
107:      (bind/tee
108:       ;; 設置quote-coin的選擇項
109:       (bind/property quote-coin :model)
110:       (bind/bind
111:        (bind/transform first)
112:        (bind/selection quote-coin))))
113: 
114:     ;; 綁定基準貨幣和計價貨幣的選擇事件
115:     (bind/bind
116:      (bind/funnel
117:       (bind/selection base-coin)
118:       (bind/selection quote-coin))
119:      (bind/transform (fn [[base-coin quote-coin]]
120:                        {:base-coin base-coin
121:                         :quote-coin quote-coin}))
122:      coin-pair-data)
123: 
124:     ;; 綁定交易對深度信息, 一旦更改就更新depth-view
125:     (bind/bind
126:      instruments-info
127:      (bind/transform #(% (get-current-instrument-id)))
128:      (bind/notify-later)
129:      (bind/tee
130:       (bind-transfrom-set-model #(-> (:bids %)
131:                                      depth-data-model) root :#bids-table)
132:       (bind-transfrom-set-model #(-> (:asks %)
133:                                      depth-data-model) root :#asks-table)))
134: 
135:     ;; 當前選擇的交易對修改就啓動新的深度信息服務
136:     (add-watch coin-pair-data :depth-view (fn [k _ _ new-data]
137:                                             (-> (get-current-instrument-id)
138:                                                 run-get-instrument-services!)))))
139: 
140: ;;;;;;;;;;;;;;;;;; 如下爲新加的gui加載代碼
141: 
142: (defn my-form
143:   "加載form"
144:   []
145:   (let [form (identify (DepthWindow.))]
146: 
147:     ;; 更新quote-coin的model
148:     (gui/config! (gui/select form [:#base-coin]) :model base-coins)
149:     (update-quote-coin-model! form (-> (:base-coin @coin-pair-data)
150:                                         get-quote-coins))
151: 
152:     ;; 先綁定事件,再設置默認值
153:     (add-behaviors form)
154:     (gui/value! form @coin-pair-data)
155: 
156:     form))
157: 
158: (defn -main [& args]
159:   (gui/invoke-later
160:    (let [form (my-form)]
161:      (-> form gui/pack! gui/show!))))

clojure從java加載代碼仍是很是簡單的,這裏多了一個綁定控件的name到swing id的動做。 shell

3 總結

使用netbeans設計GUI,而後從clojure中加載界面代碼仍是很是方便的。主要是從clojure中調用java很是方便,參考Clojure is a better Java than Java編程

整個項目的地址在okexjson

做者: ntestoc

Created: 2019-05-31 週五 17:40

相關文章
相關標籤/搜索