본문 바로가기

아래아한글자동화(기타+)

[파이썬-한/글] 표 안에서 현재 셀주소 알아내기 & 이동하기

안녕하세요?

회사원코딩의 마티니입니다.

요즘 이 블로그에다 파이썬-한/글 자동화에 사용하는 메서드를 하나씩 적어보고 있습니다.

예전 포스팅에서도 말씀드렸지만, 저도 새로운 자동화 스크립트를 짤 때는

매번 API문서나 스크립트매크로를 참고합니다. 이게 잘 외워지지는 않더라고요.

2020/07/28 - [아래아한글자동화(기타+)] - [파이썬-아래아한글] 제가 아래아한글 작업을 자동화하는 요령은..

 

[파이썬-아래아한글] 제가 아래아한글 작업을 자동화하는 요령은..

안녕하세요? 회사원코딩의 신명진입니다. 아래아한글 홈페이지 - 고객지원 - 개발자료실에 가면 아래아한글을 코딩으로 자동화할 수 있는 다양한 언어 예제와 API 매뉴얼이 있습니다. VB, MFC, C#, J

www.martinii.fun

API 문서 안에 대부분의 답이 있다고 생각하는데, 가끔 이런 경우가 있기도 합니다. 예를 들면 아래 메서드,

HWPFrame.HwpObject로 만든 오브젝트에는 이 메서드가 없다.

가끔 현재 커서가 위치한 셀의 위치정보를 얻어오고 싶을 때가 있죠.

표를 좀 더 섬세하게 변경하는 경우라든지, 매크로로 복잡한 표를 만드는데 사용할 수도 있고요.

다행히 HwpCtrl API.hwp 98페이지에 GetTableCellAddr 메서드가 설명되어 있고, 딱 제가 원하는 기능입니다.

근데 이게 실행되지 않아요... 한/글 내부에서 스크립트매크로로 실행할 땐 되는 것 같은데,

COM 오브젝트를 생성하면, 소스를 아무리 뒤져봐도 GetTableCellAddr이라는 메서드가 안 보입니다.

 

그런 맥락에서 이번 포스팅에서는 일종의 꼼수(?)를 이용해서 현재 셀의 주소를 얻어오는 함수를 직접 만들어봅시다.

참고로 파이썬에서 함수를 만드는 기본적인 문법은 아래와 같이 아주 간단하죠.

def hello(name="Martinii"):
    return f"Hello {name}!"
    
print(hello())
# "Hello Martinii!" 를 리턴

print(hello("World"))
# "Hello World!" 를 리턴

 

그리고 GetTableCellAddr 대신 엑셀형식의 셀주소 문자열을 받아올 수 있는 메서드가 하나 숨어 있는데

그게 바로 지난 포스팅에 언급했던 hwp.KeyIndicator() 입니다.

2020/07/30 - [아래아한글자동화(기타+)] - [파이썬-한/글] 현재 커서의 페이지를 알고 싶다?

 

[파이썬-한/글] 현재 커서의 페이지를 알고 싶다?

안녕하세요? 자동화작업을 할 때, 표 안에 이미지나 장문의 텍스트를 삽입하다 보면 다음페이지로 넘어가버릴 수 있죠. ("글자처럼 취급"한 상태면, 개체전체가 다음페이지로 넘어갈 거고, 그렇

www.martinii.fun

KeyIndicator 메서드를 HwpCtrl API.hwp에서 찾으면 다음과 같은 설명이 있습니다. "상태 바의 정보를 얻어온다"

HwpCtrl API.hwp 134페이지

커서를 표 안에 두고 파라미터 없이 hwp.KeyIndicator()를 실행하면, 튜플을 반환하는데, 그 값이 이렇게 생겼습니다.

반환된 튜플의 마지막 원소 안에...

눈치채셨나요? hwp.KeyIndicator()[-1] 안에 현재 셀의 주소가 괄호 안에 들어 있어요.

이걸 이용해서 현재 캐럿의 셀 주소를 알아낼 수 있습니다. 저는 이렇게 문자열메서드로 추출합니다.

현재셀주소 == "A1"

파이썬 문자열 다루는 문법이 익숙하지 않은 분들을 위해서 간단히 설명드리면,

코    드 설    명 실행결과
hwp.KeyIndicator() 상태표시줄 관련 튜플 리턴 (True,1,1,1,1,1,1,0, "(A1): 문자 입력")
                        [-1] 그 튜플에서 가장 마지막 원소만 꺼내서 "(A1): 문단 나눔"
                            [1:] 첫 번째 글자인 "("는 빼고
그 뒤의 문자열을 가져다가
"A1): 문단 나눔"
                                .split(")") ")"를 기준으로 분리해서(split)
리스트를 만들고
["A1", ": 문단 나눔"]
                                           [0] 그 중 첫 번째 원소만 선택 "A1"

정도가 되겠습니다.

아주 간단히 함수로 만들어보면 아래처럼 만들 수 있겠습니다.

def GetTableCellAddr():
    return hwp.KeyIndicator()[-1][1:].split(")")[0]

 

현재 커서가 셀 안에 있지 않은 경우도 있으니까, 해당 예외처리를 더해주면

def GetTableCellAddr():
    state = hwp.KeyIndicator()[-1]
    if not state.startswith("("):  # 표 안에 있을 때만 "(C4)" 형태로 괄호가 들어가니까
        raise AttributeError("현재 캐럿이 표 안에 있지 않습니다.")
    return state[1:].split(")")[0]

조금만 더 욕심을 부려보면 이렇게.

def GetTableCellAddr():
    if not hwp.CellShape:  # 표 안에 있을 때만 CellShape 오브젝트를 리턴하니까
        raise AttributeError("현재 캐럿이 표 안에 있지 않습니다.")
    return hwp.KeyIndicator()[-1][1:].split(")")[0]

 

이 정도로 만들어볼 수 있을 것 같아요.

 

 

한 가지만 더 해볼까요? 커서가 표 안에 있을 때 해당 셀주소로 찾아가는 함수는 어떻게 구현할 수 있을까요?

이건 A1 셀에 커서를 두고 while문으로 한 칸씩 우측으로 이동하다가,

목표하는 셀 주소와 같을 때 while문을 종료하면 되겠죠? 코드로 구현해보면,

def SetTableCellAddr(target_addr):
    while True:
        current_addr = hwp.KeyIndicator()[-1][1:].split(")")[0]
        hwp.Run("TableRightCell")
        if target_addr == hwp.KeyIndicator()[-1][1:].split(")")[0]:
            return

이렇게 작성해도 굴러가기는 하겠지만 막상 테스트해보면 몇 가지 예외들이 있죠. 크게 두 가지는,

1. 현재 셀이 A1이 아니라 끝에 있다면?

2. target_addr이 표의 범위를 넘은 값이라면?

둘 다 while문에 걸려서 다운이 되어버리겠죠.

그런 문제들 몇 가지를 보완한 코드는 약간 길어지는데,

def SetTableCellAddr(addr):
    init_addr = hwp.KeyIndicator()[-1][1:].split(")")[0]  # 함수를 실행할 때의 주소를 기억.
    if not hwp.CellShape:  # 표 안에 있을 때만 CellShape 오브젝트를 리턴함
        raise AttributeError("현재 캐럿이 표 안에 있지 않습니다.")
    if addr == hwp.KeyIndicator()[-1][1:].split(")")[0]:  # 시작하자 마자 해당 주소라면
        return  # 바로 종료
    hwp.Run("CloseEx")  # 그렇지 않다면 표 밖으로 나가서
    hwp.FindCtrl()  # 표를 선택한 후
    hwp.Run("ShapeObjTableSelCell")  # 표의 첫 번째 셀로 이동함(A1으로 이동하는 확실한 방법 & 셀선택모드)
    while True:
        current_addr = hwp.KeyIndicator()[-1][1:].split(")")[0]  # 현재 주소를 기억해둠
        hwp.Run("TableRightCell")  # 우측으로 한 칸 이동(우측끝일 때는 아래 행 첫 번째 열로)
        if current_addr == hwp.KeyIndicator()[-1][1:].split(")")[0]:  # 이동했는데 주소가 바뀌지 않으면?(표 끝 도착)
            # == 한 바퀴 돌았는데도 목표 셀주소가 안 나타났다면?(== addr이 표 범위를 벗어난 경우일 것)
            SetTableCellAddr(init_addr)  # 최초에 저장해둔 init_addr로 돌려놓고
            hwp.Run("Cancel")  # 선택모드 해제
            raise AttributeError("입력한 셀주소가 현재 표의 범위를 벗어납니다.")
        if addr == hwp.KeyIndicator()[-1][1:].split(")")[0]:  # 목표 셀주소에 도착했다면?
            return  # 함수 종료

if문도 많고 예외처리도 한 개 더 늘어나서 복잡해 보이지만

한/글을 어느 정도 다루시는 분이라면 주석만 읽어봐도 간단히 절차를 이해하실 거예요.

그리고 위 코드에 나온, 간단한 명령어 hwp.Run(명령어) 에 대해서는 다음 기회에 꼭 한 번 다루겠습니다.

 

 

우선 위 코드를 테스트해보면,

이렇게 생긴 표 C3(선택된 셀)로 이동하려면?
주소범위를 벗어난 값을 입력하니까 에러를 뱉고, 제대로 입력하니까 이동이 완료됨.

결과는,

커서가 C3으로 이동함.

잘 작동하네요.

그럼 오늘 포스팅은 여기서 마치겠습니다.

긴 글 읽어주셔서 감사합니다.

행복한 하루 되세요!!!