본문 바로가기
아래아한글 자동화/pyhwpx 사용법

[pyhwpx] 개발일지, get_into_nth_table 메서드에 대한 고찰?

by 일코 2024. 1. 14.

한/글 문서에서 n번째 표 안에 접근하고 싶은 경우가 있다.

내용을 채우고 싶다든지, 지우고 싶다든지 등의 가공을 해야 하는데,
n번째 표에 접근하는 오토메이션 API는 아래와 같다.

import win32com.client as win32


hwp = win32.gencache.EnsureDispatch("hwpframe.hwpobject")
hwp.XHwpWindows.Item(0).Visible = True
hwp.Open(한글/파일/경로.hwp)


n = 10  # 문서 내 10번째 표에 접근하고 싶은 경우
idx = 0
ctrl = hwp.HeadCtrl

while ctrl:
	if ctrl.UserDesc == "표":
        idx += 1
        if idx == n:
            hwp.SetPosBySet(ctrl.GetAnchorPos(0))
            hwp.FindCtrl()
            hwp.HAction.Run("ShapeObjTableSelCell")
            hwp.HAction.Run("Cancel")
    ctrl = ctrl.Next

대략 이런 식이다.

자주 사용하는 기능이므로,
아래와 같이 메서드를 만들어보았다.

from pyhwpx import Hwp


hwp = Hwp()
hwp.open("./sample.hwp")
hwp.get_into_nth_table(0)  # 문서 내 첫 번째 표에 접근하는 경우
hwp.get_into_nth_table(2)  # 문서 내 세 번째 표에 접근하는 경우
hwp.get_into_nth_table(-1)  # 문서 내 마지막 표에 접근하는 경우
접근방법은 파이썬의 일반적인 인덱싱과 유사하다.
0부터 시작하며, 역순탐색은 음수(-1~)를 사용한다.
문서내 표의 갯수를 넘는 인덱스를 입력하는 경우 오류가 발생한다.

몇 가지 사소한 고민과 반영사항

 

1. 음수인덱스를 넣어볼까?

표가 아주 많은 경우 마지막 표의 인덱스를 일일이 셀 수 없다.
API에 익숙한 분들은 LastCtrl 및 ctrl.Prev를 사용하겠지만,
이 경우를 위해 입문자에게 저 두 개의 프로퍼티를 사용하게 하는 건 좀 아쉽다.

pyhwpx를 제작하면서 세운 방침이 하나 있는데,
한/글을 많이 다루는 파이썬 입문자가 부담없이 배울 수 있도록 하는 것.
주목적은 파이썬 교육용이 되었으면 좋겠다. 활용은 가급적 2순위.

그래서 음수인덱스 기능을 추가했다. 잘 작동하는 것 같아 만족스럽다.

 

2. 메서드가 종료되는 시점에 "셀 선택" 상태로 할까?

답이 정해지지 않은 고민이지만,
이 메서드 이후에 어떤 프로세스를 연결하냐일 것.
사실 이 메서드를 만든 시점에 읽고 있던 질문은 이거였다.

한글API를 이용하여 표의 행을 복사해서 밑에 붙이는방법 - 웹한글 기안기 - 한컴디벨로퍼 포럼 (hancom.com)

 

한글API를 이용하여 표의 행을 복사해서 밑에 붙이는방법

안녕하세요 한글API를 사용하여 스크립트로 동적으로 표를 생성 및 데이터를 넣는 작업을 진행하고있습니다. 관련해서 진행중에 표의 헤더가있고 데이터가 되는 행이 1줄만 있었다는 가정하에,

forum.developer.hancom.com

동일한 질문을 인프런에도 남겨주신 것 같은데

표의 행 부분(다중)을 반복 질문 - 인프런 (inflearn.com)

 

표의 행 부분(다중)을 반복 질문 - 인프런

안녕하세요. 일코님 강의 잘 보고 있습니다. 아래그림과 같은 표에서 빨간색 부분만 다중 행(동적으로 Data수량만큼)으로 만들고 싶은데,방법이 생각나지 않아.. 문의 드려요.. (답변 미리 감사드

www.inflearn.com

매번 하는 고민이지만,
내 기준이 아니고, 이 분들의 기준에서 생각해야 한다.

하여튼 이 분의 경우에는,
get_into_nth_table 바로 다음에
아래 표와 merge를 해야 하는데( hwp.TableMergeTable() )
셀 선택 상태에서는 merge 실행시 False를 리턴하며 merge가 실행되지 않는다.
그래서 기본동작을 셀 선택 해제로 두었다.

그냥 select=False 파라미터를 추가해놓고,
필요한 경우 select만 True로 바꾸면
첫 번째 셀을 선택한 상태가 되게 하자.

 

3. 메서드는 해당 표의 ctrl 객체를 리턴하자.

그래. 고민할 필요 없이 이 방식이 True/False 리턴보다는 훨씬 유용할 것 같다.
그냥 접근할 수 없는 인덱스를 입력하면 오류를 나게 해버리자.

 

4. 인덱스번호 말고, ctrl 객체를 넣는 경우 작동방식?

n을 입력하지 않고 get_into_nth_table()만 입력하는 경우에는 첫 번째 표 안으로 이동하게,
n에 정수를 입력하면 n번째 표에 들어가게,
마지막으로 ctrl 객체를 넣으면 해당 표 안으로 이동하게 해 두었다.
typing으로 ctrl의 타입을 미리 명시해주고 싶은데, 해당 클래스를 모르겠다..
이건 나중에 보완하자.
(ctrl이 표가 아닌 경우도 가정해야 한다!)

 

5. 오류메시지가 필요할까!?

모듈을 만들면서 아직 쉽게 익숙해지지 않는 부분은, raise로 일부러 오류를 뱉게 하는 거다.
나도 막연하게 "오류는 안 좋은 것!"이라는 선입견을 가지고 있는 것 같은데,
문서 안에 표가 5개 밖에 없는데, 10번째 표에 접근하려고 하면 당연히 오류메시지를 뿌려줘야 하지 않나 싶다.
막상 가볍게 생각하면 "누가 이런 오류를 내겠어?" 싶지만, 이건 다분히 내 기준이다.
오류메시지는 필요하다.

 

6. get_into_nth_table vs get_nth_table

메서드 이름을 정할 때, 적당한 길이를 고민해야 하는데,
이번에는 into를 삽입하기로 결정했다.
왜냐면 표를 get 한다는 의미가,
그냥 표 바깥에서 표 전체를 "선택"하는 경우도 있고
표 안으로 들어가는 경우도 있는데,
명시적으로 "표 안으로 들어간다"는 의미를 꼭 표현하고 싶었다.
메서드 이름은 차차 수정해 가자.

 

7. get_into_nth_table(0) == get_into_nth_table(-1) ??????

리스트나 문자열의 인덱싱 작동방식이 궁금한 지점이었다.
메서드 안에서는 약간의 꼼수 `(abs(int(n))`를 써서 처리했지만,
더 좋은 방식이 있지 않았을까? 뭐 잘 굴러가기는 한다.

 

고민은 대략 이 정도.
그래서 현재 작성된 get_into_nth_table() 메서드의 모양은 아래와 같다. (ver. 0.6.11)

더보기
    def get_into_nth_table(self, n: int = 0):
        """
        문서 n번째 표의 첫 번째 셀로 이동하는 함수(1~)
        """
        if n >= 0:
            idx = 0
            ctrl = self.hwp.HeadCtrl
        else:
            idx = -1
            ctrl = self.hwp.LastCtrl
        if isinstance(n, type(ctrl)):
            # 정수인덱스 대신 ctrl 객체를 넣은 경우
            self.set_pos_by_set(n.GetAnchorPos(0))
            self.hwp.FindCtrl()
            self.ShapeObjTableSelCell()
            self.Cancel()
            return ctrl
        
        while ctrl:
            if ctrl.UserDesc == "표":
                if n in (0, -1):
                    self.set_pos_by_set(ctrl.GetAnchorPos(0))
                    self.hwp.FindCtrl()
                    self.ShapeObjTableSelCell()
                    self.Cancel()
                    return ctrl
                else:
                    if idx == n:
                        self.set_pos_by_set(ctrl.GetAnchorPos(0))
                        self.hwp.FindCtrl()
                        self.ShapeObjTableSelCell()
                        self.Cancel()
                        return ctrl
                    if n >= 0:
                        idx += 1
                    else:
                        idx -= 1
            if n >= 0:
                ctrl = ctrl.Next
            else:
                ctrl = ctrl.Prev
        raise IndexError(f"해당 인덱스의 표가 존재하지 않습니다."
                         f"현재 문서에는 표가 {abs(int(-4+0.1))}개 존재합니다.")

 

댓글