Довідник по Tkinter

Багато програм на сьогоднішній день використовують графічний інтерфейс, який більш інтуїтивний і зручний для користувача, ніж консоль. І за допомогою мови програмування Python також можна створювати графічні програми. Для цього в Python за замовчуванням застосовується спеціальний тулкіт - набір компонентів, який називається Tkinter. Тулкіт Tkinter доступний у вигляді окремого вбудованого модуля, який містить всі необхідні графічні компоненти - кнопки, текстові поля і т.д.

По суті Tkinter представляє інтерфейс в Python для графічної бібліотеки Tk (Власне сама назва «Tkinter» є скороченням «Tk interface»). Спочатку дана бібліотека розроблялася для мови Tcl - її створив в 1988 році Джон Остерхаут (John Ousterhout), професор computer science з Берклі для створення графічних додатків для своєї мови Tcl. Але згодом Tk була адаптована для широкого ряду динамічних мов, зокрема, для Ruby, Perl і природно для мови Python (в 1994 році). І на сьогоднішній день і бібліотека Tk, і сам тулкіт Tkinter доступні для більшості операційних систем, в тому числі для MacOS, Linux і Windows.

Переваги Tkinter:

  • Даний тулкіт за замовчуванням включений в стандартну бібліотеку мови Python у вигляді окремого модуля, тому не потрібно щось додатково встановлювати
  • Tkinter - кросплатформний, один і той же код буде працювати однаково на різних платформах (Mac OS, Linux і Windows)
  • Tkinter легко вивчати. Сам тулкіт, хоча і містить деякий готовий код, віджети і графічні елементи, але при цьому досить лаконічний і простий.
  • Tk поширюється за BSD-ліцензією, тому бібліотека може бути використана як в опенсорсних проектах, так і в комерційних напрацюваннях.

Графічний інтерфейс користувача можна створювати за допомогою команд бібліотеки Tkinter.

Для створення графічного вікна застосовується конструктор Tk(), який визначений в модулі tkinter. Створюване вікно присвоюється змінній root, і через цю змінну ми можемо керувати атрибутами вікна. Зокрема, за допомогою методу title() можна встановити заголовок вікна.

За допомогою методу geometry() - розмір вікна. Для встановлення розміру в метод geometry() передається рядок у форматі «Ширина x Висота». Якщо при створенні вікна програми метод geometry() не викликається, то вікно займає той простір, який необхідно для розміщення внутрішнього вмісту.

Створивши вікно, ми можемо розмістити в ньому інші графічні елементи. Ці елементи ще називаються віджетами. В даному випадку ми розміщуємо у вікні текстову мітку. Для цього створюємо об'єкт класу Label, який зберігає деякий текст. Потім для розміщення елемента label у вікні викликаємо у нього метод pack()

Для відображення вікна треба викликати у нього метод mainloop(), який запускає цикл обробки подій вікна для взаємодії з користувачем.

Якщо ви пишете програму на Tkinter - цей код має бути обов'язково!

В результаті при запуску скрипта ми побачимо таке майже порожнє віконце:

from tkinter import *

root = Tk() # створюємо кореневий об'єкт - вікно
root.title("Застосунок на Tkinter") # заголовок вікна
root.geometry("300x250") # встановлюємо розмір вікна

# звідси починається опис графічного інтерфейсу
# та його логіки
label = Label(text="Hello 179!") # текстовий напис
label.pack() # розміщуємо напис у вікні
# а тут опис закінчено!

root.mainloop()

Віджет Label представляє текстовий напис. Цей елемент дозволяє виводити статичний текст без можливості редагування. Для створення елемента Label застосовується конструктор, який приймає два параметри:

Label(master, options)

Параметр master представляє посилання на батьківський контейнер (у наших вправах - пишемо завжди root), а параметр options представляє наступні іменовані параметри:

  • anchor: встановлює розташування тексту (до якого краю він "притягується")
  • background: колір тла, можливі значення: колір у форматі HEX (напр. #FFCDD2) або словом (напр. Red)
  • font: шрифт тексту, приймає значення шрифту у вигляді font=("назва шрифту", розмір_шрифту), напр. font=("Arial", 14)
  • foreground: колір тексту
  • height: висота віджета
  • image: посилання на зображення, яке відображається на напис
  • justify: встановлює вирівнювання тексту. Значення LEFT вирівнює текст по лівому краю, CENTER - по центру, RIGHT - по правому краю
  • text: встановлює текст мітки
  • width: ширина віджету

Властивості напису можна змінити "на ходу", використовуючи функцію змінна_напису.configure(властивість=нове_значення_властивості).
Наприклад, у прикладі нижче змінимо текст та колір шрифту:

label.configure(text="Якийсь новий текст", fg="Red")
from tkinter import *

root = Tk() # створюємо кореневий об'єкт - вікно
root.title("Застосунок на Tkinter") # заголовок вікна
root.geometry("300x250") # встановлюємо розмір вікна

label = Label(master = None, text="Hello Tkinter", background="#FFCDD2", foreground="#B71C1C", font=("Arial", 14), justify=RIGHT, height=5, width=20)
label.pack() # розміщуємо напис у вікні

root.mainloop()

Одним з найбільш використовуваних компонентів в графічних програмах є кнопка. У Tkinter кнопки представлені класом Button

Найважливішими властивостями віджету класу Button є text, за допомогою якого встановлюється напис на кнопці, і command для установки дії, тобто того, що буде відбуватися при натисканні на кнопку. За замовчуванням розмір кнопки відповідає ширині і висоті тексту, однак за допомогою властивостей width і height ці параметри можна змінити. Одиницями вимірювання в даному випадку є знакомісця. Такі властивості як bg, fg, activebackground і activeforeground визначають відповідно колір тла і тексту, колір тла і тексту під час натискання і установки курсору миші над кнопкою.

Властивості кнопки можна змінити "на ходу", використовуючи функцію змінна_кнопки.configure(властивість=нове_значення_властивості).
Наприклад, у прикладі нижче змінимо текст та колір шрифту і фону:

from tkinter import *

def change():
  button.configure(text="Точно кнопка?", bg="Yellow", fg="Green")

root = Tk() # створюємо кореневий об'єкт - вікно
root.title("Застосунок на Tkinter") # заголовок вікна
root.geometry("300x250") # встановлюємо розмір вікна
button = Button(text="Кнопка", width=15, height=3, command=change)
# кнопка з заданими властивостями (в т.ч. текстом), при натисканні
# виконається функція change(), яка змінить зовнішній вигляд кнопки
button.pack()

root.mainloop()

Елемент Entry представляє поле для введення тексту. За допомогою конструктора Entry можна встановити ряд параметрів, основні з них:

  • background: колір тла, можливі значення: колір у форматі HEX (напр. #FFCDD2) або словом (напр. Red)
  • foreground: колір тексту, можливі значення: колір у форматі HEX (напр. #FFCDD2) або словом (напр. Red)
  • font: шрифт тексту, приймає значення шрифту у вигляді font=("назва шрифту", розмір_шрифту), напр. font=("Arial", 14)
  • justify: встановлює вирівнювання тексту. Значення LEFT вирівнює текст по лівому краю, CENTER - по центру, RIGHT - по правому краю
  • state: стан елемента, може приймати значення NORMAL (за замовчуванням) і DISABLED
  • width: ширина елемента

Елемент Entry має ряд методів. Основні з них:

  • get(): повертає текст, введений у текстове поле ("отримати")
  • insert(index, str): вставляє рядок у текстове поле за вказаним індексом
  • delete(first, last=None): видаляє символ за індексом first. Якщо вказано параметр last, то видалення проводиться до індексу last. Щоб видалити до кінця, в якості другого параметра можна використовувати значення END.
from tkinter import *

def change():
  text_in_entry = entry.get() # зчитати текст у полі вводу
  label.configure(text=text_in_entry, bg="Yellow")
root = Tk() # створюємо кореневий об'єкт - вікно
root.title("Застосунок на Tkinter") # заголовок вікна
root.geometry("300x150") # встановлюємо розмір вікна

entry = Entry(width=20)
button = Button(text="Кнопка", width=15, height=3, command=change)
label = Label()
# кнопка з заданими властивостями (в т.ч. текстом), при натисканні
# виконається функція change(), яка змінить зовнішній вигляд кнопки
entry.pack()
button.pack()
label.pack()
root.mainloop()

Створення об’єктів класу Listbox

Об’єкт класу Listbox дозволяє відображати список елементів, з якого користувач може вибрати один або декілька пунктів. Для налаштування віджета Listbox можна задати в його конструкторі такі спільні для багатьох віджетів параметри, як bg, fg (колір фону; колір шрифту); font (настройки шрифту); width, height (висота та ширина елемента). За замовчуванням висота встановлюється в 10 рядків, ширина — 20 символів. За замовчуванням у Listbox натисканням миші можна вибрати тільки один елемент. Якщо установити значення параметра selectmode = EXTENDED, можна вибрати декілька елементів, утримуючи натиснутою клавішу Ctrl.

Алгоритм створення віджету класу Listbox:

  1. створити об’єкт класу Listbox
  2. заповнити список Listbox елементами
  3. розмістити віджет у вікні
МетодПризначенняПриклад
назва_об'єкта.insert(index,n)Вставляє в список Listbox елемент n на місце з індексом index.lbox.insert(3, 100)
# Вставлено число 100 у рядок з індексом 3 (нумерація з 0).
назва_об'єкта.curselection()Повертає кортеж* індексів елементів, що виділені.print(lbox.curselection())
# Буде виведено (1, 3, 6)
назва_об'єкта.delete(n)Видаляє зі списку Listbox елемент з індексом n.lbox.delete(6)
#Видалено елемент з індексом 6.
назва_об'єкта.delete(n, m)Видаляє зі списку Listbox елементи, індекси яких належать діапазону від n до m.lbox.delete(0,END)
#Видалено всі елементи зі списку lbox.
назва_об'єкта.get(n)Повертає текст елемента з індексом n.print(lbox.get(3))
# Буде виведено Java
назва_об'єкта.get(n, m)Повертає кортеж* елементів списку Listbox, індекси яких належать діапазону від n до m.elem = list(lbox.get(0, END))
#Створено список elem з усіх елементів списку Listbox.
назва_об'єкта.size()Повертає число елементів у списку Listbox.k = lbox.size()
print(k) # Буде виведено 8

Для наповнення списку Listbox елементами деякого списку слід у циклі for пройти по всіх елементах списку і додати кожний елемент до списку Listbox за допомогою методу insert(): назва об’єкта>.insert(index, element) Як перший аргумент методу insert() передається індекс вставлення елемента. Якщо ми хочемо послідовно додавати елементи, замість індекса можна використовувати значення END.

Приклад 1. Створимо віджет класу Listbox і відобразимо у віджеті список мов програмування

from tkinter import *

root = Tk()
root.geometry('250x200')

lbox = Listbox(width = 40, selectmode = EXTENDED)
lbox.pack() # Віджет розміщується у вікні
prog_langs = ["Python", "JavaScript", "C#", "Java", "C/C++", "PHP", "Visual Basic.NET", "Ruby"]
# Заповнюється за допомогою методу insert()
for prog in prog_langs:
  lbox.insert(END, prog)

root.mainloop()

Приклад 2. Застосувати метод curselection() до списку віджета lbox. Метод викликається в обробнику події для кнопки. Застосувати метод curselection()

from tkinter import *

root = Tk()
root.geometry('250x300')

def btn_click():
  label.configure(text = "Обрані індекси: " + str(lbox.curselection()))
lbox = Listbox(width = 40, selectmode = EXTENDED)
lbox.pack() # Віджет розміщується у вікні
prog_langs = ["Python", "JavaScript", "C#", "Java", "C/C++", "PHP", "Visual Basic.NET", "Ruby"]
# Заповнюється за допомогою методу insert()
for prog in prog_langs:
  lbox.insert(END, prog)

label = Label(text="Поки нічого не обрано", height=2)
button = Button(text="Застосувати метод curselection()", width=25, command=btn_click)
label.pack()
button.pack()

root.mainloop()

Приклад 3. Запрограмувати додавання рядків до списку віджета lbox класу Listbox, уводячи по черзі значення до поля віджета entry_add класу Entry і натискаючи кнопку badd.

from tkinter import*
def badd_cl():
  # Додавання рядка з поля entry_add до списку lbox
  lbox.insert(END, entry_add.get())
  entry_add.delete(0, END) # Очищення поля entry_add
  k = lbox.size()
  # Визначення кількості рядків у списку lbox
  lab2.configure(text = 'кількість елементів ' + str(k))
# Виведення значення k
root = Tk()
lbox = Listbox(root, selectmode = EXTENDED, width = 10, height = 8)
lbox.grid(row = 0, column = 0, rowspan = 5) # Віджет lbox розміщується у вікні
lab1 = Label(root, text = 'Уведіть число: ').grid(row = 0, column = 1)
entry_add = Entry(root) # Створення текстового поля
entry_add.grid(row = 1, column = 1)
badd = Button(text = "Додати число ", command = badd_cl)
badd.grid(row = 2, column = 1)
lab2 = Label(root, text = '****')
lab2.grid(row = 3, column = 1)
root.mainloop()

Перш ніж продовжити знайомство з віджетами GUI зупинимося на питанні їх розташування у вікні. Це важливе питання, так як від продуманості інтерфейсу багато в чому залежить зручність використання програми. Організовуючи віджети в просторі, програміст частково стає дизайнером, розробником інтерфейсів.

Пакувальник (packer) викликається методом pack, який є у всіх віджетів-об'єктів. Ми вже використали його раніше. Якщо до елемента інтерфейсу не застосувати будь-який з менеджерів геометрії, то він не відобразиться у вікні.

Якщо в пакувальники не передавати аргументи, то віджети будуть розташовуватися вертикально, один над одним. Той об'єкт, який першим викличе pack, буде вгорі. Який другим - під першим, і так далі.

У методу pack є параметр side (сторона), який приймає одне з чотирьох значень - констант Tkinter - TOP (за замовчуванням, вирівнюється по верхній стороні контейнера), BOTTOM (вирівнювання по нижній стороні), LEFT (вирівнювання по лівій стороні), RIGHT (вирівнювання по правій стороні). Типово, коли в pack не вказується side, його значення дорівнює TOP. Через це віджети розташовуються вертикально.

Інші параметри цього пакувальника:

  • expand: якщо дорівнює True, то віджет заповнює весь простір контейнера.
  • fill: визначає, чи буде віджет розтягуватися, щоб заповнити вільний простір навколо. Цей параметр може приймати такі значення: NONE (за замовчуванням, елемент не розтягується), X (елемент розтягується тільки по горизонталі), Y (елемент розтягується тільки по вертикалі) і BOTH (елемент розтягується по вертикалі і горизонталі).
  • anchor: поміщає віджет в певній частині контейнера. Може приймати значення n, e, s, w, ne, nw, se, sw, c, які є скороченнями від North (північ - вгору), South (південь - низ), East (схід - права сторона), West (захід - ліва сторона) і Center (по центру). Наприклад, значення nw вказує на верхній лівий кут
  • ipadx: встановлює відступ вмісту віджету від його межі по горизонталі.
  • ipady: встановлюють відступ вмісту віджету від його межі по вертикалі.
  • padx: встановлює відступ віджету від меж контейнера по горизонталі.
  • pady: встановлює відступ віджета від меж контейнера по вертикалі
  • Створимо чотири розфарбовані мітки:

    l1 = Label(width=7, height=4, bg='yellow', text="1")
    l2 = Label(width=7, height=4, bg='orange', text="2")
    l3 = Label(width=7, height=4, bg='lightgreen', text="3")
    l4 = Label(width=7, height=4, bg='lightblue', text="4")

    і розглянемо різні комбінації значень side

    placement_1 placement_2 placement_3 placement_4 placement_5 placement_6

    Проблема двох останніх варіантів в тому, що якщо треба розмістити віджети квадратом, тобто два зверху, два знизу рівно під двома верхніми, то зробити це проблематично, якщо взагалі можливо. Тому вдаються до допоміжного віджету - фрейму (рамка), який створюється класом Frame. Фрейми розміщують у головному вікні, а вже у фреймах - віджети:

    placement_frame

Grid є одним з трьох менеджерів геометрії в Tkinter. У всіх віджетів є відповідний даному менеджеру метод grid. «Grid» з англійської перекладається як «сітка», однак за змістом правильніше говорити про таблицю.

Табличний спосіб розміщення кращий через його гнучкість і зручність, коли справа доходить до розробки відносно складних інтерфейсів. Grid дозволяє уникнути використання безлічі фреймів, що неминуче у випадку пакувальника Pack. При розміщенні віджетів методом grid батьківський контейнер (зазвичай це вікно) умовно поділяється на комірки подібно таблиці. Адреса кожної комірки складається з номера рядка і номери стовпчика. Нумерація починається з нуля. Комірки можна об'єднувати як по вертикалі, так і по горизонталі. placement_gr1 На рисунку пунктир позначає об'єднання комірок. Загальна комірка в такому випадку позначається адресою першою. Ніяких попередніх команд з розбиття батьківського віджету на осередки не виконується. Tkinter робить це сам, виходячи з зазначених позицій віджетів. Розміщення віджету в тій чи іншій комірці задається через аргументи row і column, яким присвоюються відповідно номера рядка і стовпця. Щоб об'єднати комірки за горизонталі, використовується атрибут columnspan, якому присвоюється кількість об'єднаних комірок. Параметр rowspan об'єднує комірки вертикально.

ПРИКЛАД. Нехай треба запрограмувати такий GUI. редставимо даний інтерфейс у вигляді таблиці і пронумеруємо комірки, в яких будуть розташовуватися віджети:

placement_gr2
from tkinter import *

root = Tk()
Label(text="Ім'я:").grid(row=0, column=0)
Entry(width=30).grid(row=0, column=1, columnspan=3)

Label(text="Стовпців:").grid(row=1, column=0)
Entry(width=7).grid(row=1, column=1)
Label(text="Рядків:").grid(row=1, column=2)
Entry(width=7).grid(row=1, column=3)
Button(text="Довідка").grid(row=2, column=0)
Button(text="Вставити").grid(row=2, column=2)
Button(text="Скасувати").grid(row=2, column=3)

root.mainloop()

Розглянемо третій і останній менеджер геометрії бібліотеки tk - Place, який розміщує віджети за координатами. У Tkinter використання даного керуючого розміщенням реалізується через метод place віджетів. Методом place віджету вказується його положення або в абсолютних значеннях (в пікселях), або в частках батьківського вікна, тобто відносно. Також абсолютно і відносно можна задавати розмір самого віджета. Основними параметрами place є:

  • anchor (якір) - визначає частину віджету, для якої задаються координати. Приймає значення N, NE, E, SE, SW, W, NW або CENTER. Типове значення NW (верхній лівий кут).
  • relwidth, relheight (відносні ширина і висота) - визначають розмір віджета в частках його батька.
  • relx, rely - визначають відносну позицію в батьківському віджеті. Координата (0; 0) - у лівого верхнього кута, (1; 1) - у правого нижнього.
  • width , height - абсолютний розмір віджету в пікселях. Типові значення (коли дані опції опущені) прирівнюються до природного розміру віджета, то є до того, який визначається при його створенні і конфігуруванні.
  • x, y - абсолютна позиція в пікселях. Типові значення прирівнюються до нуля.
Схема із зазначенням відносних координат: placement_pl1

Для кращого розуміння різниці між абсолютним і відносним позиціонуванням розглянемо такий приклад:

from tkinter import *

root = Tk()
root.geometry('150x150')
Button(bg='red').place(x=75, y=20)
Button(bg='green').place(relx=0.3, rely=0.5)

root.mainloop()

Кнопка, позиція якої була жорстко задана (синя стрілка), не змінює свого положення, коли вікно розсувається. Кнопка з відносними координатами зміщується (жовта та рожева стрілки). Опції relx і rely для неї як і раніше 0.3 і 0.5, але вже відносно нового розміру вікна. Теж саме з розмірами віджетів. Якщо вони відносні, то зі зміною розміру батьківського віджету, їх розміри також будуть змінюватися. Якщо потрібно, щоб віджет завжди зберігав свій розмір, останній повинен вказуватися як абсолютний. Або не вказуватися зовсім, тоді буде прирівняний до природного.

...