- Tkinter
Бібліотека tkinter мови Python

Tkinter (від англійського Tk interface) — це багатоплатформна (Windows, Linux, Mac OS X і інші) бібліотека мови Python, що є інтерфейсом до tcl / tk і слугує для створення програм з графічним інтерфейсом.

Tcl (Tool Command Language) — це потужна багатоплатформна мова програмування веб- і настільних застосунків, роботи мережі, адмініст­рування, тестування тощо.

Tkце графічний набір інструментів для створення інтерфейсу на вищому від традиційних підходів рівні. Придатний не лише для Tcl, але і для багатьох інших динамічних мов.

Імпорт tkinter здійснюють таким самим чином, як і будь-якої іншої бібліотек:

# Для версії python 2.7 і нижче
import Tkinter

# Для версії python 3.0 і вище
import tkinter

або

# Для версії python 2.7 і нижче
from Tkinter import *

# Для версії python 3.0 і вище
from tkinter import *

Віджет (widget, від англійського Window gadget) — елемент керування — стандартизований компонент графічного інтерфейсу, з яким взаємодіє користувач.

Клас Tkосновний клас застосунку Tkinter. При створенні об'єкта цього класу буде завантажено інтерпретатор tcl/tk і створено основне вікно програми.

Tkinter є подійно-орієнтованою бібліотекою. При її використанні головним є цикл опрацювання подій. У tkinter такий цикл:

Дія мінімальної програми з tkinter:

from tkinter import *
root = Tk ()
root.mainloop ()

буде такою:

Можна використовувати кілька викликів інтерпретатора tcl/tk. Після виклику методу mainloop подальші вказівки python не буде виконано до виходу з цього циклу опрацювання подій. Тому методи mainloop, крім останнього, потрібно здійснювати у режимі тла. Наприклад, таким чином:

from tkinter import *
root1 = Tk ()
root2 = Tk ()
root1.after (500, root1.mainloop)
root2.mainloop ()

При використанні двох чи більше викликів інтерпретатора необхідно, щоб об'єкти, створені в одному циклі mainloop, було використано лише в ньому. Необхідність використання кількох циклів у одному застосунку виникає вкрай рідко. Для створення додаткового вікна програми у більшості випадків досить віджета Toplevel.

Примітка. Для того, щоб використовувати літери кирилиці, потрібно використовувати рядки з кодуванням Unicode. У Python 2.x для цього потрібно перед рядком писати літеру u. Також у першому чи другому рядку файлу необхідно вказати (в коментарі) кодування файлу:

# Encoding: utf-8

У Python 3.x цього робити не потрібно, бо всі рядки в ньому мають кодування Unicode.

Спільні риси віджетів tkinter

Віджети створюють викликом конструктора відповідного класу. Перший аргумент (як правило без назви, але можна використовувати назву master) — це батьківський віджет, в який буде упаковано (поміщено) створюваний віджет. Батьківський віджет можна не вказувати. У такому випадку буде використано головне вікно програми. Далі йдуть аргументи, які конфігурують віджет. Це може бути використовуваний шрифт (font = …), колір (тла) віджета (bg = …), вказівка, яку буде виконано при активуванні віджета (command = …) тощо. Повний список всіх аргументів можна побачити у man options і man-сторінці відповідного віджета. Наприклад:

from tkinter import *
def button_clicked ():
    print("Клац!")
root = Tk ()

# Кнопка як усталено
button1 = Button ()
button1.pack ()

# Кнопка із зазначенням батьківського віджета й кількома аргументами
button2 = Button (root, bg = "red", text = "Натисни мене!", command = button_clicked)
button2.pack ()
root.mainloop ()

Після трьох клацань отримаємо таке:

Cпільні методи віджетів

«Системні» (не віджет-специфічні) методи,
що впливають на роботу інтерпретатора tcl/tk:

Основні віджети

Canvas (полотно) це об'єкт класу ТCanvas для виведення зображень, які можна змінювати і переміщати у процесі виконання програми.

На полотні можна розташувати такі об'єкти (у дужках вказано назви властивостей):

Подамо приклади (щоб побачити програму у кодуванні UTF-8, клацніть на малюнку лівою кнопкою миші):

Примітка. Для успішного виконання програми з останнього прикладу необхідно, щоб тека з програмою містила файл зображення:

Властивість anchor використовують для того, щоб вказати, чим є точка з вказаними координатами для створюваного об'єктами:

Всі подані вище приклади (для скорочення коду) подано без використання ідентифікаторів чи тегів, які буде використано у прикладах, поданих нижче.

Метод focus_set використовують для фокусування уваги на полотні, щоб мати можливість прив'язати події до виконання методів полотна.

Метод create_* використовують для створення об'єкта, де замість * пишуть тип об'єкта, вказуючи у дужках спочатку обов'язкові значення властивостей-координат, а потім — решти властивостей

Щоб зрозуміти написане, див. приклад, у якому круг жовтого кольору зсувають на 2 пікселі, натискаючи на клавіатурі відповідні клавіші зі стрілками. У цьому прикладі звертення до об'єкту (круга) здійснено через ідентифікатор (ball).

Метод coords встановлює значення координат. Якщо вказано лише ідентифікатор або тег, то метод повертає поточні значення координат.

Метод itemconfig встановлює значення властивостей, що не є координатами.

Щоб краще зрозуміти написане, див. приклад, у якому при отриманні фокусу (натискати клавішу Tab) буде змінено розмір і колір квадрата.

Метод tag_bind надає можливість прив'язати опрацювання події до певної фігури на полотні, заданої ідентифікатором або тегом.

Метод delete видаляє об'єкт. Якщо потрібно озвільнити все полотно, то замість ідентифікаторів або тегів використовують сталу ALL

— див. приклад, де перетворення зображення на текст спричинено натисканням лівої кнопки миші.

Наступний приклад показує, як можна відобразити рух об'єкта (жовтий круг)

Метод find_overlapping повертає назви усіх об'єктів, що мають непорожній перетин з прямокутником, заданим координатами протилежних вершинних. Наприклад, така програма:

from tkinter import *
root = Tk()
c = Canvas(root, width=105, height=45, bg='white')
c.pack()
c11=c.create_rectangle(( 5, 5), (20, 20))
c12=c.create_rectangle((25, 5), (40, 20)) 
c13=c.create_rectangle((45, 5), (60, 20))
c14=c.create_rectangle((65, 5), (80, 20), fill="green", tag="c14" )
c15=c.create_rectangle((85, 5),(100, 20), fill="green")
c21=c.create_rectangle(( 5,25), (20, 40))
c22=c.create_rectangle((25,25), (40, 40)) 
c23=c.create_rectangle((45,25), (60, 40))
c24=c.create_rectangle((65,25), (80, 40), fill="green")
c25=c.create_rectangle((85,25),(100, 40), fill="green")
c00=c.create_rectangle((73,15), (93, 30), fill="yellow")
print(c11, c12, c13, c14, c15)
print(c21, c22, c23, c24, c25)
print(c00)
t=c.find_overlapping(73,15,93,30)
print(t);
root.mainloop()

створює таке вікно:

і має таке консольне виведення:

1 2 3 4 5
6 7 8 9 10
11
(4, 5, 9, 10, 11)

У цьому прикладі 5 зафарбованих прямокутників (4, 5, 9, 10, 11) мають точки, що належать до жовтого прямокутника (11).

Пакувальник (менеджер геометрії, менеджер розташування) — механізм розташування (пакування) віджетів на вікні.

У бібліотеці tkinter є три пакувальники: pack, place, grid. Зауважте: в одному віджеті можна використовувати лише один тип пакування. При змішуванні різних типів пакування програма, швидше за все, не буде працювати. Розглянемо кожен з них окремо.

Пакувальники бібліотеки tkinter

Способи прив'язування подій

Форми назв подій

Модифікатори:

Типи подій

Activate, Deactivate
Ці дві події буде надіслано до кожного підвікна вікна верхнього рівня, коли буде змінено його стан. Подію Activate буде надіслано при активуванні вікна. Аналогічно, подію Deactive буде надіслано, коли вікно стане неактивним.

ButtonPress, ButtonRelease, Motion
Події виникають відповідно при натисканні, відпусканні кнопки миші або переміщенні вказівника миші. Повідомлення про події, як правило, буде надіслано до вікна, що містить вказівник. При натиснутій кнопці миші вікно, що містить вказівник миші, автоматично отримує тимчасове захоплення вказівника. Повідомлення про ці події будуть надіслані й у це вікно, незалежно від того, яке вікно містить вказівник, поки всі кнопки не будуть відпущені.

Colormap
Подія виникає при зміні, встановленні чи деісталяції кольорової мапи.

Configure
Подію буде надіслано до вікна щойно буде змінено його розмір, розташування або ширину межі, а іноді — при зміні позиції у порядку набору.

Destroy
Про цю подію буде повідомлено вікно після дій щодо його знищення. При повідомленні віджет перебуватиме у напівмертвому стані: все ще існує, але більшість операцій з ним нездійснена.

Enter, Leave
Події виникають відповідно входженні у вікно і виході з нього вказівника миші.

Expose
Подія виникає, коли все вікно або його частину потрібно перемалювати. Зазвичай клієнтським застосункам не потрібно обробляти подію Expose, бо Tk опрацьовує їх внутрішньо.

FocusIn, FocusOut
Події виникають при зміні фокусу клавіатури. Повідомлення про FocusOut буде надіслано до вікна, що втрачає фокус, а про FocusIn — тому, що набуває.

Gravity, Reparent, Circulate
ПОвідомлення про події Gravity і Reparent, як правило, не доходять до Tk застосунків. Їх включено у цей список для повноти. Подія Circulate вказує на те, що вікно переміщено до верхньої або нижньої частини порядку накладення в результаті запиту протоколу XCirculateSubwindows. Порядок складування може змінитися з інших причин, які не породжують подію Circulate. Tk не використовує XCirculateSubwindows внутрішньо. Цей тип події включено лише для повноти опису. Немає надійного способу відстежувати зміни позиції вікна у порядку складування.

KeyPress, KeyRelease
Події виникають відповідно при натисканні й відпусканні клавіші. Повідомлення про них буде надіслано до вікна з фокусом на клавіатурі.

Map, Unmap
Події створюються щоразу, коли змінюється стан відображення вікна.

MapRequest, CirculateRequest, ResizeRequest, ConfigureRequest, Create
Про ці події, як правило, не повідомляють застосунки Tk. Їх включено у перелік для повноти, щоб дозволити писати менеджери вікон X11 в Tk. Про ці події повідомляють лише тоді, коли клієнт вибрав SubstructureRedirectMask, а ядро Tk не використовує цю маску.

MouseWheel
Подія виникає при прокручуванні коліщатка миші. Подію завжди буде спрямовано до вікна з фокусом. Після отримання події, можна використати заміну %D, щоб отримати поле delta для події з цілим числом, що описує рух коліщатка миші. Найменше значення, про яке система буде повідомляти, визначено ОС. Знак значення визначає, в якому напрямку прокручувати віджет: додатні значення — прокручування вгору, від'ємні — вниз.

Property
Подія виникає при зміні чи вилученні властивості (вікна). Повідомлення про цю подію, як правило, не доходить до Tk застосунків, бо їх опрацьовує ядро Tk.

Visibility
Подія виникає, коли вікно буде перекрито іншим вікном, розташованим над ним (у порядку складування). Поле стану %s описує (новий) стан.

Клавіатурні символи
Див.
перелік і приклади частин коду:

  • <Button-1> або <1> — натиснуто ліву клавішу миші;
  • <Alt-Motion> — рух миші з натиснутою на клавіатурі клавішею Alt;
  • <Key> — натискання будь-якої клавіші на клавіатурі.

Приклад програми:

from tkinter import *
root=Tk()
def leftclick(event):
    print ('Натисли ліву кнопку миші.')
def rightclick(event):
    print('Натисли праву кнопку миші.')
button1=Button(root, text='Натискайте!')
button1.pack()
button1.bind('<Button-1>', leftclick)
button1.bind('<Button-3>', rightclick)
root.mainloop()

Додаткові методи

  • bind_all — прив'язування для всіх віджетів програми. На відміну від прив'язування до вікна верхнього рівня, цей метод прив'язує всі віджети застосунку, який може мати й кілька вікон;

  • bind_class — прив'язування для всіх віджетів даного класу — див. приклад.

    from tkinter import *
    def callback (e):
        print('Натиснуто кнопку ', e.widget['text'])
    root = Tk ()
    button1 = Button (root, text = '1')
    button1.pack ()
    button2 = Button (root, text = '2')
    button2.pack ()
    root.bind_class ('Button', '<1>', callback)
    root.mainloop ()
  • bindtags — змінює порядок опрацювання прив'язок. Стандартний порядок такий:

    • прив'язане до віджету (bind);
    • прив'язане до класу (bind_class);
    • прив'язане до вікна (root.bind);
    • прив'язане до всіх віджетів (bind_all).

    Приклад зміни порядку опрацювання на зворотний:

    from tkinter import *
    def callback1 (e): print('callback1')
    def callback2 (e): print('callback2')
    def callback3 (e): print('callback3')
    def callback4 (e): print('callback4')
    root = Tk ()
    button = Button (root)
    button.pack ()
    button.bind ('<1>', callback1)
    root.bind_class ('Button', '<1>', callback2)
    root.bind ('<1>', callback3)
    root.bind_all ('<1>', callback4)
    button.bindtags (('all', root, 'Button', button))
    root.mainloop ()
  • unbind — відв'язування віджету від події з аргументом — ідентифікатором, отриманим від методу bind.

  • unbind_all — те саме, що й unbind, але для методу bind_all.

  • unbind_class — те саме, що й unbind, але для методу bind_class.

Зображення
Для роботи із зображеннями у бібліотеці Tkinter є два класи:

  • BitmapImage — двоколірне зображення;
  • PhotoImage — багатоколірне зображення.
Аргументи конструктора класу BitmapImage:

  • background — колір тла (зазвичай чорний);
  • foreground — колір переднього плану (зазвичай білий);
  • file — шлях до файлу із зображенням;
  • maskfile — шлях до маски (вказує, які пікселі будуть прозорими);
  • data — завантажені у пам'ять дані зображення;
  • maskdata — завантажені в пам'ять дані маски.

Приклад:

from tkinter import *
data = '''#define image_width  15
          #define image_height 15
static unsigned char image_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1c, 0x30, 0x0c, 0x60, 0x06,
   0x60, 0x06, 0xc0, 0x03, 0xc0, 0x03, 0x60, 0x06, 0x60, 0x06, 0x30, 0x0c,
   0x38, 0x1c, 0x00, 0x00, 0x00, 0x00 };'''
root=Tk()
image = BitmapImage(data=data, background='blue', foreground='yellow')
button=Button(root, image=image)
button.pack()
root.mainloop()

PhotoImage дає можливість використовувати багатоколірне зображення. Клас має кілька (досить примітивних) методів для роботи з зображеннями. PhotoImage гарантовано розуміє формат GIF.

Аргументи конструктора класу PhotoImage:

  • file — шлях до файлу з зображенням;
  • data — завантажені у пам'ять дані зображення;
  • format — формат зображення;
  • width, height — ширина й висота зображення;
  • gamma — корекція гами;
  • palette — палітра зображення.

ttk (themed tk) — розширення tcl/tk з новим набором віджетів, підтримкою тем і стилів оформлення. Тому віджети ttk виглядають природніше в різних операційних системах. Починаючи з версій python 2.7 і 3.1.2 tkinter містить модуль для роботи з ttk.

ttk містить віджети, які можна використати замість відповідних віджетів tk: Button, Checkbutton, Entry, Frame, Label, LabelFrame, Menubutton, PanedWindow, Radiobutton, Scale і Scrollbar. Є кілька нових віджетів: Combobox, Notebook, Progressbar, Separator, Sizegrip и Treeview.

З точки зору програміста відмінність нових віджетів від старих полягає лише у тому, що віджетии ttk не мають опцій для конфігурування свого зовнішнього вигляду. Конфігурування зовнішнього вигляду віджетів ttk зійснюють через теми і стилі.

ttk має 4 вбудовані теми: default, classic, alt, clam. Додатково под Windows є теми winnative, xpnative i vista, а під Mac OS X — aqua.

Styleклас для работи зі стилями й темами. Саме цей клас потрібно використовувати для конфігурування зовнішнього вигляду віджетів ttk.

Основні методи класу Style

  • configure — конфігурування зовнішнього вигляду віджетів. Має аргументи: назву стиля віджета і список опцій конфігурування. Наприклад,

    style.configure("TButton",padding=6,relief="flat",background="#ccc")
  • map — конфігурування зовнішнього вигляду віджетів залежно від їхніх станів (active, pressed, disabled тощо). Має аргументи: назву стилю віджета і список параметрів конфігурації. Приклад використання:

    style.map ( "C.TButton",
        foreground = [( 'pressed', 'red'), ( 'active', 'blue')],
        background = [( 'pressed', '! disabled', 'black'), ( 'active', 'white')]
        )
  • lookup — повертає відповідну опцію конфігурації. Приклад використання:

    style.lookup ( "TButton", "font")
  • layout — змінює схему (layout) віджета. Приклад використання:

    style.layout ( "TMenubutton", [
       ( "Menubutton.background", None),
       ( "Menubutton.button", { "children":
           [( "Menubutton.focus", { "children":
               [( "Menubutton.padding", { "children":
                   [( "Menubutton.label", { "side": "left", "expand": 1})]
               })]
           })]
       }),
    ])
  • element_create — cтворює новий елемент теми.

  • element_names — повертає список елементів поточної теми.

  • element_options — повертає список параметрів конфігурації, зазначеного в аргументі елемента.

  • theme_create — cтворює нову тему, аргументи такі самі, що й у theme_settings (див. далі).

  • theme_settings — конфігурує наявну тему. Має аргументи — назва теми і словник, ключами якого є назви стилів (TButton тощо), а значеннями — схеми (layout) відповідного стилю.

  • theme_names — повертає список доступних тем.

  • theme_use — змінює поточну тему на зазначену в аргументі.

Віджет Combobox призначено для відображення списку значень, їх вибору або зміни користувачем. У версії tk є схожий віджет Listbox. Різниця полягає у тому, що Combobox має можливість згортатися, а Listbox буде відображено завжди відкритим. Щоб відобразити Combobox з наперед заданими значеннями, досить зробити таке:

import tkinter as tk
import tkinter.ttk as ttk
root = tk.Tk ()
frame = tk.Frame (root)
frame.grid ()
combobox = ttk.Combobox (frame, values=["ОДИН","ДВА","ТРИ"], height=3)
#frame  - батьківський віджет, на його поверхні буде розташовано Combobox
#values ​​- набір значень, розташованих у Combobox спочатку
#height - висота списку. Якщо кількість елементів списку менше 11, то її можна не задавати.
#         Якщо її не задано при кількості елементів, що перевищує 10, то праворуч буде смуга прокрутки.
#         Якщо у поданому прикладі задати значення height, яке менше трьох, то праворуч буде смуга прокрутки.
#         Але вона буде недоступна, а всі елементи буде відображено одночасно.
combobox.set ("ОДИН")               # Combobox встановлено на значення ОДИН спочатку
combobox.grid (column = 0, row = 0) # Combobox розташовано на формі
root.mainloop ()

Віджет Progressbar відображає рівень завантаження. Має аргументи:

  • батьківський віджет (у поданому прикладі root);
  • length — довжина смуги.

Методи Progressbar:

  • start — запускає нескінчений цикл завантаження. Крок довжиною 1 буде виконано у зазначений час (у мілісекундах);

  • stop — зупиняє цикл завантаження;

  • step — просуває процес завантаження на задану кількість кроків.

Приклад:

import tkinter as tk
import tkinter.ttk as ttk
root = tk.Tk ()
pb = ttk.Progressbar (root, length = 100)
pb.pack ()
pb.start (100)
root.mainloop ()

Змістовні приклади

Виклик вікон діалогу для відкриття файлу і його збереження

from tkinter import *
from tkinter.filedialog import *
 
root = Tk()
op = askopenfilename()
sa = asksaveasfilename()
 
root.mainloop()

Примітивний текстовий редактор з можливістю завантаження у текстове поле, перегляду, редагування і запису.

from tkinter import *
from tkinter.filedialog import *
from tkinter.messagebox import *
import fileinput
 
class Window_blank:
  def __init__(self):
    main_menu = Menu(root)
    root.config(menu=main_menu)
    first_menu = Menu(main_menu)
    main_menu.add_cascade(label="Файл", menu=first_menu)
    first_menu.add_command(label="Відкрити",     command=self.open_file)
    first_menu.add_command(label="Зберегти як…", command=self.save_file)
    first_menu.add_command(label="Про програму", command=self.about)
    first_menu.add_command(label="Вихід",        command=self.close_win)
    self.txt = Text(root, width=45, height=15)
    self.txt.pack()
 
  def open_file(self):
    op = askopenfilename()
    try:
      self.txt.delete(1.0, END)
      for i in fileinput.input(op):
        self.txt.insert(END, i)
    except:
      pass
 
  def save_file(self):
    save_as = asksaveasfilename()
    try:
      letter = self.txt.get(1.0, END)
      f = open(save_as, "w")
      f.write(letter)
      f.close()
    except:
      pass
 
  def close_win(self):
    if askyesno("Збереження файлу", "Зберегти дані?"):
      self.save_file()
      root.destroy()
    else:
      root.destroy()
 
  def about(self):
    showinfo("Примітивний редактор", "Це лише приклад застосування tkinter")
 
root = Tk()
root.title("Примітивний редактор")
 
obj_menu = Window_blank()
root.mainloop()

Роботу цього коду проілюструємо такими малюнками.

























Посилання

  1. Tcl/Tk Manual Pages.
  2. Tcl/Tk 8.5 Manual.
  3. Перелік сторінок tcl.tk/man/tcl8.5/TclCmd.
  4. An Introduction to Tkinter.
  5. Курс по библиотеке Tkinter языка Python.