728x90
개발 동기
To, 블로그씨에서 트리 만들기에 관한 글을 쓰면서 컴퓨터에서 사용할 수 있는 트리 위젯을 만들었습니다. 위젯의 기능 상 항상 화면 맨 위에 위치해 있어야했는데 구글 Gemini를 통해 파이썬으로 해당 기능을 구현할 수 있는 것을 알게되었습니다.
그래서 이번에는 카톡의 눈 오는 배경화면에서 아이디어를 얻어 컴퓨터 화면에 눈이 오게 할 수 있을까?
궁금해져 이번 코딩을 하게되었습니다.
주요 기능
- 컴퓨터의 전체 화면에 눈이 오는 듯한 효과 연출
- 작업 표시줄의 '숨겨진 아이콘'에서 해당 위젯을 마우스 오른쪽 클릭하여 종료가능



트러블 슈팅
- 눈송이가 비정상적으로 길쭉하거나 크게 늘어져서 내림
- canvas.coords로 눈송이 위치를 재설정할 때 좌표 값이 어긋나서 발생하는 현상
- 작업에 방해되지 않도록 눈송이 크기를 아주 작고 일정하게(2~4픽셀) 고정
- size = random.randint(2, 4)로 설정하여 시야를 가리지 않는 아주 작은 입자로만 구성
- 화면 아래로 사라진 눈송이가 다시 위에서 나타날 때 모양이 깨지지 않도록 코드를 수정
- coords 함수 사용 시 4개의 좌표(x1, y1, x2, y2)를 모두 정확히 넣어 눈이 재생성될 때도 원래의 동그란 모양을 유지하도록 함
- 눈이 너무 휙휙 지나가지 않도록 속도를 0.7 ~ 2.0 사이로 낮춰서 훨씬 차분한 분위기를 냄
- 작업에 방해되지 않도록 눈송이 크기를 아주 작고 일정하게(2~4픽셀) 고정
- canvas.coords로 눈송이 위치를 재설정할 때 좌표 값이 어긋나서 발생하는 현상

- 실행 창 없이 화면을 투명하게 덮고 있어 '종료 버튼'이 보이지 않음
- pystray 라이브러리를 이용하여 프로그램이 방해되지 않도록 종료 버튼을 트레이 아이콘(시계 옆 아이콘)에 넣어 우클릭으로 종료하게 만듦
코드
# 코드 실행 전 라이브러리 설치
# pip install pystray Pillow
import tkinter as tk
import random
import threading
from PIL import Image, ImageDraw
import pystray
from pystray import MenuItem as item
import sys
class Snowfall:
def __init__(self):
self.root = tk.Tk()
self.root.title("Snowing")
self.screen_width = self.root.winfo_screenwidth()
self.screen_height = self.root.winfo_screenheight()
self.root.overrideredirect(True)
self.root.attributes("-topmost", True)
self.root.attributes("-transparentcolor", "black")
self.root.geometry(f"{self.screen_width}x{self.screen_height}+0+0")
try:
import ctypes
GWL_EXSTYLE = -20
WS_EX_LAYERED = 0x00080000
WS_EX_TRANSPARENT = 0x00000020
hwnd = ctypes.windll.user32.GetParent(self.root.winfo_id())
style = ctypes.windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
ctypes.windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, style | WS_EX_LAYERED | WS_EX_TRANSPARENT)
except:
pass
self.canvas = tk.Canvas(self.root, width=self.screen_width, height=self.screen_height,
bg='black', highlightthickness=0)
self.canvas.pack()
self.root.bind("<Escape>", lambda e: self.quit_window())
self.snowflakes = []
# 눈송이 개수를 80개로 조절 (방해되지 않는 선에서 적당히)
for _ in range(80):
self.add_snowflake(initial=True)
self.animate()
self.tray_thread = threading.Thread(target=self.setup_tray, daemon=True)
self.tray_thread.start()
self.root.mainloop()
def add_snowflake(self, initial=False):
# 눈송이 크기를 2~4픽셀 사이로 아주 작게 제한
size = random.randint(2, 4)
x = random.randint(0, self.screen_width)
# 처음 생성 시에는 화면 전체에 뿌리고, 재생성 시에는 화면 위쪽에서 생성
y = random.randint(0, self.screen_height) if initial else random.randint(-50, -10)
speed = random.uniform(0.7, 2.0) # 눈 속도도 조금 더 차분하게 조절
flake = self.canvas.create_oval(x, y, x + size, y + size, fill="white", outline="white")
self.snowflakes.append({'id': flake, 'speed': speed, 'size': size})
def animate(self):
for flake_data in self.snowflakes:
flake_id = flake_data['id']
speed = flake_data['speed']
size = flake_data['size']
# 아래로 이동
self.canvas.move(flake_id, 0, speed)
pos = self.canvas.coords(flake_id)
# 화면 아래로 완전히 사라지면 위로 재배치
if pos[1] > self.screen_height:
new_x = random.randint(0, self.screen_width)
new_y = random.randint(-20, -5)
# 좌표를 다시 설정할 때 크기(size)가 유지되도록 x+size, y+size를 정확히 지정
self.canvas.coords(flake_id, new_x, new_y, new_x + size, new_y + size)
self.root.after(30, self.animate)
def create_image(self):
image = Image.new('RGB', (64, 64), color=(255, 255, 255))
dc = ImageDraw.Draw(image)
dc.rectangle((16, 16, 48, 48), fill=(0, 150, 255))
return image
def setup_tray(self):
menu = (item('종료(Exit)', self.quit_window),)
self.icon = pystray.Icon("Snowfall", self.create_image(), "Snowing", menu)
self.icon.run()
def quit_window(self):
if hasattr(self, 'icon'):
self.icon.stop()
self.root.quit()
sys.exit()
if __name__ == "__main__":
Snowfall()
실행 파일
onefile 배포 실행 파일과 onedir 배포 실행 파일 중 하나만 다운받아 사용하면 됩니다.
onefile배포와 onedir배포의 차이
onefile에는 실행 파일(.exe)하나만 다운 받으면 되고
onedir배포 파일은 zip파일을 다운받아 압축 해제 후, 그 안의 실행 파일(.exe)을 실행하여 사용합니다.
속도는 프로그램 용량에 따라 다를 수 있지만 onedir 배포 파일이 더 빠를 수 있습니다.
CodingWithGemini/Snowing/dist at main · MinjuKang727/CodingWithGemini
Contribute to MinjuKang727/CodingWithGemini development by creating an account on GitHub.
github.com

728x90