• clojure GUI编程-2


    clojure GUI编程-2

    clojure GUI编程-2

    1 简介

    接上一篇GUI开发,每次手写GUI布局代码比较不方便,可以使用netbeans的form designer设计好界面,然后从clojure中加载界面,绑定事件来进行GUI设计。

    2 实现过程

    由于要编译java代码,使用leiningen进行项目管理比较方便。先创建一个空项目, lein new okex 创建项目。

    2.1 添加依赖

    修改项目文件夹下的project.clj如下

     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。 并修改命名空间与文件名对应。

    2.3 设计gui界面

    使用netbeans新建JFrame form,并设计窗体,修改要用到的widget的name属性为对应的swing id名。

    然后保存这个文件到src/okex文件夹下,注意包名要用okex。窗体设计器自动生成的DepthWindow.java

    2.4 clojure中加载java gui代码

    修改core.clj,导入gui界面的类,并加载,代码如下:

      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的动作。

    3 总结

    使用netbeans设计GUI,然后从clojure中加载界面代码还是非常方便的。主要是从clojure中调用java非常方便,参考Clojure is a better Java than Java

    整个项目的地址在okex

    作者: ntestoc

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

  • 相关阅读:
    缩略图(转载)
    MarteEngine tutorial:Keyboard and mouse input
    MarteEngine tutorial: Hello World
    FengGUI
    位于两个内网的结点A和B都连接到一个公网的rdv,然后A与B之间发送消息,这时消息是否还经过rdv?
    MarteEngine
    MarteEngine tutorial:Basic collision
    关于PresenceService的实现方式
    MarteEngine: Animate sprite
    MarteEngine tutorial: 设置你的环境
  • 原文地址:https://www.cnblogs.com/ntestoc/p/10946524.html
Copyright © 2020-2023  润新知