Archive for 2010年10月4日|Daily archive page

newLISP で GUI する。。。または、gs:table を使ってみる。(解説編、続き)

(以下は、 newLISP v 2.8.16 以上、または、guisever 1.42以上での話です。)

 今回は、昨日の解説編の続き、テーブル・ウィジェット関数の残り3つです。

 先ずは、テーブル・データをリストにして取り出す関数gs:table-get

gs:table-get

syntax: (gs:table-get sym-id)

return: table cells. stored in gs:table-full.

全テーブルを行リストのリストとして取り出します。

Get full table as a list of row lists.

 ( ("column0" "column1" ... ) ; 1'st row
   ("column0" "column1" ... ) ; 2'nd row
   ...
   ... )
 

完全なテーブル内容が、関数gs:table-getの戻り値として、行リストのリストの収められ、
変数gs:table-full にも、保存されます。

The entire table contents is stored as a list of row lists in the
return value of gs:table-get, and is also stored in the variable
gs:table-full.

 この関数も、gs:table-get-size のように内部変数gs:table-fullにデータを保存します。
 そして、個々のセルからデータを取り出すには、gs:table-get-cell を使います。

gs:table-get-cell

syntax: (gs:table-get-cell sym-id int-row int-column)
parameter: sym-id – The name of the table.
parameter: int-row – The row of the cell.
parameter: int-column – The column of the cell.

return: cell value. stored in gs:table-cell.

int-rowint-column で指定されたセルの内容を文字列として取り出します。
(訳注:変数gs:table-cell にも、保存されます。)

Get the cell contents as a string at sepcifed int-row and int-column.

 この関数も、gs:table-get のように内部変数gs:table-cellにデータを保存します。
 つまり、昨日のブログで、“2つの内部変数、gs:table-fullgs:table-size”、としていたのは、実は、3つでした(汗)。
 最後の関数は、

gs:table-set-column

syntax: (gs:table-set-column sym-id int-column-number int-width [str-justification])
parameter: sym-id – The name of the table.
parameter: int-column-number – The column number of align.
parameter: int-width – The column width.
parameter: str-justification – The column align property, “left”, “center”, “right”.

列幅の調整とセル内容の配置のテーブル列の属性を変更します。
str-justification パラメータはオプションで、デフォルトは “left” です。

A table column property is changed, adjusting the column width and alignment of cell
contents. The str-justification parameter is optional and alignment is “left”
by default.

 セルの書式設定用ですが、列単位で、列の幅と配置(左詰め、中央、右詰め)を設定できます。
 この関数で設定される列幅は、比率のようです。実際の列幅は、ウィンドウの大きさで決まります。

 では、今日紹介した関数を先日のスクリプトに追加してみましょう。
(aif と defun は newlisp-utility.lsp に、include は init.lsp に定義してあります。)

; utility
(define *newlispDir* (env "NEWLISPDIR"))
(load (append *newlispDir* "/guiserver.lsp"))
(include "macro.lsp")
(include "newlisp-utility.lsp")
; define sub-routine
(define CRLF "\x0d\x0a")
(define regexCRLF "\r*\n")
(defun stringEx (x)
  (if (string? x) (string "\"" x "\"") (string x)))
(defun string-convert (str)
  (if (catch (eval-string str) 'res) (if res res str) str))
(defun list2csv (lst)
  (let (csv "")
    (dolist (row lst)
      (extend csv (join (map stringEx row) ",") CRLF))))
(defun csv2list (csv)
  (let (lst '())
    (dolist (row (parse (if (ends-with csv regexCRLF 0) (chop csv) csv) regexCRLF 0))
      (push (map string-convert (parse row ",")) lst -1))
    (setf (lst -1 -1) (if (string? $it) (trim $it "\r") $it))
    lst))
(defun csvfile2list (file)
  (cond ((directory? file) nil)
         ((file? file)
          (csv2list (read-file *fileName*)))
         (true nil)))
; define the function for gs:table
(defun clear-table (table)
  (gs:table-get-size table)
  (dotimes (i (gs:table-size 0))
    (dotimes (j (gs:table-size 1))
      (gs:table-set-cell table i j ""))))
(define (adjust-column)
  (gs:table-get-size 'OutputArea)
  (gs:table-get 'OutputArea)
  (dotimes (i (gs:table-size 1))
    (gs:table-set-column 'OutputArea i (* 100 (apply max (map length (map (curry nth i) gs:table-full)))))))
(defun set-table (lst)
  (clear-table 'OutputArea)
  (let (len (length (lst 0)))
    (when (< (gs:table-size 1) len)
      (for (i (i+ (gs:table-size 1)) len) (gs:table-add-column 'OutputArea ""))))
  (dolist (row lst)
    (let (r $idx)
      (if (< r (gs:table-size 0))
          (dolist (c row) (gs:table-set-cell 'OutputArea r $idx (string c)))
        (gs:table-add-row 'OutputArea (map string row)))))
  (adjust-column)
  )
; define handler
(define *fileName* (real-path))
(define *filemask* "csv CSV")
(define *description* "csv file")
(define (openfile-action id op file)
  (when file
    (setq *fileName* (base64-dec file))
    (gs:set-text 'Status *fileName*)
    (aif (csvfile2list *fileName*)
        (set-table it)
      (gs:set-text 'Status (string *fileName* " is not csv file.")))))
(define (open-file-dialog)
  (gs:open-file-dialog 'Frame 'openfile-action *fileName* *filemask* *description*))
(define (table-action id row col data)
  (println "id=" id " row=" row " col=" col " data=" data))
; initialization
(gs:init)
(setq FPosX 100 FPosY 50 FWidth 640 FHeight 480)
(gs:frame 'Frame FPosX FPosY FWidth FHeight "CSV Viewer with gs:table")
(gs:panel 'StatusPanel)
(gs:label 'Status (real-path))
(gs:button 'fileButton 'open-file-dialog "File")
(gs:table 'OutputArea 'table-action "1")
(gs:table-add-row 'OutputArea )
(gs:table-set-row-number  'OutputArea true)
(gs:add-to 'StatusPanel 'fileButton 'Status)
; mount all on frame
(gs:set-border-layout 'Frame)
(gs:set-flow-layout 'StatusPanel "left")
(gs:add-to 'Frame 'OutputArea "center" 'StatusPanel "south")
(gs:set-visible 'Frame true)
; main routine
(gs:listen)
(exit)

 関数set-table に新規関数adjust-column を追加しています。
 関数adjust-column では、関数gs:table-get を使って取得したセルの内容から各列の最大文字列長を求め、関数gs:table-set-column を使って、列幅の比率を設定しています。
 このスクリプトを実行して、ウィンドウを引き伸ばせば、

 こんな感じにできます。
 最初の行の文字列の長さで列幅比率を決定するなら、gs:table-get-cell を使って、

(define (adjust-column)
  (gs:table-get-size 'OutputArea)
  (dotimes (i (gs:table-size 1))
    (gs:table-get-cell 'OutputArea 0 i)
    (gs:table-set-column 'OutputArea i (* 100 (length gs:table-cell)))))

 で済みます。
 どちらも、列幅を文字列長の 100倍にしているのは、列幅比率の精度を上げるため。倍率は、フォントサイズの数倍以上がお薦めかも。

 以上、如何でしょうか?