2014年4月24日 星期四

Visual Lisp 的輸入 對話盒範例二

    雖然又是個簡單的例子,但是牽扯東西很多,先來談談 LISP,LISP 是個古老的語言 (噢,又說了一次),他是 LIST PROCESSOR 的縮寫,LIST 是什麼 ? LIST 直翻成串列,簡單講,就是將一串東西  (資料) 串起來,現實中可能馬上浮現日本的麻糬,魚丸...,等等,但 LISP 的資料更像烤肉串,上面可能有小香腸、青椒、培根等等美食 (不同種類資料),而香腸+大蒜也是這一串食物中的一小串,OK,簡單講,LISP 主要將不同型態的資料串接形成一串 (一筆資料),你也許會說,我記得 C 好像也可以,我要說的是,那個年代,這可是革命性的概念。


      那要這種混和形式的資料幹什麼呢 ?  講到 LISP  又得再提到一樣東西  A.I. ,你的腦袋中是否又浮起一個小男孩的畫面,當初開發 LISP 主要就是要處理人工智慧的問題,這時要處理的就不只是數字文字而已,而是要處理現實世界各式各項的資訊,幾十年過去了,LISP 不再是主流語言,甚至於在 A.I. 領域中也不在獨尊,而因為 Autodesk 選擇開發語言一個決定,讓一個搞土木的竟然也跟  LISP 發生關係,再回頭看,用 LISP 處理電腦輔助設計問題,又何嘗不是另一個意外。


     好吧!來看看串列的資料長的什麼樣子,假設有個串列,裡面有三個資料,"A" "B" "C" 三個字元形成一個串列就叫 CHAR

     (setq CHAR '("A" "B" "C"))

讓它執行 (例如在 AUTOCAD VLIDE 中的主控台),傳回

    ("A" "B" "C")

     又如何,很像一維矩陣啊
     記不記得 LISP 的一項特徵 S 表示式 (S-expression),例如 1 + 2 就寫成 (+ 1 2),事實上這個格式跟串列 (資料) 是同一型式,LISP 靠此達成程式碼跟資料格式統一。 

    換句話說,如果把 (+ 1 2),視為一個串列,第一個元素 + 就是函數名稱,第二個元素 1、第三個元素 2 就是輸入參數;程式在運作時,只要參數沒取完,就一值執行(累加),所以在 LISP 中 1 + 2 + 3 + 4 + 5 不是寫成

    (+ 1 (+ 2 (+ 3 (+ 4 5))))

而是

    (+ 1 2 3 4 5)

反而比傳統方式節省字元空間 (雖然不重要)

      另外一個特色就是,傳入函數的變數是什麼形式,不就是變數嗎? LISP 可以將函數當變數傳入函數,到這裡,你應該已經頭昏腦脹了?先停下來,來看看今天的主題 (是的),這才是這篇文章要講的,在視窗對話盒中有一個元件叫 popup_list (例如我的程式集中 型鋼剖面程式內型鋼型號選取)


    這些元件中有預設的選項,按下它後便可選取預設的項目,那麼程式怎麼知道預設的
項目是哪些呢?當然是要程式中給的,怎們給?

        (setq CHAR '("A" "B" "C"))
        (start_list
"popup_list_char")
        (mapcar 'add_list CHAR)
        (end_list)
 
    第三行 (mpacar) 就是一個例子,這個函數第一個變數 add_list  就是一個函數 (注意在這裡是用 '不是用括號),他將一個項目加到 "popup_list_char" 中,但是 CHAR 中有三個項目,別的語言要怎麼做,首先要知道 CHAR 中有幾個元素,再來個迴圈執行 add_list,但是在 LISP 中就這一行,很多高手出手時就喜歡用這個函數,看的人一時間天旋地轉,日月無光。

    再舉個例子,有個串列(setq A '(1 2 3 4 )),想要將每個元素加一,LISP 執行就

     (mapcar '1+ A)       A  變成   (2 3 4 5)

    一行搞定 ,(mapcar) 就是將 A 這個串列中每一個元素 1 2 3 4 ,去執行 1+ 這個函數,這算是最淺顯的例子,再講下去我就要露出馬腳了,LISP 程式名稱來由也算名符其實,最後附上這個例子程式碼
   (PS  要知道使用者選那個選項,使用
           (get_tile "popup_list_char")
           傳回 "0" 第一項"1" 第二項,以此類推)


 //---------------------- Popup_list.lst
(defun C:Popup_list( / dcl_ID)
    ; 載入對話盒
    (setq dcl_ID (load_dialog "Popup_list.dcl"))
    (if( not (new_dialog "Popuplist" dcl_ID))
        (progn
        (alert "PopuList 對話盒無法載入!")
        (exit)
    )
    )
   
    (setq CHAR '("A" "B" "C"))
    (start_list "popup_list_char")
    (mapcar 'add_list CHAR)
    (end_list)
 
    ; 按下 "確定" 時離開
    (action_tile "accept" "(done_dialog 1)")
    ; 啟動對話盒
 
    (start_dialog)
    (unload_dialog dcl_ID)

    (gc)
)

 //---------------------- Popup_list.dcl
Popuplist : dialog {
    key = "popuplist";
        label = "Example" ;
    spacer_1;
    :popup_list {
        key = "popup_list_char";
        label = "字元:";
        width = 20;
    }
        spacer_1;
    ok_cancel;       
}

2014年4月21日 星期一

Visual Lisp 輸入 含對話盒程式例子


   這是一個有對話盒的 Visual Lisp 程式例子,簡單說,這是一個讓使用者可以輸入密碼,程式核對後確認是否通過的密碼對話盒,為了變點花樣,程式讓使用者可以輸入三次錯誤密碼的機會,超過三次便拒絕輸入。
    先然看看 DCL 檔案內容,就叫它 Password.DCL

PassWord : dialog {
            key = "password";
            label = "輸入密碼" ;
            : boxed_column{
                    spacer_1;
                :edit_box {
                        key = "edbox_pw";
                        label = "密碼:";
                        edit_width = 20;
                        password_char = "*";
                }
                spacer_1;
        }
            spacer_1;
        ok_cancel;
}

先從簡單的講
spacer_1;                對話盒內空一行
ok_cancel;              標準的確定與取消按鈕
:boxed_column{  }  劃出方框,無標題 (通常將相關聯之元件綁到一起)
:edit_box{ }             編輯盒 (是的,就是 Windows 中讓人輸入的方框)

再來於 { } 元件內特性定義,如 edit_box
        key = "edbox_pw";        元件關鍵字,供程式辨認
        label = "密碼:";            顯示予使用者之提示
        edit_width = 20;             編輯框之寬度
       password_char = "*";     密碼輸入使用,定義 "*" 元為顯示之符號 (輸入密碼的標準動作)
  另外注意末端 ; { } 成對等小細節。

那麼 ok_cancel; 的性質呢 ? 它是預設的按鈕,會顯示 "確定" "取消" 兩個按鈕,"確定" key = "accept"; "取消" key = "cancel"

DCL 檔案最前頭
PassWord : dialog {
                key = "password";
                label = "輸入密碼" ;

PassWord Lisp 呼叫辨認的
key = "password";         當然就是整個對話盒的關鍵字
label = "輸入密碼" ;       整個對話框的 TITLE

執行前 VLIDE  提供預覽功能,工具 / 介面工具(I) / 預覽編輯器的 DCL(E)



就可以看到對話盒樣子 (很陽春吧)



OK,再來看 LISP 程式碼部分

(defun C:Password( / dcl_ID)
    ; 載入對話盒
    (setq dcl_ID (load_dialog "password.dcl"))
    (if( not (new_dialog "PassWord" dcl_ID))
        (progn
            (alert "PassWord 對話盒無法載入!")
            (exit)
        )  
    )
    ; nCount 計算錯誤之次數
    ; sPw 正確之密碼
    (setq nCount 0
          sPW "Hello World")
    ; 設定對話方框輸入後之動作 (檢查密碼)
    (action_tile "accept" "(CheckPW)")
    (action_tile "cancel" "(done_dialog 0)")
    ; 啟動對話盒

    (start_dialog)
    (unload_dialog dcl_ID)

    (gc)
)
; 檢查密碼對話盒函數
(defun CheckPW ( / input)
     (setq input (get_tile "edbox_pw"))
     (if (= input sPW)
         (progn
             (alert "密碼正確")
             (done_dialog 1) ; 關閉對話盒
         )  
         (progn
             (setq nCount (1+ nCount))
             (set_tile "password" (strcat "密碼錯誤 "
                                          (itoa nCount)
                                          " "))
             (alert (strcat "密碼錯誤!\n輸入密碼為:" input))
             (if (>= nCount 3) ; 錯誤三次封鎖對話盒
                 (progn
                     (alert "密碼錯誤 3 \n拒絕接受輸入")
                     (mode_tile "accept" 1)
                     (mode_tile "edbox_pw" 1)
                 )  
             )
         )  
     )  
)

    標準呼叫程序就不說了,來看看
(action_tile "accept" "(CheckPW)")
(action_tile "cancel" "(done_dialog 0)")

    (action_tile) 函數即是設定對話盒受到 "刺激" 後之反應,(action_tile "accept" "(CheckPW)") 便是按下 "確定" 按鈕後要產生之反應,是的,就是執行 (CheckPW) 函數檢查密碼,(action_tile "cancel" "(done_dialog 0)"),按下 "取消" 後便關閉對話盒,並傳回失敗之訊息 0

    (CheckPW) 是自訂函數,執行檢查動作之函數,每次按下 "確定" 便執行一次,函數執行首先取得編輯盒中之文字,並儲存於 input 之變數中
(setq input (get_tile "edbox_pw"))

若輸入之密碼與內存之密碼相符 (sPW, "Hello Wrold"),顯示密碼正確,再跳出對話盒 (done_dialog 1) 傳回正確訊息。
(alert "密碼正確")
(done_dialog 1)



若錯誤,則先將錯誤次數累加一次 (nCount 是整體變數)
(setq nCount (1+ nCount))

再來,將錯誤次數顯示在以 "password" 為關鍵字之元件,是什麼呢?,回頭查查 DCL 之定義,就是整個對話盒的標題
(set_tile "password" (strcat "密碼錯誤 "
                                          (itoa nCount)
                                          " "))
 
並提出訊息告訴使用者密碼錯誤,並將錯誤之密碼提示與使用者。
(alert (strcat "密碼錯誤!\n輸入密碼為:" input))


最後檢查錯誤是否超過三次
(if (>= nCount 3)
….
)
若超過,先提示使用者錯誤已超過三次,無法再輸入。
(alert "密碼錯誤 3 \n拒絕接受輸入") 




      並將 "密碼" 編輯盒與 "確定" 之按鈕鎖住 (無法使用),只能按下 "取消" 按鈕。
(mode_tile "accept" 1)
(mode_tile "edbox_pw" 1)
(注意,參數 1 才是 disable)




這雖然是個小小小程式,但是 DCL 輸入該說的東西已有十之七八了。