tkinter

백동기·2025년 1월 28일

Python 활용

목록 보기
1/5
post-thumbnail

1. tkinter란?

  • tkinter 패키지는 tk GUI 툴킷에 대한 파이썬 바인딩을 말한다.

장점

  • 별도의 설치 없이 파이썬 표준 라이브러리에 포함되어 있어 사용하기 간편하다.

단점

  • 현대적인 GUI 디자인을 생성하기에는 어려움이 있다.

사용하는 방법

  • 다음과 같은 방식으로 import한다.
from tkinter import *
from tkinter import ttk

2. tkinter 구성 요소

1) 프레임

프레임 설정

from tkinter import *

frame = Tk()
frame.title("frame")
frame.geometry("640x480+100+300")
frame.resizable(False, False)
frame.mainloop()
  • title : 제목 설정
  • geometry : 창 크기 설정 (가로x세로+x좌표+y좌표 형식으로 설정)
  • resizable : 창 크기 변경 허용 여부 (x값, y값 순)

프레임 활용

from tkinter import *

frame = Tk()
frame.title("frame")

Label(frame, text="Menu").pack(side="top")

Button(frame, text="주문하기").pack(side="bottom")

frame_burger = Frame(frame, relief="solid", bd=1)
Button(frame_burger, text="hamburger").pack()
Button(frame_burger, text="cheeze burger").pack()
Button(frame_burger, text="chicken burger").pack()
frame_burger.pack(side="left", fill="both", expand=True)

frame_drink = LabelFrame(frame, text="drinks")
Button(frame_burger, text="coke").pack()
Button(frame_burger, text="cider").pack()
frame_drink.pack(side="right", fill="both", expand=True)

frame.mainloop()
  • Frame 옵션
  • pack() : 위젯을 창에 배치하도록 함
    - 옵션
    • side : 해당 위젯이 특정 위치에 있도록 정렬함
    • fill : 할당된 공간에 맞게 크기 변경함 (x, y, both)
    • expand : 할당되지 않는 미사용 공간을 모두 해당 위젯에게 할당함

2) 버튼

from tkinter import *

frame = Tk()
frame.title("frame")

btn1 = Button(frame, text="버튼1")
btn1.pack() # pack()을 사용하면 위젯을 창에 배치할 수 있다.

btn2 = Button(frame, padx=5, pady=10, text="*******버튼2*******")
btn2.pack()

btn3 = Button(frame, padx=5, pady=10, text="버튼3")
btn3.pack()

btn4 = Button(frame, width=10, height=3, text="버튼4")
btn4.pack()

btn5 = Button(frame, fg="red", bg="yellow", text="버튼5")
btn5.pack()

photo = PhotoImage(file="python_gui/button_bg.png")
btn6 = Button(frame, image=photo)
btn6.pack()

def btncmd():
    print("버튼이 클릭되었습니다.")
btn7 = Button(frame, text="동작하는 버튼", command=btncmd)
btn7.pack()
frame.mainloop()
  • Button(프레임, 옵션) : 버튼을 생성
    - 옵션
    • text : 버튼 안에 내용을 입력
    • padx, pady : 여백의 크기를 지정
    • width, height : 고정 크기를 지정
    • fg : 글씨의 색깔 지정
    • bg : 버튼의 색깔 지정
    • image : 이미지 형태의 파일을 지정
    • command : 실행할 동작 호출

3) 레이블

레이블은 화면에 글자나 이미지를 보여줄 때 사용하는 도구이다.

from tkinter import *

frame = Tk()
frame.title("frame")

label1 = Label(frame, text="Hello World")
label1.pack()

photo = PhotoImage(file="python_gui/button_bg.png")
label2 = Label(frame, image=photo)
label2.pack()

def change():
    label1.config(text="Good Bye")
    global change_photo # 전역변수로 선언해야 변경사항을 적용함
    change_photo = PhotoImage(file="python_gui/change_bg.png")
    label2.config(image=change_photo)

btn = Button(frame, text="Click Me", command=change)
btn.pack()

frame.mainloop()
  • Label(프레임, 옵션) : 레이블을 생성
버튼 누르기 전버튼 누르기 후

4) 텍스트와 엔트리

텍스트와 엔트리는 문자열을 입력할 수 있는 위젯이다.

텍스트

from tkinter import *

frame = Tk()
frame.title("frame")

txt = Text(frame, width=30, height=5)
txt.pack()

txt.insert(END, "Insert Contents\n")

frame.mainloop()
  • Text(프레임, 옵션) : 텍스트 박스를 생성
  • insert(삽입 커서의 위치, 내용) : 텍스트 박스에 내용을 추가함

엔트리

from tkinter import *

frame = Tk()
frame.title("frame")

entry = Entry(frame)
entry.insert(0, "한 줄만 입력 가능")
entry.pack()

frame.mainloop()
  • Entry(프레임, 옵션) : 엔트리를 생성

텍스트와 엔트리의 차이

엔트리는 한 줄만 입력을 받을 수 있지만, 텍스트는 여러 줄을 입력 받을 수 있다.
즉, 엔트리에서는 Enter를 입력 받을 수 없다.

버튼을 이용하여 텍스트와 엔트리 내용 추가 및 수정

from tkinter import *

frame = Tk()
frame.title("frame")

txt = Text(frame, width=30, height=5)
txt.insert(END, "Insert Contents\n")
txt.pack()

entry = Entry(frame)
entry.insert(0, "한 줄만 입력 가능")
entry.pack()

def btncmd():
    print(txt.get("1.0", END))
    print(entry.get())

    txt.delete("1.0", END)
    entry.delete(0, END)

btn = Button(frame, text="Click Me", command=btncmd)

frame.mainloop()
  • get("시점 행.시점 열", 종점) : 텍스트의 내용을 가져옴
  • get() : 엔트리의 내용을 가져옴
  • delete("시점 행.시점 열", 종점) : 텍스트의 내용을 삭제
  • delete(시점, 종점) : 엔트리의 내용을 삭제
버튼 누르기 전버튼 누른 후

5) 리스트 박스, 콤보 박스

리스트 박스와 콤보 박스는 박스 안의 항목들 중에서 선택을 할 수 있도록 하는 위젯이다.

리스트 박스

from tkinter import *

frame = Tk()
frame.title("frame")

listbox = Listbox(frame, selectmode="extended", height=0)
listbox.insert(0, "Apple")
listbox.insert(1, "Banana")
listbox.insert(2, "Cherry")
listbox.insert(END, "Grape")
listbox.pack()

def btncmd():
    listbox.delete(END)
    listbox.delete(0)

    print("Listbox Size: ", listbox.size())

    print("1st to 3rd: ", listbox.get(0, 2))
    print("Selected Item: ", listbox.curselection())
    
btn = Button(frame, text="Click Me", command=btncmd)
btn.pack()

frame.mainloop()
  • Listbox(프레임, selectmode, 옵션) : 리스트 박스를 생성
    - height : 한 화면에 보여주는 리스트 박스 목록 개수 (0으로 지정할 경우에는 리스트 박스에 있는 모든 내용을 보여준다)
    - selectmode : 선택 모드를 지정
    • single : 1개만 선택 가능
    • extended : 1개 이상 선택 가능
  • insert(위치, 내용) : 리스트 박스의 목록을 추가
  • size() : 크기를 반환
  • get() : 항목을 가져옴
  • curselection() : 선택된 항목의 index를 반환함
버튼 클릭 전버튼 클릭 후

콤보 박스

from tkinter import *
import tkinter.ttk as ttk

frame = Tk()
frame.title("frame")

date = [str(i) + "일" for i in range(1, 32)]
combobox = ttk.Combobox(frame, height=5, values=date, state="readonly")
# state : readonly(선택만 가능), normal(수정 가능)
combobox.pack()
combobox.set("날짜 선택") # 최초 목록 제목 설정
# combobox.current(0) # 0번째 인덱스 값 선택

def btncmd():
    print(combobox.get()) # 선택된 값 표시
    print(date.index(combobox.get()) + 1) # 선택된 값의 인덱스 표시
    
btn = Button(frame, text="Click Me", command=btncmd)
btn.pack()

frame.mainloop()
  • 콤보박스는 thinter.ttk 패키지에 있으므로 따로 import 해야 함
  • Combobox(프레임, 옵션) : 콤보 박스를 생성함
    - state : 콤보 박스에 내용 추가 가능 여부 설정
    • readonly : 선택만 가능함
    • normal : 수정도 가능함
  • set(내용) : 초기값 설정
  • current(인덱스) : 특정 인덱스 값을 선택함
버튼 누르기 전버튼 누른 후

6) 체크 버튼, 라디오 버튼

체크 버튼과 리디오 버튼은 선택할 항목 버튼 중에서 선택하도록 하는 위젯이다.

체크 버튼

from tkinter import *

frame = Tk()
frame.title("frame")

chkvar = IntVar() # chkvar에 int형으로 값을 저장
chkbox = Checkbutton(frame, text="오늘 하루 보지 않기", variable=chkvar)
chkbox.select() # 자동 선택 처리
chkbox.pack()

chkvar2 = IntVar()
chkbox2 = Checkbutton(frame, text="일주일동안 보지 않기", variable=chkvar2)
chkbox2.deselect()
chkbox2.pack()

def btncmd():
    print(chkvar.get()) # 0 : 해제, 1 : 선택
    print(chkvar2.get())
    
btn = Button(frame, text="Click Me", command=btncmd)
btn.pack()

frame.mainloop()
  • Checkbutton(프레임, text, 옵션) : 체크 박스를 생성함
    - variable : 체크 박스를 누르면 실행하는 함수 지정
  • select() : 기본값으로 체크박스가 체크 되어 있도록 설정
  • deselect() : 기본값으로 체크박스가 해제 되어 있도록 설정
실행 후 버튼만 클릭한 화면

라디오 버튼

from tkinter import *

frame = Tk()
frame.title("frame")

label1 = Label(frame, text="burger").pack()

burger_var = IntVar()
btn_burger1 = Radiobutton(frame, text="classic burger", value=1, variable=burger_var)
btn_burger1.select()
btn_burger2 = Radiobutton(frame, text="cheese burger", value=2, variable=burger_var)
btn_burger3 = Radiobutton(frame, text="chicken burger", value=3, variable=burger_var)

btn_burger1.pack()
btn_burger2.pack()
btn_burger3.pack()

label2 = Label(frame, text="drink").pack()

drink_var = IntVar()
btn_drink1 = Radiobutton(frame, text="coke", value=1, variable=drink_var)
btn_drink1.select()
btn_drink2 = Radiobutton(frame, text="sprite", value=2, variable=drink_var)

btn_drink1.pack()
btn_drink2.pack()

def btncmd():
    print(burger_var.get())
    print(drink_var.get())

btn = Button(frame, text="Order", command=btncmd)
btn.pack()

frame.mainloop()
  • Radiobutton(프레임, text, value, 옵션) : 라디오 버튼을 생성함
    - value : 라디오 버튼이 표출되는 순서(value를 다르게 새로 설정하면 새로운 라디오 버튼 그룹이 생성이 된다.)

체크 버튼과 라디오 버튼의 차이

체크 버튼은 중복해서 선택할 수 있지만 라디오 버튼은 중복해서 선택할 수 없다

7) 프로그레스 바

프로그레스 바는 진행도를 나타내는 위젯이다.

from tkinter import *
import tkinter.ttk as ttk
import time

frame = Tk()
frame.title("frame")

progressbar = ttk.Progressbar(frame, maximum=100, mode="indeterminate")
progressbar.start(10)
progressbar.pack()

def btncmd():
    progressbar.stop()
    print("중지")

btn = Button(frame, text="중지", command=btncmd)
btn.pack()

def btncmd2():
    for i in range(1, 101):
        time.sleep(0.01) # 0.01초 대기
        p_var2.set(i)
        progressbar2.update()
        
p_var2 = DoubleVar()
progressbar2 = ttk.Progressbar(frame, maximum=100, length=150, variable=p_var2, mode="determinate")
progressbar2.pack()

btn2 = Button(frame, text="시작", command=btncmd2)
btn2.pack()

frame.mainloop()
  • 프로그래스 바는 thinter.ttk 패키지에 있으므로 따로 import 해야 함
  • Progressbar(프레임, mode, 옵션) : 프로그래스 바를 생성함
    • mode
      • indeterminate : 무한으로 프로그래스 바가 실행되는 모드
      • determinate : 특정 값까지 프로그래스 바가 실행되는 모드- 옵션
    • 옵션
      • maximum : 프로그래스 바가 최대로 올라 갔을 때의 값
      • length : 프로그래스 바의 길이
  • start(초) : 해당 초 마다 움직임이 발생 하도록 함
  • stop() : 프로그래스 바 움직임을 중지 시킴
  • update() : UI 업데이트를 진행함
버튼 누르기 전버튼 누른 후

8) 메뉴

from tkinter import *

frame = Tk()
frame.title("frame")

main_menu = Menu(frame)

# File Menu
menu_file = Menu(main_menu, tearoff=0)
menu_file.add_command(label="New File")
menu_file.add_command(label="New Window")
menu_file.add_separator()
menu_file.add_command(label="Open file...")
menu_file.add_separator()
menu_file.add_command(label="Save All", state="disabled") # 비활성화
menu_file.add_command(label="Exit", command=frame.quit)
main_menu.add_cascade(label="File", menu=menu_file)

# Edit Menu
main_menu.add_cascade(label="Edit")

# Language Menu (Radio 버튼을 통해 택1)
menu_lang = Menu(main_menu, tearoff=0)
menu_lang.add_radiobutton(label="Python")
menu_lang.add_radiobutton(label="C++")
menu_lang.add_radiobutton(label="Java")
main_menu.add_cascade(label="Language, ", menu=menu_lang)

# View Menu ()
menu_view = Menu(main_menu, tearoff=0)
menu_view.add_checkbutton(label="Show Minimap")
menu_view.add_checkbutton(label="Open View")
main_menu.add_cascade(label="View", menu=menu_view)

frame.config(menu=main_menu)
frame.mainloop()
  • Menu(프레임) : 메뉴 프레임을 생성함
  • Menu(메뉴 프레임, tearoff) : 메뉴를 생성 함
    - tearoff : 하위 메뉴의 분리 기능을 사용할 지 여부 설정
  • add_command(label) : 클릭 할 수 있는 메뉴의 항목을 생성
  • add_seperator() : 구분자(줄)을 생성
  • add_radiobutton(label) : 한 가지만 체크할 수 있는 메뉴 항목을 생성
  • add_checkbutton(label) : 여러 가지를 체크할 수 있는 메뉴 항목을 생성
  • add_cascade(label, menu) : 생성한 메뉴를 붙이는 역할
    • label : 상위 메뉴 이름
    • menu : 붙일 메뉴를 지정
command & seperatorradiobuttoncheckbutton

9) 메시지 박스

메시지 박스는 Error 메시지 등을 팝업 형태로 표출하는 위젯이다.

from tkinter import *
import tkinter.messagebox as msgbox

frame = Tk()
frame.title("frame")

def info():
    msgbox.showinfo("알림", "info")

def warn():
    msgbox.showwarning("경고", "warning")

def error():
    msgbox.showerror("에러", "error")

def okcancel():
    msgbox.askokcancel("확인 / 취소", "ok? or cancel?")

def retrycancel():
    msgbox.askretrycancel("재시도 / 취소", "Retry? or Cancel?")

def yesno():
    msgbox.askyesno("예 / 아니오", "yes? or no?")

def yesnocancel():
    response = msgbox.askyesno(title=None, message="yes? or no? or cancel?")
    print("응답:", response) # True(1), False(0), None(기타)
    if response == 1:
        print("예")
    elif response == 0:
        print("아니요")
    else:
        print("취소")
    
Button(frame, command=info, text="알림").pack()
Button(frame, command=warn, text="경고").pack()
Button(frame, command=error, text="에러").pack()

Button(frame, command=okcancel, text="확인 / 취소").pack()
Button(frame, command=retrycancel, text="재시도 / 취소").pack()
Button(frame, command=yesno, text="예 / 아니요").pack()
Button(frame, command=yesnocancel, text="예 / 아니요 / 취소").pack()

frame.mainloop()
  • 메시지 박스는 thinter.messagebox 패키지에 있으므로 따로 import 해야 함
  • msgbox : 메시지 박스를 생성함
    • showinfo(title, message) : 알림 메시지 출력
    • showwarning(title, message) : 경고 메시지 출력
    • showerror(title, message) : 에러 메시지 출력
    • askokcancel(title, message) : 확인 / 취소 버튼이 달린 메시지 출력
    • askretrycancel(title, message) : 재시도 / 취소 버튼이 달린 메시지 출력
    • askyesno(title, message) : 예 / 아니오 버튼이 달린 메시지 출력
    • askyesnocancel(title, message) : 예 / 아니오 / 취소 버튼이 달린 메시지 출력

      ※ yes는 1, no는 0, 취소는 그 외 숫자로 처리한다.

10) 스크롤 바

from tkinter import *

frame = Tk()
frame.title("frame")

miniframe = Frame(frame)
miniframe.pack()

scrollbar = Scrollbar(frame)
scrollbar.pack(side="right", fill="y")

listbox = Listbox(miniframe, selectmode="extended", height=10, yscrollcommand=scrollbar.set) # set하지 않으면 스크롤을 내려도 다시 올라옴

for i in range(1, 32):
    listbox.insert(END, str(i), "일")
listbox.pack()

scrollbar.config(command=listbox.yview) #scrollbar에서도 listbox에 사용 할거라고 알려줘야 함함

frame.mainloop()
  • Scrollbar(프레임) : 스크롤 바를 생성함
  • yscrollcommand=scrollbar.set : 위젯에서 세로 스크롤 바를 사용하도록 set함 (가로 스크롤 바인 경우에는 xscrollcommand 사용)
  • scrollbar.config(command=listbox.yview) : 세로 스크롤 바에서 어떤 위젯에 사용할 지를 알려줌 (가로 스크롤 바인 경우에는 xview 사용)

11) 그리드

그리드는 Frame을 격자로 나누어서 배치할 때 사용한다.

from tkinter import *

frame = Tk()
frame.title("frame")

btn_minus = Button(frame, text="-", width=5, height=2)
btn_plus = Button(frame, text="+", width=5, height=2)
btn_div = Button(frame, text="NumLk", width=5, height=2)

btn_7 = Button(frame, text="7", padx=10, pady=10)
btn_8 = Button(frame, text="8", padx=10, pady=10)
btn_9 = Button(frame, text="9", padx=10, pady=10)

btn_4 = Button(frame, text="4", padx=10, pady=10)
btn_5 = Button(frame, text="5", padx=10, pady=10)
btn_6 = Button(frame, text="6", padx=10, pady=10)

btn_1 = Button(frame, text="1", padx=10, pady=10)
btn_2 = Button(frame, text="2", padx=10, pady=10)
btn_3 = Button(frame, text="3", padx=10, pady=10)

btn_0 = Button(frame, text="0", padx=10, pady=10)
btn_dot = Button(frame, text=".", padx=10, pady=10)

btn_minus.grid(row=0, column=0, sticky=N+E+W+S, padx=3, pady=3)
btn_plus.grid(row=0, column=1, sticky=N+E+W+S, padx=3, pady=3)
btn_div.grid(row=0, column=2, sticky=N+E+W+S, padx=3, pady=3)

btn_7.grid(row=1, column=0, sticky=N+E+W+S, padx=3, pady=3)
btn_8.grid(row=1, column=1, sticky=N+E+W+S, padx=3, pady=3)
btn_9.grid(row=1, column=2, sticky=N+E+W+S, padx=3, pady=3)

btn_4.grid(row=2, column=0, sticky=N+E+W+S, padx=3, pady=3)
btn_5.grid(row=2, column=1, sticky=N+E+W+S, padx=3, pady=3)
btn_6.grid(row=2, column=2, sticky=N+E+W+S, padx=3, pady=3)

btn_1.grid(row=3, column=0, sticky=N+E+W+S, padx=3, pady=3)
btn_2.grid(row=3, column=1, sticky=N+E+W+S, padx=3, pady=3)
btn_3.grid(row=3, column=2, sticky=N+E+W+S, padx=3, pady=3)

btn_0.grid(row=4, column=0, sticky=N+E+W+S, padx=3, pady=3)
btn_dot.grid(row=4, column=1, columnspan=2, sticky=N+E+W+S, padx=3, pady=3)

frame.mainloop()
  • grid(row, column, 옵션) : 그리드 형태로 위젯을 배치함
    • row : 행 위치 지정
    • column : 열 위치 지정
    • 옵션
      • sticky : 확장 방향 설정 (N, E, W, S)
      • padx, pady : 컨텐츠 크기 기준으로 확장할 크기 설정
      • width, height : 절대적인 크기 설정
      • columnspan : 한 위젯이 점유할 열 수 설정
      • rowspan : 한 위젯이 점유할 행 수 설정

출처

3. 프로젝트

1. 메모장 만들기

  • 주제 : windows 메모장을 tkinter를 활용하여 만들어 보기
  • 조건
    - 제목 : 메모장
    - 실제 메뉴 구현 : 열기, 저장, 종료
    - 하단 status 바는 구현 하지 않음
    - 프로그램 크기, 위치는 자유롭게 하되 크기 조정이 가능해야 함
    - 본문 우측에 상하 스크롤바 넣기
    - 그 외 사항은 아래의 그림과 같이 구현하기

1) 기초 골조 만들기

  • frame 생성
  • 글 쓰는 공간 생성
  • 스크롤 바 생성
from tkinter import *

main = Tk()
main.title("메모장")

scrollbar = Scrollbar(main)
scrollbar.pack(side='right', fill='y')

txt = Text(main, yscrollcommand=scrollbar.set)
txt.pack(fill="both", expand=True)

scrollbar.config(command=txt.yview)

main.mainloop()

2) 메뉴 생성

※ 위의 코드에 다음 내용을 추가

main_menu = Menu(main)

file_menu = Menu(main_menu, tearoff=0)
file_menu.add_command(label = "새 탭")
file_menu.add_command(label = "새 창")
--- (중략) ---
file_menu.add_command(label = "창 닫기")
file_menu.add_command(label = "종료")

edit_menu = Menu(main_menu, tearoff=0)
edit_menu.add_command(label = "실행 취소")
edit_menu.add_separator()
--- (중략) ---
edit_menu.add_separator()
edit_menu.add_command(label = "글꼴")

view_menu = Menu(main_menu, tearoff=0)
view_menu.add_command(label = "확대")
view_menu.add_command(label = "축소")
--- (중략) ---
view_menu.add_checkbutton(label = "상태 표시줄")
view_menu.add_checkbutton(label = "자동 줄바꿈")

main_menu.add_cascade(label="파일", menu=file_menu)
main_menu.add_cascade(label="편집", menu=edit_menu)
main_menu.add_cascade(label="보기", menu=view_menu)
main.config(menu=main_menu)
파일 메뉴 편집 메뉴
보기 메뉴

3) 일부 메뉴 구현

  • mynote.txt

(1) 열기

  • mynote.txt 파일 내용 열어서 보여주기
--- (생략) ---
def open_file():
    with open("mynote.txt", "r", encoding="utf8") as content:
        txt.delete(1.0, END)
        txt.insert(END, content.read())
file_menu.add_command(label = "열기", command=open_file)
--- (생략) ---
  • 문제 발생
    - 파일 내용을 불러오지 못했다.
def open_file():
    content = open("mynote.txt", "r", encoding="utf8")
    while True:
        line = content.readline()
        if not line:
            break
        txt.insert(END, line)
        txt.insert("\n")
file_menu.add_command(label = "열기", command=open_file)
  • 원인
    - 이는 기존에 있는 문자열을 제거하지 않았다
    - insert 메서드에 여러 개의 인수를 사용했다
    - 개행 문자를 별도로 삽입할 필요가 없는데 삽입했다
  • 해결 및 보완
    • 리소스 낭비를 줄이기 위해서 with로 파일 입출력을 함
    • 이미 존재하던 빈 문자열을 delete로 제거 함

(2) 저장

  • mynote.txt 파일에 현재 내용 저장하기
--- (생략) ---
def save_file():
    with open("mynote.txt", "w", encoding="utf8") as content:
        content.write(txt.get(1.0, END))
file_menu.add_command(label = "저장")
--- (생략) ---

(3) 끝내기

  • 프로그램을 끝냄
--- (생략) ---
file_menu.add_command(label = "종료", command = main.quit)
--- (생략) ---
profile
코딩하는 찍찍이 🐀

0개의 댓글