소개
안녕하세요?
일상의코딩 블로그를 운영중인 일코입니다.
업무자동화와 관련한 콘텐츠 제작 및 강의 등을 하고 있습니다.
강의
수강평
- 직장인에게 꼭 필요한 파이썬-아래아한글 자동화 레시피
- 직장인에게 꼭 필요한 파이썬-아래아한글 자동화 레시피
- 직장인에게 꼭 필요한 파이썬-아래아한글 자동화 레시피
- 직장인에게 꼭 필요한 파이썬-아래아한글 자동화 레시피
게시글
질문&답변
pyhwpx의 set_table_width함수가 파일에 따라 안될때가 있습니다..
상민님 안녕하세요?저도 이것저것 테스트를 해봤는데ㅜ문제가 되는 경우를 아직 찾지 못했습니다.ㅜ대신 실행이 안 되는 이유를 몇 가지 짐작해보면, ① 커서위치가 표보다 우측이나 아래에 있으면 실행이 되지 않습니다.내부적으로 hwp.SelectCtrlFront() 메서드를 사용해서 표에 접근하기 때문입니다. ② 커서와 표 사이에 다른 컨트롤이 있어도 실행되지 않습니다.위와 동일한 이유로, hwp.SelectCtrlFront()는 우측이나 아래의 가장 가까운 컨트롤을 선택하기 때문입니다. ③ 메서드 내부에서 표 부분만 XML 추출을 하는데요..만약 표가 XML로 딱 잘려 나올 수 있는 모양이 아니라면? 오류가 날 수도 있을 것 같습니다. (추측ㅜ) 그 외에는... 아무리 생각해봐도 이유를 잘 모르겠습니다;;;코드가 제법 긴 편이어서 어딘가에서 오류가 날 수는 있겠다고 생각합니다. 코드 전체는 대략 아래와 같습니다. 주석을 열심히 달아보았습니다.def set_table_width(self, width: int = 0, as_: Literal["mm", "hwpunit", "hu"] = "mm"): """ 표 전체의 너비를 원래 열들의 비율을 유지하면서 조정하는 메서드. 내부적으로 xml 파싱을 사용하는 방식으로 변경. :param width: 너비(단위는 기본 mm이며, hwpunit으로 변경 가능) :param as_: 단위("mm" or "hwpunit") :return: 성공시 True """ if not width: # 만약 값을 입력하지 않고 `hwp.set_table_width()`라고만 실행하면? sec_def = self.hwp.HParameterSet.HSecDef # 구역정보를 받아서 self.hwp.HAction.GetDefault("PageSetup", sec_def.HSet) width = ( sec_def.PageDef.PaperWidth - sec_def.PageDef.LeftMargin - sec_def.PageDef.RightMargin - sec_def.PageDef.GutterLen - self.get_table_outside_margin_left(as_="hwpunit") - self.get_table_outside_margin_right( as_="hwpunit")) # 표의 목표너비를 용지영역과 동일하게 설정 elif as_ == "mm": # 메서드를 실행할 때 정수 값을 입력했다면 width = self.mili_to_hwp_unit(width) # 해당 정수값으로 표 목표너비를 설정 ratio = width / self.get_table_width(as_="hwpunit") # 현재 표의 너비와 목표너비의 비율 계산 cur_pos = self.get_pos() # 현재 위치 저장해 두고(메서드실행 끝나면 돌아가기 위해서) self.SelectCtrlFront() # 전방(문서 끝 방향)으로 컨트롤을 찾아감. ctrl = self.CurSelectedCtrl # 현재 선택한 컨트롤을 t = self.GetTextFile("HWPML2X", "saveblock") # XML 포맷 문서데이터로 저장 root = ET.fromstring(t) # 저장한 데이터 파싱 시작 table = root.find('.//TABLE') # 표를 찾아서 if table is not None: # 표를 찾았으면 for cell in table.findall('.//CELL'): # 셀을 찾아서 width = cell.get('Width') # 셀의 너비를 추출한 다음, if width: # (셀의 너비가 0이 아니라면) cell.set('Width', str(int(width) * ratio)) # 위에서 저장한 "비율"을 곱함. t = ET.tostring(root, encoding='UTF-16').decode('utf-16') # 변경한 데이터 저장. cur_view_state = self.ViewProperties.Item("OptionFlag") # 조판부호 보기상태 현재값 저장(메서드 끝나고 원상복구 예정) if cur_view_state not in (2, 6): # 조판부호가 보이는 상태가 아니면 prop = self.ViewProperties prop.SetItem("OptionFlag", 6) # 조판부호 보이게 설정변경(모든 경우에 표를 제대로 선택하기 위함) self.ViewProperties = prop self.move_to_ctrl(ctrl) # 처음 선택했던 표(조판부호 앞)으로 이동 self.MoveSelRight() # 표컨트롤 선택 self.SetTextFile(t, format="HWPML2X", option="insertfile") # 표내용을 XML변경데이터로 덮어씌움 prop = self.ViewProperties # 조판부호 보기상태 되돌림 prop.SetItem("OptionFlag", cur_view_state) self.ViewProperties = prop self.set_pos(*cur_pos) # 처음 커서위치로 되돌아감 혹시 괜찮으시다면, 오류가 나는 문서를 공유해주시면 감사하겠습니다.set_table_width 메서드를 업데이트하는데 큰 도움이 될 것 같습니다.제 메일주소는 martinii.fun@지메일입니다. 행복한 하루 되세요!!!
- 1
- 2
- 24
질문&답변
아래아한글 여러 파일 병합 코드의 기능 개선 문의
주현님 안녕하세요?hwp.insert_file 메서드는 제가 임의로 추가하긴 했지만,원래 소스는 win32의 hwp.InsertFile입니다.파라미터는 keep_section=1, keep_charshape=1, keep_parashape=1, keep_style=1, move_doc_end=False 로 이미 기본값이 부여되어 있어, 체크박스 네 개를 다 켠 것과 동일한 상태입니다. pyhwpx를 사용하시면서 win32의 기능을 그대로 사용하시려면,hwp 객체 안의 hwp 객체를 사용하시면 됩니다. (win32의 hwp도 로딩이 되어 있습니다.)예를 들면 아래처럼,hwp.hwp.InsertFile(FilePath, KeepSection=1, KeepCharshape=1, KeepParashape=1, KeepStyle=1)이라고 실행하시면 됩니다.제 PC에서 재현해보려고 문서 몇 개를 테스트해봤는데,스타일과 용지, 글자모양, 문단모양을 전부 다르게 잡고 병합해봐도정상 작동합니다...ㅜㅜㅜ 혹시 아래 링크의 압축파일을 다운받아 압축을 푸신 후 코드를 한 번 적용해보시겠어요?https://drive.google.com/file/d/1a_QvC85TLabHGbT5u11__YanOSY7Axp9/view?usp=sharing파일들은 각각 아래와 같은 서식입니다.문서01.hwp(사진)문서02.hwp(사진) 문서03.hwp(사진) 스타일, 글자모양, 문단모양, 페이지여백을 모두 다르게 적용한 상태입니다.아래 코드를 실행하면,from pyhwpx import Hwp hwp = Hwp() import os target_dir = r"C:\Users\Administrator\Desktop\새 폴더" os.chdir(target_dir) file_list = [i for i in os.listdir() if i.endswith(".hwp")] hwp.open(file_list[0]) hwp.MoveDocEnd() for i in file_list[1:]: hwp.insert_file(i, move_doc_end=True)제대로 병합이 되는 듯 합니다... (제 PC에서만 그런가 의심은 됩니다ㅜ)(사진)기존 소스코드 중에 BreakPage 라인은 제거하였습니다. keep_section이 켜져 있어서 자동으로 자르고 다음페이지로 넘어가니까요ㅜ 추측해보건대,메서드의 문제라기보다는 문서 때문에 문제가 발생하는 게 아닌가 싶기도 합니다.혹시 괜찮으시다면 병합이 안 되는 예시파일을 샘플로 전송해주시면제가 직접 테스트해봐도 될까요? 메일주소는 martinii.fun@지메일입니다.만약 한글2014VP 이상의 버전을 사용 중이시면CopyPage 및 PastePage를 반복해서 사용하시는 방법도 추천드립니다.페이지별로 옮기는 메서드라서 InsertFile에 비해 다소 느리기는 하지만ㅠ큰 문제가 발생했던 적은 없어서, 자주 사용을 했습니다. 회신 기다리겠습니다.감사합니다.
- 1
- 1
- 18
질문&답변
날짜 뒤에 요일 붙이기가 작동이 안되네요
제가 답변이 늦었습니다...예전에는 hwp.FindCtrl()을 실행하면, 멀리 있는 컨트롤도 선택했던 것 같은데,지금은 조판부호에 붙어있지 않으면 선택하는 않는 방식으로 업데이트가 되었나봅니다^^;(기억이 가물가물하네요...ㅜㅜㅜ 아이고)그래도 예전 hwp.FindCtrl()의 기능을(거의 동일하게) hwp.HAction.Run("SelectCtrlFront")로 사용할 수 있는 듯 합니다.한컴 개발자 포럼의 개발자 분들도 대부분의 답변에서 FindCtrl보다는 SelectCtrlFront를 추천해주고 계신데요.FindCtrl은 커서 좌측에 컨트롤이 있으면 그걸 선택해버리는 반면SelectCtrlFront는 무조건 우측이나 아래로, 그리고 SelectCtrlPrev는 무조건 좌측이나 위쪽 방향으로컨트롤을 찾아가기 때문에 FindCtrl에 비해 오류발생 가능성이 현저히 적어 보입니다.(사진) 그냥 마치기는 좀 죄송해서,단순한 예제이긴 하지만 pyhwpx 모듈과 정규식으로도 동일 예제를 짜본 코드도 보여드려봅니다.(사진)import datetime as dt from pyhwpx import Hwp def get_weekday(text): week_list = ["월", "화", "수", "목", "금", "토", "일"] month, day = [int(i) for i in text.split(".")[:2]] week_num = dt.date(2022, month, day).weekday() week_day = week_list[week_num] return f"({week_day})" hwp = Hwp() hwp.Open(r"C:\Users\Administrator\Desktop\실작업공정표.hwpx") hwp.MoveDocBegin() while hwp.find(r"\d+\.\d+\.", regex=True): date = hwp.get_selected_text() hwp.insert_text(date + get_weekday(date)) 항상 자답 남겨주셔서 감사하고 죄송하고 그렇습니다.행복한 하루 되세요^^
- 1
- 2
- 24
질문&답변
한 파일의 변경사항을 다수의 파일에 동일하게 반영하고 싶습니다.
금열님 안녕하세요?ㅎ기존에 파일을 작성하실 때 누름틀이나 필드 같은 장치를 사용하지 않으셨다면,일일이 찾아가서 문구를 수정(또는 붙여넣기)하는 코드를 짜는 것이 최선의 방법 같습니다. (더 좋은 방법이 있을 수도 있지만)제가 보통 접근하는 방법은 아래와 같습니다. 1. 바뀔 영역의 패턴을 정의하기저라면, "제3조(개인정보 보호 원칙)~" 으로 시작하면서"제4조(정보주체의 권리)"를 포함하지 않는 범위까지 선택하겠습니다. 2. 기본 파일의 변경부분을 Ctrl-C일회용 코드인데 굳이 GetPos 같은 메서드를 남발하지 않고,기본파일의 3조 부분을 선택 후 Ctrl-C로 복사하겠습니다.단, 주의할 점은 3조 끝부분까지만 선택하는지,4조 시작부분까지 선택하는지가 중요합니다.(저는 4조 첫글자 앞까지 선택하는 것으로 하겠습니다.) 3. for문으로 바꿀파일 모두 수정① os.listdir()로 바꿀 파일을 순차적으로 열면서② "제3조" 시작부분을 찾고 나서,③ 선택범위를 한줄씩 넓혀 가다가④ "제4조(정보주체의 권리)" 문자열이 감지되면 해당 문단 윗문단까지만 선택하고⑤ 붙여넣기 후 저장 위 과정을 코드로 작성해보면 아래와 같습니다.아래 영상과 동일한 실습을 위한 첨부파일은 여기를 클릭하여 다운로드하실 수 있습니다.(src 폴더의 문서들이 변경 전, dst 폴더의 문서들이 코드 적용 후 완성본입니다.)(사진) 실행결과 일부를 보여드리면,(사진) 실행한 코드는 아래와 같습니다.from tkinter.filedialog import askopenfilenames from pyhwpx import Hwp hwp = Hwp() hwp.open("./기본파일.hwpx") # 3조 전체 직접 복사(Ctrl-C)# 직접 복사한 후 아래 코드 이어서 실행 file_list = askopenfilenames() # 바꿀파일 전부 선택 for f in file_list: hwp.open(f) # 하나씩 열어서 hwp.MoveDocBegin() # 문서 시작으로 이동한 후 hwp.find("제3조(개인정보 보호 원칙)") # 3조를 찾고, while "제4조(정보주체의 권리)" not in hwp.get_selected_text(): # 4조가 선택될 때까지 hwp.MoveSelNextParaBegin() # 한문단씩 추가로 선택하다가 hwp.MoveSelPrevParaBegin() # (4조도 포함되었으면) 해당 문단은 선택해제하고 hwp.Paste() # 붙여넣기로 덮어씌우기 hwp.save() # 저장하기코드가 짧기도 하고 크게 복잡하지는 않으므로주석 정도만 읽어보셔도 프로세스는 이해하실 것으로 생각됩니다. 혹여, 대소문자가 뒤죽박죽인 것처럼 느껴지실 수 있습니다ㅜㅜ파스칼케이스의 hwp.Paste나 hwp.MoveSel~ 같은 애들은Run 액션 기반 메서드라서 기존 액션명을 유지하느라 대문자가 들어 있고,hwp.open이나 hwp.save는, 일반 메서드이기 때문에 소문자로 명명했습니다.hwp.find도 제가 임의로 추가한 메서드라서 소문자입니다..처음엔 자주 헷갈릴 수 있으니 가급적 자동완성을 많이 쓰시는 걸 추천드립니다. 도움이 되었길 바랍니다.행복한 하루 되세요!!
- 1
- 2
- 21
질문&답변
여러줄의 블록설정한 text를 파이썬 변수에 할당하기
앗, GetText는 문단별로 끊어서 리턴을 합니다.while 등 반복문이랑 같이 쓰셔야 해요. 제가 임의로 만들어 사용하고 있는 get_selected_text 함수를 보여드리겠습니다.( pyhwpx 모듈의 get_selected_text 메서드 일부입니다.)(사진) def get_selected_text(as_="str"): """ 한/글 문서 선택 구간의 텍스트를 리턴하는 메서드. as_="list"로 설정하면 문단별로 리스트에 담아 리턴한다. 표 안에서 선택한 셀의 문자열들을 가져오는 경우에도 as_="list" 옵션을 주는 것이 좋다. """ if hwp.SelectionMode == 0: if hwp.KeyIndicator()[-1].startswith("("): hwp.HAction.Run("TableCellBlock") else: hwp.HAction.Run("Select") hwp.HAction.Run("Select") if not hwp.InitScan(Range=0xff): return "" if as_ == "list": result = [] else: result = "" state = 2 while state not in [0, 1]: state, text = hwp.GetText() if as_ == "list": result.append(text) else: result += text hwp.ReleaseScan() return result if type(result) == str else result[:-1]
- 1
- 1
- 15
질문&답변
엑셀문서 값을 필드에 입력하기 관련 문의
한글2014에서는 실행이 안 되는군요...(아무리 2014라도 실행은 될 듯 한데 이상하네요ㅜ)
- 1
- 3
- 25
질문&답변
블록 내에서 표 갯수 확인하기, 표 찾기
병현님 안녕하세요?(메일은 오늘에야 회신을 드렸습니다^^;) 1. 블록 내 표의 갯수두 가지 방법이 있습니다.적절한 방법을 선택하시면 되겠습니다.1-1. 선택부분을 블록저장하고 Table 갯수 세기hwp.GetTextFile 메서드를 사용하면 간편합니다.(사진)hwp.GetTextFile(format="HWPML2X", option="saveblock:true").count("대신 표 안의 표도 별개로 카운트한다는 점 유의하시기 바랍니다. 1-2. ctrl의 pos를 일일이 확인하는 방법hwp.GetSelectedPos() 메서드는 블록의 범위를 튜플로 반환합니다.(블록상태이면 True, 시작리스트, 시작문단, 시작글자, 끝리스트, 끝문단, 끝글자)(사진)hwp.GetSelectedPos()로 탐색할 범위를 찾았으니 표의 갯수는 표 ctrl을 순회하면서 해당하는 갯수만 찾으면 되겠습니다.(사진)코드는 아래와 같습니다. (표 안의 표는 카운트하지 않습니다.)# 블록선택 상태에서 slist, spara, spos, elist, epara, epos = hwp.GetSelectedPos()[1:] count = 0 ctrl = hwp.HeadCtrl.Next.Next while ctrl: if ctrl.UserDesc == "표": ctrl_pos = ctrl.GetAnchorPos(0) if spara 2. 표의 갯수를 확인하고 진입하기위의 두 번째 방법은 사실 ctrl에 접근해서 갯수를 세는 방법이므로위 코드를 조금만 수정하면 표 안에 접근하는 코드를 구현할 수 있습니다.(사진) 블록영역(본문) 내 각 표의 A1셀에 진입한 후, "Hello!\r\n" 문자열을 삽입하는 예시코드입니다.# 블록선택 상태에서 slist, spara, spos, elist, epara, epos = hwp.GetSelectedPos()[1:] count = 0 ctrl = hwp.HeadCtrl.Next.Next while ctrl: if ctrl.UserDesc == "표": ctrl_pos = ctrl.GetAnchorPos(0) if spara 도움이 되었길 바랍니다.행복한 하루 되세요!!!
- 1
- 1
- 25
질문&답변
스타일 개요 수준 추가
개요번호, 즉 개요수준만 변경하는 방법은 아래와 같습니다. (스타일을 변경하지 않습니다.)(사진)def 개요수준변경(level:int): """ 현재 문단의 개요수준을 변경하는 함수. 0~9 입력가능 """ pset = hwp.HParameterSet.HParaShape hwp.HAction.GetDefault("ParagraphShape", pset.HSet) pset.Level = level return hwp.HAction.Execute("ParagraphShape", pset.HSet) 위처럼 스크립트매크로를 함수로 변환하여 사용하는 방법도 있고,아래 방식의 코드로도 개요수준 변경이 가능합니다.(사진)level = 5 # 0~9 prop = hwp.ParaShape prop.SetItem("Level", level) hwp.ParaShape = prop(hwp 내부에서 작동하는 방식은 완전히 동일합니다.)도움이 되었길 바랍니다ㅎ행복한 하루 되세요! [참고] 위 "개요수준변경" 함수 코드를 찾는 방법사용자의 동작 거의 대부분은 스크립트매크로로 구현 가능합니다.(다른이름으로 저장, 커서 위치 찾기, 필드에 값 넣기처럼, 메서드로 제공되는 기능을 제외하고는요.)위의 "개요수준변경" 함수도 방금 스크립트매크로 녹화를 통해 빠르게 만들었습니다.그 과정을 한 번 보여드리겠습니다. 1. 스크립트매크로 녹화부터(사진)위 과정을 통해 코드를 추출합니다.추출한 스크립트매크로 코드는 아래와 같습니다.function OnScriptMacro_중국어1성() { HAction.GetDefault("ParagraphShape", HParameterSet.HParaShape.HSet); with (HParameterSet.HParaShape) { Level = 4; } HAction.Execute("ParagraphShape", HParameterSet.HParaShape.HSet); }이 코드는 파이썬 문법과 다른 jscript로 작성된 상태이므로, 파이썬 문법으로 변경해줘야 합니다. 2. 스크립트매크로를 파이썬 문법으로 변경(사진)조금 번거롭지만, 위 변환과정을 통해서 완성된 코드는 아래와 같습니다.hwp.HAction.GetDefault("ParagraphShape", hwp.HParameterSet.HParaShape.HSet) hwp.HParameterSet.HParaShape.Level = 4 hwp.HAction.Execute("ParagraphShape", hwp.HParameterSet.HParaShape.HSet)코드 재활용 및 가독성 등을 위해 함수로 만드는 것이 좋겠습니다. 3. 위 코드를 함수로 만들기① 함수 이름은 "개요수준변경"② level을 매개변수(int, 0~9)로 받아야 함③ (선택) 마지막 Execute 라인을 return 에 넣어줌 : 성공시 True, 실패시 False를 리턴함④ (선택) 중복되는 hwp.HParameterSet.HParaShape을 pset이라는 변수로 지정위 사항들을 반영하여 완성한 함수는 아래와 같습니다.def 개요수준변경(level): pset = hwp.HParameterSet.HParaShape hwp.HAction.GetDefault("ParagraphShape", pset.HSet) pset.Level = level return hwp.HAction.Execute("ParagraphShape", pset.HSet)이렇게 함수를 만드는 과정까지 완료하였습니다. 4. 마치며아래아한글의 모든 기능을 미리 이렇게 파이썬 함수로 만들어놓으면 편할 것 같지만,그러기 위해서는 코드가 2만 줄 이상 될 것으로 추측해봅니다.미리 다 만들어놓는 것은 굉장히 힘듭니다. (올해 중순까지 1만 줄 정도 작성해보다가 포기한 상태거든요ㅜ)그래서 위 과정처럼,그때그때 필요한 기능을 스크립트매크로를 통해 파이썬코드로 만드는 작업에 익숙해지시면자동화가 훨씬 수월해질 것입니다.또 궁금하신 부분 있으면 질문 남겨주세요.
- 0
- 2
- 19
질문&답변
한글 파일 팝업 안뜨게 하기 에서 계속 false 가 리턴 되네요
아, 이름이 다르다고 댓글을 달던 중이었는데 제가 여러 발 늦었네요ㅜㅜㅜ
- 0
- 3
- 20
질문&답변
pyhwpx 공식문서?
병현님 안녕하세요ㅎ공식문서 같은 걸 만들어야겠다 생각만 하고 아직 실행에 옮기지 못하고 있습니다ㅜ(아직 한창 개발중이라, 문서화에 신경을 쓰지 못했습니다..ㅜ)그나마 간단한 사용예시 위주로위키독스에 정리를 해보고 있었는데, 참고해주시기 바랍니다.네이버 블로그나 티스토리 블로그에도 조금씩 업데이트를 하다가 잠시 멈춰둔 상태입니다ㅜ 내년 1월 초까지 매듭지어야 하는 급한 일이 두 건 있어서ㅜ1월 이후로 상세하게 문서작업 착수 예정입니다. 관심 가져 주셔서 감사합니다. 문서는 없지만, 메서드 등을 확인하는 방법VSCode나 파이참에서 Hwp 클래스에 대고 "Go to declaration" 같은 기능을 실행하시면pyhwpx 모듈의 소스코드를 열어줍니다. pandas나 openpyxl처럼 구조가 복잡하거나 파일이 분할되어 있지 않고딱 한 개의 파이썬 파일(pyhwpx.py)에 단 한 개의 클래스(Hwp)로만 코드를 짜고 있어코드를 살펴보시기는 다소 수월할 듯 싶습니다. 기본적인 사용법은 win32의 HwpObject와 동일한데,경미한 개선점이 몇 가지 있습니다. ① 기존에 hwp.HAction.Run("액션아이디") 방식으로 실행하던 Run메서드를 hwp.액션아이디() 방식으로 실행하실 수 있습니다. -> Run 메서드의 자동완성이 됩니다. ② 거의 모든 hwp.HAction.Execute가 실행 성공시 True, 실패시 False를 리턴합니다. ③ 기존에 자주 사용하는 메서드의 사용성을 개선하였습니다.예를 들어, hwp.put_field_text()는 필드값으로 파이썬 자료형(dict나 list, DataFrame)이나 엑셀파일 등을 삽입할 수 있고,hwp.insert_picture()는 절대경로를 쓰지 않아도 되고, 셀 안에서 이미지 삽입시 (기존과 다르게) 너비를 맞춰 삽입해줍니다. ④ 자주 쓰이는 액션 패턴을 커스텀 메서드로 만들어 두었습니다.예를 들어 hwp.create_table()이나, hwp.get_table_width() 같은 메서드가 추가되어 있습니다. 부족하나마 네이버블로그를 참고하시면 도움이 되지 않을까 싶습니다.행복한 하루 되세요^^
- 1
- 2
- 25