초간단 아날로그 시계 만들기 - GPT를 이용한 코딩 연습 - 7부(배포판 수정, 레지스트리를 사용해 저장하기)
2024. 10. 24. 19:29ㆍ코딩 연습/아날로그 시계 만들기
이전 글에서 최종판에 문제점이 두 개 있었다.
- 초기 상태에 사진이 등록되지 않았다.
- 사진값이 들어가면 config.json 파일이 생긴다.
우선 두번째 문제인 config.json 파일을 없애보자.
기존의 코드 중 아래와 같은 부분이 있다.
self.load_config() # 설정 파일 불러오기
얼핏 봐도 설정값을 불러오겠다는 뜻인데, 더 자세히 알고 싶어 GPT에게 물어봤다. 이 함수는 특정 파일을 열도록 구현되어 있다고 한다. 그래서 config.json 을 자동으로 읽어들인다. 마찬가지로 save_config() 함수는 config.json 파일을 쓰도록 구현되어있다.
config.json 파일을 쓰지 않을 것이기 때문에 이 코드는 이제 지워준다. 또 json 자체를 쓸 일이 없으므로 맨 위의 import json과 CONFIG_FILE = "config.json" 역시 지운다.
# 항상 위에 표시할지 여부 설정
self.always_on_top = tk.BooleanVar()
self.always_on_top.set(self.config.get("always_on_top", False))
self.root.attributes("-topmost", self.always_on_top.get())
이 부분도 config 파일을 안 쓰므로 수정할 필요가 있다.
# 항상 위에 표시할지 여부 설정
self.always_on_top = tk.BooleanVar()
self.root.attributes("-topmost", self.always_on_top.get())
항상 위에 표시가 체크되어 있었는지는 이 프로그램에서 중요하지 않으므로 지운다.
# 배경 이미지 설정
self.background_image = None
if self.config.get("background_image"):
self.load_background_image(self.config["background_image"])
else:
self.load_background_image(DEFAULT_BACKGROUND_IMAGE)
역시 config가 사라졌으므로 위 코드도 바꿔야한다.
# 배경 이미지 설정
self.load_background_image(self.background_image_path)
이렇게 바꿔준다. 전반적으로 코드를 이런 식으로 수정한다.
그럼 자연스럽게 그래서 이미지는 어디다 저장해야 하는가 하는 의문이 남는다.
초기 사진은 이전 글에서 썼던 방법인 --add-data 를 활용해서 _MEIPASS (exe 파일을 실행할 때 프로그램 안에서 쓰는 임시파일 경로) 안으로 넣어주면 될 것이다.
근데 사용자가 사진을 변결할 때의 경로는 어떻게 해야할까? GPT에게 물어본다. config.json 같은 파일을 사용하지 않으면 일반적으로 사용자 값을 저장할 수 없다고 한다. 그래서 윈도우에선 레지스트리를 사용한다.
그리고 장단점을 설명해준다.
레지스트리 사용의 장단점
- 장점: 시스템 수준에서 설정을 저장하므로 파일을 따로 관리하지 않아도 됩니다.
- 단점: 사용자의 레지스트리에 직접 접근하기 때문에 시스템에 영향을 줄 수 있으며, 권한 문제가 발생할 수 있습니다.
나는 exe파일 하나만 남기고 싶으므로 레지스트리를 쓰기로 했다. 특히 현재 사용자(HKEY_CURRENT_USER)의 레지스트리를 사용하는 경우에는 관리자 권한 없이도 접근 가능하다고 하니 사용하기도 편리해 보인다.
이제 GPT에게 코드를 수정해달라고 했다. 몇 차례 오류로 코드를 분석하며 다시 되묻거나 직접 고치는 과정도 필요했다. GPT는 아직 완벽하진 않다. 이제 코드도 제법 길어져 모든 순간을 다 보여줄 수는 없을 거 같아 이런 과정은 생략했다.(이전에도 생략은 많았지만...)
그렇게 수정한 코드 중 중요한 부분만 보자면 다음과 같다.
import winreg
DEFAULT_BACKGROUND_IMAGE = "시계 배경.jpg" # 기본 배경 이미지 경로
REGISTRY_PATH = r"Software\HeumDuNeoClock" # 레지스트리에 저장할 경로
DEFAULT_ICON_IMAGE = "HEUMDUNEO ClockAPP.ico" # 기본 아이콘 이미지 경로
우선 레지스트리를 다뤄야하므로 관련 라이브러리인 winreg를 import한다.
다음 자주 쓰는 것을 변수에 넣었다. 그동안 절대 경로를 자주 썼는데, 파이썬 스크립트가 있는 폴더에 시계 배경 사진과 아이콘 그림을 넣어 절대 경로를 쓰지 않게 되었다.
레지스트리 경로에는 r이 붙어있는데 역슬래시와 결합되어있는 문자가 이스케이프 문자로 작동하는 걸 막는 역할을 한다.
class AnalogClockApp:
def __init__(self, root):
self.root = root
self.root.title("아날로그 시계") # 창 제목 설정
# 실행 파일에 아이콘 포함시키기 위해 경로 설정
if hasattr(sys, '_MEIPASS'):
# PyInstaller로 패키징된 경우 '_MEIPASS' 경로 사용
icon_path = os.path.join(sys._MEIPASS, DEFAULT_ICON_IMAGE)
self.background_image_path = self.load_background_image_from_registry() or os.path.join(sys._MEIPASS, DEFAULT_BACKGROUND_IMAGE) # 패키징된 이미지 경로
else:
# 개발 중일 때는 현재 디렉토리의 아이콘 파일 사용
icon_path = DEFAULT_ICON_IMAGE
self.background_image_path = self.load_background_image_from_registry() or DEFAULT_BACKGROUND_IMAGE
위 코드를 보면 이전 글에 설명했지만 살짝 반복하자면 PyInstaller 라이브러리를 이용해 실행파일을 만들면 이 실행파일에 임시경로가 부여된다. 그 경로가 _MEIPASS 이다. 그래서 _MEIPASS 가 존재하면 exe로 실행시켰다고 보고 if 문을 실행하고 _MEIPASS가 없다면 파이썬 스크립트를 그냥 실행시키는 것으로 판단하고 else 문을 실행한다. 그래서 단일 실행 파일이지만 그 파일에 숨어있는 아이콘 그림과 시계 배경 그림을 _MEIPASS에서 불러오는 것이다.
self.background_image_path 앞에는 self가 붙는데 icon_path에는 self가 없다. 이유는 간단한데, 아이콘은 __init__ 메서드에서만 쓰이기 때문이다. 반면 배경그림은 __init__ 메서드 말고도 다른 메서드에서도 쓰인다. 그래서 클래스 인스턴스 속성으로 다른 메서드에서 쓸 수 있도록 만들어야 하기 때문에 self.를 붙여야한다.
다시 아래 코드를 보자
self.background_image_path = self.load_background_image_from_registry() or os.path.join(sys._MEIPASS, DEFAULT_BACKGROUND_IMAGE)
load_background_image_from_registry() 함수는 밑에서 별도로 만들 메서드인데, 말 그대로 레지스트리에서 배경사진 경로를 불러오는 메서드이다. 그래서 레지스트리에 배경사진 경로가 있으면 그 경로가 background_image_path에 저장되고, 없으면 _MEIPASS 의 기본 사진 경로가 저장된다.
def load_background_image_from_registry(self):
# 레지스트리에서 배경 이미지 경로를 불러오는 함수
try:
registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, REGISTRY_PATH, 0, winreg.KEY_READ)
value, _ = winreg.QueryValueEx(registry_key, "BackgroundImagePath")
winreg.CloseKey(registry_key)
return value
except FileNotFoundError:
return None
def save_background_image_to_registry(self, image_path):
# 레지스트리에 배경 이미지 경로를 저장하는 함수
try:
registry_key = winreg.CreateKey(winreg.HKEY_CURRENT_USER, REGISTRY_PATH)
winreg.SetValueEx(registry_key, "BackgroundImagePath", 0, winreg.REG_SZ, image_path)
winreg.CloseKey(registry_key)
except Exception as e:
print(f"Error saving background image to registry: {e}")
위 코드를 보자.
winreg.OpenKey() 함수를 이용해 해당 경로의 레지스트리 키를 연다.
winreg.QueryValueEx() 함수로 방금 연 레지스트리 키에서 BackgroundImagePath 이름의 값을 읽어온다.
winreg .CloseKey() 함수로 레지스트리 키를 닫는다.
저장도 마찬가지다. winreg.REG_SZ는 저장할 값이 문자열이란 뜻이다.
이만하면 된 듯하다. 초기 사진을 단일 실행 파일에 넣기 위해 CMD에서 파이썬 스크립트가 있는 경로에 가서 pyinstaller에 --add-data 옵션을 통해 _MEIPASS에 초기에 쓸 사진을 넣어준다.
pyinstaller --onefile --noconsole --icon="HEUMDUNEO ClockAPP.ico" --add-data "HEUMDUNEO ClockAPP.ico;." --add-data "시계 배경.jpg;." TimeAPP.py
이제 완성되었다.
작동 시켜보니 잘 작동한다.
그런데 코드에 아쉬운 점이 있다. 매초 시침 분침 초침만 고치는데, 배경 사진까지 매번 새로고침한다. 배경 해상도가 달라지면 사진도 고쳐야하니 처음에 그렇게 코드를 짰다. 문제없이 잘 작동하지만 쓸데없이 자원을 먹게 된다.
다음엔 이것을 고쳐보겠다.
지금까지 전체 코드와 아이콘 이미지를 첨부한다. 처음에 프로젝트 명을 나도 모르게 TimeAPP이라고 했는데 생각해보니 ClockAPP으로 했어야했다. 다음엔 프로젝트 명도 고치겠다.
아이콘
파이썬 스크립트
'코딩 연습 > 아날로그 시계 만들기' 카테고리의 다른 글
초간단 아날로그 시계 만들기 - GPT를 이용한 코딩 연습 - 11부(기능 추가, 프로그램 정보) (4) | 2024.11.04 |
---|---|
초간단 아날로그 시계 만들기 - GPT를 이용한 코딩 연습 - 10부(배포판 수정, 최적화 3) (0) | 2024.10.28 |
초간단 아날로그 시계 만들기 - GPT를 이용한 코딩 연습 - 9부(배포판 수정, 최적화 2) (1) | 2024.10.28 |
초간단 아날로그 시계 만들기 - GPT를 이용한 코딩 연습 - 8부(배포판 수정, 최적화 1) (3) | 2024.10.27 |
초간단 아날로그 시계 만들기 - GPT를 이용한 코딩 연습 - 6부(배포판 만들기) (3) | 2024.10.24 |
초간단 아날로그 시계 만들기 - GPT를 이용한 코딩 연습 - 5부(1차 완성) (1) | 2024.10.23 |
초간단 아날로그 시계 만들기 - GPT를 이용한 코딩 연습 - 4부 (2) | 2024.10.22 |
초간단 아날로그 시계 만들기 - GPT를 이용한 코딩 연습 - 3부 (0) | 2024.10.22 |