본문 바로가기
아래아한글 자동화/python+hwp 중급

[교육업무자동화7/7] 영수증자동화(개별저장 및 찾아바꾸기)

by 일코 2021. 1. 31.

지난 포스팅은...

 

[교육업무자동화6/10] 청구서자동화6(pyinstaller로 배포하기)

지난 포스팅은... [교육업무자동화5/10] 청구서자동화5(개선할 부분 찾아보기) #중급 지난 포스팅은... [교육업무자동화4/10] 청구서자동화4(청구서 마무리하기) 지난 포스팅은... [교육업무자동화3/1

www.martinii.fun

지난 포스팅까지 6개 과정으로 청구서 자동화까지 마쳤다.
(다소 부족한 대로) tkinter의 GUI도 붙여보았고, exe파일로 컴파일도 해보았다. 
꼼꼼히 따라해보았던 독자들께서는 한/글에서 기본으로 제공하는 API 메서드 외에도
추가적인 기능보완을 위해 파이썬 단에서 일종의 함수를 만들어 사용하거나,
파이썬의 기본문법이나 모듈을 활용하여 원하는 기능으로 보완할 수 있다는 것도 알게 되었을 것이다.

 


 

이번 시간에는 지난 청구서를 복습한다는 기분으로, 비슷한 서식의 "영수증"을 작성해보자.
다만, 그 서식이 청구서와 거의 동일하기 때문에,
지난 포스팅처럼 엑셀에서 데이터를 불러오는 것이 아니라,
기존의 hwp파일을 수정하는 유용한 몇 가지 방법을 활용해서 진행하려고 한다.
"왜 굳이 이런 비효율적인 방법으로 해야 하지?" 라는 생각은 잠시 접고
"자주 활용할 수 있는 유용한 메서드를 배운다"는 마음으로 따라와주면 감사하겠다.

그 방법은 바로.........

 


 

100장의 청구서.HWP를 쪼개고 수정해서 100개의 영수증.HWP 만들기(;;;)

어차피 똑같이 생긴 문서(아래 이미지 참고), 이번엔 지난 포스팅과 조금 다르게 접근해보자.
아래 이미지의 좌측은 지난 포스팅에 만들었던 청구서 서식이고, 우측이 새로 작성할 영수증 서식이다.

 

왼쪽이 청구서, 오른쪽이 영수증이다. 비슷하게 생겼다. 구분하기도 힘들 정도로;

 

이번 포스팅에서 진행할 전체과정은 아래 세 줄로 요약할 수 있다.

1) 파이썬을 통해 기존에 만들어진 백장의 청구서.HWP 파일 내용을 하나씩 쪼개서 다시 백개의 HWP로 만들기
2) 기본API메서드인 FindReplace(찾아바꾸기) 대신 다양한 방법(단축메서드, 좌표, 커서이동 등)으로 찾아바꾸기
3) HWP파일 닫기 전에 다른이름의 HWP(또는 PDF)로 저장

위 3개 과정을 진행하기 위해 직전의 포스팅에서 완성했던 100장짜리 청구서를 열어서
100개의 파일로 나누는 과정부터 시작하겠다.

효율적인 코딩을 고민하는 분이라면,
찾아바꾸기를 먼저 하고, 그 후에 파일을 쪼개는 게 효율적이지 않을까 생각하실 수도 있겠다.
필자는 그저, 여러 개의 파일을 순회하며 열고닫고 하는 과정을 보여드리고 싶었다.
이렇게 저렇게 시도해보면서 효율적인 코드를 고민하는 과정도 분명 필요하기는 하다.

 

이 시점부터 따라하시는 분들을 위해 기존 100장짜리 청구서를 아래에 첨부해놓았다.

 

청구서100p.hwp
0.14MB
지난 시간에 만들었던 100페이지 문서 (위의 첨부파일)

 

1. 100장짜리 문서를 쪼개서 100개의 파일로 개별저장하기

100페이지 문서를 100개의 파일로 쪼개는 작업은, 설명에 주석을 일일이 달았으니 아래 코드를 참고하자.

"""
100페이지짜리 한/글 파일을
한페이지짜리 100개의 파일로 변환하는 코드
"""

from time import sleep  # 쉬어주는 함수
import win32com.client as win32  # 한/글 프로그램 열기 위한 모듈


# 아래아한글(템플릿) 열기
hwp = win32.gencache.EnsureDispatch("HWPFrame.HwpObject")  # 한/글 인스턴스화
hwp.RegisterModule("FilePathCheckDLL", "FilePathCheckerModule")  # 보안승인모듈 활성화
hwp.Open(r"C:\Users\smj02\Desktop\청구서100p.hwp")  # 파일 열기
hwp.XHwpWindows.Item(0).Visible = True  # 숨김해제
hwp.MovePos(2)  # 문서 처음으로 이동

for i in range(hwp.PageCount):# 페이지 수만큼 반복작업
    hwp.Run("CopyPage")  # 현재쪽 복사
    hwp.Run("DeletePage")  # 현재쪽 삭제
    hwp.XHwpDocuments.Add(isTab=True)  # 새 탭 열기
    hwp.Run("PastePage")  # 붙여넣기
    hwp.Run("MoveTopLevelBegin")  # 붙여넣기 == hwp.MovePos(2)
    hwp.Run("DeletePage")  # 현재쪽 삭제
    이름, 소속 = hwp.GetFieldText("이름"), hwp.GetFieldText("소속")  # 파일이름 짓기 위한 추출
    hwp.SaveAs(rf"C:\Users\smj02\Desktop\청구서모음(100개)\청구서_{이름}({소속}).hwp")  # 다른이름으로 저장
    hwp.XHwpDocuments.Item(0).Close(isDirty=False)  # 현재탭 닫기
    sleep(0.1)  # 0.1초 쉬어줌(꼭 필요)

# 완료시 팝업 하나 띄워주기
msgbox = hwp.XHwpMessageBox  # 메시지박스 생성(아직 보이지는 않음)
msgbox.string = f"총 {hwp.PageCount}개의 파일로 \r\n분할작업이 완료되었습니다."  # 메시지박스에 문자열 삽입
msgbox.DoModal()  # 메시지 박스 띄워주기(확인/취소 버튼)

# 한/글 닫기
hwp.XHwpDocuments.Item(0).Close(isDirty=False)  # 원래 문서도 저장하지 않고 닫기
hwp.Quit()  # 한/글 닫기

가급적 단축메서드인 Run을 많이 사용했다. 코드 길이가 대체로 짧아지고 가독성도 높아지는 편이다.

한 가지 짚고 넘어갈 점은,
위의 코드에서 27번 라인에 sleep함수를 넣지 않으면 (필자의 PC에서는) 오류가 발생하는데,
해당 커맨드의 실행이 완료되기 전에 다음 커맨드가 미리 실행되어 오류가 발생하는 경우가 간혹 있다.
필자의 경험상 거의 대부분은 hwp.Run("액션아이디") 방식으로 실행하는 메서드가 들어있을 때, 
그리고 주로 새 창을 열거나 닫는 등의 작업을 실행할 때 오류가 자주 발생하는 경향이 있는 것으로 보인다.

셀레늄으로 웹테스팅이나 크롤링을 해 본 독자라면, 페이지가 전부 로딩되기 전에 JS코드를 실행한다든지, 아직 로딩이 완료되지 않은 요소에 접근하려 한다든지 할 때 발생하는 오류와 비슷하다고 생각한다.

sleep 함수를 넣어 아주 조금만 기다렸다 실행하면 간단히 문제가 없어지니,
위와 같은 오류가 뜨는 경우 테스트 후 오류가 나는 커맨드 앞에 (PC사양에 따라 적절히) 0.1초 정도 넣어보자. 

 

(GIF클릭) 위의 "쪼개기" 코드 실행화면

 

위 과정을 건너뛰는 분들을 위해, 위 코드 실행결과로 얻은 100개의 파일을 아래에 첨부했다.

 

청구서모음(100개).zip
1.69MB

 

 


 

2. 100개의 HWP파일을 순회하면서 하나씩 열고 닫기

프로그래밍 경험이 있는 독자라면 금방 for문을 떠올렸을 것이다.
근데 파일 이름은 어떻게 얻는지 모른다면?
이 참에 os모듈의 listdir 메서드를 익혀보자.
파일 이름이나 폴더 이름을 수정할 때 아주 유용하게 쓰인다.
아래 코드와 실행화면을 한 번 보자.

 

(GIF클릭) os.listdir() 실행화면

 

커맨드라인을 열고 실행해본 코드 중 살펴볼 코드는 아래와 같다.

import os

os.listdir()  # 현재 폴더 내의 파일&폴더 리스트 출력

for i in os.listdir():  # 현재 폴더 내의 파일 또는 폴더 중에
    if i.endswith(".hwp"):  # 확장자가 hwp이면?
        print(i)  # 파일명을 출력해라.

os.listdir()의 실행결과는 대략 아래와 같았다.

 

os.listdir() 실행결과

 

이 코드의 7번째 라인인 "print(i)" 대신 우리가 원하는 코드인
"해당파일을 열어 찾아바꾸기 실행 및 저장 후 닫기"를 삽입하면
이번 포스팅, "영수증 자동화"도 마무리될 것이다.
전체 코드의 대략의 뼈대는 아래와 같다.

import os
import win32com.client as win32

BASE_DIR = os.getcwd()  # BASE_DIR 변수에 현재 폴더의 경로 저장

hwp = win32.gencache.EnsureDispatch("HWPFrame.HwpObject")  # 한/글 프로그램 실행
hwp.RegisterModule("FilePathCheckDLL", "FilePathCheckerModule")  # 보안승인모듈 활성화

hwp_list = [i for i in os.listdir() if i.endswith(".hwp")]  # 현재 폴더에서 HWP만 골라 리스트 생성
for i in hwp_list:  # hwp_list 안에 있는 원소(파일명)를 하나씩 꺼내서,
    # 2.1 파일 열기
    # 2.2 찾아바꾸기
    # 2.3 다른 이름으로 저장
    # 2.4 파일 닫기
# 2.5 작업완료 팝업 띄워주기
# 2.6 한/글 종료

 

위 코드 하단의 주석 2.1~2.6을 "목차"라고 생각하면, 현재 진행과정을 파악하기 쉬울 것이다.

 


 

2.1 파일 열기

win32로 한/글을 열고 HWP파일을 불러올 때
(꼼꼼한 독자들은 이미 발견했겠지만) 꼭 유의할 점이 하나 있다.
그것은 바로, Open 메서드 안에 경로를 생략하고 "파일명"만 입력하면 오류가 발생한다는 것이다.
현재 콘솔이 정확히 파일 위치에 있더라도 말이다.
그래서 아래아한글을 열 때, 경로와 파일을 결합하기 위한 os.path.join이나 os.getcwd()를 사용하게 된다.
그렇게 2.1의 코드를 완성해보면 아래와 같다.

import os
import win32com.client as win32

BASE_DIR = os.getcwd()  # BASE_DIR 변수에 현재 폴더의 경로 저장

hwp = win32.gencache.EnsureDispatch("HWPFrame.HwpObject")  # 한/글 프로그램 실행
hwp.RegisterModule("FilePathCheckDLL", "FilePathCheckerModule")  # 보안승인모듈 활성화

hwp_list = [i for i in os.listdir() if i.endswith(".hwp")]  # 현재 폴더에서 HWP만 골라 리스트 생성
for i in hwp_list:  # hwp_list 안에 있는 원소(파일명)를 하나씩 꺼내서,
    hwp.Open(os.path.join(BASE_DIR, i))  # 2.1 파일 열기
    # 2.2 찾아바꾸기
    # 2.3 다른 이름으로 저장
    # 2.4 파일 닫기
# 2.5 작업완료 팝업 띄워주기
# 2.6 한/글 종료

 


 

2.2 찾아바꾸기

바꿀 곳은 아래의 녹색 네모 세 군데.

 

제목 수정, "영수", 입금계좌 삭제 등 세 가지만 바꾸면 된다.

 

가장 간단한 방법은 역시 "찾아바꾸기"이다. 그래서 이번 포스팅에서는 찾아바꾸기를 하지 않고
몇 가지 흥미로운 방법을 써 보려고 한다.

 


 

2.2.1 라인삭제 후 문구 삽입(청  구  서 → 영  수  증)

단축메서드를 사용하여 문서 시작점으로 가서,
기존 라인의 내용("청  구  서")만 전부 삭제한 후, 
"영  수  증" 문자열을 삽입한다.

코드는 아래와 같다.

hwp.Run("MoveDocBegin")  # 문서 시작으로 이동(시작위치에 캐럿이 있으므로 생략가능;)
hwp.Run("DeleteLineEnd")  # 캐럿 뒤의 내용 삭제(줄바꿈은 유지)
hwp.HAction.GetDefault("InsertText", hwp.HParameterSet.HInsertText.HSet)  # 문자열 삽입
hwp.HParameterSet.HInsertText.Text = "영  수  증"
hwp.HAction.Execute("InsertText", hwp.HParameterSet.HInsertText.HSet)

실행결과는 아래와 같다.

 

(GIF클릭) 제목 변경

 

위의 코드는 문서 시작점으로 이동해서, 해당라인(제목)을 삭제하되 줄바꿈은 유지한 채로
"영  수  증" 문자열을 삽입하는 코드이다. 설명하기 민망할 정도로 코드가 직관적이고 짧아졌다.
단축메서드 덕분이다.

 


 

2.2.2 좌표값을 찍어 이동/수정하기 (청구합니다. → 영수합니다.)

아래아한글에도 "좌표"란 게 있다. 직교좌표계에서 (x, y, z)로 위치를 표현하듯이,
아래아한글 문서에서도 (리스트list, 문단para, 글자순서pos)로 캐럿의 위치를 표시한다.
그리고 좌표를 찍어 이동할 수도 있다.
아래에 hwp문서 좌표의 세 축인 List, Para, pos에 대한 설명을 간략히 남겨둔다.
(추후에 기본편에서 한 번 더 자세히 다루고 싶다.)


리스트(list)는 본문과 메모, 표나 글상자 등, 하물며 표의 캡션까지
별도의 문자열 삽입이 가능한 모든 구간별로 매겨놓은 일종의 "순서" 같은 개념이다.
본문은 항상 0이며, 1은 항상 "메모"로 예약되어 있다. 문서에 메모가 있든 없든 말이다.
예를 들어 3x3 사이즈의 표가 하나 삽입되어 있으면 첫 번째 표 A1셀의 리스트값이 2가 되며, 
표 우측하단의 C3셀의 리스트값은 10이 된다.
또한, list의 인덱스는 생성된 순서가 아닌 조판 위치의 순서이다.
위의 표 앞에 3x3표를 하나 더 생성하면, 기존 표 A1셀의 리스트값이 2에서 11로 바뀌게 된다.

문단(para)은 "줄바꿈(엔터)"을 기준으로 나뉘는 paragraph의 순서를 의미한다.
첫 번째 문단은 0이며, 줄바꿈으로 구분된 두 번째 문단의 para는 1이 된다.
내용이 하나도 없는 빈 줄바꿈이 계속돼도 para의 값이 1씩 동일하게 늘어난다.

글자순서(pos)는 현재 캐럿이 해당 para에서 몇 번째 글자 앞에 있는지를 의미하며(012345?6)
(문서 시작점의 Para를 제외하고는) 모든 문단에서 pos는 0부터 시작한다.
단, 첫 번째 Para의 pos는 0이 아닌 16부터 시작된다. 아마도 숨겨진 조판부호 같은 게 있는건지?
또한 누름틀, 메모 등 일부 조판부호는 8칸(pos값 기준)을 차지한다.
좌표와 관련된 대표적인 메서드로는 현재 좌표를 구하는 hwp.GetPos()와,
입력한 좌표로 캐럿을 이동하는 hwp.SetPos(List, Para, pos)가 있다.


간략히 좌표에 대한 설명을 마쳤고, 이제 본문을 고쳐보자.
필자는 캐럿을 어디로 옮기고 싶냐면,

 

"청구" 바로 뒤 노란 부분으로 캐럿을 옮기고 싶다.

 

캐럿을 "청구"뒤의 노란 부분에 놓고 hwp.GetPos()를 실행해봤더니, (0, 6, 53)을 리턴했다.
이 좌표를 가지고 아래처럼 코드를 작성해보았다.

hwp.SetPos(0, 6, 53)  # 해당 좌표로 이동
hwp.Run("DeleteWordBack")  # Ctrl-Backspace
hwp.HAction.GetDefault("InsertText", hwp.HParameterSet.HInsertText.HSet)  # 문자열 삽입
hwp.HParameterSet.HInsertText.Text = "영수"
hwp.HAction.Execute("InsertText", hwp.HParameterSet.HInsertText.HSet)

실행결과는 아래와 같다.

 

"청구" 뒤로 가서 Ctrl-Backspace 후, "영수"를 삽입하는 코드를 실행했다.

 

위의 예시처럼, 형태가 바뀌지 않는 문서의 일부를 수정할 때, 누름틀이나 찾아바꾸기 외에도
SetPos를 이용해서 좌표값으로 이동하는 방법도 이처럼 가끔 쓸모가 있다. (단, 좌표값을 정확히 아는 경우 한정이지만;;)

 


 

2.2.3 화살표키 누르듯 옮겨다니기(입금계좌정보 삭제)

마지막으로 소개하는 캐럿 이동방법이다. 여러모로 직관적이고(가끔 스크래치나 엔트리 느낌이 나기도;)
고민할 필요가 거의 없는 "가장 아날로그에 가깝지만 가장 쉬운 방법"이다.
현재 캐럿은 여전히 "영수" 뒤에 있으며, "※입금계좌" 라인까지는 열네 번 아래방향키를 눌러야 한다.
그 다음엔 라인을 하나씩 지운다. 직접 실행한 과정은 아래와 같다.

 

파이썬으로 말고, 한/글에서 직접 커맨드를 입력해 일종의 "데모액션"을 해보았다.

 

원하는 결과가 나온 것 같으니, 위 방법대로 코드를 짜보자.

hwp.Run("MoveDown")  # ↓
hwp.Run("MoveDown")  # ↓
hwp.Run("MoveDown")  # ↓
hwp.Run("MoveDown")  # ↓
hwp.Run("MoveDown")  # ↓
hwp.Run("MoveDown")  # ↓
hwp.Run("MoveDown")  # ↓
hwp.Run("MoveDown")  # ↓
hwp.Run("MoveDown")  # ↓
hwp.Run("MoveDown")  # ↓
hwp.Run("MoveDown")  # ↓
hwp.Run("MoveDown")  # ↓
hwp.Run("MoveDown")  # ↓
hwp.Run("MoveDown")  # ↓(아래방향키 14번)
hwp.Run("MoveLineBegin")  # Home
hwp.Run("DeleteLineEnd")  # Alt-Y 한 줄,
hwp.Run("MoveDown")  # ↓
hwp.Run("MoveLineBegin")  # Home
hwp.Run("DeleteLineEnd")  # Alt-Y 두 줄, 
hwp.Run("MoveDown")  # ↓
hwp.Run("MoveLineBegin")  # Home
hwp.Run("DeleteLineEnd")  # Alt-Y 세 줄, 
hwp.Run("MoveDown")  # ↓
hwp.Run("MoveLineBegin")  # Home
hwp.Run("DeleteLineEnd")  # Alt-Y 네 줄 삭제 완료

;;;;반복되는 부분을 for문으로 잘 묶으면?

for i in range(13): hwp.Run("MoveDown")
for i in range(4):
    hwp.Run("MoveDown")
    hwp.Run("MoveLineBegin")
    hwp.Run("DeleteLineEnd")

실행결과는 아래와 같다.

 

아래로 14줄 내려가서 입금계좌정보 네 줄 삭제 완료.

 

이걸로 세 부분 수정하는 코드 작성을 마쳤다.

2.2.4 수정코드 모아보기

세 군데를 수정하는 코드를 모아보면,

# 2.2.1 "청  구  서" -> "영  수  증"
hwp.Run("MoveDocBegin")
hwp.Run("DeleteLineEnd")
hwp.HAction.GetDefault("InsertText", hwp.HParameterSet.HInsertText.HSet)
hwp.HParameterSet.HInsertText.Text = "영  수  증"
hwp.HAction.Execute("InsertText", hwp.HParameterSet.HInsertText.HSet)

# 2.2.2 "청구합니다." -> "영수합니다."
hwp.SetPos(0, 6, 53)
hwp.Run("DeleteWordBack")
hwp.HAction.GetDefault("InsertText", hwp.HParameterSet.HInsertText.HSet)
hwp.HParameterSet.HInsertText.Text = "영수"
hwp.HAction.Execute("InsertText", hwp.HParameterSet.HInsertText.HSet)

# 2.2.3 입금계좌정보 네 줄 삭제
for i in range(13): hwp.Run("MoveDown")
for i in range(4):
    hwp.Run("MoveDown")
    hwp.Run("MoveLineBegin")
    hwp.Run("DeleteLineEnd")

실행결과는,

 

(GIF클릭) 세 개의 코드를 같이 실행한 모습. 한 번에 수정이 완료된다.

 

 


 

2.3 다른이름으로 저장하기

수정을 마쳤으니 이제 다른 이름으로 저장하자. hwp파일을 다른이름으로 저장할 때는,
hwp.SaveAs(Path=경로명, Format="HWP")를 사용하면 편하다.
그렇다면 경로와 파일명은 어떻게 작성해볼 수 있을까?

청구서 파일명은 이미 위의 hwp_list에서 i라는 인덱스로 가져왔다.
대략 "청구서_마티니네잔(코딩하는회사원들).hwp" 이런 내용이다.
우리는 "청구서"라는 문자열만 "영수증"으로 바꾸면 되므로, 문자열 메서드 replace를 활용해보자.

참고로 PDF로 저장하는 방법은 상당히 다양한데,
개인적으로 가장 간단한 방법은 SaveAs 메서드를 이용하는 것이다.
hwp.SaveAs(Path=경로명.replace(".hwp", ".pdf"), Format="PDF")
이 때 주의할 점은 확장자를 .pdf로 꼭 지정해줘야 한다는 것이다.

코드는 아래와 같다.

import os
import win32com.client as win32

BASE_DIR = os.getcwd()  # BASE_DIR 변수에 현재 폴더의 경로 저장

hwp = win32.gencache.EnsureDispatch("HWPFrame.HwpObject")  # 한/글 프로그램 실행
hwp.RegisterModule("FilePathCheckDLL", "FilePathCheckerModule")  # 보안승인모듈 활성화

hwp_list = [i for i in os.listdir() if i.endswith(".hwp")]  # 현재 폴더에서 HWP만 골라 리스트 생성
for i in hwp_list:  # hwp_list 안에 있는 원소(파일명)를 하나씩 꺼내서,
    # 2.1 파일 열기(완료)
    # 2.2 찾아바꾸기(완료)
    hwp.SaveAs(Path=os.path.join(BASE_DIR, i.replace("청구서", "영수증")), 
               Format="HWP")  # 2.3 다른 이름으로 저장
    # hwp.SaveAs(Path=os.path.join(BASE_DIR, filename.replace("청구서", "영수증").replace(".hwp", ".pdf")), Format="PDF")
    # 2.4 파일 닫기
# 2.5 작업완료 팝업 띄워주기
# 2.6 한/글 종료

문자열.replace() 메서드의 사용법은 보시다시피 굉장히 간단하다.
차례대로 "무엇을", "어떻게" 바꿀지 인수로 넣으면 된다.
또한 .replace() 메서드로 문자열이 리턴되므로, 아래처럼 일종의 method chaining이 가능하다.

"asdfqwer".replace("er", "qw").replace("as", "df").replace("dfqw", "qwdf") == "dfqwdfqw"
# True를 리턴함. 좋은 예는 아닌 듯

 


 

2.4 파일 닫기

마지막으로 파일을 닫을 때는, 주로 hwp.Run("FileClose") 또는 hwp.Clear()를 사용한다.
필자는 hwp.Clear(1)을 자주 사용하는데, 안에 들어가는 option 파라미터로 1을 넣으면
저장한 이후 변경내용이 있더라도 전부 버리고(저장하지 않음) 파일을 닫아주는 간편함 때문이다.

hwp.Clear(option=1)

실행하면 다음과 같이 빈 문서가 된다.

 

Clear를 실행하면 파일이 닫히고 "빈 문서"가 된다.

 

 


 

2.5 작업완료 팝업 띄워주기

100개의 파일 순회를 마쳤는지, 작업을 제대로 마치기는 한 건지 궁금할 수 있다.
그래서, 한/글에서 기본으로 제공하는 메시지박스를 사용하면,
최소한 '코드 마지막까지 실행은 되었구나!' 하고 파악할 수 있다.
코드는 아래와 같다.

msgbox = hwp.XHwpMessageBox
msgbox.string = "작업을 완료하였습니다."
msgbox.DoModal()

실행화면은 아래와 같다.

 

작업을 마치면 메시지박스 팝업을 띄워주자.

 

 


 

2.6 한/글 종료

한/글을 종료하는 코드는 hwp.Quit()이다.
한/글 자동화프로그램을 완성하기 위해 꼭 필요한 라인인데,
일반적으로 시간이 많이 걸리는 작업은 백그라운드에서 자동화로 돌려놓고
우리들은 다른 일을 하곤 하는데,
내부 프로세스를 제대로 종료해 주지 않으면
백그라운드에서 아래아한글이 여러 개씩 열려 있는 경우가 발생한다.

try&except 문으로 프로그램이 제대로 종료되지 않았을 때
무조건 hwp.Quit()을 실행해주는 것도 괜찮은 방법이다.

 

hwp.Quit()

실행화면은 아래와 같다.

 

hwp.Quit()을 실행하면 한/글이 종료된다.

 

 


 

3. 코드 종합하기

이제 위에서 작성한 모든 코드를 하나의 파일로 모아보자.
챕터이름 주석 및 일부 주석은 일부러 지우지 않았으니, 최종코드 리뷰시 참고해 주기 바란다.

"""
한/글을 열고 백 개의 파일을 각각 한 번씩 열어서
일부 내용을 수정하고 다른이름으로 저장하는 작업을 모두 마친 후에
작업완료 팝업을 클릭하면 한/글을 닫는 코드.
"""

import os
import win32com.client as win32


BASE_DIR = os.getcwd()  # BASE_DIR 변수에 현재 폴더의 경로 저장

hwp = win32.gencache.EnsureDispatch("HWPFrame.HwpObject")  # 한/글 프로그램 실행
hwp.RegisterModule("FilePathCheckDLL", "FilePathCheckerModule")  # 보안승인모듈 활성화
hwp.XHwpWindows.Item(0).Visible = False

hwp_list = [i for i in os.listdir() if i.endswith(".hwp")]  # 현재 폴더에서 HWP만 골라 리스트 생성
for filename in hwp_list:  # hwp_list 안에 있는 원소(파일명)를 하나씩 꺼내서,
    hwp.Open(os.path.join(BASE_DIR, filename))  # 2.1 파일 열기

    # 2.2 찾아바꾸기

    # 2.2.1 제목 수정
    hwp.Run("MoveDocBegin")  # 문서 시작으로 이동(시작위치에 캐럿이 있으므로 생략가능;)
    hwp.Run("DeleteLineEnd")  # 캐럿 뒤의 내용 삭제(줄바꿈은 유지)
    hwp.HAction.GetDefault("InsertText", hwp.HParameterSet.HInsertText.HSet)  # 문자열 삽입
    hwp.HParameterSet.HInsertText.Text = "영  수  증"
    hwp.HAction.Execute("InsertText", hwp.HParameterSet.HInsertText.HSet)

    # 2.2.2 "영수합니다" 수정
    hwp.SetPos(0, 6, 53)  # 해당 좌표로 이동
    hwp.Run("DeleteWordBack")  # Ctrl-Backspace
    hwp.HAction.GetDefault("InsertText", hwp.HParameterSet.HInsertText.HSet)  # 문자열 삽입
    hwp.HParameterSet.HInsertText.Text = "영수"
    hwp.HAction.Execute("InsertText", hwp.HParameterSet.HInsertText.HSet)

    # 2.2.3 계좌정보 삭제
    for i in range(13): hwp.Run("MoveDown")
    for i in range(4):
        hwp.Run("MoveDown")
        hwp.Run("MoveLineBegin")
        hwp.Run("DeleteLineEnd")

    # 2.3 다른 이름으로 저장
    hwp.SaveAs(Path=os.path.join(BASE_DIR, filename.replace("청구서", "영수증")), Format="HWP")
    # hwp.SaveAs(Path=os.path.join(BASE_DIR, filename.replace("청구서", "영수증").replace(".hwp", ".pdf")), Format="PDF")

    # 2.4 파일 닫기
    hwp.Clear(option=1)

# 2.5 작업완료 팝업 띄워주기
msgbox = hwp.XHwpMessageBox
msgbox.string = "작업을 완료하였습니다."
msgbox.DoModal()


# 2.6 한/글 종료
hwp.Quit()

코드 실행화면은 아래와 같다.

 

백그라운드에서 실행하고, 완료되면 한/글에서 팝업을 띄워준다.

 

 


 

4. 코드 컴파일하기(pyinstaller)

위 코드를 pyinstaller로 컴파일하는 과정은 지난 포스팅과 동일하다.
이번에는 파이참 터미널(Alt-F12)을 열어서 컴파일하는 과정을 GIF로 보여드린다.
이번에는 실행파일 아이콘으로 회사원코딩 썸네일인 face.ico를 사용하겠다. (코드파일과 같은 경로에 복사)

위 코드가 들어있는 폴더로 이동한 후 아래 명령어를 실행한다.

pyinstaller -F -w 코드.py -i 아이콘.ico

pyinstaller 실행화면은 아래와 같다. (컴파일된 실행파일을 첨부하였다. 한/글이 설치되어 있어야 실행된다.)

 

파이참에서 터미널을 열어서 pyinstaller 실행
5_modify_100ea.zip
9.88MB

 

청구서 100개가 들어있는 폴더에 이 파일을 옮겨넣고 실행해보자.

 

실행 및 완료화면... 아무리 백그라운드 작업이라지만 너무 황량하긴 하다.. 기분 탓인가?

 

이걸로 이번 포스팅을 마친다.
부디 많은 분들에게 도움이 되었으면 좋겠다.

행복한 하루 되시길~

이번 포스팅 셀프후기

(물론 여러 아쉬운 점이 있지만) 가장 아쉬운 점은 역시, 백그라운드작업이 썰렁하다는 것이다. 여러시간 걸리는 작업인데 불안정한 백그라운드 프로세스를 향해 "부디 잘 되고 있기를~" 하고 기도하는 건 정말 안타까운 일이다. 메인윈도우와 프로그레스바 정도의 GUI를 넣는 게 사치는 아니니까, 조만간 심플한 GUI에 대한 강좌도 정리해서 올리면 좋겠다.

 

 

 


donaricano-btn

댓글