16장. GUI 프로그래밍-1

asda주asda·2022년 2월 3일
0

Python

목록 보기
20/31

tkinter

tkinter는 파이썬에서 그래픽 사용자 인터페이스(GUI : Graphic User Interface)를 개발할 때 필요한 모듈이다. GUI는 사용자가 컴퓨터를 사용할 때, 그래픽을 통하여 상호작용을 할 수 있는 환경을 말한다. 대부분의 프로그램에서의 사용자는 마우스를 이용하여 화면에 표시된 메뉴를 선택하여 작업을 하고 실행 결과도 화면에 그래픽으로 표시된다.

tkinter를 이용하면 윈도우를 생성하고 버튼이나 레이블과 같은 위젯을 이용하여 사용자와 상호작용하는 프로그램을 작성할 수 있다.

tkinter은 'TK interface'의 약자로 만약 tkinter가 없었다면 파이썬은 그다지 매력적이지 못할 수 있다. 파이썬에는 GUI 프로그램의 개발을 위한 다양한 모듈들이 있지만 tkinter가 가장 많이 이용되고 있다.

첫 번째 tkinter 프로그램

아주 간단한 예로 레이블만을 가지고 있는 윈도우를 작성하자. 이 때 레이블(Lable)은 텍스트를 표시하는 위젯이다. 위젯(widget: window gadget)이란 윈도우와 같은 GUI 기반의 운영체제에서 많이 사용되는 시각적인 요소들을 말한다. 대표적으로 버튼, 레이블, 테이블, 텍스트필드, 메뉴 등이 있다. 여기서 레이블은 사용자가 볼 수 있지만 상호작용은 불가하다.

아래의 코드는 윈도우를 하나 생성하고 여기서 'Hello World!'를 표시하는 레이블을 배치한다.

from tkinter import *

window = Tk() 
label = Label(window, text="Hello World!")
label.pack()
window.mainloop()

위의 코드를 하나씩 살펴보면

  • from tkinter import *
    제일 먼저 tkinter 모듈을 가져온다. tkinter 모듈은 Tk 툴킷을 사용하는데 필요한 모든 클래스와 함수를 가지고 있다. 일반적인 경우에는 tkinter 모듈에서 모든 것을 포함시키면 된다.

  • window = Tk()
    다음으로 루트 윈도우(root window)를 생성한다. tkinter 모듈 안에 있는 Tk클래스가 윈도우를 나타낸다. Tk 클래스는 제목을 가지고 있는 일반적인 윈도우이다. Tk클래스의 객체를 생성하면 화면에 하나의 윈도우가 생성된다. 이 윈도우 안에 여러 위젯을 추가할 수 있다. 각 프로그램은 오직 하나의 루트 윈도우를 가져야 한다. 또한 다른 위젯들 보다 먼저 생성되어야 한다. 왜냐하면 명칭 그대로 모든 것의 근원이 되는 윈도우이기 때문이다.

  • label = Label(window, text= "Hello, World!")
    다음으로 루트 윈도우의 자식으로 레이블 위젯을 생성한다. 레이블 위젯은 텍스트나 이미지를 표시할 수 있는 위젯이다. 여기서 텍스트 'Hello, World' 를 표시한다.

  • label.pack()
    위젯에 대하여 pack() 메소드가 호출 된다. pack()은 텍스트에 표시할 정도로만 레이블 위젯의 크기를 축소하라는 의미이다. pack()이 호출되어야 화면에 나타난다.

  • window.mainloop()
    pack()이 호출되면 위젯이 화면에 나타난다. 하지만 윈도우는 우리가 tkinter의 이벤트 루프에 들어가기 전까지 나타나지 않는다. 이 프로그램은 우리가 윈도우를 닫을 때 까지, 이벤트 루프에서 대기를 한다.
    이벤트 루트는 사용자로 부터 오는 마우스나 키보드 이벤트 뿐만 아니라 윈도우 시스템에서 오는 이벤트도 함께 처리한다. 또 tkinter 자신에 의하여 큐(Queue)에 넣어지는 이벤트도 처리한다. 예를 들면 배치 관리이벤트가 있다. 앞의 pack() 메소드가 배치 관리이벤트를 추가한다. 따라서 프로그램이 메인 루트에 진입하기 전까지는 화면에 윈도우가 나타나지 않을 것이다.

즉 윈도우가 나타나면서 사용자의 동작(이벤트) 대기를 한다.

버튼과 이벤트 처리

버튼과 이벤트 처리

버튼은 다양한 용도로 사용되는 표준 tkinter 위젯이다. 버튼은 사용자와 상호작용을 할 목적으로 설계된 위젯이다.
사용자는 버튼을 마우스로 누르면 어떤 동작이 시작된다. 버튼은 텍스트와 이미지를 포함할 수 있다. 레이블은 다양한 글꼴로 텍스트를 표시할 수 있는 반면, 버튼은 하나의 글꼴만 사용할 수 있다. 버튼에 특정한 함수를 지정하여, 버튼이 눌리면 그 함수가 호출이 되도록 할 수 있다.

아래는 간단한 버튼 프로그램이다.

from tkinter import *

window = Tk()
b1 = Button(window, text= '파이썬 버튼')
b1.pack()
window.mainloop()

파이썬 버튼은 Button 클래스로 생성할 수 있다. 클래스의 첫 번째 인수는 '루트 윈도우' 이며, text 인수는 버튼의 텍스트를 나타낸다. 버튼 또한 반드시 pack() 메서드를 호출해야 화면에 버튼이 표시된다.

위의 코드에 대해 살펴보자면

  • from tkinter import *
    tkinter 모듈을 가져온다.

  • window = Tk()
    루트 윈도우를 생성하고 이를 변수 window에 할당한다. 이 문장이 실행된다면 'tk'라는 제목의 작은 윈도우가 화면에 등장한다. 그리고 이 제목은 변경이 가능하다.

  • b1 = Button(window, text = '파이썬 버튼')
    Button 생성자의 첫 번째 인수는 부모 윈도우이며, 나머지 인수의 text가 전달된다.

  • b1.pack()
    위의 코드로 버튼을 윈도우에 표시가 되게 한다. 이와 같은 코드를 '배치 관리자' 라고 하는데, 가장 일반적인 배치 관리자는 'pack'그리고 'grid' 이다.
    압축 배치 관리자로 위젯을 부모 안에 압축하여 배치할 수 있다. side(TOP,LEFT,RIGHT,BOTTOM)로 지정할 수 있으며 이로 위젯은 부모의 경계에 배치된다. 기본값으로는 TOP으로 설정되어 있다.

배치 관리자 예시

from tkinter import *
window = Tk() 
b1 = Button(window, text="이것이 파이썬 버튼입니다.")
b2 = Button(window, text="이것이 파이썬 버튼입니다.")
b1.pack()
b2.pack()
window.mainloop()


실행 결과
위의 그림에서 b1.pack()이 실행되면 버튼 b1이 윈도우에 배치되고 윈도우 자체가 버튼의 크기로 축소되는 것을 확인할 수 있다. 두 번째 버튼 b2가 배치되면 윈도우가 이것을 수용할 수 있도록 확장된다. 기본적으로 pack 배치 관리자는 위젯을 추가된 순서에 따라 수직으로 쌓는다.

from tkinter import *
window = Tk() 
b1 = Button(window, text="이것이 파이썬 버튼입니다.")
b2 = Button(window, text="이것이 파이썬 버튼입니다.")
b1.pack(side=LEFT)
b2.pack(side=LEFT)
window.mainloop()


실행 결과
압축 배치 관리자는 위젯을 수직으로 배치하거나 수평으로 배치하는데 사용된다. 현재 버튼은 너무 압축되어 보인다.약간의 패딩을 추가하여 이 문제를 해결할 수 있다. 패딩(padding)은 안쪽 여백을 말한다. "pady"는 상단과 하단에 픽셀을 추가하고 "padx"는 왼쪽 및 오른쪽에 픽셀을 추가한다.

from tkinter import *
window = Tk() 
b1 = Button(window, text="이것이 파이썬 버튼입니다.")
b2 = Button(window, text="이것이 파이썬 버튼입니다.")
b1.pack(side=LEFT, padx =10)
b2.pack(side=LEFT, padx =10)
window.mainloop()


실행 결과
padx를 각각 10씩 주어 서로 간의 간격을 두고 있다. 결과에서보면 버튼 사이의 공간은 20을 띄고 있다.


버튼안의 버튼은 다시 새롭게 작성하는 것이 가능하다.

…
b1.pack(side=LEFT, padx =10)
b2.pack(side=LEFT, padx =10)
b1["text"] = "One"
b2["text"] = "Two"


실행 결과
b1, b2의 속성이 “text”값을 변경시키고 출력한 것이다.

이벤트 처리

tkinter 프로그램은 이벤트에 기반을 두고 동작한다. 그렇기에 tkinter에서 버튼의 이벤트가 가장많이 이용된다.

사용자가 버튼을 누르면 버튼에서는 이벤트가 발생한다. 버튼에는 이벤트를 처리하는 함수를 붙일 수 있다. 버튼에 이벤트를 처리하는 함수가 연결되어 있으면 이벤트가 발생했을 때, 그 함수가 호출된다. 이벤트가 발생하였을 때 호출되는 함수를 콜백함수(Callback function), 또는 핸들러(handler)라고 한다.

버튼에 콜백함수의 등록은 버튼의 생성자를 호출할 때, command 매개변수에 이벤트를 처리하는 함수명을 지정하면 된다. 버튼이 클릭되면 command에 등록된 함수가 호출된다.


아래의 코드는 버튼을 눌렀을 때, 버튼의 텍스트가 변경되는 프로그램이다.

from tkinter import *

def callback():
	button["text"] = "버튼이 클릭되었음!"
window = Tk()
# 생성자에 command의 callback함수를 지정하였다.
button = Button (window, text="클릭", command=callback)

button.pack(side=LEFT)
window.mainloop()

아래는 레이블과 버튼을 동시에 표시하는 코드이다.

from tkinter import *

window = Tk()
label = Label(window, text="안녕하세요!")
label.pack() 
button = Button (window, text="tkinter로 버튼을 쉽게 만들 수 있습니다.")
button.pack()
window.mainloop()

실행 결과

pack 배치관리자에 의해 먼저 입력된 순으로 표시된다. 위의 코드는 따로 command로 지정하지 않았기에 클릭해도 아무런 버튼이 나타나지 않는다.

tkinter의 위젯들

위젯 클래스

tkinter는 GUI 작성에 필요한 위젯들을 제공하는데, 아래는 필수적인 위젯만을 작성한 것이다.

위젯설명
Button간단한 버튼으로 명령을 수행할 때 사용된다.
Canvas화면에 무언가를 그릴 때 사용한다.
Checkbutton2가지의 구별되는 값을 가지는 변수를 표현한다.
Entry한 줄의 텍스트를 입력 받는 필드이다.
get()을 사용하면 입력한 내용을 가져올 수 있고, delete()를 사용하면 사용자의 입력을 삭제할 수 있다.
Frame컨테이너 클래스이다. 프레임은 경계선과 배경을 가지고 있다. 다른 위젯들을 그룹핑 하는데 사용된다.
Label텍스트나 이미지를 표시한다.
Listbox선택 사항을 표시한다.
Menu메뉴를 표시한다. 풀다운 메뉴나 팝업 메뉴가 가능하다.
Menubutton메뉴 버튼이다. 풀다운 메뉴가 가능하다.
Message텍스트를 표시한다. 레이블 위젯과 비슷하다. 하지만 자동적으로 주어진 크기로 텍스트를 축소할 수 있다.
Radiobutton여러 값을 가질 수 있는 변수를 표시한다.
Scale슬라이더를 끌어서 수치값을 입력하는데 사용된다.
Scrollbar캔버스, 엔트리, 리스트 박스, 텍스트 위젯을 위한 스크룰바를 제공한다.
Text형식을 가지는 텍스트를 표시한다. 여러 가지 스타일과 속성으로 텍스트를 표시할 수 있다.
Toplevel최상위 윈도우로 표시되는 독립적인 컨테이너 위젯이다.
LabelFrame경계선과 제목을 가지는 프레임 위젯의 변형이다.
Panedwindow자식 위젯들을 크기 조절이 가능한 패널로 관리하는 컨테이너 위젯이다.
Spinbox특정한 범위에서 값을 선택하는 엔트리 위젯의 변형

Entry 예시

# 단순 위젯 중에서 : Entry 위젯
# Entry 위젯: 사용자가 키보드로 입력한 내용을 전달하는 위젯
# 예를 들면 아이디, 패스워드 등
# get() 를 사용하면 입력한 내용을 가져올 수 있다
# 사용자의 입력을 삭제하려면 delete() 메서드를 사용한다
# 온도 변환기 이벤트 처리
from tkinter import *

# 이벤트 처리함수의 정의
def process():
    # e1 엔트리 클래스에서 사용자가 입력한 값을 get() 을 통해서 가져옴
    tf = float(e1.get())
    tc = (tf-32.0)*5/9 # 화씨에서 섭씨로 변경
    e2.delete(0, END) # e2 엔트리의 값을 처음부터 끝까지 다 지운다.
    e2.insert(0,str(tc))

window = Tk()
Label(window, text='화씨').grid(row=0, column=0)
Label(window, text='섭씨').grid(row=1, column=0)
# 아래와  같이 분리를 해줘야 NoneType 에러를 발생하지 않는다.
e1 = Entry(window)
e1.grid(row=0, column=1)
e2 = Entry(window)
e2.grid(row=1, column=1)

Button(window,text='화씨->섭씨', command=process).grid(row=2, column=1)

window.mainloop()

Frame 예시

# 여러 배치관리자 혼용하여 실습하기

from tkinter import *

window = Tk()
# 가로 600px, 세로 100px의 크기로 지정한다.
window.geometry('600x100')
# Frame은 위젯이긴 하나, 단순 위젯이 아닌 컨테이너 위젯이다.
# 루트 윈도우 안에 프레임을 만든다.
frame =Frame(window)


# 버튼 3개를 프레임 안에 생성하고 배치한 것이다.
button1 = Button(frame, text= '버튼 #1', bg='red', fg='white',  width=10,height=2)
button2 = Button(frame, text= '버튼 #2', bg='green', fg='white', width=10,height=2)
button3 = Button(frame, text= '버튼 #3', bg='blue', fg='white',  width=10,height=2)
button1.pack(side=LEFT)
button2.pack(side=LEFT)
button3.pack(side=LEFT)

label = Label(window, text='이 레이블은 버튼 위에 배치됨')

# 레이블은 루트 윈도우에 압축배치관리자로 배치가 된다.
label.pack()
# 프레임도 루트 윈도우에 압축배치관리자로 배치가 된다.
frame.pack()
window.mainloop()

단순 위젯과 컨테이너 위젯

파이썬이 제공하는 위젯은 크게 단순 위젯과 컨테이너 위젯으로 나누어 진다. 컨테이너란 다른 위젯을 내부에 넣을 수 있는 위젯을 위미한다.

단순 위젯에는 Button, Canvas, Checkbutton, Entry, Label, Message 등이 여기에 속한다.
컨테이너 컴포넌트는 다른 컴포넌트를 안에 포함시킬 수 있는 컴포넌트로서 Frame, Toplevel, LabelFrame, PanedWindow 등이 여기에 속한다.

배치 관리자

배치 관리자는 컨테이너 안에서 위젯들의 위치와 크기를 결정하는 객체이다.
독서실에는 보통 관리자가 있어서 독서실에 출입하는 학생들의 위치를 결정해준다. 배치 관리자도 위젯들의 위치와 크기를 관리한다.

  • Pack
    압축 배치 관리자(Pack Geometry Manager)는 위젯들을 부모 위젯안에 압축한다. 자식 위젯들을 사각형의 블록으로 간주하여 프레임 안에 배치한다. 압축 배치 관리자의 사용은 pack()메소드를 이용한다.
  • Grid
    격자 배치 관리자(Grid Geometry Manager)는 테이블 형태의 배치를 가능케 한다. 위젯을 2차원적인 그리드에 배치한다. 격자 배치 관리자의 사용은 grid()메소드를 이용한다.

루트 윈도우를 테이블의 셀로 분할되고 ,각 위젯은 특정한 셀에 배치된다. 얼마나 많은 행과 열이 실제로 필요한지를 추적하고 또한 가장 큰 위젯을 수용할 수 잇도록 행과 열의 크기를 결정한다. 모든 행이 같은 높이가 될 필요는 없고 폭도 같을 필요는 없다.

from tkinter import *
window = Tk()

b1 = Button(window, text= '박스 #1', bg='red', fg='white')
b2 = Button(window, text= '박스 #2', bg='green', fg='white')
b3 = Button(window, text= '박스 #3', bg='orange', fg='white')
b4 = Button(window, text= '박스 #4', bg='black', fg='white')

b1. grid(row = 0 , column=0)
b2. grid(row = 0 , column=1)
b3. grid(row = 1 , column=0)
b4. grid(row = 1 , column=1)
window.mainloop()
  • Place
    절대 배치 관리자(Place Geometry Manager)는 주어진 위치에 위젯을 배치한다. 절대 관리자의 사용은 place() 메소드를 이용한다. 좌표값 x, y를 매개변수로 받는다.

from tkinter import *
window = Tk()
# 루트윈도우의 크기를 설정한다.
window.geometry('500x500')
# x, y 매개변수는 좌표값에 해당하여 절대적인 위치에 위젯을 배치시킨다.
b1 = Button(window, text= '박스 #1', bg='red', fg='white')
b1.place(x= 10, y=10)
b2 = Button(window, text= '박스 #2', bg='green', fg='white')
b2.place(x= 100, y=50)
b3 = Button(window, text= '박스 #3', bg='orange', fg='white')
b3.place(x= 200, y=10)
b4 = Button(window, text= '박스 #4', bg='black', fg='white')
b4.place(x= 100, y=10)
window.mainloop()

색상과 폰트

색상

대부분의 위젯은 배경(bg,Back-Ground)과 전경(fg,Figure-Ground) 속성을 사용하여 위젯 및 택스트를 지정할 수 있다. 색의 지정은 색상 이름(영어)을 사용한다.
버튼의 배경색과 전경색은 아래의 형식으로 변경이 가능하다.

from tkinter import *
window = Tk()
button = Button(window, text="버튼을 클릭하세요")
button.pack()
button["fg"] "yellow"
button["bg"] = "green"

실행 결과:

또 다른 방법으로 16진수를 이용해 RGB 값을 표기하는 것이다.

…
button["fg"] = "#ff0000"
button["bg"] = "#00ff00"

실행 결과:

tkinter.colorchooser 모듈의 askcolor()를 호출하면 대화상자를 통하여 색상의 값을 입력 받을 수 있다.

from tkinter import *
from tkinter.colorchooser import *

window = Tk()
button = Button(window, text='버튼을 클릭하세영!')
button.pack()
# 색을 받는다. 
color = askcolor()
# 받은 색은 튜플 형태로 저정된다.
# ex) ((255.99609375, 255.99609375, 128.5), '#ffff80')
print(type(color))
button['fg'] = color[1]

window.mainloop()
출력결과
<class 'tuple'>

폰트

텍스트를 표시하는 위젯을 사용할 때, 폰트를 지정할 수 있다. 모든 위젯은 디폴트 폰트를 가지고 있기 때문에 레이블과 버튼 등 간단한 요소에는 폰트를 지정할 필요가 없다. 바꿔도 된다. 폰트는 튜플로 아래와 같이 지정할 수 있다.
( 폰트 이름, 폰트의 크기, 폰트 스타일 )
거의 모든 위젯이 font 라는 속성이 있는데 이를 이용하여 폰트를 변경한다.

w = Label(master, text=”안녕", font=("Helvetica", 16))

위에 튜플을 문자열로 입력하여도 폰트 지정이 가능하다. 위의 코드를 ("Helvetica", 16)를 문자열 "Helvetica 16" 바꿔도 동일한 기능을 수행한다. 폰트도 미리 객체로 생성해 놓고 여러 곳에서 사용하면 메모리를 절약할 수 있다.


아래는 첫 번째 버튼을 누르면 레이블의 폰트가 작아졌다가 두 번째 버튼을 누르면 폰트가 켜지는 프로그램의 코드이다.

configure 메서드를 통해 프로그램의 실행 도중 텍스트나, 폰트의 크기의 변경이 가능하다.

from tkinter import *
from tkinter import font

class App(object):
    def __init__(self):
        root = Tk()
        # 폰트 지정
        self.customFont =font.Font(family='Helvetica', size = 12)

        label = Label(root, text= 'hello, man~', font=self.customFont)
        label.pack()
        bigger = Button(root, text= "폰트를 작게한다.", command=self.BigFont)
        smaller = Button(root, text= "폰트를 크게한다.", command=self.SmallFont)
        bigger.pack()
        smaller.pack()
        root.mainloop()

    def BigFont(self):
     size = self.customFont['size']
     print('폰트 크게: ',size)
     self.customFont.configure(size= size+2)
     print('폰트 크게함: ',self.customFont)

    def SmallFont(self):
     size = self.customFont['size']
     print('폰트 작게: ',size)
     self.customFont.configure(size= size-2)
     print('폰트 작게함: ',self.customFont)

if __name__ == '__main__':
    APP = App()

0개의 댓글

관련 채용 정보