본문 바로가기
Python/Python tkinter

[Python tkinter] 2. 빈 창에 위젯(Widget) 생성하기

by Rosmary 2022. 1. 12.
728x90
반응형

 

 

지난 포스팅에서는 tkinter 패키지의 Tk() 클래스를 선언하여 Window 창을 생성하고, 생성한 창의 설정을 진행하는 방식에 대해 알아보았다.

 

tkinter로 생성한 창은 아무것도 없는 완전무결한 빈 공간이다. 실제 우리가 프로그램에서 볼 수 있는 텍스트 입력 창이나, 버튼이나, 스크롤 등은 별도로 추가해주어야 한다. 창 내의 버튼, 텍스트 입력창, 드롭 박스 등을 tkinter에서는 위젯(Widget)이라고 하는데, 이 위젯들을 생성하고 창 내에 배치해주는 작업을 거쳐야 한다. 이번 포스팅에서는 위젯을 창에 생성하고 배치하는 방법에 대해 간략하게 알아보려 한다.

 

 

 

I. 위젯 클래스 선언 및 위젯 생성

 

이 위젯들 역시 Tk() 클래스와 마찬가지로 tkinter 패키지에 클래스 형태로 존재한다. 따라서 위젯 생성을 하려면, Tk() 클래스로 창을 생성하듯이 위젯 관련 클래스를 선언해주어야 한다. 예시를 몇 개만 들자면, 클릭하는 버튼의 경우 tkinter.Button() 클래스 선언 시, 텍스트 입력창은 tkinter.Entry()나 tkinter.Text() 클래스를 선언함으로써 위젯을 생성할 수 있다. 

 

위젯 생성을 위해 각 위젯 클래스를 선언할 때, 위젯이 어느 창에 생성될지를 지정해주어야 한다. 만약 Tk() 클래스를 window라는 변수에 할당하여 선언했다고 하면(window = tkinter.Tk()), window 창에 버튼을 생성하는 코드는 아래와 같은 포맷을 가진다.

 

button1 = tkinter.Button(master=위젯을 생성할 창 객체 변수명, 위젯 옵션 )

 

클래스 선언 시 사용하는 인자값 중 master는 필수값으로 입력되어야하는데, 이 master로 지정된 값은 생성할 위젯이 배치될 창 또는 객체를 의미한다. 또한 위젯 타입에 따라 작성할 수 있는 옵션 인자를 선택적으로 설정할 수 있는데, 위젯 옵션과 관련된 내용은 추후 개별 위젯에 대한 포스팅을 진행하면서 다룰 예정이다.

 

새 창을 하나 만들어, 내부에 "Login"과 "Cancel" 이라는 이름의 버튼 2개를 생성한다고 해보자. 코드는 아래와 같다.

 

분명 2개의 Button 위젯을 window 창 객체에 생성하는 코드를 작성하였으나, 실제 실행해보면 버튼이 필자가 생성한 창에 나타나지 않는다. 위에서 필자가 언급한 내용을 다시 보면 그 이유가 나타나는데, 필자가 위젯을 창에 생성한다고 선언하기만 했지 배치한다는 코드는 어느 곳에도 작성하지 않았기 때문이다. 

 

 

II. 창 내 위젯 배치

 

위젯 선언 시, window 창 객체에 위젯을 생성한다고 설정했기 때문에, 배치 코드만 작성하면 된다. 

 

모든 위젯 객체는 창에 배치되기 위한 공통 매서드를 지니고 있다. 특이하게도 위젯 배치와 관련된 매서드는 3 개나 존재하는데, 그 목록은 아래와 같다.

 

- pack()

- grid()

- place()

 

 각 매서드들에 대해 하나씩 살펴보자. 

 

1. pack()

 

Pack은 창을 단순히 구역화 한 뒤, 위젯을 구역 내에 순차적으로 배치해주는 매서드다. 필자의 설명이 얼핏 이해되지 않을테니 코드를 통해 알아보자. 2개의 Button 생성 코드 아래에 각 위젯에 대한 pack() 매서드를 작성하여 Button들을 창에 배치해보자.

위의 결과를 보면, pack() 매서드에 의해 "Log In"과 "Cancel" 버튼이 위에서부터 순차적으로 생긴것을 확인할 수 있다. 

pack() 매서드로 위젯을 배치할 경우, 하나의 위젯에 대해 위젯 크기에 따라 단순하게 공간을 할당해주고, 해당 공간의 한 가운데에 위젯을 배치하게 된다. 다수의 위젯이 pack() 매서드로 배치되는 경우, 위에서부터 순차적으로 공간을 차지하게 된다. 

 

pack() 매서드는 몇 가지 속성을 통해 위젯 배치 위치를 조정하거나 위젯 모양을 어느정도 선에서 변경하는 것이 가능하다. 

 

 

(1) side, 인자값 = ["top", "left", "bottom", "right"]

pack() 매서드 인자 중, side라는 인자가 존재한다. 이 side는 창 내 위젯을 순차적으로 쌓을 방향을 결정하는 것이라 보면 된다. 기본값은 "top"이며, 이 때문에 다수의 위젯을 인자 없이 pack()으로 배치할 경우 위에서부터 순차적으로 배치된다.

 

side 값을 "left"로 지정하면, "Log In" 버튼이 창의 왼쪽 가운데에, "Cancel" 버튼이 바로 우측 가운데에 생성된다.

 

 

(2) anchor, 인자값=["center", "n", "e", "w", "s"]

다시 side 값을 기본값으로 변경하고 결과를 보자. 그러면 각 위젯이 할당된 공간 내에서 가운데에만 위치하고 있음을 확인할 수 있다. 이 위젯의 위치도 할당된 공간 내에서 조정이 가능하며, 이 때 사용하는 인자는 anchor다. anchor는 "center"를 기본값으로 가지기 때문에 별도의 설정이 없는 경우 모든 위젯은 할당 공간의 가운데에 위치하게 된다.

 

anchor 값은 특이하게도 "left", "right"가 아니라 동서남북을 뜻하는 "e", "w", "s", "n"를 사용한다. 따라서 필자가 위젯을 왼쪽으로 배치하고 싶다면 anchor 값을 "w"로, 우측으로 배치하고 싶다면 "e"를 지정하면 된다. e,w 와 n,s는 중첩하여 사용하는 것(ne, nw, se, sw)도 가능하기 때문에 8방과 가운데까지 9군데에 위젯 배치가 가능하다.

 

 

 

(3) fill, 인자값=["x", "y", "both"]

anchor 값도 기본값으로 되돌리자. 이제 필자는 저 버튼을 할당된 공간 전체에 꽉 차게 만들고 싶다. 이 때 사용하는 인자는 fill이다. fill은 기본값이 side="top", "bottom"일 때 "y", "left", "right"일 때 "x"로 설정된다. "x"로 지정할 경우 버튼 크기가 할당 공간의 가로축 전체를 차지할 정도로 늘어나게 되며, 반대로 "y"로 지정할 경우 할당 공간의 세로축 전체로 늘어나게 된다. "both"를 지정할 경우 할당 공간의 가로/세로축 전체를 차지할 정도로 위젯이 늘어난다.

 

먼저 side="top", fill="x" 설정으로 결과를 확인해보자. 아래의 그림과 같이 모든 버튼이 가로축으로 길게 창 공간을 차지하고 있음을 확인할 수 있다.

 

side="left", fill="y"로 지정할 경우, 세로축으로 공간이 할당된 버튼 위젯들이 세로축으로 길게 늘어난 형태로 배치된다.

 

 

(4) padx, pady, 인자값=여백Pixel값(int)

pack() 매서드를 사용하면 각 위젯들이 다닥다닥 붙어 배치되는 것을 볼 수 있다. 이 위젯들의 위치를 조금 떨어뜨리고 싶은 경우도 분명 발생할텐데, 이 때는 padx, pady 값을 지정하여 해결할 수 있다.

 

pad는 padding, 즉 여백이라는 뜻이며, 여백을 x 또는 y 방향으로 둔다는 의미로 pad 뒤에 x, y를 붙인 것이 위 인자 이름의 유래로 보면 된다.

 

side="left"인 상태에서 두 버튼 사이 여백을 준다고 가정해보자. 그럼 각 버튼 위젯의 x축에 여백이 들어가야하므로, padx 값을 지정해주면 된다. 

사실, 위젯 생성 시, padx와 pady에 대한 설정도 가능하기 때문에 pack() 매서드 내에서 padx, pady를 사용하는 일은 드물다.

 

 

pack()은 빠르게 위젯들을 배치할 수 있다는 장점이 있지만, 섬세한 배치가 필요한 위젯들에는 사용하기가 어렵다는 문제가 있다. 이로 인해 섬세한 위젯 배치가 필요한 경우, grid()나 place() 매서드를 주로 사용하게 된다.

 

 

2. grid()

 

grid() 매서드의 이름에서 기능을 대략적으로 유추할 수 있는데, 이 매서드는 창을 격자(grid) 형태로 나누어 배치하는 방식으로 위젯을 배치한다. grid()내의 인자는 행과 열의 번호를 입력하며 아래와 같은 포맷으로 매서드를 사용한다.

 

grid(row=행 번호, column=열 번호)

 

grid(row=0, column=0)인 경우 창 내 좌측 상단에 위젯이 배치되고, grid(row=0,column=1)인 경우 grid(row=0,column=0) 위치 바로 우측에 위젯이 위치하게 된다. 이러한 구조를 볼 수 있는 가장 좋은 예는 계산기인데, tkinter의 grid() 매서드를 통해 window의 기본 프로그램 중 하나인 계산기와 동일한 창을 만드는 것도 가능하다.

 

필자는 pack()에서 썼던 코드 아래에 window2라는 이름으로 새 창을 만들고, 해당 창에 아래와 같이 button 9개를 생성한 뒤, 3x3 형태의 격자에 button을 배치해보았다.

 

결과 창을 보면 마치 계산기와 유사하게 모양이 나타나는 것을 볼 수 있다.

 

(1) rowspan, columnspan

grid() 매서드는 특정 위젯이 하나 이상의 row, column 공간을 차지할 수 있도록 하는 인자도 제공한다. 바로 rowspan과 columnspan이 그것이며, rowspan이 2로 지정된 경우, 위젯이 세로방향 격자 2칸의 공간을, columnspan이 2로 지정된 경우 위젯이 가로방향 격자 2칸의 공간을 차지하게 된다.

 

필자가 버튼 2개를 추가로 생성하고, (3,1) 위치의 버튼은 가로방향 격자 2칸을 차지하도록 설정한다고 해보자.

 

(2) sticky

분명 버튼 (3,1)이 2개의 가로 공간을 차지하고 있는 것이 보인다. 하지만, 위젯이 크기는 고정된 채로 공간의 가운데에 배치되어 있어 모양새가 상당히 좋지 않다. 다행히 grid() 매서드는 이러한 상황에서 위젯의 크기를 공간에 맞도록 설정할 수 있는 인자도 제공한다. sticky라는 인자가 그 역할을 하며, 이 인자는 이 포스팅의 앞부분에서 보았던 anchor와 동일하게 "n", "e", "w", "s"를 인자값으로 가진다. 

 

만약 필자가 (3,1) 버튼의 columnspan을 2로 설정하고, sticky를 "ew"(동,서 방향 지정)로 설정하면, 버튼 (3,1) 위젯이 가로축 격자 2개의 공간을 가득 채우는 것을 볼 수 있다. 

 

따라서 grid()를 사용하여 특정 위젯에게 더 많은 공간을 할당할 때, rowspan / columnspan과 동시에 sticky 인자를 사용하는 경우가 많다.

 

참고로 여러 위젯을 하나의 공간에 배치할 때, pack()과 grid()를 혼용하여 사용하면 에러가 난다. 당연한 이야기지만, pack()은 가로축 또는 세로축 전체 공간을 위젯에 할당하기 때문에, grid()에 의한 격자 위치가 겹치는 경우가 많기 때문이다.

 

pack() 매서드와 비교했을 때, grid() 매서드는 구조화된 창 형태를 만들때는 상당히 유용하지만, 역시나 위젯 배치를 매우 섬세하게 진행해야하는 경우 사용하기가 적절하지 않다. 마지막으로 알아볼 매서드인 place()는 pack()과 grid()와 달리, 코더가 원하는 위치에 위젯을 정확하게 배치하도록 하는 기능을 가진다.

 

 

3. place()

 

place() 매서드는 위젯 배치가 매우 직관적이다. 창 내에서 위젯을 배치할 좌표 x, y를 pixel 값으로 지정해주면 된다. 

 

place(x=가로축 pixel 위치, y=세로축 pixel위치

 

참고로 버튼을 클릭하면 배경 색이 변하도록 버튼 설정을 넣어놓았다.

 

 


 

이번 포스팅에서는 창에 표시할 버튼, 텍스트 박스 등의 위젯을 배치하는 방법 3가지에 대해 알아보았다.

지난 포스팅과 마찬가지로 이번 포스팅에서 사용한 코드를 첨부한다.

002_tkinter_creating_widget.py
0.00MB

 

다음 포스팅부터 본격적으로 tkinter의 위젯에 대해 하나씩 알아볼 예정이다.

 

 

 

 

Fin.

반응형

댓글