본문 바로가기
GUI 튜토리얼/tkinter 한꼬집씩만 따라해보기

31. 커스텀 테마 적용하기

by 일코 2022. 11. 28.

이전 포스팅

2022.11.28 - [기타/tkinter 한꼬집씩만 따라해보기] - 30. 위젯에 툴팁 붙이기

 

30. 위젯에 툴팁 붙이기

이전 포스팅 2022.11.28 - [기타/tkinter 한꼬집씩만 따라해보기] - 29. 스핀박스 위젯 추가하기 29. 스핀박스 위젯 추가하기 이전 포스팅 2022.11.28 - [기타/tkinter 한꼬집씩만 따라해보기] - 28. 메인윈도우

martinii.fun


 

이번 포스팅에서는 : 커스텀 테마 적용하기

tkinter로 만든 GUI에 특별한 디자인 테마를 적용할 수 있습니다.

적용방법은 여러 가지가 있지만, 이번 포스팅에서는

① 가장 간편하게 GUI의 룩앤필을 수정할 수 있는 ttkthemes 모듈,

② 미리 작성된(수정가능한) tcl 테마파일을 다운로드하고 적용하는 방법,

③ 마지막으로 ttkbootstrap에 대해서 소개해드리려고 합니다.

 

1. ttkthemes 적용하기

ttkthemes의 사용방법은 너무너무 간단해서, 임포트 한 줄, 코드 한 줄이면 됩니다.

우선 pip로 ttkthemes를 설치해줍니다.

아래는 기존 ttk GUI입니다. 이 GUI창을 기준으로 비교해보려고 합니다.

코드는 별 의미 없지만 바로 아래에 첨부해놓겠습니다.

코드는 이것저것 다 때려넣느라 다소 깁니다. 아무 영양가 없는 코드지만,

(우측의 트리뷰를 제외하고) 지금까지 배웠던 거의 모든 위젯에 대해 쭉 다루고 있으니

복습 겸 훑어보셔도 괜찮겠습니다.

더보기
import tkinter as tk
from tkinter import ttk

# from ttkthemes import ThemedTk

# win = ThemedTk(theme="blue")  # "arc","blue","clearlooks","elegance","kroc","plastik","radiance","ubuntu","winxpblue"
win = tk.Tk()
win.title("blue 테마")
win.iconbitmap("./ddsomb.ico")
win.geometry("640x420")
win.resizable(width=False, height=False)

상태표시줄여부 = tk.IntVar()
상태표시줄여부.set(1)


def 더미_콜백():
    pass


메뉴바 = tk.Menu(win)  # font="gulim 18"
win.config(menu=메뉴바)
win.option_add("*Font", '"D2Coding Ligature" 10')

파일 = tk.Menu(메뉴바, tearoff=False)
편집 = tk.Menu(메뉴바, tearoff=False)
서식 = tk.Menu(메뉴바, tearoff=False)
보기 = tk.Menu(메뉴바, tearoff=False)
확대축소 = tk.Menu(보기, tearoff=False)
도움말 = tk.Menu(메뉴바, tearoff=False)

메뉴바.add_cascade(label="파일(F)", menu=파일)
메뉴바.add_cascade(label="편집(E)", menu=편집)
메뉴바.add_cascade(label="서식(O)", menu=서식)
메뉴바.add_cascade(label="보기(V)", menu=보기)
메뉴바.add_cascade(label="도움말(H)", menu=도움말)

파일.add_command(label="새로 만들기(N)", command=더미_콜백)
파일.add_command(label="새 창(W)", command=더미_콜백)
파일.add_command(label="열기(O)", command=더미_콜백)
파일.add_command(label="저장(S)", command=더미_콜백)
파일.add_command(label="다른 이름으로 저장(A)", command=더미_콜백)
파일.add_separator()  # 구분자
파일.add_command(label="페이지 설정(U)", command=더미_콜백)
파일.add_command(label="인쇄(P)", command=더미_콜백)
파일.add_separator()  # 구분자
파일.add_command(label="끝내기(X)", command=더미_콜백)

편집.add_command(label="실행 취소(U)", command=더미_콜백)
편집.add_separator()  # 구분자
편집.add_command(label="잘라내기(T)", command=더미_콜백)
편집.add_command(label="복사(C)", command=더미_콜백)
편집.add_command(label="붙여넣기(P)", command=더미_콜백)
편집.add_command(label="삭제(L)", command=더미_콜백)
편집.add_separator()  # 구분자
편집.add_command(label="Bing으로 검색(S)", command=더미_콜백)
편집.add_command(label="찾기(F)", command=더미_콜백)
편집.add_command(label="다음 찾기(N)", command=더미_콜백)
편집.add_command(label="이전 찾기(V)", command=더미_콜백)
편집.add_command(label="바꾸기(R)", command=더미_콜백)
편집.add_command(label="이동(G)", command=더미_콜백)
편집.add_separator()  # 구분자
편집.add_command(label="모두 선택(A)", command=더미_콜백)
편집.add_command(label="시간/날짜(D)", command=더미_콜백)

서식.add_command(label="자동 줄 바꿈(W)", command=더미_콜백)
서식.add_command(label="글꼴(F)", command=더미_콜백)

보기.add_cascade(label="확대하기/축소하기", menu=확대축소)  # 하위메뉴 있음
보기.add_checkbutton(label="상태 표시줄(S)", variable=상태표시줄여부)

확대축소.add_command(label="확대(I)", command=더미_콜백)
확대축소.add_command(label="축소(O)", command=더미_콜백)
확대축소.add_command(label="확대하기/축소하기 기본값 복원", command=더미_콜백)

도움말.add_command(label="도움말 보기(H)", command=더미_콜백)
도움말.add_command(label="피드백 보내기(F)", command=더미_콜백)
도움말.add_separator()  # 구분자
도움말.add_command(label="메모장 정보(A)", command=더미_콜백)

tab = ttk.Notebook(win, width=640, height=410)
tab.grid(columnspan=2, )
frame1 = ttk.Frame(tab)
frame2 = ttk.Frame(tab)
frame3 = ttk.Frame(tab)
tab.add(frame1, text="   탭 1   ")
tab.add(frame2, text="   탭 2   ")
tab.add(frame3, text="   탭 3   ")
ttk.Label(frame1, text="ttk 레이블").grid(columnspan=2, row=0, pady=10)
ttk.Checkbutton(frame1, text="ttk 체크버튼").grid(columnspan=2, row=1, pady=10)
ttk.Radiobutton(frame1, text="ttk 라디오버튼").grid(columnspan=2, row=2, pady=10)
ttk.Label(frame1, text="ttk 콤보박스").grid(columnspan=2, row=3, pady="20 0")
ttk.Combobox(frame1, values=("a", "b", "c", "d", "e")).grid(columnspan=2, row=4, pady=10, padx="20 0")
ttk.Label(frame1, text="ttk 스핀박스").grid(columnspan=2, row=5, pady="20 0")
ttk.Spinbox(frame1, values=("1", "2", "3", "4", "5")).grid(columnspan=2, row=6, pady=10, padx="20 0")
ttk.Label(frame1, text="ttk 엔트리").grid(columnspan=2, row=0, column=2)
ttk.Entry(frame1).grid(columnspan=2, row=1, column=2, pady=10, padx=10)
frame = ttk.Labelframe(frame1, text="레이블 프레임", labelanchor="n")
frame.grid(row=2, column=2, pady=10, sticky="we")
frame.grid_columnconfigure(0, weight=1)
ttk.Label(frame, text="레이블1", anchor="center").grid(columnspan=2, row=0, column=0, pady=10)
ttk.Label(frame, text="레이블2", anchor="center").grid(columnspan=2, row=1, column=0, pady=10)

ttk.Label(frame1, text="ttk 프로그레스바").grid(columnspan=2, row=3, column=2, pady="20 0")
ttk.Progressbar(frame1, mode="indeterminate").grid(columnspan=2, row=4, column=2, pady=10)

ttk.Label(frame1, text="ttk 버튼").grid(columnspan=2, row=5, column=2, pady="20 0")
ttk.Button(frame1, text="Quit", command=win.destroy).grid(columnspan=2, row=6, column=2, pady=10)

ttk.Label(frame1, text="ttk 트리뷰").grid(row=0, column=4)
tv = ttk.Treeview(frame1, height=14)
tv.grid(row=1, column=4, rowspan=6)
tv['columns'] = ('Rank', 'Name', 'Badge')
tv.column('#0', width=0, stretch=tk.NO)
tv.column('Rank', anchor=tk.CENTER, width=80)
tv.column('Name', anchor=tk.CENTER, width=80)
tv.column('Badge', anchor=tk.CENTER, width=80)

tv.heading('#0', text='', anchor=tk.CENTER)
tv.heading('Rank', text='순번', anchor=tk.CENTER)
tv.heading('Name', text='위젯명', anchor=tk.CENTER)
tv.heading('Badge', text='난이도', anchor=tk.CENTER)

tv.insert(parent='', index=0, iid="0", text='', values=('0', '레이블', '하'))
tv.insert(parent='', index=1, iid="1", text='', values=('1', '버튼', '하'))
tv.insert(parent='', index=2, iid="2", text='', values=('2', '엔트리', '하'))
tv.insert(parent='', index=3, iid="3", text='', values=('3', '콤보박스', '하'))
tv.insert(parent='', index=4, iid="4", text='', values=('4', '체크버튼', '하'))
tv.insert(parent='', index=5, iid="5", text='', values=('5', '라디오버튼', '중'))
tv.insert(parent='', index=6, iid="6", text='', values=('6', '텍스트창', '중'))
tv.insert(parent='', index=7, iid="7", text='', values=('7', '프레임', '하'))
tv.insert(parent='', index=8, iid="8", text='', values=('8', '프로그레스바', '상'))
tv.insert(parent='', index=9, iid="9", text='', values=('9', '메뉴바', '상'))
tv.insert(parent='', index=10, iid="10", text='', values=('10', '탭', '중'))
tv.insert(parent='', index=11, iid="11", text='', values=('11', '메시지박스', '하'))
tv.insert(parent='', index=12, iid="12", text='', values=('12', '스핀박스', '하'))
tv.insert(parent='', index=13, iid="13", text='', values=('13', '툴팁', '하'))
tv.insert(parent='', index=14, iid="14", text='', values=('14', '캔버스', '상'))

win.mainloop()

 

이제 이 GUI창에 커스텀 테마를 적용해보겠습니다.

추가/수정해야 하는 라인은 딱 두 줄, 임포트문과 win=tk.Tk() 라인입니다.

기존코드 최상단을

import tkinter as tk
from tkinter import ttk

win = tk.Tk()

# ~

아래처럼 바꿔주시면 됩니다.

import tkinter as tk
from tkinter import ttk

from ttkthemes import ThemedTk

win = ThemedTk(theme="blue")

# ~

코드에서 보셨다시피

메인윈도우인 win을 생성할 때 win = tk.Tk()로 생성하는 대신

win = ThemedTk(theme=테마이름) 으로 생성하기만 하면 됩니다.

ttkthemes에서 제공하는 기본 테마는 아래 25개입니다.

공식 문서에서는 모든 테마의 예제 GUI를 보여주고 있습니다.

 

Themes — ttkthemes v3.2.2 documentation

ttkthemes includes a wide variety of different themes, and there is always room for more themes, no matter how ugly or obscure! Even though some themes may not be used in practice, the original goal of the project has not been forgotten: To gather and pres

ttkthemes.readthedocs.io

"adapta", "aquativo", "arc", "black", "blue", "breeze", "clearlooks", "elegance", "equilux",
"itft1", "keramik", "kroc", "plastik", "radiance", "scidblue", "scidgreen", "scidgrey",
"scidmint", "scidpink", "scidpurple", "scidsand", "smog", "ubuntu", "winxpblue", "yaru",

이번 포스팅에서는 이 중 아홉 가지 테마인

"arc", "blue", "clearlooks", "elegance", "kroc", "plastik", "ubuntu", "winxpblue", "breeze"을 보여드리겠습니다.

나머지 테마들이 덜 멋지다는 뜻은 아닙니다. 랜덤하게 아홉 개만 골랐습니다.

1. arc

win = ThemedTk(theme="arc")

2. blue

win = ThemedTk(theme="blue")

3. clearlooks

win = ThemedTk(theme="clearlooks")

4. elegance

win = ThemedTk(theme="elegance")

5. kroc

win = ThemedTk(theme="kroc")

6. plastik

win = ThemedTk(theme="plastik")

7. ubuntu

win = ThemedTk(theme="ubuntu")

8. winxpblue

win = ThemedTk(theme="winxpblue")

9. breeze

win = ThemedTk(theme="breeze")

 

여기까지

엄청나게 간편하게 룩앤필을 적용하거나 수정할 수 있는 ttkthemes에 대해 알아보았습니다.

 

2. tcl 테마파일 이용하기

아래 웹페이지를 참고하시면 tkinter와 호환되는 서드파티 테마인 tcl 파일을 다운받으실 수 있습니다.

List of ttk themes | Tkinter docs (rdbende.github.io)

 

List of ttk themes | Tkinter docs

List of ttk themes Most of the ttk themes are old, and ugly, but there are nice and modern ones. This is a complete list of ttk themes with additional useful information about them.

rdbende.github.io

저는 깃헙의 azure 레포에서 dark.tcl 파일을 적용해보겠습니다.

GitHub - rdbende/Azure-ttk-theme: A stunning modern theme for ttk inspired by Fluent Design 💠

 

GitHub - rdbende/Azure-ttk-theme: A stunning modern theme for ttk inspired by Fluent Design 💠

A stunning modern theme for ttk inspired by Fluent Design 💠 - GitHub - rdbende/Azure-ttk-theme: A stunning modern theme for ttk inspired by Fluent Design 💠

github.com

적용방법은 아래와 같습니다. 위젯삽입은 위와 동일한 코드를 사용하였습니다.

import tkinter as tk
from tkinter import ttk


def darkstyle(root):
    style = ttk.Style(root)
    root.tk.call('source', 'azure dark/azure dark.tcl')
    style.theme_use('azure')
    style.configure("Accentbutton", foreground='white')
    style.configure("Togglebutton", foreground='white')
    return style


win = tk.Tk()
style = darkstyle(win)
# ~

azure 예제파일에서는 아래와 같은 코딩스타일로 테마를 적용하고 있습니다.

# ~

style = ttk.Style(root)
root.tk.call('source', r'C:\Users\smj02\PycharmProjects\tkinter_tutorial\azure dark\azure dark.tcl')
style.theme_use('azure')

# ~

 

아래는 azure dark 테마의 example.py 파일의 코드 전체입니다.

더보기
import tkinter as tk
from tkinter import ttk

root = tk.Tk()
root.title('Azure')

window_height = 530
window_width = 800

screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()

x_cordinate = int((screen_width/2) - (window_width/2))
y_cordinate = int((screen_height/2) - (window_height/2))

root.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate))

style = ttk.Style(root)
root.tk.call('source', r'C:\Users\smj02\PycharmProjects\tkinter_tutorial\azure dark\azure dark.tcl')
style.theme_use('azure')

options = ['', 'OptionMenu', 'Value 1', 'Value 2']
a = tk.IntVar()
b = tk.IntVar()
b.set(1)
c = tk.IntVar()
d = tk.IntVar()
d.set(2)
e = tk.StringVar()
e.set(options[1])
f = tk.IntVar()
g = tk.IntVar()
g.set(75)
h = tk.IntVar()

frame1 = ttk.LabelFrame(root, text='Checkbuttons', width=210, height=200)
frame1.place(x=20, y=12)

frame2 = ttk.LabelFrame(root, text='Radiobuttons', width=210, height=160)
frame2.place(x=20, y=252)

check1 = ttk.Checkbutton(frame1, text='Unchecked', variable=a, offvalue=0, onvalue=1)
check1.place(x=20, y=20)
check2 = ttk.Checkbutton(frame1, text='Checked', variable=b, offvalue=0, onvalue=1)
check2.place(x=20, y=60)
check3 = ttk.Checkbutton(frame1, text='Third state', variable=c, offvalue=0, onvalue=1)
check3.state(['alternate'])
check3.place(x=20, y=100)
check4 = ttk.Checkbutton(frame1, text='Disabled', state='disabled')
check4.place(x=20, y=140)

radio1 = ttk.Radiobutton(frame2, text='Deselected', variable=d, value=1)
radio1.place(x=20, y=20)
radio2 = ttk.Radiobutton(frame2, text='Selected', variable=d, value=2)
radio2.place(x=20, y=60)
radio3 = ttk.Radiobutton(frame2, text='Disabled', state='disabled')
radio3.place(x=20, y=100)

entry = ttk.Entry(root)
entry.place(x=250, y=20)
entry.insert(0, 'Entry')

spin = ttk.Spinbox(root, from_=0, to=100, increment=0.1)
spin.place(x=250, y=70)
spin.insert(0, 'Spinbox')

combo1 = ttk.Combobox(root, value=['Combobox', 'Editable item 1', 'Editable item 2'])
combo1.current(0)
combo1.place(x=250, y=120)

combo2 = ttk.Combobox(root, state='readonly', value=['Readonly combobox', 'Item 1', 'Item 2'])
combo2.current(0)
combo2.place(x=250, y=170)

menu = tk.Menu(root, tearoff=0)
menu.add_command(label='Menu item 1')
menu.add_command(label='Menu item 2')
menu.add_separator()
menu.add_command(label='Menu item 3')
menu.add_command(label='Menu item 4')

menubtn = ttk.Menubutton(root, text='Menubutton', menu=menu, direction='below')
menubtn.place(x=250, y=220)

menubtn = ttk.OptionMenu(root, e, *options)
menubtn.place(x=250, y=270)

def callback():
    print('Button callback')

button = ttk.Button(root, text='Button', command=callback)
button.place(x=250, y=320)

accentbutton = ttk.Button(root, text='Accent button', style='Accentbutton', command=callback)
accentbutton.place(x=250, y=370)

toggle = ttk.Checkbutton(root, text='Toggle button', style='Togglebutton', variable=f, offvalue=0, onvalue=1)
toggle.place(x=250, y=420)

def scale(i):
    g.set(int(scale.get()))

scale = ttk.Scale(root, from_=100, to=0, variable=g, command=scale)
scale.place(x=80, y=430)

progress = ttk.Progressbar(root, value=0, variable=g, mode='determinate')
progress.place(x=80, y=480)

switch = ttk.Checkbutton(root, text='Toggle switch', style='Switch', variable=h, offvalue=0, onvalue=1)
switch.place(x=250, y=470)
switch.invoke()

size = ttk.Sizegrip(root)
size.place(x=780, y=510)

sep1 = ttk.Separator()
sep1.place(x=20, y=235, width=210)

notebook = ttk.Notebook(root)
notebookTab1 = ttk.Frame(notebook, width=335, height=150)
notebook.add(notebookTab1, text='Tab 1')
notebookTab2 = ttk.Frame(notebook, width=335, height=150)
notebook.add(notebookTab2, text='Tab 2')
notebookTab3 = ttk.Frame(notebook, width=335, height=150)
notebook.add(notebookTab3, text='Tab 3')
notebook.place(x=420, y=330)

treeFrame = ttk.Frame(root)
treeFrame.place(x=420, y=20)

treeScroll = ttk.Scrollbar(treeFrame)
treeScroll.pack(side='right', fill='y')

treeview = ttk.Treeview(treeFrame, selectmode="extended", yscrollcommand=treeScroll.set, columns=(1, 2), height=12)
treeview.pack()

treeScroll.config(command=treeview.yview)

treeview.column("#0", width=120)
treeview.column(1, anchor='w', width=100)
treeview.column(2, anchor='w', width=100)

treeview.heading("#0", text="Treeview", anchor='center')
treeview.heading(1, text="Column 1", anchor='center')
treeview.heading(2, text="Column 2", anchor='center')

treeview.insert(parent='', index='end', iid=1, text="Parent", values=("Item 1", "Value 1"))
treeview.item(1, open=True)
treeview.insert(parent=1, index='end', iid=2, text="Child", values=("Subitem 1.1", "Value 1.1"))
treeview.insert(parent=1, index='end', iid=3, text="Child", values=("Subitem 1.2", "Value 1.2"))
treeview.insert(parent=1, index='end', iid=4, text="Child", values=("Subitem 1.3", "Value 1.3"))
treeview.insert(parent=1, index='end', iid=5, text="Child", values=("Subitem 1.4", "Value 1.4"))
treeview.insert(parent='', index='end', iid=6, text="Parent", values=("Item 2", "Value 2"))
treeview.item(6, open=True)
treeview.insert(parent=6, index='end', iid=13, text="Child", values=("Subitem 2.1", "Value 2.1"))
treeview.insert(parent=6, index='end', iid=7, text="Sub-parent", values=("Subitem 2.2", "Value 2.2"))
treeview.item(7, open=True)
treeview.insert(parent=7, index='end', iid=8, text="Child", values=("Subitem 2.2.1", "Value 2.2.1"))
treeview.insert(parent=7, index='end', iid=9, text="Child", values=("Subitem 2.2.2", "Value 2.2.2"))
treeview.selection_set(9)
treeview.insert(parent=7, index='end', iid=10, text="Child", values=("Subitem 2.2.3", "Value 2.2.3"))
treeview.insert(parent=6, index='end', iid=11, text="Child", values=("Subitem 2.3", "Value 2.3"))
treeview.insert(parent=6, index='end', iid=12, text="Child", values=("Subitem 2.4", "Value 2.4"))
treeview.insert(parent='', index='end', iid=14, text="Parent", values=("Item 3", "Value 3"))
treeview.item(14, open=True)
treeview.insert(parent=14, index='end', iid=15, text="Child", values=("Subitem 3.1", "Value 3.1"))
treeview.insert(parent=14, index='end', iid=16, text="Child", values=("Subitem 3.2", "Value 3.2"))
treeview.insert(parent=14, index='end', iid=17, text="Child", values=("Subitem 3.3", "Value 3.3"))
treeview.insert(parent=14, index='end', iid=18, text="Child", values=("Subitem 3.4", "Value 3.4"))
treeview.insert(parent='', index='end', iid=19, text="Parent", values=("Item 4", "Value 4"))
treeview.item(19, open=True)
treeview.insert(parent=19, index='end', iid=20, text="Child", values=("Subitem 4.1", "Value 4.1"))
treeview.insert(parent=19, index='end', iid=21, text="Sub-parent", values=("Subitem 4.2", "Value 4.2"))
treeview.item(21, open=True)
treeview.insert(parent=21, index='end', iid=22, text="Child", values=("Subitem 4.2.1", "Value 4.2.1"))
treeview.insert(parent=21, index='end', iid=23, text="Child", values=("Subitem 4.2.2", "Value 4.2.2"))
treeview.insert(parent=21, index='end', iid=24, text="Child", values=("Subitem 4.2.3", "Value 4.2.3"))
treeview.insert(parent=19, index='end', iid=25, text="Child", values=("Subitem 4.3", "Value 4.3"))

root.mainloop()

실행해보면

굉장히 깔끔하네요.

 

3. ttkbootstrap 적용하기

주의사항 : 첫 번째로 보여드린 ttkthemes와 ttkbootstrap을 같이 사용하는 경우 오류가 발생합니다.

마지막으로 보여드릴 테마적용 방법은 ttkbootstrap 모듈을 이용하는 것입니다.

다양하고 멋진 테마들

 

이 모듈이 참 재밌는 게, 이름에 걸맞게 웹개발에 사용하는 bootstrap의 클래스명이 여기에 유사하게 적용됩니다.

ttkbootstrap - ttkbootstrap

 

ttkbootstrap - ttkbootstrap

ttkbootstrap A supercharged theme extension for tkinter that enables on-demand modern flat style themes inspired by Bootstrap. 📦 Features ✔️ Built-in Themes Over a dozen curated dark and light themes ✔️ Pre-defined Styles: Loads of beautiful pre

ttkbootstrap.readthedocs.io

공식문서에서도 ttkbootstrap이 실제 bootstrap에서 영감을 받아 제작된 모듈이라고 소개하고 있습니다.

A supercharged theme extension for tkinter that enables on-demand modern flat style themes inspired by Bootstrap.

 

1. 적용방법

ttkbootstrap을 적용하려면 먼저 pip로 ttkbootstrap을 설치해야 합니다.

 

적용방법도 간단합니다. from tkinter import ttk 대신에

import ttkbootstrap as ttk
from ttkbootstrap.constants import *

이 두 줄로 임포트를 합니다.

특정 엘리먼트에 스타일을 적용하는 방법은

import tkinter as tk
import ttkbootstrap as ttk
from ttkbootstrap.constants import *

root = tk.Tk()

b1 = ttk.Button(root, text="Button 1", bootstyle=SUCCESS)
b1.pack(side=LEFT, padx=5, pady=10)

b2 = ttk.Button(root, text="Button 2", bootstyle=(INFO, OUTLINE))
b2.pack(side=LEFT, padx=5, pady=10)

root.mainloop()

이런 식으로 요소 인스턴스를 생성할 때 bootstyle을 적용하는 것입니다.

위 코드를 실행하면 아래와 같은 창이 생성됩니다.

메인윈도우창마저도 ttkbootstrap 적용이 가능한데,

ttkbootstrap.Window 클래스를 tk.Tk 대신 사용하면 됩니다.

import ttkbootstrap as ttk
from ttkbootstrap.constants import *

root = ttk.Window()

b1 = ttk.Button(root, text="Button 1", bootstyle=SUCCESS)
b1.pack(side=LEFT, padx=5, pady=10)

b2 = ttk.Button(root, text="Button 2", bootstyle=(INFO, OUTLINE))
b2.pack(side=LEFT, padx=5, pady=10)

root.mainloop()

특정 요소가 아니라 메인윈도우 전체에 특정 테마를 적용하는 방법은 아래와 같습니다.

import tkinter as tk
import ttkbootstrap as ttk

아래 두 가지 방법 중 택일

# 1. 기존 tk위젯과 호환이 필요한 경우(traditional approach)
root = tk.Tk()
style = ttk.Style("darkly")

# 2. ttkbootstrap의 요소만 사용하는 경우-추천(new approach)
root = ttk.Window(themename="darkly")

위 코드를 적용하여 GUI창을 생성해보면

2. 부트스트랩 스타일의 컬러

부트스트랩 스타일로 위젯의 스타일 컬러를 지정할 수 있습니다.

코드는 아래와 같습니다. (bootstyle에 bootstrap의 클래스명을 입력합니다.)

더보기
import ttkbootstrap as ttk
from ttkbootstrap.constants import *

root = ttk.Window()

b1 = ttk.Button(root, text='primary', bootstyle=PRIMARY)
b1.pack(side=LEFT, padx=5, pady=5)

b2 = ttk.Button(root, text='secondary', bootstyle=SECONDARY)
b2.pack(side=LEFT, padx=5, pady=5)

b3 = ttk.Button(root, text='success', bootstyle=SUCCESS)
b3.pack(side=LEFT, padx=5, pady=5)

b4 = ttk.Button(root, text='info', bootstyle=INFO)
b4.pack(side=LEFT, padx=5, pady=5)

b5 = ttk.Button(root, text='warning', bootstyle=WARNING)
b5.pack(side=LEFT, padx=5, pady=5)

b6 = ttk.Button(root, text='danger', bootstyle=DANGER)
b6.pack(side=LEFT, padx=5, pady=5)

b7 = ttk.Button(root, text='light', bootstyle=LIGHT)
b7.pack(side=LEFT, padx=5, pady=5)

b8 = ttk.Button(root, text='dark', bootstyle=DARK)
b8.pack(side=LEFT, padx=5, pady=5)

root.mainloop()

 

3. 유연한 입력방식

또한 bootstyle 파라미터는 ttkbootstrap 내부적으로 제법 강력한 정규식 파싱을 거치기 때문에 

아래와 같이 다양한 파라미터로도 동일한 결과를 출력해줍니다.

  • "info-outline"
  • "infooutline"
  • "info outline"
  • "outline-info"
  • ("info", "outline")
  • (INFO, OUTLINE)

4. 다양한 테마

ttkbootstrap에서는 기본적으로 18개의 테마를 제공하고 있습니다.(light 13개, dark 5개)

아래는 18개 테마의 데모입니다.

더보기
cosmo
flatly
journal
litera
lumen
minty
pulse
sandstone
united
yeti
morph
simplex
cerculean
solar
superhero
darkly
cyborg
vapor

 

5. 다양한 위젯

ttkbootstrap에서는 기존 위젯 뿐 아니라 datepicker(날짜선택 위젯) 나 floodgauge(레이블이 들어간 프로그레스바) 등의 새로운 위젯도 함께 제공하고 있습니다.

아래는 ttkbootstrap에서 제공하는 위젯들입니다.

더보기
버튼
아웃라인버튼
링크버튼
체크버튼
툴버튼(체크)
아웃라인툴버튼(체크)
둥근 토글버튼
각진 토글버튼
콤보박스
날짜선택기
날짜선택팝업
엔트리
플러드게이지(레이블+프로그레스바)
프레임
레이블
반전레이블
레이블프레임
메뉴버튼
아웃라인 메뉴버튼
미터기
커스텀 미터기
노트북(탭)

 

panedwindow
프로그레스바
striped 프로그레스바
라디오버튼
툴버튼(라디오)
아웃라인 툴버튼(라디오)
스케일
스크롤바
둥근 스크롤바
구분자
사이즈그립
스핀박스
트리뷰
color chooser
color dropper
font dialog
messagebox
쿼리박스
스크롤 텍스트
테이블뷰
토스트모듈
툴팁모듈
윈도우 및 탑레벨 모듈

 

ttkbootstrap은 약 15만 회의 다운로드(월평균 8천회), 730개 깃헙스타를 받은 중견급(?) 모듈인 만큼

다른 모듈에 비해 활발한 업데이트가 이루어지고 있습니다. 게다가 굉장히 느슨한 MIT라이선스를 따르고,

커스텀 테마 제작을 위한 TTK 크리에이터 프로그램(별도설치)이나, 스타일 빌더 등을 제공하고 있어

개인적으로는 ttkbootstrap 모듈의 활용도가 가장 높아 보이기도 합니다.

 

마치며

tkinter의 시각적인 부분의 약점을 충분히 보완해줄 수 있는 서드파티 스타일을 적용하는 방법에 대해 알아보았습니다.

스타일 적용을 통해 여러분의 GUI에 스타일의 날개를 달아보시기 바랍니다.

 


다음 포스팅

작성중

앞으로도 도움이 되는 콘텐츠를 만들어 가겠습니다.

 

댓글