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
반응형

+ Recent posts