달수가 생각하는 VisualLISP 과 Excel 연동 1

Auto CAD/lisp|2019. 4. 4. 11:08

 

  • 저는 LISP 알아도 Excel  모릅니다.

  • VBA AutoCAD에서 도움말을 보는 수준이고 만들어서 사용하진 못합니다.

 

  • 그런데 엑셀의 DATA AutoCAD 끌어들이거나 AutoCAD DATA Excel

  • 기록을 해야 하는 경우가 종종 생깁니다.

  • 물론 텍스트 화일을 이용해도  불편이 없기에 Excel과의 연동을

  • 그동안 중요하게 생각하지 않았었는데 가끔씩 필요할 때가 있어서 정리해 보기로 했습니다

 

 

 

  • LISP Excel 연동시키는 자료가 국내 싸이트를 검색해 봐도 충분한 자료나 설명글이 없네요.

  • 외국싸이트를 뒤진 결과 몇가지의 좋은 자료를 수집하였으나..이넘의 꼬부랑 글씨가

  • 저를 답답하게 하게 하고 개념을 설명한 글도 없네요..또한 제컴의 운영체제가

  • 윈도우7이고 64bit며 엑셀은 2007인데 객체의 특성이나 메쏘스를 확인하려면 먹통이 되기 일쑤고..

  • 그렇지만 LISP을 하던 경험으로 또한 특성이나 메쏘스는 Excel의 도움말을 참조해서

  • 구문 하나 하나 점검해 가면서 습득했습니다.

 

 

 

  • LISP   알면서 Excel과의 연동을 시작하려는 분들께

  • 좋은 길잡이가 되기를 바라면서 습득한 과정을 설명합니다.

 

  • 설명을 하기전에 크게 몇가지로 나누어서 구분해 보겠습니다.

  •     1.  연동의 주체가 LISP이니 자료를 주고 받는 DATA 모두 LIST 활용합니다.

  •     2. LISP ActiveX에서 Excel 사용하려면 Excel 실행화일 경로를 알아야 합니다.

  •     3. 실행화일 경로를 알았다면 Excel Application ActiveX 연결합니다.

  •     4. 연결이  이후에 특성값과 메쏘스를 이용해 읽거나 쓰기합니다.

 

 

 

  • 2번과 3번은 이해하지 못해도 아래의 구문을 그대로 사용하면 됩니다.

  • 4번은 약간씩 응용을 해서 사용합니다.

아래의 과정을 설명 보면서 쭈욱 그대로 따라해 보세요..

 

1. EXCEL DATA A1 B1 C1...A2 B2 C2...셀의 값들로 이루어져 있는데..이것을 읽은   값을

    (setq Lst (list (list A1의값 B1의값 C1의값...)(list A2의값 B2의값 C2의값...)...)) 리스트로 저장하고

    또한 반대로 리스트의 값을 Excel 셀에 위치에 쓰기 합니다.

    여기서 리스트는 다중리스트가 됩니다.

 

2. Excel 실행화일 경로 알기

    ;Excel 프로그램이 설치되어있다는 가정하에 아래의 구문을 실행

    (setq excelPath

        (vl-registry-read

            "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Excel.exe"

            "Path"

        )

    )

    (setq excelPath (strcat excelPath "Excel.exe"))

    ;레지스터리 값을 이용해서 엑셀의 실행화일 경로를 알아냅니다.(이 부분은 운영체제에 따라 다를수도 있습니다)

 

3. ActiveX Excel 연결 시키기

    (if (not msxl-acos)

        (vlax-import-type-library

            :tlb-filename excelPath

            :methods-prefix "msxl-"

            :properties-prefix "msxl-"

            :constants-prefix "msxl-"

        )

    )

    (setq ExcelApp (vlax-get-or-create-object "Excel.Application"))

   

    2번과 3번을 합쳐서 서브함수로 만들어 사용하면 편리할거 같아서 아래와 같이 만들었습니다.

    운영체제  Excel 버전에 따라 다를수도 있습니다.

    ;;;*************************************************************************

    ;;;     : 엑셀 로딩

    ;;;   : (setq ExcelApp (subLoadExcel))

    ;;;*************************************************************************

    (defun subLoadExcel (/ excelPath)

        (if (and

                (setq excelPath

                    (vl-registry-read

                        "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Excel.exe"

                        "Path"

                    )

                )

                (setq excelPath (strcat excelPath "Excel.exe"))

            )

            (progn

                (if (not msxl-acos)

                    (vlax-import-type-library

                        :tlb-filename excelPath

                        :methods-prefix "msxl-"

                        :properties-prefix "msxl-"

                        :constants-prefix "msxl-"

                    )

                )

                (setq ExcelApp (vlax-get-or-create-object "Excel.Application"))

            )

        )

        ExcelApp

    )

 

    * 여기 까지는 이해 못해도 상관없습니다.

   

4. VLISP에서 Excel 접근하려면 Excel 구조를 먼저 알아야겠군요..

   

    . Excel 열려있는 화일들

    . 선택한 화일

    . 시트들

    . 선택한 시트

    . 셀들

    . 선택한 

   

    위의 단계들을 차례대로 해보겠습니다.

   

    ;서브함수 (subLoadExcel) 로딩한다

    (setq ExcelApp (subLoadExcel))

   

    ;엑셀을 실행시킨다.

    (vlax-put Excelapp "visible" :vlax-true) ;<-:vlax-false 경우는 실행을 안시킴

    ;엑셀을 실행시키지 않고도 자료를 읽기하거나 쓰는데는 문제가 없지만

    ;지금은 공부단계 과정이니 실행을 시켜놓고 합니다.

   

    ;열려있는 화일들

    (setq Workbooks (vlax-get-property ExcelApp 'Workbooks))

   

    ;이쯤에서 기록하는 방법과 읽는 방법으로 나누어지네요..

    ;일단 기록하는 방법부터 해보고 열기해서 값을 읽어오게 하는 것은 맨아래 예제 서브함수로 대치하겠습니다.

   

        ;새로운 엑셀화일을 만들때

        ;>---------------------------------------------------------------------------------------------

            (setq CurrentWBook (vlax-invoke-method Workbooks 'Add))

            ; 다른 방법 (setq CurrentWBook (msxl-Add Workbooks))

            ;엑셀의 새로만들기  효과가 있으며 그것을 변수 CurrentWBook 저장한다.

        ;<---------------------------------------------------------------------------------------------

 

               화일이 열려져 있으니 vlax-get-property 함수를 이용하여 현재 시트들을 변수로 저장시키고

            (setq Sheets (vlax-get-property ExcelApp 'Sheets)) ;Sheet 집합체

           

            현재 활성된 시트도 변수로 저장하고..    

            (setq AcSheet (vlax-get-property ExcelApp 'ActiveSheet)) ;현재 활성된 시트

            ; 다른 방법 (setq AcSheet (msxl-get-ActiveSheet ExcelApp))

 

            * 참고

                property method 구분

                ActiveX 객체를 다루는 응용기술인데 특성(property) 메쏘스(method) 나누어 집니다.

                어느글에서 설명을 한것을 인용해서 설명하면

               

                객체 강아지라고 하고

               

                    강아지는 얼굴도 있고 눈도 있고 발도 있고 꼬리도 있고..등등으로 구성되어 있습니다.

                    여기서 얼굴   꼬리...등을 특성(property)이라고 합니다.

                     객체가 고유하게 가지고 있는것들이져..

                   

                    그런데 여기서 강아지가 꼬리를 흔듭니다.

                    강아지고 멍멍 하고 짖습니다. 강아지가 뜁니다.

                    이러한 행동을 하는것을 메쏘스(method)라고 누군가 설명하더군요..

                   

                    캐드에서 라인객체를 위 설명과 비교해보면

                    라인은 시작점이 있고 끝점이 있고 레이어도 있고 선의 종류도 있고...이걸 특성(property)이라고 하고요

                    라인을 지워라..복사해라..이동해라..회전시켜라...등등을 메쏘스(method)라고 할 수 있겠네요..

               

          엑셀을 접근하는 방식에서

           

            특성값을 읽어 올때는 vlax-get-property 함수를 사용하고

            특성값을 변경 시킬때는 vlax-put-property 함수를 사용하고

            메쏘스를 사용할때는 vlax-invoke-method 함수를 사용합니다.

               

            이거 이외에도 ActiveX에서 연결시키는 것에 따라 msxl- <-이런류의 함수도 사용됩니다.

           

            여기서는 vlax-get-property , vlax-put-property , vlax-invoke-method  세가지 함수 위주로 해보겠습니다.

           

           캐드에서 ActiveX 함수로 값을 쓰거나 읽기 할때 배열을 사용하기도 하는데

                배열을 만들때는 (setq a (vlax-make-variant "")) <- 이런식으로 만들고

                 값을 확인할때는 (vlax-variant-value a) <- 이런식으로 확인합니다.

                * 구체적인 방법에 대해서는 배열함수 따로 보세요..

                       

   

    ;>--------------------------------------------------------------------------------------------

        시트를 다루는 방법 몇가지를 해보겠습니다.

            . 시트들의 이름을 알아낸다.

            . 내가 알고 있는 시트로 이동하고 현재 활성된 시트로 변경시킨다.

            . 새로운 시트를 추가한다.

            . 시트 이름을 변경한다.

            . 특정 시트를 삭제한다.

           

            시트를 다루는 경우의 수가 몇가지 안되네요..

           

            ;시트들의 이름을 알아낸다.

            (setq SheetNameList '())

            (vlax-for item Sheets

                (setq SheetNameList (append SheetNameList (list (vla-get-name item))))

            )

            변수 SheetNameList 결과값이 ("Sheet1" "Sheet2" "Sheet3") <- 이렇게 리스트로 나옵니다.

               

                * vlax-for 함수는 ActiveX  설명을 참조하고 여기서는 생략합니다.

           

            ;현재 활성화된 시트이름을 알아낸다.

            (setq AcSheetName (vla-get-name AcSheet))

            "Sheet1" 이라고 나오네요

           

            ;내가 알고 있는 시트로 이동하고 현재 활성된 시트로 변경시킨다.

            "Sheet2" 이동시켜서 활성화 시킵니다.

           

            먼저 "Sheet2" 객체로 잡으려면

            (setq SheetName "Sheet2")

            (setq Sht (vlax-get-property Sheets 'Item SheetName))

            변수 Sht "Sheet2" 객체입니다.

           

            Sht객체를 현재 시트로 활성화 합니다.

            (setq AcSheet (vlax-invoke-method Sht 'Activate))

           

                ;또다른 방법

                (setq SheetName "Sheet2")

                (vlax-for item Sheets

                    (if (= (vlax-get-property item 'Name) SheetName)

                        (vlax-invoke-method item 'Activate)

                    )

                )

           

            ;시트를 추가하려면

            (setq SheetName "myArchiMore")

            (vlax-put-property (vlax-invoke-method Sheets 'Add) 'Name SheetName)

            엑셀을 보면 "myArchiMore" 시트가 생겨났져?

           

            ;시트를 삭제하려면

            (setq SheetName "Sheet2")

            (vlax-for item (vlax-get-property ExcelApp 'Sheets)

                (if (= (vlax-get-property item 'Name) SheetName)

                    (vlax-invoke-method item 'Delete)

                )

            )

        ;<----------------------------------------------------------------------------------------

       

    

        ;>----------------------------------------------------------------------------------------

            시트에 대해 다루어 봤으니 이젠 셀을 다루어 보겠습니다.

            셀에 값을 넣어주고 읽고 하는 방법이 실제로 우리가 필요로 하는것들이져..

           

            셀은 고유의 주소값이 있습니다. A2 B15 C7 ...

            엑셀에서 표현하는 주소방식이 여러가지 있는거로 알지만 우리는 캐드 사용자이니..

            캐드식의 주소로 사용하겠습니다.

           

            A2  가로로 A 이고 세로로 2 입니다.

            B15  가로로 B 이고 세로로 15 입니다.

            C7  가로로 C 이고 세로로 7 입니다.

           

            알파벳을 숫자로 표현하면 A B C D E F -> 1 2 3 4 5 이렇게 되겠져

           

            그런데 이걸 둘다 숫자로 표현할때는 세로가 먼저고 가로가 나중입니다.(엑셀에서 정한 규칙인거 같아요)

            A2 2 1 , B15 15 2 , C7 7 3

           

            ; A1셀에 "I love ArchiMore" 값을 넣으려면

            (setq Cells (vlax-get-property AcSheet 'Cells)) ;<-셀의 집합체

            (setq colY 1 rowX 1) ;<- 1,1 A1

            (setq val "I love ArchiMore")

            (vlax-put-property Cells 'Item colY rowX val) ;<-A1셀에 "I love ArchiMore"를 입력해라.

           

            ; 리스트의 값을 셀에 넣으려면

            ; 만약 B3에서 부터 가로로 쭈욱 넣으려면...B3 세로3 가로2번의 위치이져

            (setq colY 3 rowX 2) ;<- 요건 B3

            (setq SomeData '("나는" "아키모아에서" "달수가" "강의하는" "글을" "보고" "있습니다."))

            (mapcar

                '(lambda (val)

                    (vlax-put-property Cells 'Item colY rowX val)

                    (setq rowX (1+ rowX))

                )

                SomeData

            )

           

            반대로 엑셀에 값이 있다고 하고..가로로 한줄을 읽으려면

            (setq colY 3 rowX 2)

            (setq Lst '())

            (while

                (setq val

                    (vlax-variant-value

                        (vlax-get-property

                            (vlax-variant-value (vlax-get-property Cells 'Item colY rowX))

                            'value

                        )

                    )

                )

                (setq Lst (append Lst (list val)))

                (setq rowX (1+ rowX))

            )          

            ;여기서 값을 읽을때는 vlax-variant-value 함수를 사용했습니다. 기록할때는 배열을 사용하지 않았는데

            ;읽기 할때는  사용되었을까요? 저의 추정으로는 기록할때는 ActiveX에서 자동으로 배열을 만들어

            ;준거 같습니다. 문자인지 숫자인지 스스로 알아서 입력 해주는거로 추정됩니다.

            ;설명서 같은게 없다보니 추정밖에  수가 없군요..

           

            여기까지가 캐드에서 얻어진 리스트를 엑셀에 기록하는 과정이었습니다.

             중요한거라 생각됩니다.

        ;<----------------------------------------------------------------------------------------

       

       

        ;>----------------------------------------------------------------------------------------

            지금부터는 셀에 폭을 변경하고 색상을 넣어보는 과정을 해보겠습니다.

            참고로 저는 엑셀 VBA 모릅니다. 그런데  도움말을 참고해서  글을 쓰고 있습니다.

           

            vlax-get-property vlax-put-property vlax-invoke-method vlax-for

             <- 이것은 ActiveX 함수이며

 

            'Add 'ActiveSheet 'Item 'Activate 'Name 'Sheets 'Delete

             <- 이것은 Excel에서 쓰여지는 특성  메쏘스입니다.

           

            특성  메쏘스는 Excel VBA 도움말로 확인하세요..

            

            특정한 셀을 객체로 잡아서

            (setq colY 1 rowX 1) ;<- 1,1 A1

            (setq cel (vlax-variant-value (vlax-get-property Cells 'Item colY rowX)))

            <- cel 변수에 A1셀이라는 객체를 잡았습니다.

           

            배열로 응답해서 vlax-variant-value 함수로  값을 확인합니다.

           

            cel 들어있는 값을 확인하려면

            (setq val (vlax-variant-value (vlax-get-property cel 'value)))

           

            cel 폭을 확인하려면

            (setq wid (vlax-variant-value (vlax-get-property cel 'ColumnWidth)))

           

            cel 배경색을 확인하려니깐..Interior 객체를 잡아야 하는군요..

            ; interior 정보확인

            (setq int (vlax-get-property cel 'Interior))

           

            cel 배경색 확인

            (setq col (vlax-variant-value (vlax-get-property int 'ColorIndex)))

           

            확인이 끝났으니 이번엔 반대로 변경을 시켜보겠습니다.

           

            (vlax-put-property cel 'Item colY rowX "아키모아") <- 값을 변경

            (vlax-put-property cel 'ColumnWidth 25) <- 폭을 25 변경

            (vlax-put-property int 'ColorIndex 6) <- 노랑색으로 배경을 변경

           

            위의 구문을 예제로 삼아서 나머지 특성들도 직접 바꾸어 보세요..           

        ;<----------------------------------------------------------------------------------------

 

 

            닫기, 저장하기, 다른이름으로저장하기 등의 메쏘스는 엑셀 VBA도움말을 참조하시고..

            ActiveX에서는 지금 까지 사용된 객체를 메모리에서 모두 제거해야겠지요..

 

            (vlax-release-object AcSheet)

            (vlax-release-object Sheets)

            (vlax-release-object CurrentWBook)

            (vlax-release-object Workbooks)

            (vlax-release-object ExcelApp)

                       

            위의 구문들을 이용하여 서브함수로 만들어서 사용하면

            깊이 공부할 필요없이 읽고 쓰기는 가능해질거 같습니다.

           

 

 

            엑셀의 값을 읽어서 리스트화 시키는 과정을 서브함수로 만들어 보면

            ;;;*************************************************************************

            ;;;     : 엑셀화일을 읽어 리스트화 시키고  리스트값을 변수에 저장한다.

            ;;;   : (setq totValLst (subReadValListMain))

            ;;;*************************************************************************

            (defun subReadValListMain (/ fileName ExcelApp Workbooks CurrentWBook Sheets AcSheet Cells totValLst)

                    ;;;*************************************************************************

                    ;;;     : 엑셀 로딩

                    ;;;   : (setq ExcelApp (subLoadExcel))

                    ;;;*************************************************************************

                    (defun subLoadExcel (/ excelPath)

                        (if (and

                                (setq excelPath

                                    (vl-registry-read

                                        (strcat

                                            "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows"

                                            "\\CurrentVersion\\App Paths\\Excel.exe"

                                        )

                                        "Path"

                                    )

                                )

                                (setq excelPath (strcat excelPath "Excel.exe"))

                            )

                            (progn

                                (if (not msxl-acos)

                                    (vlax-import-type-library

                                        :tlb-filename excelPath

                                        :methods-prefix "msxl-"

                                        :properties-prefix "msxl-"

                                        :constants-prefix "msxl-"

                                    )

                                )

                                (setq ExcelApp (vlax-get-or-create-object "Excel.Application"))

                            )

                        )

                        ExcelApp

                    )

                    ;;;*************************************************************************

                    ;;;     : 셀값을 읽어옴

                    ;;; 매개변수: Cells colY(세로번호) rowX(가로번호)

                    ;;;   : (setq val (subReadCell Cells colY rowX))                   

                    ;;;*************************************************************************

                    (defun subReadCell (Cells colY rowX)

                        (vlax-variant-value

                              (vlax-get-property

                                   (vlax-variant-value (vlax-get-property Cells 'Item colY rowX))

                                   'value

                              )

                        )

                    )

                    ;;;*************************************************************************

                    ;;;     : 엑셀값을 읽어서 리스트화 시킨다.

                    ;;; 매개변수: Cells colY(세로의시작위치) rowX(가로의시작위치)

                    ;;;  용 법: (setq totValLst (subReadValList Cells colY rowX))

                    ;;;*************************************************************************

                    (defun subReadValList (Cells colY rowX / rowXtmp valLst totValLst val)

                        (setq rowXtmp rowX valLst '() totValLst '())

                        (while (setq val (subReadCell Cells colY rowXtmp))

                            (while (setq val (subReadCell Cells colY rowXtmp))

                                (setq valLst (append valLst (list val)))

                                (setq rowXtmp (1+ rowXtmp))

                            )

                            (setq totValLst (append totValLst (list valLst)))

                            (setq colY (1+ colY) rowXtmp rowX valLst '())

                        )

                        totValLst

                    )

                (if (setq fileName (getfiled "Select Excel file to read :" (getvar "dwgprefix") "xls" 8))

                    (progn

                        (setq ExcelApp (subLoadExcel))

                        (setq Workbooks (vlax-get-property ExcelApp 'Workbooks))

                        (setq CurrentWBook (vlax-invoke-method Workbooks 'Open fileName))

                        (setq Sheets (vlax-get-property ExcelApp 'Sheets))

                        (setq AcSheet (vlax-get-property ExcelApp 'ActiveSheet))

                        (setq Cells (vlax-get-property AcSheet 'Cells))

                        (setq totValLst (subReadValList Cells 1 1))

                        (vlax-invoke-method CurrentWBook 'Close :vlax-False)

                        (vlax-release-object Cells)

                        (vlax-release-object AcSheet)

                        (vlax-release-object Sheets)

                        (vlax-release-object CurrentWBook)

                        (vlax-release-object Workbooks)

                        (vlax-release-object ExcelApp)

                         totValLst

                    )

                )

            )

      ;메인함수 구문중에 엑셀의 값을 읽어와야 할때 위에서 만든 서브함수를 실행시키면

      (setq totValLst (subReadValListMain)) ;<- 엑셀의 값이 totValLst 저장되어 있습니다.

 

 

 

 

 

 ;참고) 범위지정 하는 방법

 (setq Ran (vlax-get-property ExcelApp 'Range "A2:C5"))

 (setq acRan (vlax-invoke-method Ran 'Activate))

 

 

이로써 LISP Excel연동관계에 대한 입문과정을 모두 마쳤습니다.^^

 

출처 : 아키모아 달수 http://cafe.daum.net/archimore/IP9e/58

댓글()