달수가 생각하는 VisualLISP 과 Excel 연동 1
아래의 과정을 설명 보면서 쭈욱 그대로 따라해 보세요..
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
'Auto CAD > lisp' 카테고리의 다른 글
VLAX-dump-object (객체의 정보값 DXF코드 역할) (0) | 2019.04.04 |
---|---|
VLA-GetBoundingBox (Entity의 좌하 우상 좌표) (2) | 2019.04.04 |
달수가 생각하는 VisualLISP 과 Excel 연동 2 (0) | 2019.04.04 |
TGK - 태극기 그리기 (0) | 2019.02.04 |
Extended Entity Data (0) | 2019.01.26 |