하이어시스템 소개
원문 : https://pythonassets.com/posts/placing-widgets-in-tk-tkinter/
Tkinter에서는 윈도우에서 위젯을 배치하는 3가지 방법을 제공하고 있습니다. 그 메서드는 각각 pack(), place(), grid() 입니다. 이들은 사용방법이 전혀 다르고 장단점이 확연하게 차이가 납니다. 따라서 원하는 결과물에 따라 사용할 메서드가 달라집니다. 각 메서드에 대해 접근해보고 작동 방식을 알아보겠습니다. 하나의 윈도우에 세 가지 메서드를 혼합해서 사용하는 방법은 일부 제한적이지만 가능하나 가독성 및 유지보수에 좋지 않으므로 추천하지 않습니다.
place(절대 위치 지정)
place() 함수는 위젯의 절대 위치 (X와 Y)를 부모 위젯에 상대적으로 지정하여 위젯을 배치하는 데 사용됩니다. 위젯에 부모가 없는 경우, 부모는 창 자체가 됩니다. 예를 들어, 다음 코드를 살펴보겠습니다.
import tkinter as tk
from tkinter import ttk
main_window = tk.Tk()
main_window.title("Tkinter에서 위젯 배치하기")
main_window.config(width=300, height=200)
main_window.mainloop()
이 프로그램은 창(main_window)을 생성하고, 이 창은 다른 모든 위젯을 포함하는 부모 위젯이 됩니다. 창의 크기는 300x200 픽셀입니다. 이제 버튼을 추가하고 (60, 40) 위치 (즉, x=60, y=40)에 배치해보겠습니다.
import tkinter as tk
from tkinter import ttk
main_window = tk.Tk()
main_window.title("Tkinter에서 위젯 배치하기")
main_window.config(width=300, height=200)
button = ttk.Button(text="Hello, world!")
button.place(x=60, y=40)
main_window.mainloop()
좌표 시스템의 원점 (즉, (0, 0) 위치)은 좌측 상단 모서리입니다. 따라서 창의 좌측 테두리와 버튼 사이에 60px 거리가 있으며, 창의 상단 테두리와 버튼 사이에 40px 거리가 있습니다.
너비와 높이 매개변수를 사용하여 버튼의 크기 (또는 다른 위젯의 크기)를 픽셀로 지정할 수도 있습니다.
button.place(x=60, y=40, width=100, height=30)
너비와 높이 매개변수를 사용하여 버튼의 크기 (또는 다른 위젯의 크기)를 픽셀로 지정할 수도 있습니다.
이 네 가지 속성은 부모 위젯(이 경우는 창 자체)에 상대적인 비율로 설정할 수도 있습니다. 예를 들어, 버튼 크기를 창 크기의 절반으로 지정할 수 있습니다.
button.place(relwidth=0.5, relheight=0.5)
이렇게 하면 창이 확장되거나 축소될 때 Tkinter가 버튼 크기를 자동으로 조정하여 지정한 비율을 유지합니다.
위와 비슷하게, relx와 rely는 위젯의 위치를 상대적인 값으로 나타냅니다.
button.place(relx=0.1, rely=0.1, relwidth=0.5, relheight=0.5)
위의 같이 코드를 작성하였다면 애플리케이션을 열 때 창 크기가 300x200인 경우, 버튼은 (30, 20)에 배치됩니다. 이는 300x0.1 = 30이고 200x0.1 = 20이기 때문입니다. 창 크기가 변경될 때마다 Tkinter는 버튼 위치를 업데이트하여 항상 창의 10%에 해당하도록 합니다. relwidth, relheight, relx 및 rely는 0과 1 사이의 값을 지원합니다.
place() 메서드를 사용하여 위젯을 배치하는 것은 사용하기 매우 간단합니다. 특히 데스크톱 응용 프로그램을 개발한 경험이 있는 사용자에게는 매우 직관적입니다. 인터페이스 내의 각 객체에 대해 정확성을 제공하며 많은 경우에 유용합니다.
주요 단점은 창을 확장하거나 축소할 때 발생합니다. 앞에서 설명한 비례 인수를 이용하면 어느정도 해소할 수 있지만 충분하지 않습니다. 절대 위치로 위젯을 배치하는 경우는 크기기 지정된 정적인 윈도우 화면에 적합합니다. 만약 사용자가 확대하면 빈 공간이 생기거나 창을 축소하면 일부 위젯이 보이지 않게 됩니다.
pack(상대 위치 지정)
pack() 함수는 세 가지 방식 중 가장 간단한 방법입니다. 위젯의 좌표를 지정하는 대신 Tkinter에게 위젯 추가를 요청하면 기존에 추가된 위젯을 기준으로 상대위치에 추가합니다. 상대위치를 지정하는 방법은 위, 아래, 왼쪽, 오른쪽 과 같은 키워드를 사용할 수 있습니다.
import tkinter as tk
from tkinter import ttk
main_window = tk.Tk()
main_window.title("Tkinter에서 위젯 배치하기")
entry = ttk.Entry()
entry.pack()
button = ttk.Button(text="안녕, 세계!")
button.pack()
main_window.mainloop()
이 예제에서는 텍스트 상자와 버튼을 만들고 pack() 함수를 사용하여 창에 배치합니다. 인수를 전달하지 않았으므로 Tk는 기본적으로 위젯을 서로 위에 놓게 됩니다. 다음 이미지에서 보여지는 것처럼 서로 위에 놓이게 됩니다.
따라서 라벨과 같은 다른 위젯을 추가하면 해당 위젯은 버튼 아래에 배치됩니다.
label = ttk.Label(text="...from tkinter!")
label.pack()
위젯의 상대위치를 지정하는 속성은 side이며 side의 인자로 지정할 수 있는 값은 tk.TOP (디폴트), tk.BOTTOM, tk.LEFT, or tk.RIGHT 입니다. 아래와 같이 텍스트박스를 왼쪽에 배치해야 한다면 side=tk.LEFT 코드로 구현할 수 있습니다. 그외에 다른 2개의 위젯은 기존처럼 상하 위치를 유지합니다.
entry = ttk.Entry()
entry.pack(side=tk.LEFT)
pack() 함수는 after 및 before 매개변수도 허용하며, 이를 통해 창 위젯이 배치되는 순서를 제어할 수 있습니다. 다음 코드는 Tk에게 라벨을 텍스트 상자보다 먼저 배치하도록 지시합니다.
entry = ttk.Entry()
entry.pack()
button = ttk.Button(text="안녕, 세계!")
button.pack()
label = ttk.Label(text="...from tkinter!")
label.pack(before=entry)
before와 after는 기준이 되는 위젯으로서 어떤 위젯이든 값을 받아들입니다.
pack()이 허용하는 다른 속성으로는 padx, ipadx, pady, ipady가 있습니다. 이것들은 위젯의 외부 및 내부 여백을 (픽셀 단위로) 지정합니다. 예를 들어, 다음 코드에서는 버튼과 창 사이에 30px 공간(외부 여백)이 있지만, 버튼 테두리와 텍스트 사이에 50px 공간(내부 여백)이 있습니다.
main_window = tk.Tk()
main_window.title("Tkinter에서 위젯 배치하기")
button = ttk.Button(text="안녕, 세계!")
button.pack(padx=30, pady=30, ipadx=50, ipady=50)
main_window.mainloop()
마지막으로, Tk의 pack 메서드는 창 크기가 변경될 때 어떤 위젯이 확장되거나 축소되어야 하는지, 그리고 어떤 방식으로(수직 또는 수평으로) 확장해야 하는지를 지정할 수 있습니다. expand 및 fill 속성을 사용하여 이를 설정합니다.
button = ttk.Button(text="안녕, 세계!")
button.pack(expand=True, fill=tk.X)
이 예에서는 버튼이 창 크기가 변경될 때 수평으로 크기가 조정됩니다(fill=tk.X). 버튼을 수직으로만 조정하려면 속성을 fill=tk.Y로 설정하면 됩니다. fill=tk.BOTH로 설정하면 두 방향으로 확장됩니다.
button = ttk.Button(text="안녕, 세계!")
button.pack(expand=True, fill=tk.BOTH, padx=10, pady=10)
grid(그리드 위치 지정)
그리드 메서드는 작은 애플리케이션에서부터 크고 복잡한 사용자 인터페이스에 이르기까지 항상 좋은 선택입니다. 개념적으로 메인 창을 행과 열로 나누어 위젯을 배치하는 것을 의미합니다. 예를 살펴보겠습니다.
import tkinter as tk
from tkinter import ttk
main_window = tk.Tk()
main_window.title("Tkinter에서 요소 배치하기")
entry = ttk.Entry()
entry.grid(row=0, column=0)
button = ttk.Button(text="여기를 누르세요")
button.grid(row=0, column=1)
label = ttk.Label(text="안녕, 세계!")
label.grid(row=1, column=0)
main_window.mainloop()
7번째와 14번째 줄 사이에서 세 개의 위젯(텍스트 상자, 버튼, 라벨)을 생성하고 grid() 함수를 사용하여 그리드 내에서 위치를 설정합니다.
(빨간 선들은 설명하기 쉽도록 표시한 선으로 실제 UI가 아닙니다) 위 이미지에서 볼 수 있듯이 현재 그리드는 두 개의 행과 두 개의 열을 가지고 있으며, 이로 인해 총 (2x2=4) 네 개의 셀이 생깁니다.
텍스트 상자는 열 0, 행 0에 있습니다. 이 규칙을 따르면 버튼은 (1, 0) 셀에, 라벨은 (0, 1) 셀에 배치됩니다. (1, 1) 셀에는 위젯이 없습니다. 그리드에는 우리가 원하는 만큼 많은 열과 행을 가질 수 있습니다.
위젯에 한 위젯이 하나 이상의 행 또는 열을 차지하도록 지정할 수 있습니다. 예를 들어, (1, 1) 셀이 비어있으므로 라벨이 해당 셀을 차지하도록 지정할 수 있습니다.
label.grid(row=1, column=0, columnspan=2)
columnspan은 위젯이 차지해야 하는 열의 수를 지정합니다(기본값은 1입니다). rowspan은 행에 대해서도 비슷한 방식으로 작동하지만, 행을 위한 것입니다.
기본적으로 열과 행은 창 크기가 변경되더라도 확장되거나 축소되지 않습니다. 이를 위해 rowconfigure() 및 columnconfigure() 함수를 사용하여 weight 매개변수를 사용합니다. 예를 들어, 다음 코드는 열 0과 행 0이 확장되어야 함을 Tk에게 알립니다.
main_window.columnconfigure(0, weight=1)
main_window.rowconfigure(0, weight=1)
위 이미지는 창을 확대한 후에 셀이 확장된 것을 보여줍니다. 텍스트 상자는 셀의 중앙에 남아 있습니다. 위젯이 셀의 위, 아래, 오른쪽 또는 왼쪽에 고정되지 않고 셀의 가운데에 머무르지 않도록 하려면 sticky 매개변수를 사용합니다. 이 매개변수의 지원되는 값은 "n" (북), "s" (남), "e" (동), "w" (서)입니다.
entry.grid(row=0, column=0, sticky="n")
이렇게 함으로써 위젯을 가로로 확장합니다("ew"). 세로로만 조정하려면 속성을 "ns"로 설정하면 됩니다. 두 방향으로 확장하려면 "nsew"로 설정하면 됩니다.
entry.grid(row=0, column=0, sticky="nsew")
grid() 함수는 pack()과 마찬가지로 padx, pady, ipadx 및 ipady 인수를 허용하여 여백을 설정합니다.
entry.grid(row=0, column=0, sticky="nsew", padx=10, pady=10)
마지막으로, Tk의 grid 메서드는 얼마나 많은 열과 행이 확장되는지를 구성할 수 있습니다. 예를 들어, 다음 코드를 살펴보겠습니다.
import tkinter as tk
from tkinter import ttk
main_window = tk.Tk()
main_window.title("Tkinter에서 요소 배치하기")
label1 = tk.Label(text="안녕, 세계!", bg="#FFA500")
label1.grid(row=0, column=0, sticky="nsew")
label2 = tk.Label(text="안녕, 세계!", bg="#1E90FF")
label2.grid(row=1, column=0, sticky="nsew")
main_window.columnconfigure(0, weight=1)
main_window.rowconfigure(0, weight=1)
main_window.rowconfigure(1, weight=1)
main_window.mainloop()
이 창에서는 두 개의 라벨을 만들고 같은 열(0)에 배치하지만 다른 행(0과 1)에 배치합니다. 그런 다음 rowconfigure() 및 columnconfigure()를 사용하여 창(부모 위젯)이 변경될 때 위젯이 확장되고 축소되도록 지정합니다.
이 이미지에서 볼 수 있듯이 두 위젯은 사용 가능한 공간을 공유하므로 크기가 항상 같습니다. 창의 크기가 얼마나 크든 작든 상관없이 항상 동일한 크기입니다. 하지만 때로는 한 위젯이 다른 위젯보다 더 많이 확장되거나 그 반대로 하고 싶을 수 있습니다. 이를 위해 행 또는 열이 Tk가 확장할 때 얼마나 더 크게 할 것인지를 지정할 수 있습니다.
main_window.rowconfigure(0, weight=5)
main_window.rowconfigure(1, weight=1)
이 구성으로 인해 행 0(주황색 라벨이 있는 행)의 크기는 항상 행 1의 크기보다 4배(5-1) 큽니다. 다음 이미지에서 확인할 수 있습니다.
그리드 방식은 하이어시스템의 표준 레이아웃입니다. 좀 더 자세한 설명을 원하시면 아래의 포스팅을 참고하세요.
https://wise-office-worker.tistory.com/37
요약
요약하면, place() 메서드는 창의 크기를 조정할 필요가 없는 작고 중간 크기의 사용자 인터페이스에 적합합니다. 사용자가 창 크기를 조정하지 않을 것으로 예상되는 경우입니다.
pack()은 사용하기 매우 간단하며 풍부하고 복잡한 인터페이스를 구현할 수 있습니다. 그러나 각 위젯의 위치가 다른 위젯에 의존하는 것이 변경 사항을 가리킬 때(특히 기존 응용 프로그램을 수정하는 동안) 어려움을 일으킬 수 있습니다.
마지막으로 grid()를 통해 위젯을 관리하는 것은 항상 좋은 선택입니다. 작동 방식을 알고 조심스럽게 적용한 후에는 작은 것부터 큰 것까지 완전히 조정 가능하고 사용하기 쉬운 사용자 인터페이스를 만들 수 있습니다.
'파이썬코드공유' 카테고리의 다른 글
셀레니움 selenium 매크로 탐지 회피 전략 (0) | 2023.08.04 |
---|---|
Tkinter Background Task 백그라운드 작업 (0) | 2023.08.03 |
Tkinter 그리드 레이아웃(grid layout) (1) | 2023.08.03 |
파이썬 셀레니움 네이버 로그인 코드 공유 (0) | 2023.07.29 |
[강추] 파이썬 EXE 실행파일 디컴파일 초간단 방법(Magic Number 불필요) (3) | 2023.07.17 |