초간단 아날로그 시계 만들기 - GPT를 이용한 코딩 연습 - 8부(배포판 수정, 최적화 1)

2024. 10. 27. 23:46코딩 연습/아날로그 시계 만들기

이전엔 매초 시계의 모든 부분을 업데이트 했다. 시침, 분침, 초침은 물론이고 배경 사진까지 업데이트 했다. 처음엔 창 크기 변경에 따른 사진 크기 조정 때문에 그랬지만, 이제 최적화를 해볼까 한다.

    def update_clock(self):
        # 시계를 업데이트하는 함수
        self.canvas.delete("clock")  # 기존 시계 삭제
        self.canvas.delete("background")  # 기존 배경 삭제
        if self.background_image:
            self.load_background_image(self.background_image_path)

기존 코드 중 기존 배경 삭제를 매번 하는 코드

 

 

이걸 GPT에게 수정을 부탁했다. 

    def on_resize(self, event):
# 크기 조정 후 100ms 지연 후 배경 이미지 로드
             self.root.after(100, lambda: self.load_background_image(self.background_image_path))

 

처음엔 이렇게 일정 시간 지연 후 이미지를 로드 하게 했다. 얼핏 보면 그럴싸하다. 하지만 실제 창 크기를 조정해보면 버벅인다.

버벅인다며 다시 한번 수정을 요구했다.

 

    def on_resize(self, event):
        # 창의 크기가 변경될 때 호출되는 함수
        if event.widget == self.root:  # 이벤트가 창(root)에서 발생했는지 확인
            # 만약 `resize_after_id` 속성이 존재한다면, 이전에 예약된 after 이벤트 취소
            if hasattr(self, 'resize_after_id'):
                self.root.after_cancel(self.resize_after_id)
        # 1000ms 지연 후 배경 이미지 로드
        self.resize_after_id = self.root.after(1000, lambda: self.load_background_image(self.background_image_path))

화면 크기를 변경하면 배경화면을 조정하는 작업을 1초후에 실행하도록 예약하고 resize_after_id에 작업 아이디를 저장한다.

그런데 그 사이 창 크기 조절이 있었다면, resize_after_id 값이 있었을 것이고 이 값이 있으면 그 작업을 취소한다. 그리고 다시 1초 후 배경화면 변경 작업을 예약한다.

 

상당히 그럴싸하다. 그런데 실제 실행해보면 1초후 엄청난 랙이 생긴다. 아마 1초동안 이전 작업이 취소되지 않고 모든 이벤트가 동시에 실행되는 듯하다.

 

GPT에게 이런 내용을 알려주고 고쳐달라고 했다. 아래가 GPT의 대답이다.

코드는 다음과 같다.

def on_resize(self, event):
    # 창의 크기가 변경될 때 호출되는 함수
    if event.widget == self.root:
        # 현재 크기 조정 중임을 표시
        self.is_resizing = True

        # 이전 예약된 after 이벤트 취소 (중복 실행 방지)
        if hasattr(self, 'resize_after_id'):
            self.root.after_cancel(self.resize_after_id)

        # 1초 후에 크기 조정이 완료되었을 때만 배경 이미지 로드
        self.resize_after_id = self.root.after(1000, self.resize_complete)

def resize_complete(self):
    # 실제로 배경 이미지를 로드하고, 크기 조정 상태를 초기화
    if self.is_resizing:
        self.load_background_image(self.background_image_path)
        self.is_resizing = False

 

이해가 되는가? 사실 이전 코드가 완벽하다 생각되니까 이해가 안 된다. 먼저 이전 코드가 왜 작동하지 않는지 봐야하겠다. 이전 코드는 완벽해보이지만, 실제 실행해보면 제대로 after_cancel이 제대로 작동 되지 않아서 창을 끌며 발생된 수많은 이벤트 들이 다 취소되지 않고 동시에 실행되는 버그가 있었다. 왜 제대로 동작하지 않을까? 정확히는 나도 모르겠다. 추정하자면 너무 빠른 이벤트 발생으로 after_cancle 을 완료 하기도 전에 다시 이벤트를 호출하기 때문으로 보인다.

이전 코드와 달라진 것은 load_background_image() 메서드를 바로 예약하지 않고 resize_complete() 메서드를 예약하고 이 메서드가 또 load_background_image() 메서드를 실행시킨다는 것이다. 그리고 resize_complete()메서드는 is_resizing 변수를 이용해 is_resizing 변수가 True 상태일 때만 작동하도록 하고 마지막엔 다시 False 상태로 변경한다. 다시말해 1초 사이에 수많은 창 변화로 on_resize()가 호출되었다 하더라도 resize_complete()메서드는 딱 한번 실행 되고 마는 것이다. is_resizing이 True상태는 결국 한번 뿐일테니까 말이다. 실제 실행해보면 잘 작동한다.

 

근데 그럼 기존 after_cancel은 필요 없는 거 아닌가? 다시 GPT에게 물어보니 맞다고 다시 코드를 고쳐줬다. 아직까진 GPT는 완벽하지 않다. 코드를 보며 소통하며 고쳐나가야한다.

def on_resize(self, event):
    # 창의 크기가 변경될 때 호출되는 함수
    if event.widget == self.root:
        # 현재 크기 조정 중임을 표시
        self.is_resizing = True

        # 크기 조정이 완료된 후 1초 뒤에 작업 예약
        self.root.after(1000, self.resize_complete)

def resize_complete(self):
    # 크기 조정이 끝났을 때만 배경 이미지를 로드
    if self.is_resizing:
        self.load_background_image(self.background_image_path)
        # 작업이 완료되었으므로 플래그 초기화
        self.is_resizing = False

 

잘 작동한다. 위 예제에선 1초지만 좀 늦는 감이 있어, 0.3초로 고쳤다. 근데 어쨌든 0.3초든 1초든 배경 이미지의 갱신이 있을 때 약간의 버벅임이 느껴진다. 그리고 이제껏 고해상도 사진을 써서 몰랐지만 저해상도 사진을 쓰면 시계를 미쳐 다 못채운다는 것도 알게 돼었다. 다음엔 이 부분을 고쳐보겠다.