728x90
반응형

mimi_s.ico
0.12MB

이번엔 저번에 구현했던 미로 게임을 나름 업그레이드해봤다.

https://jinho-study.tistory.com/1081

 

기본적인 게임 개발 기술(실시간 처리, 키 입력 받기, 미로 게임)

이번엔 tkinter를 사용해서 기본적인 게임 개발 기술(실시간 처리, 키 입력 받기, 미로 게임)에 대해 알아보자. 1 실시간 처리 구현하기 파이썬에서는 after() 명령으로 실시간 처리를 수행할 수 있다

jinho-study.tistory.com

 

전체 코드는 아래와 같다.

import tkinter
import tkinter.messagebox


mx = 1 # 캐릭터의 가로 뱡향 위치를 관리하는 변수
my = 1 # 캐릭터의 세로 뱡향 위치를 관리하는 변수
state = 0 # 게임 상황, 0: 게임 진행, 1: 게임 클리어, 2: 게임 클리어 불가능
key = 0 # 키 이름을 입력할 변수 선언

# 키를 눌렀을 때 실행할 함수 정의
def key_down(e):
    global key # key을 전역 변수로 취급
    key = e.keysym # 눌려진 키 이름을 key에 대입
    
# 키를 눌렀다 뗐을 때 실행할 함수 정의
def key_up(e):
    global key # key을 전역 변수로 취급
    key = "" # key에 빈 문자열 대입

# 캐릭터 이동 함수
def move():
    global mx, my
    
    # key 방향이 통로라면 그 방향에 맞게 mx, my값을 변경
    if key == "Up" and maze[my-1][mx] == 0: 
        my -= 1
    if key == "Down" and maze[my+1][mx] == 0:
        my += 1
    if key == "Left" and maze[my][mx-1] == 0:
        mx -= 1
    if key == "Right" and maze[my][mx+1] == 0:
        mx += 1    
    
    # 캐릭터가 있는 장소가 벽이 아니라면 리스트 값을 2로 변경,
    # 칠한 회수를 1 증가시키고, 해당 위치를 분홍색으로 칠한다.
    if maze[my][mx] == 0:
        maze[my][mx] = 2
        # PAINT(지났던 길) 태그 추가
        canvas.create_rectangle(mx*80, my*80, mx*80 + 79, my*80 + 79, 
                                   fill="pink", width=0, tag="PAINT")
    canvas.delete("MYCHR")
    canvas.create_image(mx*80 + 40, my*80 + 40, image=img, tag="MYCHR") # 캐릭터 이미지 이동

# 칠해지지 않은 칸 수를 세주는 함수    
def count_tile():
    cnt = 0
    for i in range(7):
        for j in range(10):
            if maze[i][j] == 0:
                cnt += 1
    return cnt
    
# 게임 상태 확인 함수    
def check():
    cnt = count_tile()
    
    # 게임 클리어 불가능
    if 0 not in [maze[my-1][mx], maze[my+1][mx], maze[my][mx-1], maze[my][mx+1]]:
        return 2
    # 게임 클리어
    elif cnt == 0:
        return 1
    # 게임 진행
    else:
        return 0
        
# 게임 초기화 함수
def reset():
    global mx, my, state
    state = 0
    canvas.delete("PAINT")
    mx = 1
    my = 1
    for y in range(7):
        for x in range(10):
            if maze[y][x] == 2:
                maze[y][x] = 0
                
# 실시간 처리를 수행할 함수 정의
def main_proc():
    global mx, my, state, key
        
    # Esc 키를 누를 시 게임 종료
    if key == "Escape":
        key = 0
        ret = tkinter.messagebox.askyesno("종료", "게임을 종료하시겠습니까?")
        if ret == True:
            root.destroy()
            return ;
        
    # 왼쪽 Shift 키를 눌렀고 미로가 2칸 이상 칠해진 상태라면 게임 초기화
    if key == "Shift_L":
        reset()
                            
    state = check()
    # 게임 진행
    if state == 0:
        # 캐릭터 이동
        move()
    # 클리어 메시지를 표시해준 후에 게임 초기화
    if state == 1:
        canvas.update()
        tkinter.messagebox.showinfo("축하합니다!", "모든 바닥을 칠했습니다!")
        reset()
    # 클리어 불가능 메시지를 표시해준 후에 게임 초기화
    if state == 2:
        tkinter.messagebox.showinfo("망했어요!", "클리어가 불가능합니다\n 다시 시작하세요!")
        reset()
    
    root.after(100, main_proc)

    
root = tkinter.Tk()
root.title("미로를 칠하는 중")
root.bind("<KeyPress>", key_down)
root.bind("<KeyRelease>", key_up)
canvas = tkinter.Canvas(width=800, height=560, bg="white")
canvas.pack()

# 미로 생성
maze = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 1, 0, 0, 1],
    [1, 0, 1, 1, 0, 0, 1, 0, 0, 1],
    [1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 1, 1, 1, 1, 1, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
for y in range(7):
    for x in range(10):
        if maze[y][x] == 1:
            canvas.create_rectangle(x * 80, y * 80, x * 80 + 79, y * 80 + 79, fill="skyblue", width=0)

img = tkinter.PhotoImage(file="mimi_s.png")
# MYCHR(캐릭터) 태그 추가
canvas.create_image(mx * 80 + 40, my * 80 + 40, image=img, tag="MYCHR")
main_proc()
root.mainloop()

 

 

게임 종료와 다시 시작 관련해서 기능을 추가하고 구조를 조금 수정했다. 

  • 0.2초 딜레이는 뭔가 답답해서 0.1초로 줄였다.
  • 게임을 클리어한 후에 Shift 키를 누르지 않아도 자동으로 게임을 초기화하고 다시 시작한다.
  • Esc 키를 입력받으면 yesorno 메시지 박스를 표시해주고 yes면 destroy() 명령을 사용해 게임을 종료시킨다.
  • yuka변수를 없애고 reset(게임 초기화), check(게임 상태 확인), count_tile(칠해지지 않은 칸 수 확인) 함수를 추가했다.
  • state 변수를 사용해서 게임 진행을 관리하는 식으로 메인 함수 구조를 수정했다.
    0: 게임 진행, 1: 게임 클리어, 2: 게임 클리어 불가능

 

게임 진행 관리 부분 코드

    state = check()
    # 게임 진행
    if state == 0:
        # 캐릭터 이동
        move()
    # 클리어 메시지를 표시해준 후에 게임 초기화
    if state == 1:
        canvas.update()
        tkinter.messagebox.showinfo("축하합니다!", "모든 바닥을 칠했습니다!")
        reset()
    # 클리어 불가능 메시지를 표시해준 후에 게임 초기화
    if state == 2:
        tkinter.messagebox.showinfo("망했어요!", "클리어가 불가능합니다\n 다시 시작하세요!")
        reset()

 

pyinstaller용 코드와 명령어

전체 코드

import tkinter
import tkinter.messagebox
import os


def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

mx = 1 # 캐릭터의 가로 뱡향 위치를 관리하는 변수
my = 1 # 캐릭터의 세로 뱡향 위치를 관리하는 변수
state = 0 # 게임 상황, 0: 게임 진행, 1: 게임 클리어, 2: 게임 클리어 불가능
key = 0 # 키 이름을 입력할 변수 선언

# 키를 눌렀을 때 실행할 함수 정의
def key_down(e):
    global key # key을 전역 변수로 취급
    key = e.keysym # 눌려진 키 이름을 key에 대입
    
# 키를 눌렀다 뗐을 때 실행할 함수 정의
def key_up(e):
    global key # key을 전역 변수로 취급
    key = "" # key에 빈 문자열 대입

# 캐릭터 이동 함수
def move():
    global mx, my
    
    # key 방향이 통로라면 그 방향에 맞게 mx, my값을 변경
    if key == "Up" and maze[my-1][mx] == 0: 
        my -= 1
    if key == "Down" and maze[my+1][mx] == 0:
        my += 1
    if key == "Left" and maze[my][mx-1] == 0:
        mx -= 1
    if key == "Right" and maze[my][mx+1] == 0:
        mx += 1    
    
    # 캐릭터가 있는 장소가 벽이 아니라면 리스트 값을 2로 변경,
    # 칠한 회수를 1 증가시키고, 해당 위치를 분홍색으로 칠한다.
    if maze[my][mx] == 0:
        maze[my][mx] = 2
        # PAINT(지났던 길) 태그 추가
        canvas.create_rectangle(mx*80, my*80, mx*80 + 79, my*80 + 79, 
                                   fill="pink", width=0, tag="PAINT")
    canvas.delete("MYCHR")
    canvas.create_image(mx*80 + 40, my*80 + 40, image=img, tag="MYCHR") # 캐릭터 이미지 이동

# 칠해지지 않은 칸 수를 세주는 함수    
def count_tile():
    cnt = 0
    for i in range(7):
        for j in range(10):
            if maze[i][j] == 0:
                cnt += 1
    return cnt
    
# 게임 상태 확인 함수    
def check():
    cnt = count_tile()
    
    # 게임 클리어 불가능
    if 0 not in [maze[my-1][mx], maze[my+1][mx], maze[my][mx-1], maze[my][mx+1]]:
        return 2
    # 게임 클리어
    elif cnt == 0:
        return 1
    # 게임 진행
    else:
        return 0
        
# 게임 초기화 함수
def reset():
    global mx, my, state
    state = 0
    canvas.delete("PAINT")
    mx = 1
    my = 1
    for y in range(7):
        for x in range(10):
            if maze[y][x] == 2:
                maze[y][x] = 0
                
# 실시간 처리를 수행할 함수 정의
def main_proc():
    global mx, my, state, key
        
    # Esc 키를 누를 시 게임 종료
    if key == "Escape":
        key = 0
        ret = tkinter.messagebox.askyesno("종료", "게임을 종료하시겠습니까?")
        if ret == True:
            root.destroy()
            return ;
        
    # 왼쪽 Shift 키를 눌렀고 미로가 2칸 이상 칠해진 상태라면 게임 초기화
    if key == "Shift_L":
        reset()
                            
    state = check()
    # 게임 진행
    if state == 0:
        # 캐릭터 이동
        move()
    # 클리어 메시지를 표시해준 후에 게임 초기화
    if state == 1:
        canvas.update()
        tkinter.messagebox.showinfo("축하합니다!", "모든 바닥을 칠했습니다!")
        reset()
    # 클리어 불가능 메시지를 표시해준 후에 게임 초기화
    if state == 2:
        tkinter.messagebox.showinfo("망했어요!", "클리어가 불가능합니다\n 다시 시작하세요!")
        reset()
    
    root.after(100, main_proc)

    
root = tkinter.Tk()
root.title("미로를 칠하는 중")
root.bind("<KeyPress>", key_down)
root.bind("<KeyRelease>", key_up)
canvas = tkinter.Canvas(width=800, height=560, bg="white")
canvas.pack()

# 미로 생성
maze = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 1, 0, 0, 1],
    [1, 0, 1, 1, 0, 0, 1, 0, 0, 1],
    [1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 1, 1, 1, 1, 1, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
for y in range(7):
    for x in range(10):
        if maze[y][x] == 1:
            canvas.create_rectangle(x * 80, y * 80, x * 80 + 79, y * 80 + 79, fill="skyblue", width=0)

img = tkinter.PhotoImage(file=resource_path("mimi_s.png"))
# MYCHR(캐릭터) 태그 추가
canvas.create_image(mx * 80 + 40, my * 80 + 40, image=img, tag="MYCHR")
main_proc()
root.mainloop()

명령어

pyinstaller -w -F --add-data "mimi_s.png;." -i "mimi_s.ico" maze_game.py

 

github: https://github.com/kimjinho1/Python-Game/blob/main/%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9D%B8%20%EA%B2%8C%EC%9E%84%20%EA%B0%9C%EB%B0%9C%20%EA%B8%B0%EC%88%A0/maze_game.ipynb

 

kimjinho1/Python-Game

Contribute to kimjinho1/Python-Game development by creating an account on GitHub.

github.com

728x90
반응형
728x90
반응형

mimi.png
0.02MB
mimi_s.png
0.01MB

이번엔 tkinter를 사용해서 기본적인 게임 개발 기술(실시간 처리, 키 입력 받기, 미로 게임)에 대해 알아보자.

 

1 실시간 처리 구현하기

파이썬에서는 after() 명령으로 실시간 처리를 수행할 수 있다.

import tkinter

# 사건 카운트 변수
tmr = 0
def count_up():
    global tmr # tmr을 전역 변수로 취급
    tmr += 1
    label["text"] = tmr
    root.after(1000, count_up) # 1초 후에 다시 이 함수를 실행
    
root = tkinter.Tk()
label = tkinter.Label(font=("Times New Roman", 80))
label.pack()
root.after(1000, count_up)
root.mainloop()

위의 코드를 실행하면 아래와 같이 윈도우에 표시된 숫자가 1초마다 1 증가하는 것을 확인할 수 있다,

 

2 키 입력 받기

사용자가 소프트웨어에 대해 키나 마우스를 조작하는 것을 이벤트(event)라고 하는데

파이썬에서는 bind() 명령으로 이벤트를 받을 수 있다.

2.1 이벤트 받기

import tkinter

# 키 코드 입력 변수 선언
key = 0
# 키를 눌렀을 때 실행할 함수 정의
def key_down(e):
    global key # key을 전역 변수로 취급
    key = e.keycode
    print(f"KEY: {key}")
    
root = tkinter.Tk()
root.title("키 코드 얻기")
root.bind("<KeyPress>", key_down)
root.mainloop()

 

위 코드를 실행하고 키보드 키를 누르면 아래와 같이 누른 키보드 키에 해당하는 아스키코드 값이 출력된다.

 

2.2 bind() 명령을 사용해 얻을 수 있는 이벤트

bind() 명령은 bind("<이벤트>", 함수) 같은 방식으로 사용하고 얻을 수 있는 주요 이벤트는 아래와 같다.

  • KeyPress 혹은 key -> 키를 누름  
  • KeyRelease -> 키를 눌렀다가 뗌  
  • Motion -> 마우스 포인터 움직임  
  • ButtonPress 혹은 Button -> 마우스 버튼 클릭

 

3 키 입력에 따라 이미지 움직이기

3.1 실시간 키 입력

import tkinter

# 키 코드 입력 변수 선언
key = 0
# 키를 눌렀을 때 실행할 함수 정의
def key_down(e):
    global key # key을 전역 변수로 취급
    key = e.keycode

# 실시간 처리를 수행할 함수 정의
def main_proc():
    label["text"] = key
    root.after(100, main_proc)
    
root = tkinter.Tk()
root.title("실시간 키 입력")
root.bind("<KeyPress>", key_down)
label = tkinter.Label(font=("Times New Roman", 80))
label.pack()
main_proc()
root.mainloop()

위의 코드를 실행하면 아래와 같이 누른 키의 코드가 윈도우에 표시된다.

주요 키 코드는 아래와 같다.

  • 방향키(좌상우하) -> 37~40  
  • Space키 -> 32  
  • Enter키 -> 13  
  • 알파벳 A~Z -> 65~90  
  • 숫자 0~9 -> 48~57

 

3.2 keysym 값을 사용해 판정하기

import tkinter

# 키 이름을 입력할 변수 선언
key = 0
# 키를 눌렀을 때 실행할 함수 정의
def key_down(e):
    global key # key을 전역 변수로 취급
    key = e.keysym # 눌려진 키 이름을 key에 대입

# 실시간 처리를 수행할 함수 정의
def main_proc():
    label["text"] = key
    root.after(100, main_proc)
    
root = tkinter.Tk()
root.title("실시간 키 입력")
root.bind("<KeyPress>", key_down)
label = tkinter.Label(font=("Times New Roman", 80))
label.pack()
main_proc()
root.mainloop()

위의 코드를 실행하고 Space키를 누르면 space, Enter나 return 키를 누르면 Return이라는 문자가 표시된다.  

keysym으로 얻은 키 이름은 윈도우, 맥 공통이므로 keysym 값으로 판정하는 것이 편리하다고 한다!

 

3.3 실시간으로 캐릭터 움직이기

import tkinter

# 키 이름을 입력할 변수 선언
key = 0
# 키를 눌렀을 때 실행할 함수 정의
def key_down(e):
    global key # key을 전역 변수로 취급
    key = e.keysym # 눌려진 키 이름을 key에 대입

# 키를 눌렀다 뗐을 때 실행할 함수 정의
def key_up(e):
    global key # key을 전역 변수로 취급
    key = "" # key에 빈 문자열 대입

cx = 400 # 캐릭터의 X 좌표를 관리할 변수
cy = 300 # 캐릭터의 Y 좌표를 관리할 변수

# 실시간 처리를 수행할 함수 정의
def main_proc():
    global cx, cy # cx, cy를 전역 변수로 선언
    if key == "Up": # 위쪽 방향키 눌렀다면 Y 좌표 20 감소
        cy -= 20
    if key == "Down": # 아래쪽 방향키 눌렀다면 Y 좌표 20 증가
        cy += 20
    if key == "Left": # 왼쪽 방향키 눌렀다면 X 좌표 20 감소
        cx -= 20
    if key == "Right": # 오른쪽 방향키 눌렀다면 X 좌표 20 증가
        cx += 20
    canvas.coords("MYCHR", cx, cy) # 캐릭터 이미지 이동
    root.after(100, main_proc) # 0.1초 후 main_proc 함수 지정

        
root = tkinter.Tk()
root.title("캐릭터 이동")
root.bind("<KeyPress>", key_down)
root.bind("<KeyRelease>", key_up)
canvas = tkinter.Canvas(width=800, height=600, bg="lightgreen")
canvas.pack()
img = tkinter.PhotoImage(file="mimi.png")
canvas.create_image(cx, cy, image=img, tag="MYCHR")
main_proc()
root.mainloop()

위의 코드를 실행하면 캐릭터가 표시되고 방향키를 통해 상하좌우로 이동할 수 있다.

coords()는 표시 중인 이미지를 새로운 위치로 이동시켜주는 명령이다. 

canvas.create_image(cx, cy, image=img, tag="MYCHR") 부분에서 tag는 캔버스에 그리는 
도형이나 이미지에 붙일 수 있고, 도형이나 이미지를 움직이거나 지우는 경우에 사용한다.  
태그 명은 자유롭게 붙일 수 있다!

 

4 미로 게임 구현하기

이제 앞에서 봤던 것들을 사용해서 미로 게임을 구현해보자.

단계 별로 추가된 것들을 이해하며 넘어가도록 하자!

단계 1: 미로 데이터 정의

maze -> 통로: 0, 벽: 1

import tkinter

root = tkinter.Tk()
root.title("미로 표시")
canvas = tkinter.Canvas(width=800, height=560, bg="white")
canvas.pack()
#  리스트로 미로 정의
maze = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 1, 0, 0, 1],
    [1, 0, 1, 1, 0, 0, 1, 0, 0, 1],
    [1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 1, 1, 1, 1, 1, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
for y in range(7):
    for x in range(10):
        # maze[y][x]가 1, 즉 벽이라면 회색 사각형을 그림
        if maze[y][x] == 1:
            canvas.create_rectangle(x*80, y*80, x*80 + 80, y*80 + 80, fill="gray")
root.mainloop()

아래와 같이 미로가 생성됐다.

 

단계 2: 미로 안 걷기

import tkinter

# 키 이름을 입력할 변수 선언
key = 0
# 키를 눌렀을 때 실행할 함수 정의
def key_down(e):
    global key # key을 전역 변수로 취급
    key = e.keysym # 눌려진 키 이름을 key에 대입

# 키를 눌렀다 뗐을 때 실행할 함수 정의
def key_up(e):
    global key # key을 전역 변수로 취급
    key = "" # key에 빈 문자열 대입

mx = 1 # 캐릭터의 가로 뱡향 위치를 관리하는 변수
my = 1 # 캐릭터의 세로 뱡향 위치를 관리하는 변수

# 실시간 처리를 수행할 함수 정의
def main_proc():
    global mx, my # mx, my를 전역 변수로 선언
    # key 방향이 통로라면 그 방향에 맞게 mx, my값을 변경
    if key == "Up" and maze[my-1][mx] == 0: 
        my -= 1
    if key == "Down" and maze[my+1][mx] == 0:
        my += 1
    if key == "Left" and maze[my][mx-1] == 0:
        mx -= 1
    if key == "Right" and maze[my][mx+1] == 0:
        mx += 1
    canvas.coords("MYCHR", mx*80 + 40, my*80 + 40) # 캐릭터 이미지 이동
    root.after(200, main_proc) # 0.2초 후 main_proc 함수 지정


root = tkinter.Tk()
root.title("미로 안 이동하기")
root.bind("<KeyPress>", key_down)
root.bind("<KeyRelease>", key_up)
canvas = tkinter.Canvas(width=800, height=560, bg="white")
canvas.pack()

maze = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 1, 0, 0, 1],
    [1, 0, 1, 1, 0, 0, 1, 0, 0, 1],
    [1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 1, 1, 1, 1, 1, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
for y in range(7):
    for x in range(10):
        if maze[y][x] == 1:
            canvas.create_rectangle(x * 80, y * 80, x * 80 + 79, y * 80 + 79, fill="skyblue", width=0)

img = tkinter.PhotoImage(file="mimi_s.png")
canvas.create_image(mx * 80 + 40, my * 80 + 40, image=img, tag="MYCHR")
main_proc()
root.mainloop()

 

고양이 캐릭터가 미로 안을 걸을 수 있게 됐다.

 

단계 3: 지나간 장소 분홍색으로 칠하기

maze -> 통로: 0, 벽: 1, 지나간 위치: 2

import tkinter

# 키 이름을 입력할 변수 선언
key = 0
# 키를 눌렀을 때 실행할 함수 정의
def key_down(e):
    global key # key을 전역 변수로 취급
    key = e.keysym # 눌려진 키 이름을 key에 대입

# 키를 눌렀다 뗐을 때 실행할 함수 정의
def key_up(e):
    global key # key을 전역 변수로 취급
    key = "" # key에 빈 문자열 대입

mx = 1 # 캐릭터의 가로 뱡향 위치를 관리하는 변수
my = 1 # 캐릭터의 세로 뱡향 위치를 관리하는 변수

# 실시간 처리를 수행할 함수 정의
def main_proc():
    global mx, my # mx, my를 전역 변수로 선언
    # key 방향이 통로라면 그 방향에 맞게 mx, my값을 변경
    if key == "Up" and maze[my-1][mx] == 0: 
        my -= 1
    if key == "Down" and maze[my+1][mx] == 0:
        my += 1
    if key == "Left" and maze[my][mx-1] == 0:
        mx -= 1
    if key == "Right" and maze[my][mx+1] == 0:
        mx += 1    
    '''
    캐릭터가 있는 장소가 벽이 아니라면 리스트 값을 2로 변경하고
    해당 위치를 분홍색으로 칠한다.
    '''
    if maze[my][mx] == 0:
        maze[my][mx] = 2
        canvas.create_rectangle(mx*80, my*80, mx*80 + 79, my*80 + 79, 
                                   fill="pink", width=0)
    canvas.delete("MYCHR")
    canvas.create_image(mx*80 + 40, my*80 + 40, image=img, tag="MYCHR") # 캐릭터 이미지 이동
    root.after(200, main_proc) # 0.2초 후 main_proc 함수 지정


root = tkinter.Tk()
root.title("미로를 칠하는 중")
root.bind("<KeyPress>", key_down)
root.bind("<KeyRelease>", key_up)
canvas = tkinter.Canvas(width=800, height=560, bg="white")
canvas.pack()

maze = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 1, 0, 0, 1],
    [1, 0, 1, 1, 0, 0, 1, 0, 0, 1],
    [1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 1, 1, 1, 1, 1, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
for y in range(7):
    for x in range(10):
        if maze[y][x] == 1:
            canvas.create_rectangle(x * 80, y * 80, x * 80 + 79, y * 80 + 79, fill="skyblue", width=0)

img = tkinter.PhotoImage(file="mimi_s.png")
canvas.create_image(mx * 80 + 40, my * 80 + 40, image=img, tag="MYCHR")
main_proc()
root.mainloop()

고양이 캐릭터가 지나간 길은 분홍색으로 칠해지고 왔던 길을 다시 돌아갈 수 없다.

 

단계 4: 게임 클리어 판정하기

import tkinter
import tkinter.messagebox #  메시지 박스 표시를 위해 import

# 키 이름을 입력할 변수 선언
key = 0
# 키를 눌렀을 때 실행할 함수 정의
def key_down(e):
    global key # key을 전역 변수로 취급
    key = e.keysym # 눌려진 키 이름을 key에 대입

# 키를 눌렀다 뗐을 때 실행할 함수 정의
def key_up(e):
    global key # key을 전역 변수로 취급
    key = "" # key에 빈 문자열 대입

mx = 1 # 캐릭터의 가로 뱡향 위치를 관리하는 변수
my = 1 # 캐릭터의 세로 뱡향 위치를 관리하는 변수
yuka = 0 # 칠해진 칸을 세는 변수

# 실시간 처리를 수행할 함수 정의
def main_proc():
    global mx, my, yuka # mx, my, yuka를 전역 변수로 선언
    # key 방향이 통로라면 그 방향에 맞게 mx, my값을 변경
    if key == "Up" and maze[my-1][mx] == 0: 
        my -= 1
    if key == "Down" and maze[my+1][mx] == 0:
        my += 1
    if key == "Left" and maze[my][mx-1] == 0:
        mx -= 1
    if key == "Right" and maze[my][mx+1] == 0:
        mx += 1    
    '''
    캐릭터가 있는 장소가 벽이 아니라면 리스트 값을 2로 변경,
    칠한 회수를 1 증가시키고, 해당 위치를 분홍색으로 칠한다.
    '''
    if maze[my][mx] == 0:
        maze[my][mx] = 2
        yuka += 1
        canvas.create_rectangle(mx*80, my*80, mx*80 + 79, my*80 + 79, 
                                   fill="pink", width=0)
    canvas.delete("MYCHR")
    canvas.create_image(mx*80 + 40, my*80 + 40, image=img, tag="MYCHR") # 캐릭터 이미지 이동
    
    # 30개 칸을 모두 칠했다면 클리어 메시지를 표시해주고
    # 그렇지 않다면 0.2초 후 main_proc 함수를 실행한다.
    if yuka == 30:
        canvas.update()
        tkinter.messagebox.showinfo("축하합니다!", "모든 바닥을 칠했습니다!")
    else:
        root.after(200, main_proc)


root = tkinter.Tk()
root.title("미로를 칠하는 중")
root.bind("<KeyPress>", key_down)
root.bind("<KeyRelease>", key_up)
canvas = tkinter.Canvas(width=800, height=560, bg="white")
canvas.pack()

maze = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 1, 0, 0, 1],
    [1, 0, 1, 1, 0, 0, 1, 0, 0, 1],
    [1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 1, 1, 1, 1, 1, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
for y in range(7):
    for x in range(10):
        if maze[y][x] == 1:
            canvas.create_rectangle(x * 80, y * 80, x * 80 + 79, y * 80 + 79, fill="skyblue", width=0)

img = tkinter.PhotoImage(file="mimi_s.png")
canvas.create_image(mx * 80 + 40, my * 80 + 40, image=img, tag="MYCHR")
main_proc()
root.mainloop()

통로를 다 지나면 게임 클리어 메시지 박스를 띄어준다.

 

단계 5: 다시 시작 처리 추가하기

import tkinter
import tkinter.messagebox #  메시지 박스 표시를 위해 import

# 키 이름을 입력할 변수 선언
key = 0
# 키를 눌렀을 때 실행할 함수 정의
def key_down(e):
    global key # key을 전역 변수로 취급
    key = e.keysym # 눌려진 키 이름을 key에 대입

# 키를 눌렀다 뗐을 때 실행할 함수 정의
def key_up(e):
    global key # key을 전역 변수로 취급
    key = "" # key에 빈 문자열 대입

mx = 1 # 캐릭터의 가로 뱡향 위치를 관리하는 변수
my = 1 # 캐릭터의 세로 뱡향 위치를 관리하는 변수
yuka = 0 # 칠해진 칸을 세는 변수

# 실시간 처리를 수행할 함수 정의
def main_proc():
    global mx, my, yuka # mx, my, yuka를 전역 변수로 선언
    
    # 왼쪽 Shift 키를 눌렀고 미로가 2칸 이상 칠해진 상태라면 게임 초기화
    if key == "Shift_L" and yuka > 1:
        canvas.delete("PAINT")
        mx = 1
        my = 1
        yuka = 0
        for y in range(7):
            for x in range(10):
                if maze[y][x] == 2:
                    maze[y][x] = 0
    
    # key 방향이 통로라면 그 방향에 맞게 mx, my값을 변경
    if key == "Up" and maze[my-1][mx] == 0: 
        my -= 1
    if key == "Down" and maze[my+1][mx] == 0:
        my += 1
    if key == "Left" and maze[my][mx-1] == 0:
        mx -= 1
    if key == "Right" and maze[my][mx+1] == 0:
        mx += 1    
    '''
    캐릭터가 있는 장소가 벽이 아니라면 리스트 값을 2로 변경,
    칠한 회수를 1 증가시키고, 해당 위치를 분홍색으로 칠한다.
    '''
    if maze[my][mx] == 0:
        maze[my][mx] = 2
        yuka += 1
        # 태그 추가
        canvas.create_rectangle(mx*80, my*80, mx*80 + 79, my*80 + 79, 
                                   fill="pink", width=0, tag="PAINT")
    canvas.delete("MYCHR")
    canvas.create_image(mx*80 + 40, my*80 + 40, image=img, tag="MYCHR") # 캐릭터 이미지 이동
    
    # 30개 칸을 모두 칠했다면 클리어 메시지를 표시해주고
    # 그렇지 않다면 0.2초 후 main_proc 함수를 실행한다.
    if yuka == 30:
        canvas.update()
        tkinter.messagebox.showinfo("축하합니다!", "모든 바닥을 칠했습니다!")
    else:
        root.after(200, main_proc)


root = tkinter.Tk()
root.title("미로를 칠하는 중")
root.bind("<KeyPress>", key_down)
root.bind("<KeyRelease>", key_up)
canvas = tkinter.Canvas(width=800, height=560, bg="white")
canvas.pack()

maze = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 1, 0, 0, 1],
    [1, 0, 1, 1, 0, 0, 1, 0, 0, 1],
    [1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 1, 1, 1, 1, 1, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
for y in range(7):
    for x in range(10):
        if maze[y][x] == 1:
            canvas.create_rectangle(x * 80, y * 80, x * 80 + 79, y * 80 + 79, fill="skyblue", width=0)

img = tkinter.PhotoImage(file="mimi_s.png")
canvas.create_image(mx * 80 + 40, my * 80 + 40, image=img, tag="MYCHR")
main_proc()
root.mainloop()

 

마지막으로 왼쪽 Shift 키를 누르면 게임이 리셋되는 기능을 추가했다!

 

github: https://github.com/kimjinho1/Python-Game/blob/main/%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9D%B8%20%EA%B2%8C%EC%9E%84%20%EA%B0%9C%EB%B0%9C%20%EA%B8%B0%EC%88%A0/%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9D%B8%20%EA%B2%8C%EC%9E%84%20%EA%B0%9C%EB%B0%9C%20%EA%B8%B0%EC%88%A0(%EC%8B%A4%EC%8B%9C%EA%B0%84%20%EC%B2%98%EB%A6%AC%2C%20%ED%82%A4%20%EC%9E%85%EB%A0%A5%20%EB%B0%9B%EA%B8%B0%2C%20%EB%AF%B8%EB%A1%9C%20%EA%B2%8C%EC%9E%84).ipynb 

 

kimjinho1/Python-Game

Contribute to kimjinho1/Python-Game development by creating an account on GitHub.

github.com

728x90
반응형
728x90
반응형

mina.png
0.12MB

이번엔 Tkinter의 Entry, Text, Checkbox, messagebox에 대해 알아보고 고양이 지수 진단 프로그램을 만들어봤다.

1 텍스트 입력 필드 배치하기

텍스트 입력을 수행하는 파이썬 GUI에는 Entry라는 1행 입력 필드와 Text라는 여러 행 입력 필드가 있다.

1.1 1행 텍스트 입력 필드

1행 입력 필드는 Entry() 명령으로 생성할 수 있고, 텍스트 입력 필드도 마찬가지로 place 명령으로 배치할 수 있다.

import tkinter

root = tkinter.Tk()
root.title("첫번째 텍스트 입력 필드")
root.geometry("400x200")
# 20 문자 크기 입력 필드 컴포넌트 생성
entry = tkinter.Entry(width=20)
# 윈도우에 입력 필드 컴포넌트 배치
entry.place(x=10, y=10)
root.mainloop()

코드를 실행하면 아래와 같이 텍스트를 입력 필드가 생성된다.

 

1.2 Entry 내 문자열 조작하기

Entry 내 문자열은 get() 명령으로 얻을 수 있다.

import tkinter

# 버튼 클릭 시 입력 필드(entry)의 문자열을 받아서 버튼의 문자열에 대입해주는 함수
def click_btn():
    txt = entry.get()
    button["text"] = txt

root = tkinter.Tk()
root.title("첫번째 텍스트 입력 필드")
root.geometry("400x200")
entry = tkinter.Entry(width=20)
entry.place(x=20, y=20)
button = tkinter.Button(text="문자열 얻기", command=click_btn)
button.place(x=20, y=100)
root.mainloop()

코드를 실행한 후에 입력 필드에 아무거나 입력한 후에 버튼을 눌러주면

아래와 같이 버튼의 텍스트가 입력 필드의 문자열로 바뀐다.

Entry 내 문자열을 삭제할 때는 delete(), 삽입할 때는 insert() 명령을 사용할 수 있다.

import tkinter

# 버튼 클릭 시 입력 필드(entry)의 문자열을 받아서 버튼의 문자열에 대입해주는 함수
def click_btn_get():
    txt = entry.get()
    get_button["text"] = txt

# 버튼 클릭 시 버튼의 문자열의 맨 처음 인덱스에 abc를 삽입해주는 함수
def click_btn_insert():
    entry.insert(0, "abc")

# 버튼 클릭 시 버튼의 문자열의 맨 처음 인덱스를 삭제해주는 함수
def click_btn_delete():
    entry.delete(0)
    
root = tkinter.Tk()
root.title("첫번째 텍스트 입력 필드")
root.geometry("400x200")
entry = tkinter.Entry(width=20)
entry.place(x=20, y=20)
get_button = tkinter.Button(text="문자열 얻기", command=click_btn_get)
get_button.place(x=20, y=100)
insert_button = tkinter.Button(text="insert", command=click_btn_insert)
insert_button.place(x=20, y=130)
delete_button = tkinter.Button(text="delete", command=click_btn_delete)
delete_button.place(x=20, y=160)
root.mainloop()

문자열 얻기 버튼은 입력 필드의 텍스트를 가져오고, insert버튼은 입력 필드에 "abc"를 삽입,

delete버튼은 입력 필드의 맨 앞 글자를 지워준다! 

 

2 여러 행 텍스트 입력 필드 배치하기

여려 행 입력 필드는 Text() 명령으로 생성할 수 있다.

Text 내 문자열 조작하기

Entry와 똑같이 Text도 insert, get, delete를 사용해 문자열을 조작할 수 있는데, 예를 들어 입력 필드의 

문자열 전체를 가져오고 싶다면 get("1.0", "end-1c") 같은 방식으로  사용하면 된다. 

1.0은 1행 0번째 문자열, end-1c는 문자열의 끝 인덱스, tkinter.END는 문자열의 끝의 다음 인덱스를 의미한다.

import tkinter

# 버튼 클릭 시 입력 필드의 문자열 전체를 가져오는 함수
def click_btn_get():
    txt = text.get("1.0", "end-1c")
    get_button["text"] = txt

# 버튼 클릭 시 텍스트 입력 필드 마지막에 문자열을 추가해주는 함수
def click_btn_insert():
    text.insert(tkinter.END, "몬스터가 나타났다!")    
    
# 버튼 클릭 시 입력 필드의 문자열 전체를 지워주는 함수
def click_btn_delete():
    text.delete("1.0", "end-1c")
    
root = tkinter.Tk()
root.title("여러 행 텍스트 입력")
root.geometry("400x400")
get_button = tkinter.Button(text="메시지", command=click_btn_get)
get_button.place(x=20, y=120)
insert_button = tkinter.Button(text="insert", command=click_btn_insert)
insert_button.place(x=20, y=240)
delete_button = tkinter.Button(text="delete", command=click_btn_delete)
delete_button.place(x=20, y=280)
text = tkinter.Text()
text.place(x=20, y=10, width=360, height=100)
root.mainloop()

동작은 Entry 때와 똑같다!

 

3 체크 버튼 배치하기

3.1 체크 버튼 배치하기

체크 버튼은 Checkbutton() 명령으로 만들 수 있다.

import tkinter

root = tkinter.Tk()
root.title("체크 버튼 다루기")
root.geometry("400x200")
# 체크 버튼 컴포넌트 생성
cbtn = tkinter.Checkbutton(text="체크 버튼")
# 체크 버튼 컴포넌트 배치
cbtn.pack()
root.mainloop()

코드를 실행하면 아래와 같이 체크 버튼이 생성된다.

 

3.2 체크 여부 확인하기

체크 버튼의 체크 여부는 BooleanVar() 명령을 사용해서 설정할 수 있다.

아래는 체크된 상태의 체크 버튼을 생성해주는 코드인데

import tkinter

root = tkinter.Tk()
root.title("처음부터 체크된 상태 만들기")
root.geometry("400x200")
# BooleanVar() 객체 준비
cval = tkinter.BooleanVar()
# 객체 True 설정
cval.set(True)
cbtn = tkinter.Checkbutton(text="체크 버튼", variable=cval)
cbtn.pack()
root.mainloop()

실행하면 아래와 같이 체크 버튼이 체크된 상태로 생성된 것을 확인할 수 있다.

체크 여부를 확인할 때는 get() 메서드를 사용하면 된다.

import tkinter

# 체크 버튼 클릭 시 체크 여부를 출력해주는 함수
def check():
    if cval.get() == True:
        print("체크되어 있습니다")
    else:
        print("체크되어 있지 않습니다")
    
root = tkinter.Tk()
root.title("처음부터 체크된 상태 만들기")
root.geometry("400x200")
cval = tkinter.BooleanVar()
cval.set(True)
cbtn = tkinter.Checkbutton(text="체크 버튼", variable=cval, command=check)
cbtn.pack()
root.mainloop()

코드를 실행하고 체크 버튼을 누르면 체크 여부가 출력된다.

 

4 메시지 박스 표시하기

메시지 박스를 표시하는 주요 명령어는 아래와 같다.

  • showinfo() -> 정보를 표시해주는 메시지 박스  
  • showwarning() -> 경고를 표시해주는 메시지 박스  
  • showerror() -> 에러를 표시해주는 메시지 박스  
  • askyesno() -> "네", '아니오' 버튼이 있는 메시지 박스  
  • askokcancel() -> 'ok', '취소' 버튼이 있는 메시지 박스

메시지 박스를 사용하기 위해서는 tkinter.messagebox 모듈을 import 해줘야 한다.

import tkinter
import tkinter.messagebox

# 버튼 클릭 시 메시지 박스를 표시해주는 함수
def click_btn():
    tkinter.messagebox.showinfo("정보", "버튼을 눌렀습니다")

root = tkinter.Tk()
root.title("첫번째 메시지 박스")
root.geometry("400x200")
btn = tkinter.Button(text="테스트", command=click_btn)
btn.pack()
root.mainloop()

 

코드를 실행하고 버튼을 누르면 아래와 같이 메시지 박스가 표시된다! 

 

5 고양이 지수 진단 프로그램 만들기

이제 간단한 고양지 지수 진단 프로그램을 만들어보자.

순서대로 코드를 작성해보고 결과를 확인하면 좋을 것 같다!

단계 1: GUI 배치하기

import tkinter

root = tkinter.Tk()
root.title("고양이 지수 진단 게임")
root.resizable(False, False)
canvas = tkinter.Canvas(root, width=800, height=600)
canvas.pack()
gazou = tkinter.PhotoImage(file="mina.png")
canvas.create_image(400, 300, image=gazou)
button = tkinter.Button(text="진단하기", font=("Times New Roman", 32),
                            bg="lightgreen")
button.place(x=400, y=480)
text = tkinter.Text(width=40, height=5, font=("Times New Roman", 16))
text.place(x=320, y=30)
root.mainloop()

 

단계 2: 여러 체크 버튼 배치

import tkinter

root = tkinter.Tk()
root.title("고양이 지수 진단 게임")
root.resizable(False, False)
canvas = tkinter.Canvas(root, width=800, height=600)
canvas.pack()
gazou = tkinter.PhotoImage(file="mina.png")
canvas.create_image(400, 300, image=gazou)
button = tkinter.Button(text="진단하기", font=("Times New Roman", 32),
                            bg="lightgreen")
button.place(x=400, y=480)
text = tkinter.Text(width=40, height=5, font=("Times New Roman", 16))
text.place(x=320, y=30)

# BooleanVal 객체용 리스트
bvar = [None] * 7
# 체크 버튼용 리스트
cbtn = [None] * 7
# 체크 버튼 질문 정의
ITEM = [
    "높은 곳이 좋다",
    "공을 보면 굴리고 싶어진다",
    "깜짝 놀라면 털이 곤두선다",
    "쥐구멍이 마음에 든다",
    "개에게 적대감을 느낀다",
    "생선 뼈를 발라 먹고 싶다",
    "밤, 기운이 난다"
]

# 반복해서 체크 버튼 배치
for i in range(7):
    bvar[i] = tkinter.BooleanVar()
    bvar[i].set(False)
    cbtn[i] = tkinter.Checkbutton(text=ITEM[i], font=("Times New Roman", 12), 
                                    variable=bvar[i], bg="#dfe")
    cbtn[i].place(x=400, y=160 + 40*i)

root.mainloop()

 

단계 3: 체크된 버튼 얻기

import tkinter

# 버튼 클릭 시 체크한 버튼의 개수를 입력 필드에 대입해주는 함수
def click_btn():
    pts = 0
    for i in range(7):
        if bvar[i].get() == True:
            pts += 1
    text.delete("1.0", tkinter.END)
    text.insert("1.0", f"체크된 수는 {pts}")

root = tkinter.Tk()
root.title("고양이 지수 진단 게임")
root.resizable(False, False)
canvas = tkinter.Canvas(root, width=800, height=600)
canvas.pack()
gazou = tkinter.PhotoImage(file="mina.png")
canvas.create_image(400, 300, image=gazou)
button = tkinter.Button(text="진단하기", font=("Times New Roman", 32),
                            bg="lightgreen", command=click_btn)
button.place(x=400, y=480)
text = tkinter.Text(width=40, height=5, font=("Times New Roman", 16))
text.place(x=320, y=30)

# BooleanVal 객체용 리스트
bvar = [None] * 7
# 체크 버튼용 리스트
cbtn = [None] * 7
# 체크 버튼 질문 정의
ITEM = [
    "높은 곳이 좋다",
    "공을 보면 굴리고 싶어진다",
    "깜짝 놀라면 털이 곤두선다",
    "쥐구멍이 마음에 든다",
    "개에게 적대감을 느낀다",
    "생선 뼈를 발라 먹고 싶다",
    "밤, 기운이 난다"
]

# 반복해서 체크 버튼 배치
for i in range(7):
    bvar[i] = tkinter.BooleanVar()
    bvar[i].set(False)
    cbtn[i] = tkinter.Checkbutton(text=ITEM[i], font=("Times New Roman", 12), 
                                    variable=bvar[i], bg="#dfe")
    cbtn[i].place(x=400, y=160 + 40*i)

root.mainloop()

 

 

단계 4: 주석 출력하기

import tkinter

# 버튼 클릭 시 체크한 버튼의 개수를 확인하고 고양이 지수를 출력해주는 함수
def click_btn():
    pts = 0
    for i in range(7):
        if bvar[i].get() == True:
            pts += 1
    nekodo = int(100 * pts / 7)
    text.delete("1.0", tkinter.END)
    text.insert("1.0", f"<진단결과>\n당신의 고양이 지수는 {nekodo}%입니다\n {RESULT[pts]}")

root = tkinter.Tk()
root.title("고양이 지수 진단 게임")
root.resizable(False, False)
canvas = tkinter.Canvas(root, width=800, height=600)
canvas.pack()
gazou = tkinter.PhotoImage(file="mina.png")
canvas.create_image(400, 300, image=gazou)
button = tkinter.Button(text="진단하기", font=("Times New Roman", 32),
                            bg="lightgreen", command=click_btn)
button.place(x=400, y=480)
text = tkinter.Text(width=40, height=5, font=("Times New Roman", 16))
text.place(x=320, y=30)

# BooleanVal 객체용 리스트
bvar = [None] * 7
# 체크 버튼용 리스트
cbtn = [None] * 7
# 체크 버튼 질문 정의
ITEM = [
    "높은 곳이 좋다",
    "공을 보면 굴리고 싶어진다",
    "깜짝 놀라면 털이 곤두선다",
    "쥐구멍이 마음에 든다",
    "개에게 적대감을 느낀다",
    "생선 뼈를 발라 먹고 싶다",
    "밤, 기운이 난다"
]
# 진단 결과 주석을 리스트로 정의
RESULT = [
    "전생에 고양이었을 가능성은 매우 낮습니다.",
    "보통 사람입니다.",
    "특별히 이상한 곳은 없습니다.",
    "꽤 고양이 다운 구석이 있습니다.",
    "고양이와 비슷한 성격 같습니다.",
    "고양이와 근접한 성격입니다.",
    "전생에 고양이었을지도 모릅니다.",
    "겉모습은 사람이지만, 속은 고양이일 가능성이 있습니다."
]

# 반복해서 체크 버튼 배치
for i in range(7):
    bvar[i] = tkinter.BooleanVar()
    bvar[i].set(False)
    cbtn[i] = tkinter.Checkbutton(text=ITEM[i], font=("Times New Roman", 12), 
                                    variable=bvar[i], bg="#dfe")
    cbtn[i].place(x=400, y=160 + 40*i)

root.mainloop()

 

끝! 아래는 최종 결과이다.

 

github: https://github.com/kimjinho1/Python-Game/blob/main/GUI%20%EA%B8%B0%EC%B4%88%202(tkinter)/GUI%20%EA%B8%B0%EC%B4%88%202.ipynb 

 

kimjinho1/Python-Game

Contribute to kimjinho1/Python-Game development by creating an account on GitHub.

github.com

728x90
반응형
728x90
반응형

icon.ico
0.16MB

이번엔 저번에 만든 가위바위보 게임 프로그램을 pyinstaller 사용해서 exe파일로 만들어봤다.

가위바위보 게임: https://jinho-study.tistory.com/1078 

 

파이썬으로 가위바위보 게임 구현하기(Tkinter)

저번에는 tkinter(window, label, button, canvas)에 대해 알아보고 간단한 제비뽑기 프로그램을 만들었는데 https://jinho-study.tistory.com/1077 파이썬 GUI 기초 1(Tkinter ) 1 GUI란? GUI: 소프트웨어의 조..

jinho-study.tistory.com

 

아래는 전체 코드인데 파일 경로 부분에서 조금 추가된 것이 있다.

import tkinter
import random
# from functools import partial
from PIL import ImageTk
import os


# PyInstaller에 의해 임시폴더에서 실행될 경우 임시폴더로 접근하는 함수
def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

def click_btn(user_choice):
    # 가위, 바위, 보 중 랜덤으로 하나 선택하고 com_rps_label에 출력
    computer_choice = random.choice(["가위", "바위", "보"])
    com_rps_label["text"] = computer_choice
    com_rps_label.update()
    
    # 사용자가 선택한 것을 user_rps_label에 출력
    user_rps_label["text"] = user_choice
    user_rps_label.update()
    
    # 승패를 판별하고 result_label에 출력
    res = ""
    if computer_choice == user_choice:
        res = "비김!" 
    else:
        if user_choice == "가위":
            res = "승리!" if computer_choice == "보" else "패배!"
        elif user_choice == "바위":
            res = "승리!" if computer_choice == "가위" else "패배!"
        elif user_choice == "보":
            res = "승리!" if computer_choice == "바위" else "패배!"
    result_label["text"] = res
    result_label.update()
    
if __name__ == "__main__":
    root = tkinter.Tk() 
    root.title("가위바위보 프로그램")
    root.resizable(False, False) 
    canvas = tkinter.Canvas(root, width=800, height=480)
    canvas.pack()

    # 이미지 파일에 따라 tkinter.PhotoImage가 잘 작동하지 않는 것 같다.
    # ImageTk.PhotoImage 명령을 대신 사용했다.
    # gazou = tkinter.PhotoImage(file="alphago.png")
    gazou = ImageTk.PhotoImage(file=resource_path("alphago.png"))
    canvas.create_image(400, 240, image=gazou)
    
    # 컴퓨터, 사용자 라벨
    com_label = tkinter.Label(root, text="컴퓨터", font=("Times New Roman", 55),
                            bg="white")
    user_label = tkinter.Label(root, text="사용자", font=("Times New Roman", 55),
                            bg="white")
    # 컴퓨터 가위, 바위, 보 라벨
    com_rps_label = tkinter.Label(root, text="??", font=("Times New Roman", 55),
                            bg="white")
    # 사용자 가위, 바위, 보 라벨
    user_rps_label = tkinter.Label(root, text="??", font=("Times New Roman", 55),
                            bg="white")
    # 승패 결과 라벨
    result_label = tkinter.Label(root, text="승패", font=("Times New Roman", 60),
                            bg="white")
    com_label.place(x=350, y=25)
    com_rps_label.place(x=600, y=25)
    user_label.place(x=350, y=145)
    user_rps_label.place(x=600, y=145)
    result_label.place(x=470, y=250)

    # 가위, 바위, 보 버튼 3개
    # 버튼 명령에 인수를 전달하기 위해 lambda를 사용했다.
    # partial(clict_btn, "가위") 같은 방식으로도 사용할 수 있다.
    s_button = tkinter.Button(root, text="가위", font=("Times New Roman", 36),
                            fg="skyblue", command=lambda: click_btn("가위"))
    r_button = tkinter.Button(root, text="바위", font=("Times New Roman", 36),
                            fg="skyblue", command=lambda: click_btn("바위"))
    p_button = tkinter.Button(root, text="보", font=("Times New Roman", 36),
                            fg="skyblue", command=lambda: click_btn("보"))
    s_button.place(x=350, y=365)
    r_button.place(x=520, y=365)
    p_button.place(x=690, y=365)

    root.mainloop() 

 

파일 경로 문제

처음에 exe파일로 만드는 것은 바로 성공했는데 계속~~ 실행이 안됬었는데 이유를 찾아보니 파일 경로 문제였다.

pyinstaller로 만든 exe파일은 실행 시에 필요한 모든 파일을 임시 폴더에 풀어낸 후에 실행되는데,

이때 이 임시 폴더는 실행시마다 경로가 달라진다. 그래서 필요한 파일에 그냥 접근하면 오류가 나기에 

sys._MEIPASS 라는 변수를 사용해서 접근해야 한다. 아래 글 덕분에 알게 되었다!

https://www.inflearn.com/questions/57133

 

이미지가 onefile로 안들어간거 같아요 - 인프런 | 질문 & 답변

[사진] 안녕하세요 강의 몇번씩 보고 있어요 pyautogui exe만들려고하는데요 이미지가 함께 안들어가는거 같아요 onefile로 했는데요 - 질문 & 답변 | 인프런...

www.inflearn.com

아래 함수를 사용해서 이미지를 불러오면 OK다.

def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

 

pyinstaller 명령어

pyinstaller 명령어를 알아보자면 

-w: 콘솔 윈도우 표시 안 함

-F: 1개의 exe파일로 변환

-i: 아이콘 설정

--add-data <SRC;DEST or SRC:DEST>: 파일 추가 

등이 있는데 여기서 특히 --add-data는 데이터 유형에 따라 DEST가 다르다.

  • Single file: "alphago.png:." -> DEST = .
  • multiple files: "images/*.png:sfx" -> DEST = sfx
  • folder: "images:data" -> DEST = data

추가로 직접 사용은 안 해봤지만 아래 같은 옵션들도 존재한다.

-n: 이름 지정

-D: 한 개 폴더로 변환

--hidden-import: 코드에서 직접적으로 보이지 않는 모듈 이름을 지정 

아래 글을 참고했다!

https://stackoverflow.com/questions/41870727/pyinstaller-adding-data-files

 

Pyinstaller adding data files

I'm struggling with pyinstaller. Whenever I build this specific script with a kivy GUI and a .kv file, and run the .exe after the build, I get a fatal error: IOError: [Errno 2] No such file or dire...

stackoverflow.com

 

pyinstaller -w -F --add-data "alphago.png;." -i "icon.ico" rps_game.py

 

이제 위의 명령어를 실행하면 dist 폴더 안에 rps_game.exe 파일이 생긴 것을 확인할 수 있다. 

아래 같이 아이콘도 잘 적용되었다. 아이콘 파일은 맨 위에 첨부했다! 

exe 파일

 

만약 exe파일이 실행이 안된다면 필요한 데이터가 잘 추가되었는지, 현재 환경에 코드에 필요한 라이브러리가

다 설치가 되어있는 상태인지, resource_path 함수를 사용해서 데이터를 불러왔는지 등을 확인해주면 될 것 같다.

여기서 데이터가 잘 추가되었는지는 pyinstaller 명령어를 실행했을 때 생기는 .spec 파일을

열어보면 알 수 있는데, datas 쪽을 확인해주면 된다! 

코드로 치기 귀찮으면 그냥 여기서 데이터를 직접 추가해줘도 된다.

spec 파일 내부

 

추가로 아나콘다 환경에서 pyinstaller를 사용하면 exe파일의 용량이 엄청 커진다.

예를 들어 위의 가위바위보 게임을 exe파일로 생성했을 때 아나콘다에서는 74Mb인데 

그냥 cmd에서 생성하면 12Mb이다. 이거에 대한 정확한 이유는 모르겠다만

내 아나콘다 환경에 쓸데없는 라이브러리가 많이 포함되어있어서 그런 것 같기도 하다.

 

github: https://github.com/kimjinho1/Python-Game

 

kimjinho1/Python-Game

Contribute to kimjinho1/Python-Game development by creating an account on GitHub.

github.com

728x90
반응형
728x90
반응형

alphago.png
0.08MB

저번에는 tkinter(window, label, button, canvas)에 대해 알아보고 간단한 제비뽑기 프로그램을 만들었는데

https://jinho-study.tistory.com/1077

 

파이썬 GUI 기초 1(Tkinter )

1 GUI란? GUI: 소프트웨어의 조작 방법을 직관적으로 알 수 있게 해주는 인터페이스 1.1 윈도우 표시하기 import tkinter root = tkinter.Tk() # 윈도우 요소(객체) 생성 root.mainloop() # 윈도우 표시 tkinter.T..

jinho-study.tistory.com

 

이번에는 배운걸 좀 더 활용해서 가위바위보 게임을 만들어봤다.

기존 제비뽑기 프로그램에서 배경화면을 변경, 라벨과 버튼을 추가, 가위바위보 승패 판별 및 출력 기능을 추가했다. 

맨 위에 첨부한 이미지 파일이 코드와 같은 경로에 있어야 한다!!

 

아래 이미지는 코드를 실행하고 플레이해본 결과와 전체 코드이다!

가위바위보 프로그램

import tkinter
import random
# from functools import partial
from PIL import ImageTk

def click_btn(user_choice):
    # 가위, 바위, 보 중 랜덤으로 하나 선택하고 com_rps_label에 출력
    computer_choice = random.choice(["가위", "바위", "보"])
    com_rps_label["text"] = computer_choice
    com_rps_label.update()
    
    # 사용자가 선택한 것을 user_rps_label에 출력
    user_rps_label["text"] = user_choice
    user_rps_label.update()
    
    # 승패를 판별하고 result_label에 출력
    res = ""
    if computer_choice == user_choice:
        res = "비김!" 
    else:
        if user_choice == "가위":
            res = "승리!" if computer_choice == "보" else "패배!"
        elif user_choice == "바위":
            res = "승리!" if computer_choice == "가위" else "패배!"
        elif user_choice == "보":
            res = "승리!" if computer_choice == "바위" else "패배!"
    result_label["text"] = res
    result_label.update()
    
        
root = tkinter.Tk() 
root.title("가위바위보 프로그램")
root.resizable(False, False) 
canvas = tkinter.Canvas(root, width=800, height=480)
canvas.pack()

# 이미지 파일 타입에 따라 tkinter.PhotoImage가 잘 작동하지 않는 것 같다.
# ImageTk.PhotoImage 명령을 대신 사용했다.
# gazou = tkinter.PhotoImage(file="alphago.png")
gazou = ImageTk.PhotoImage(file="alphago.png")
canvas.create_image(400, 240, image=gazou)

# 컴퓨터, 사용자 라벨
com_label = tkinter.Label(root, text="컴퓨터", font=("Times New Roman", 55),
                        bg="white")
user_label = tkinter.Label(root, text="사용자", font=("Times New Roman", 55),
                        bg="white")
# 컴퓨터 가위, 바위, 보 라벨
com_rps_label = tkinter.Label(root, text="??", font=("Times New Roman", 55),
                        bg="white")
# 사용자 가위, 바위, 보 라벨
user_rps_label = tkinter.Label(root, text="??", font=("Times New Roman", 55),
                        bg="white")
# 승패 결과 라벨
result_label = tkinter.Label(root, text="승패", font=("Times New Roman", 60),
                        bg="white")
com_label.place(x=350, y=25)
com_rps_label.place(x=600, y=25)
user_label.place(x=350, y=145)
user_rps_label.place(x=600, y=145)
result_label.place(x=470, y=250)

# 가위, 바위, 보 버튼 3개
# 버튼 명령에 인수를 전달하기 위해 lambda를 사용했다.
# partial(clict_btn, "가위") 같은 방식으로도 사용할 수 있다.
s_button = tkinter.Button(root, text="가위", font=("Times New Roman", 36),
                        fg="skyblue", command=lambda: click_btn("가위"))
r_button = tkinter.Button(root, text="바위", font=("Times New Roman", 36),
                        fg="skyblue", command=lambda: click_btn("바위"))
p_button = tkinter.Button(root, text="보", font=("Times New Roman", 36),
                        fg="skyblue", command=lambda: click_btn("보"))
s_button.place(x=350, y=365)
r_button.place(x=520, y=365)
p_button.place(x=690, y=365)

root.mainloop() 

 

주석으로도 설명이 되어있긴 하다만 추가한 라벨과 버튼, 수정된 부분을 정리하자면 아래와 같다. 

라벨 

com_label, user_label

  • "컴퓨터"와 "사용자"를 출력해주는 라벨

com_rps_label

  • 컴퓨터가 가위, 바위, 보 중 랜덤으로 고른 것을 출력해주는 라벨
  • 처음엔 "??"가 출력되고 나중엔 "가위", "바위", "보" 중 하나가 출력되는 식이다.
  • s_button, r_button, p_button 중 하나가 눌릴 때마다 업데이트된다. 

user_rps_label

  • 사용자가 선택한 버튼에 따라서 "가위", "바위", "보" 중 하나를 출력해주는 라벨
  • s_button, r_button, p_button 중 하나가 눌릴 때마다 업데이트된다.

result_label

  • 가위바위보 승패 결과를 출력해주는 라벨
  • 처음엔 "승패"가 출력되고 나중엔 "승리!", "패배!", "비김!" 중 하나가 출력되는 식이다.
  • s_button, r_button, p_button 중 하나가 눌릴 때마다 업데이트된다.

 

버튼

s_button, r_button, p_button

  • 순서대로 "가위", "바위", "보" 중 하나를 사용자가 선택할 수 있도록 하기 위해 만든 버튼들이다. 
  • 이 중 하나를 누르면 click_btn 함수가 실행되는데 이때 인자(user_choice)로 각 함수에 맞는 값이 전달된다.
  • s_button -> "가위", r_button -> "바위", p_button -> "보"

 

tkinter.PhotoImage -> ImageTk.PhotoImage

tkinter.PhotoImage는 이미지 파일 타입에 따라 오류가 날 때가 있는 것 같아 ImageTk.PhotoImage를 대신 사용했다.

만약 ImageTk.PhotoImage 부분에서 오류가 난다면 pillow를 설치해보자! -> pip install pillow

 

# 이미지 파일 타입에 따라 tkinter.PhotoImage가 잘 작동하지 않는 것 같다.
# ImageTk.PhotoImage 명령을 대신 사용했다.
# gazou = tkinter.PhotoImage(file="alphago.png")
gazou = ImageTk.PhotoImage(file="alphago.png")
canvas.create_image(400, 240, image=gazou)

 

click_btn 함수 수정

  • 컴퓨터가 가위, 바위, 보 중 랜덤으로 하나를 선택하게 하고 선택한 것을 com_rps_label에 업데이트
  • 인자로 받은 user_choice(사용자가 선택한 버튼 -> "가위", "바위", "보")를 user_rps_label에 업데이트
  • 컴퓨터와 사용자의 선택을 비교하여 승패를 판결하고 그 결과를 result_label에 업데이트

위의 3개의 기능을 구현했다.

def click_btn(user_choice):
    # 가위, 바위, 보 중 랜덤으로 하나 선택하고 com_rps_label에 출력
    computer_choice = random.choice(["가위", "바위", "보"])
    com_rps_label["text"] = computer_choice
    com_rps_label.update()
    
    # 사용자가 선택한 것을 user_rps_label에 출력
    user_rps_label["text"] = user_choice
    user_rps_label.update()
    
    # 승패를 판별하고 result_label에 출력
    res = ""
    if computer_choice == user_choice:
        res = "비김!" 
    else:
        if user_choice == "가위":
            res = "승리!" if computer_choice == "보" else "패배!"
        elif user_choice == "바위":
            res = "승리!" if computer_choice == "가위" else "패배!"
        elif user_choice == "보":
            res = "승리!" if computer_choice == "바위" else "패배!"
    result_label["text"] = res
    result_label.update()

 

command에 인자가 있는 함수 넘겨주기

command로 인자가 있는 함수를 넘겨줄 때는 제비뽑기 프로그램처럼 그냥 command=click_btn 같은 식으로

하면 안되고 lambda나 partial를 사용해서 처리해줘야 하는 것 같다. 예를 들면

lambda -> command=lambda: click_btn("가위") 

partial -> command=partial(clict_btn, "가위") 같은 식이다.

# 버튼 명령에 인수를 전달하기 위해 lambda를 사용했다.
# partial(clict_btn, "가위") 같은 방식으로도 사용할 수 있다.
s_button = tkinter.Button(root, text="가위", font=("Times New Roman", 36),
                        fg="skyblue", command=lambda: click_btn("가위"))
r_button = tkinter.Button(root, text="바위", font=("Times New Roman", 36),
                        fg="skyblue", command=lambda: click_btn("바위"))
p_button = tkinter.Button(root, text="보", font=("Times New Roman", 36),
                        fg="skyblue", command=lambda: click_btn("보"))

 

 

코드 github 주소: https://github.com/kimjinho1/Python-Game/blob/main/GUI%20%EA%B8%B0%EC%B4%88%201(tkinter)/%EA%B0%80%EC%9C%84%EB%B0%94%EC%9C%84%EB%B3%B4.ipynb 

 

kimjinho1/Python-Game

Contribute to kimjinho1/Python-Game development by creating an account on GitHub.

github.com

728x90
반응형
728x90
반응형

hyunju.png
0.08MB
miko.png
0.52MB

오늘은 GUI, Tkinter의 Window, Label, Button, Canvas에 대해 알아보고 간단한 제비뽑기 프로그램을 만들어봤다.

1 GUI란?

GUI: 소프트웨어의 조작 방법을 직관적으로 알 수 있게 해주는 인터페이스

1.1 윈도우 표시하기

import tkinter

root = tkinter.Tk() # 윈도우 요소(객체) 생성
root.mainloop() # 윈도우 표시

tkinter.Tk() 명령으로 윈도우 요소(객체)를 생성할 수 있다.

mainloop 명령으로 화면에 윈도우를 표시할 수 있다.

1.2 제목과 크기 지정하기

import tkinter

root = tkinter.Tk() 
root.title("첫 번째 윈도우") # 윈도우 제목 지정
root.geometry("800x600") # 윈도우 크기 지정
root.mainloop() 

제목은 title, 크기는 geometry 명령으로 지정할 수 있다.

 

2 라벨 배치하기

2.1 라벨 배치

import tkinter

root = tkinter.Tk() 
root.title("첫 번째 윈도우") 
root.geometry("800x600") 
# 라벨 컴포넌트 생성
label = tkinter.Label(root, text="라벨 문자열", font=("Times New Roman", 24)) 
# 윈도우에 라벨 배치
label.place(x=200, y=100)
root.mainloop() 

라벨은 Label 명령으로 만들고 place 명령으로 배치한다.

아래 같이 라벨이 지정한 위치에 생성된다.

2.2 사용 가능한 폰트 확인

import tkinter
import tkinter.font

root = tkinter.Tk()
fonts = tkinter.font.families()
print(len(fonts))

무려 553개나 사용할 수 있다! 하지만 계속 Times New Roman을 사용할 예정이다.

 

3 버튼 배치하기

3.1 버튼 배치

import tkinter

root = tkinter.Tk() 
root.title("첫 번째 버튼") 
root.geometry("800x600") 
# 버튼 컴포넌트 생성
button = tkinter.Button(root, text="버튼 문자열", font=("Times New Roman", 24))
# 윈도우에 버튼 배치
button.place(x=200, y=100) 
root.mainloop() 

라벨과 마찬가지로 버튼은 Button 명령으로 만든 뒤 place 명령으로 배치한다.

아직은 버튼을 눌러도 아무 반응이 없다.

3.2 버튼 클릭 시 반응

import tkinter

# 버튼 문자열 변경 함수
def click_btn():
    button["text"] = "클릭했습니다"

root = tkinter.Tk() 
root.title("첫 번째 버튼") 
root.geometry("800x600") 
# 버튼 컴포넌트 생성 -> command=로 클릭 시 동작할 함수 지정
button = tkinter.Button(root, text="버튼 문자열",
                        font=("Times New Roman", 24), command=click_btn)
button.place(x=200, y=100) 
root.mainloop() 

버튼을 클릭했을 때의 처리를 함수로 정의해놓고, 버튼을 생성하는 식 안에 'command=함수'를 입력하면

버튼을 클릭했을 때 해당 함수를 실행하고 이를 확인할 수 있다.

이제 버튼을 누르면 "버튼 문자열"이 "클릭했습니다"로 바뀐다.

 

4 캔버스 사용하기

캔버스: 이미지나 도형을 그리는 GUI

4.1 캔버스 배치

import tkinter

root = tkinter.Tk() 
root.title("첫 번째 캔버스") 
# 캔버스 컴포넌트 생성
canvas = tkinter.Canvas(root, width=400, height=600,
                       bg="skyblue")
# 윈도우에 캔버스 배치
canvas.pack()
root.mainloop() 

캔버스는 Canvas 명령으로 생성하고 pack, place 명령으로 배치한다.

pack() 명령으로 배치하면 캔버스 크기에 맞춰 윈도우 크기가 결정된다.  
윈도우에 캔버스만을 배치하는 경우에는 아래 코드와 같이 root.geometry()를 생략할 수 있다.

4.2 캔버스에 이미지 표시하기

import tkinter

root = tkinter.Tk() 
root.title("첫 번째 캔버스") 
canvas = tkinter.Canvas(root, width=400, height=600)
canvas.pack()
# gazou에 이미지 파일 로딩
gazou = tkinter.PhotoImage(file="hyunju.png")
# 캔버스에 이미지 그리기
canvas.create_image(200, 300, image=gazou)
root.mainloop() 

캔버스에 이미지를 표시할 때는 PhotoImage 매서드로 이미지를 로딩하고 

create_image 매서드로 이미지를 그린다.

create_image() 명령에 지정한 X, Y 좌표는 이미지의 중점이다. 다른 값을 넣으면 이미지가 잘린다. 

 

5 제비뽑기 프로그램 만들기

앞에서 봤던 라벨, 버튼, 캔버스를 사용해서 간단한 제비뽑기 프로그램을 만들어 보자!

5.1 이미지 표시

import tkinter

root = tkinter.Tk() 
root.title("제비봅기 프로그램")
# 윈도우 크기 고정
root.resizable(False, False) 
canvas = tkinter.Canvas(root, width=800, height=600)
canvas.pack()
gazou = tkinter.PhotoImage(file="miko.png")
canvas.create_image(400, 300, image=gazou)
root.mainloop() 

우선 캔버스를 배치하고 이미지를 표시한다. 

resizable() 명령으로 윈도우 크기를 변경하지 못하게 한다.  
첫 번째 인수는 가로 방향 크기 변경 여부, 두 번째 인수는 세로 방향 크기 변경 여부를 의미한다.

5.2 GUI 배치

import tkinter

root = tkinter.Tk() 
root.title("제비봅기 프로그램")
root.resizable(False, False) 
canvas = tkinter.Canvas(root, width=800, height=600)
canvas.pack()
gazou = tkinter.PhotoImage(file="miko.png")
canvas.create_image(400, 300, image=gazou)

# 라벨 추가
label = tkinter.Label(root, text="??", font=("Times New Roman", 120),
                        bg="white")
label.place(x=380, y=60)
# 버튼 추가
button = tkinter.Button(root, text="제비뽑기", font=("Times New Roman", 36),
                        fg="skyblue")
button.place(x=360, y=400)

root.mainloop() 

두 번째로 캔버스 상에 라벨과 버튼을 배치했다. 

5.3 버튼 클릭 시 반응하기

import tkinter

# 버튼 클릭시 실행될 함수 정의 -> 라벨 문자열 무작위로 변경
def click_btn():
    label["text"] = random.choice(["대길", "중길", "소길", "흉"])
    label.update()

root = tkinter.Tk() 
root.title("제비봅기 프로그램")
root.resizable(False, False) 
canvas = tkinter.Canvas(root, width=800, height=600)
canvas.pack()
gazou = tkinter.PhotoImage(file="miko.png")
canvas.create_image(400, 300, image=gazou)

label = tkinter.Label(root, text="??", font=("Times New Roman", 120),
                        bg="white")
label.place(x=380, y=60)
# 버튼 생성, command 인자로 클릭 시 호출할 함수 지정
button = tkinter.Button(root, text="제비뽑기", font=("Times New Roman", 36),
                        fg="skyblue", command=click_btn)
button.place(x=360, y=400)

root.mainloop() 

버튼을 클릭할 때 제비뽑기 결과가 표시되도록 했다. 

버튼을 누르면 대길. 중길, 소길, 흉 중 랜덤으로 하나가 표시된다!

 

6 캔버스에 도형 그리기

import tkinter

root = tkinter.Tk()
root.title("캔버스에 도형 그리기")
root.geometry("500x400")
cvs = tkinter.Canvas(root, width=500, height=400, bg="white")
cvs.pack()
cvs.create_text(250, 25, text="문자열", fill="green", font=("Times New Roman", 24))
cvs.create_line(30, 30, 70, 80, fill="navy", width=5)
cvs.create_line(120, 20, 80, 50, 200, 80, 140, 120, fill="blue", smooth=True)
cvs.create_rectangle(40, 140, 160, 200, fill="lime")
cvs.create_rectangle(60, 240, 120, 360, fill="pink", outline="red", width=5)
cvs.create_oval(250 - 40, 100 - 40, 250 + 40, 100 + 40, fill="silver", outline="purple")
cvs.create_oval(250 - 80, 200 - 40, 250 + 80, 200 + 40, fill="cyan", width=0)
cvs.create_polygon(250, 250, 150, 350, 350, 350, fill="magenta", width=0)
cvs.create_arc(400 - 50, 100 - 50, 400 + 50, 100 + 50, fill="yellow", start=30, extent=300)
cvs.create_arc(400 - 50, 250 - 50, 400 + 50, 250 + 50, fill="gold", start=0, extent=120, style=tkinter.CHORD)
cvs.create_arc(400 - 50, 350 - 50, 400 + 50, 350 + 50, outline="orange", start=0, extent=120, style=tkinter.ARC)
cvs.mainloop()

직선: create_line(x1, y1, x2, y2, fill=?, width=?)

사각형: create_rectangle(x1, y1, x2, y2, fill=?, outline=?, width=?)

타원형: create_oval(x1, y1, x2, y2, fill=?, outline=?, width=?)

다각형: create_polygon(x1, y1, x2, y2, x3, y3, ..., fill=?, outline=?, width=?)

원호: create_arc(x1, y1, x2, y2, x3, y3, ..., fill=?, outline=?, start=?, extent=?, style=?)

끝!

여기까진 책에서 본 그래도 따라한 거니까, 이번엔 제비뽑기 말고 다른 걸로 직접 프로그램을 만들어봐야겠다.

 

코드 github: https://github.com/kimjinho1/Python-Game/blob/main/GUI%20%EA%B8%B0%EC%B4%88%201(tkinter)/GUI%20%EA%B8%B0%EC%B4%88%201.ipynb

 

kimjinho1/Python-Game

Contribute to kimjinho1/Python-Game development by creating an account on GitHub.

github.com

728x90
반응형

+ Recent posts