Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

파이썬 tkinter로 간단한 GUI 계산기 만들기 완벽 가이드

소개

파이썬에서는 tkinter 라이브러리를 사용해 GUI 구성 요소를 만들고 더 나은 사용자 인터페이스를 구현할 수 있습니다.

이 글에서는 tkinter를 활용하여 간단한 GUI 기반 계산기 애플리케이션을 만드는 방법을 단계별로 알아보겠습니다.

시작하기 전 준비 사항

본격적으로 시작하기에 앞서 몇 가지 준비해야 할 것이 있습니다.

먼저, 로컬 시스템에서 이미지를 불러오기 위해 사용할 파이썬의 이미징 라이브러리인 PIL(Pillow)을 설치합니다. 터미널을 열고 아래 명령어를 입력하세요.

pip install Pillow

패키지 설치가 완료되었다면, 이제 계산기에 필요한 아이콘을 다운로드해야 합니다.

구글 이미지에서 필요한 아이콘을 검색해 받을 수도 있습니다. 하지만 이 프로젝트에서 사용한 것과 동일한 아이콘 세트가 필요하다면 아래 링크에서 다운로드할 수 있습니다.

https://www.dropbox.com/sh/0zqd6zd9b8asmor/AAC3d2iOvMRl8INkbCuMUo_ya?dl=0

모든 아이콘은 반드시 "asset"이라는 이름의 폴더에 저장해 주세요.

다음으로, 필요한 모듈들을 임포트합니다.

from tkinter import *
from PIL import Image # pip install Pillow
from PIL import ImageTk

이것으로 모든 준비가 끝났습니다. 이제 본격적으로 개발을 시작할 수 있습니다.

핵심 함수 만들기

먼저, GUI 구성 요소들이 사용할 함수들을 정의해야 합니다.

핵심 함수는 총 세 가지입니다. 숫자나 연산 기호 버튼이 눌렸을 때 실행되는 함수, 등호(=) 버튼이 눌렸을 때 실행되는 함수, 그리고 지우기(Clear) 버튼이 눌렸을 때 실행되는 함수입니다.

먼저 몇 가지 전역 변수를 초기화하겠습니다.

txt = ""
res = False
ans = 0

숫자·기호 입력 함수

숫자 또는 연산자 키가 눌렸을 때 호출되는 함수입니다.

def press(num):
    global txt, ans, res
    if (res==True):
        txt = ans
        res = False
    txt = txt + str(num)
    equation.set(txt)

이전 계산 결과가 화면에 표시된 상태(res == True)라면 새로운 입력을 받기 위해 결과값으로 초기화하고, 그렇지 않으면 입력된 값을 문자열로 이어 붙여 화면에 갱신하는 방식입니다.

등호(=) 버튼 함수

등호 버튼이 눌렸을 때 호출되며, 입력된 수식을 실제로 계산합니다.

def equal():
    try:
        global txt, ans, res
        ans = str(eval(txt))
        equation.set(ans)
        res = True
    except:
        equation.set("ERROR : Invalid Equation")
        txt=""

eval() 함수로 수식을 평가하고, 오류가 발생하면 예외 처리를 통해 에러 메시지를 출력하도록 작성했습니다.

지우기(Clear) 버튼 함수

지우기 버튼이 눌렸을 때 화면과 변수를 초기화하는 함수입니다.

def clear():
    global txt, ans, res
    txt = ""
    equation.set("")
    res = False

함수 정의가 끝났으니, 이제 메인 부분을 작성하고 GUI 구성 요소를 만들어 보겠습니다.

메인 윈도우 구성

if __name__ == "__main__":
    window = Tk()
    window.configure(background="black")
    window.title("Calculator")
    window.iconbitmap("assets\Calculator\Logo.ico")
    window.geometry("343x417")
    window.resizable(0,0)

위 코드는 계산기 창의 기본 뼈대를 구성합니다. 배경색, 제목, 아이콘, 크기, 리사이즈 여부 등을 설정합니다.

참고: 오류를 피하려면 위 코드와 동일한 파일 구조를 유지해야 합니다. 로고 아이콘은 assets 폴더 안의 Calculator 폴더에 저장하세요.

권장 폴더 구조는 다음과 같습니다.

+---Working Directory
    +---Calculator.py
    +---assets
        +---Calculator
            +---All the icons.

입력 화면(디스플레이) 만들기

다음은 숫자와 계산 결과가 표시될 텍스트 필드를 디자인합니다.

equation = StringVar()

txt_field = Entry(relief=RIDGE,textvariable=equation,bd=10,font=("Aerial",20),bg="powder blue")

txt_field.grid(columnspan=4,ipady=10,ipadx=10,sticky="nsew")

StringVar()로 변수를 선언하고 Entry 위젯에 연결하면, press() 함수에서 equation.set()을 호출할 때마다 화면이 자동으로 갱신됩니다.

버튼 추가하기

이제 아이콘 이미지를 GUI 창에 하나씩 추가하는 반복 작업을 진행합니다. 아래는 숫자 1 버튼을 추가하는 예제이며, 나머지 버튼도 동일한 패턴으로 작성하거나 글 마지막의 전체 코드를 참고하면 됩니다.

width=80
height=80
img1 = Image.open("assets/Calculator/one.PNG")
img1 = img1.resize((width,height))
oneImage = ImageTk.PhotoImage(img1)
button1 = Button(window, image=oneImage,bg="white",command = lambda:press(1),height=height,width=width)
button1.grid(row=2,column=0,sticky="nsew")

작동 원리는 간단합니다. Pillow의 Image.open()으로 아이콘 파일을 불러오고, resize()로 크기를 조정한 뒤 ImageTk.PhotoImage()로 tkinter에서 사용 가능한 형태로 변환합니다. 그 후 Button 위젯에 이미지를 지정하고 lambda를 통해 클릭 시 press() 함수가 해당 값과 함께 호출되도록 연결합니다.

같은 방식으로 button2, button3부터 모든 숫자와 연산 기호 버튼까지 차례대로 작성하면 됩니다.

여기까지 완료했다면 프로그램을 실행했을 때 계산기가 화면에 나타날 것입니다.

중간 과정을 따라가기 어려웠다면 아래의 전체 코드를 활용하세요.

전체 코드

from tkinter import *
from PIL import Image
from PIL import ImageTk

txt = ""
res = False
ans = 0

def press(num):
    global txt, ans, res
    if (res==True):
        txt = ans
        res = False
    txt = txt + str(num)
    equation.set(txt)
def equal():
    try:
        global txt, ans, res
        ans = str(eval(txt))
        equation.set(ans)
        res = True
    except:
        equation.set("ERROR : Invalid Equation")
        txt=""
def clear():
    global txt, ans, res
    txt = ""
    equation.set("")
    res = False
if __name__ == "__main__":
    window = Tk()
    window.configure(background="black")
    window.title("Calculator")
    window.iconbitmap("assets\Calculator\Logo.ico")
    window.geometry("343x417")
    window.resizable(0,0)
    equation = StringVar()
    txt_field = Entry(relief=RIDGE,textvariable=equation,bd=10,font=("Aerial",20),bg="powder blue")
    txt_field.grid(columnspan=4,ipady=10,ipadx=10,sticky="nsew")
    width=80
    height=80
    img1 = Image.open("assets/Calculator/one.PNG")
    img1 = img1.resize((width,height))
    oneImage = ImageTk.PhotoImage(img1)
    button1 = Button(window, image=oneImage,bg="white",command = lambda:press(1),height=height,width=width)
    button1.grid(row=2,column=0,sticky="nsew")
    img2 = Image.open("assets/Calculator/two.PNG")
    img2 = img2.resize((width,height))
    twoImage = ImageTk.PhotoImage(img2)
    button2 = Button(window, image=twoImage,bg="white",command = lambda:press(2),height=height,width=width)
    button2.grid(row=2,column=1,sticky="nsew")
    img3 = Image.open("assets/Calculator/three.PNG")
    img3 = img3.resize((width,height))
    threeImage = ImageTk.PhotoImage(img3)
    button3 = Button(window, image=threeImage,bg="white",command = lambda:press(3),height=height,width=width)
    button3.grid(row=2,column=2,sticky="nsew")
    img4 = Image.open("assets/Calculator/four.PNG")
    img4 = img4.resize((width,height))
    fourImage = ImageTk.PhotoImage(img4)
    button4 = Button(window, image=fourImage,bg="white",command = lambda:press(4),height=height,width=width)
    button4.grid(row=3,column=0,sticky="nsew")
    img5 = Image.open("assets/Calculator/five.PNG")
    img5 = img5.resize((width,height))
    fiveImage = ImageTk.PhotoImage(img5)
    button5 = Button(window, image=fiveImage,bg="white",command = lambda:press(5),height=height,width=width)
    button5.grid(row=3,column=1,sticky="nsew")
    img6 = Image.open("assets/Calculator/six.PNG")
    img6 = img6.resize((width,height))
    sixImage = ImageTk.PhotoImage(img6)
    button6 = Button(window, image=sixImage,bg="white",command = lambda:press(6),height=height,width=width)
    button6.grid(row=3,column=2,sticky="nsew")
    img7 = Image.open("assets/Calculator/seven.PNG")
    img7 = img7.resize((width,height))
    sevenImage = ImageTk.PhotoImage(img7)
    button7 = Button(window, image=sevenImage,bg="white",command = lambda:press(7),height=height,width=width)
    button7.grid(row=4,column=0,sticky="nsew")
    img8 = Image.open("assets/Calculator/eight.PNG")
    img8 = img8.resize((width,height))
    eightImage = ImageTk.PhotoImage(img8)
    button8 = Button(window, image=eightImage,bg="white",command = lambda:press(8),height=height,width=width)
    button8.grid(row=4,column=1,sticky="nsew")
    img9 = Image.open("assets/Calculator/nine.PNG")
    img9 = img9.resize((width,height))
    nineImage = ImageTk.PhotoImage(img9)
    button9 = Button(window, image=nineImage,bg="white",command = lambda:press(9),height=height,width=width)
    button9.grid(row=4,column=2,sticky="nsew")
    img0 = Image.open("assets/Calculator/zero.PNG")
    img0 = img0.resize((width,height))
    zeroImage = ImageTk.PhotoImage(img0)
    button0 = Button(window, image=zeroImage,bg="white",command = lambda:press(0),height=height,width=width)
    button0.grid(row=5,column=1,sticky="nsew")
    imgx = Image.open("assets/Calculator/multiply.PNG")
    imgx = imgx.resize((width,height))
    multiplyImage = ImageTk.PhotoImage(imgx)
    buttonx = Button(window, image=multiplyImage,bg="white",command = lambda:press("*"),height=height,width=width)
    buttonx.grid(row=2,column=3,sticky="nsew")
    imgadd = Image.open("assets/Calculator/add.PNG")
    imgadd = imgadd.resize((width,height))
    addImage = ImageTk.PhotoImage(imgadd)
    buttonadd = Button(window, image=addImage,bg="white",command = lambda:press("+"),height=height,width=width)
    buttonadd.grid(row=3,column=3,sticky="nsew")
    imgdiv = Image.open("assets/Calculator/divide.PNG")
    imgdiv = imgdiv.resize((width,height))
    divImage = ImageTk.PhotoImage(imgdiv)
    buttondiv = Button(window, image=divImage,bg="white",command = lambda:press("/"),height=height,width=width)
    buttondiv.grid(row=5,column=3,sticky="nsew")
    imgsub = Image.open("assets/Calculator/subtract.PNG")
    imgsub = imgsub.resize((width,height))
    subImage = ImageTk.PhotoImage(imgsub)
    buttonsub = Button(window, image=subImage,bg="white",command = lambda:press("-"),height=height,width=width)
    buttonsub.grid(row=4,column=3,sticky="nsew")
    imgeq = Image.open("assets/Calculator/equal.PNG")
    imgeq = imgeq.resize((width,height))
    eqImage = ImageTk.PhotoImage(imgeq)
    buttoneq = Button(window, image=eqImage,bg="white",command = equal,height=height,width=width)
    buttoneq.grid(row=5,column=2,sticky="nsew")
    imgclear = Image.open("assets/Calculator/clear.PNG")
    imgclear = imgclear.resize((width,height))
    clearImage = ImageTk.PhotoImage(imgclear)
    buttonclear = Button(window, image=clearImage,bg="white",command = clear,height=height,width=width)
    buttonclear.grid(row=5,column=0,sticky="nsew")

window.mainloop()

위 코드에서 포맷팅 문제가 발생한다면 https://github.com/SVijayB/PyHub/blob/master/Graphics/Simple%20Calculator.py 에서도 코드를 받을 수 있습니다.

실행 결과

파이썬 tkinter로 간단한 GUI 계산기 만들기 완벽 가이드