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

Зміст

  1. Загальні поняття
  2. Імпорт tkinter і виклик циклу опрацювання подій
  3. Створення елемента керування
  4. Назви кольорів
  5. Cпільні методи віджетів
  6. «Системні» (не віджет-специфічні) методи
  7. Основні елементи керування
    • Toplevel — вікно верхнього рівня
    • Button — кнопка
    • Label — напис без можливості редагування
    • Entry — для введення рядка тексту
    • Text — для введення багаторядкового тексту
    • Listbox — список для вибору одного або кількох пунктів
    • Frame — рамка
    • Checkbutton — елемент списку з місцем для мітки
    • Radiobutton — перемикач
    • Scale — шкала для вибору значення з діапазону
    • Scrollbar — для «прокручування» іншого елемента керування
  8. Canvas — виведення зображень на полотно
  9. Розташування елементів за допомогою пакувальників
  10. Опрацювання подій
  11. Зображення
  12. Вікна діалогу
  13. Розширення ttk
  14. Приклад: примітивний текстовий редактор

Посилання

1. Загальні поняття

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

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

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

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

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

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

2. Імпорт tkinter і виклик циклу опрацювання подій

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

або from Tkinter import * — для версії python 2.7 і нижче;
  • from tkinter import * — для версії python 3.0 і вище,
  • Дія мінімальної програми з 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.

    3. Створення елемента керування

    Елементи керування (віджети) створюють викликом конструктора відповідного класу.

    Першим у переліку аргументів-властивостей (зазвичай без назви, але можна використовувати назву master) вказують батьківський віджет, в який буде упаковано (поміщено) створюваний віджет. Батьківський віджет можна не вказувати. У такому випадку буде використано головне вікно програми.

    Далі задають значення властивостей-аргументи:

    тощо. Розглянемо приклад коду з використанням конструктора Button для кнопок button1 і button2:

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

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

    4. Назви кольорів

    Однією з властивостей елемента керування є bg — колір тла. Подану далі таблицю з назвами кольорів отримано з використанням коду, запозиченого зі сторінки сайту Smith College.

    Крім усталених назв кольорів можна використати довільний колір моделі RGB, записавши рядком у форматі "#RRGGBB", де RR, GG, BB — інтенсивності відповідно червоного, зеленого і блакитного кольорів, записані у системі числення з основою 16.

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

    6. «Системні» (не віджет-специфічні) методи

    Наступні методи впливають на роботу інтерпретатора tcl/tk.

    7. Основні елементи керування

    8. Canvas — виведення зображень на полотно

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

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

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

    Примітка.

    Властивість 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).

    9. Розташування елементів за допомогою пакувальників

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

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

    10. Опрацювання подій

    Властивість command віджетів Button, Checkbutton, Radiobutton, Spinbox, Scrollbar, Scale можна прив'язати до події. Вище вже було використано цей спосіб у прикладі з натисканням лівої кнопки миші на зображенні кнопки

    button2 = Button (root, bg="red", text="Натисни мене!", command=button_clicked)

    Такий спосіб вважають найкращим і найзручнішим способом прив'язування до події.

    bindметод пов'язування елемента керування, події і функції (обробника події) такою вказівкою:

    назва_віджета.bind(назва_події, назва_функції)

    Третім необов'язковим аргументом методу bind може бути рядок "+", якщо прив'язування додають до вже наявних. Інакше (якщо цей третій аргумент опущено або він є порожнім рядком) прив'язування заміщає всі інші прив'язування даної події до віджету.

    Метод bind повертає ідентифікатор прив'язування, який можна використати у методу unbind з протилежним призначенням відв'язування. Зауважте: якщо bind прив'язано до вікна верхнього рівня, то tkinter буде опрацьовувати події всіх віджетів цього вікна (див. bind_all нижче). Функція, яку буде викликано при настанні події, повинна приймати один аргумент. Це об'єкт класу Event, що має такі властивості (в дужках вказані події, для яких цю властивість встановлено):

    Метод bind може повертати рядки "continue" і "break". Якщо функція повертає "continue" то tkinter продовжить опрацювання інших прив'язок події, якщо "break" — опрацювання цієї події буде припинено. Якщо метод нічого не повертає (якщо повернуто None), то опрацювання подій триває. Інакше кажучи, це еквівалентно поверненню "continue".

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

    Приклади назв подій:

    Приклад програми — дві різні подіїї зі своїми розробниками пов'язано з одним елементом керування:

    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, має обов'язковий параметр, через який передають назву події. Зазвичай його записують як event, хоча він може мати іншу назву в описі обробника. Його обов'язково записують на першому місці в описі функції і другим в описі методу (див. приклад).

    from tkinter import *
    root = Tk()
    class myButton:
      def __init__(self):      # Конструктор
        self.b = Button(text='Натискайте!', width=9, height=1)
        self.b.bind('', self.change)
        self.b.pack()
      def change(self, event): # Метод опрацювання події натискання лівої кнопки миші 
        self.b['fg'] = "green"
        self.b['activeforeground'] = "red"
    myButton()
    root.mainloop()

    Передавання додаткового аргумента обробника функції у метод bind здійснюють за допомогою лямбда-функції — див. приклад коду з передаванням кольору напису.

    from tkinter import *
    root = Tk()
    def f(e, c): l['fg'] = c
    l = Label(text="Натискайте кнопки миші!")
    l.bind('<Button-1>', lambda e, c="red":   f(e, c))  # для лівої  кнопки миші
    l.bind('<Button-3>', lambda e, c="green": f(e, c))  # для правої кнопки миші
    l.pack()
    root.mainloop()

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

    Цей спосіб можна застосувати і до властивості command, якщо така є в елемента керування. В останньому випадку параметр назви події (зазвичай event) зайвий, бо обробляють лише одну подію клацання лівою кнопкою миші.

    Вбудовані події

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

    11. Зображення

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

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

    Приклад:

    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:

    12. Вікна діалогу

    Пакет tkinter містить кілька модулів доступу до вікон діалогу. Ці модулі необхідно імпортувати окремо. У поданих нижче прикладах для messagebox (потрібно використовувати лише один із запропонованих способів) у коментарі з поясненно синтаксису виклику.

    import tkinter.messagebox            # tkinter.messagebox.askyesno()
    from tkinter.messagebox import *     # askyesno()
    from tkinter import messagebox       # messagebox.askyesno()
    from tkinter import messagebox as my # замість my може бути довільна назва my.askyesno()

    messageboxмодуль доступу до вікон діалогу з вибором одного з 2−3 варіантів («Так», «Ні», «Скасувати») або інформаційного вікна з повідомленням.

    askyesno — стандартне вікно діалогу модуля messagebox для вибору одного з 2 варіантів: «Yes» або «No» («Так» або «Ні») — див. приклад.

    from tkinter import *
    from tkinter.messagebox import * 
    def f():
      if (askyesno (title="Запит", message="Перенести дані?")):
        l['text'] = e.get()
        e.delete(0, END)
    root = Tk()
    e = Entry()
    l = Label()
    b = Button(text = 'Переписати', command = f)
    e.pack()
    b.pack()
    l.pack()
    root.mainloop()








    Натискання кнопки з написом «Yes» (Так) вікна діалогу повертає у програму значення True, «No» (Ні) — False (як і закриття вікна). Таким чином можна опрацювати вибір користувача. В даному випадку при його згоді дані буде переписано з поля над кнопкою у напис під кнопкою.

    Можна записати скорочено:

    askyesno ("Запит","Перенести дані?")

    Схожі породжують інші функції:

    askokcancel


    askquestion


    askretrycancel


    askyesnocancel

    Зображення вище створено при ОС Linux Mint 18 Mate. Останні три функції повертають не значення True чи False, а відповідні рядки написів на кнопках.

    Іншу групу модуля messagebox вікна складають інформаційні вікна лише з однією кнопкою, які слугують для виведення повідомлень. Це showerror, showinfo і showwarning — див. приклад.

    from tkinter import *
    from tkinter.messagebox import * 
    def f():
      s = e.get()
      if s.isdigit():
        e.delete(0, END)
        l['text'] = s
      else:
        showerror("Помилка","Потрібно ввести десятковий запис числа!")
        showinfo("Повідомлення","Потрібно ввести десятковий запис числа!")
        showwarning("Зауваження","Потрібно ввести десятковий запис числа!")
    root = Tk()
    e = Entry()
    l = Label()
    b = Button(text = 'Переписати', command = f)
    e.pack()
    b.pack()
    l.pack()
    root.mainloop()






    filedialogмодуль, що містить такі функції:

    Обидві функції повертають назву файлу, який потрібно відкрити або зберегти, але самі вони його не відкривають і не зберігають. Це роблять програмними засобами самого Python.

    from tkinter import *
    from tkinter.filedialog import * 
    def inText():
      name = askopenfilename()
      if (str(name)!="()"):
        f = open(name)
        s = f.read()
        t.insert(1.0,s)
        f.close()
     
    def outText():
      name = asksaveasfilename(
        filetypes = (
        ("TXT files", "* .txt"),
        ("HTML files", "* .html; *. htm"),
        ( "All files", "*. *")))
      if (str(name)!=""):  
        f = open(name, 'w')
        s = t.get(1.0, END)
        f.write(s)
        f.close()
     
    root = Tk()
    t = Text(width = 30, height = 5)
    t.grid (columnspan = 2)
    b1 = Button(text = "Відкрити", command = inText)
    b2 = Button(text = "Зберегти", command = outText)
    b1.grid (row = 1, sticky = E)
    b2.grid (row = 1, column = 1, sticky = W)
    root.mainloop()






    Примітка.

    simpledialogмодуль, що містить такі функції:

    — див приклад коду.

    from tkinter import *
    from tkinter.simpledialog import *
    root = Tk()
    
    a = askstring("Введення даних", "Ваше ім'я?",parent=root)
    if (a!=None): print("Ваше ім'я ", a)
    else:         print("Ви не знаєте свого імені?")
    
    a = askinteger("Введення даних", "Скільки Вам повних років?",
        parent=root, minvalue=0, maxvalue=125)
    if (a!=None): print("Ваш вік (у роках) ", a, ".")
    else:         print("Ви не знаєте, скільки Вам років?")
    
    a = askfloat("Введення даних", "Який Ваш ріст у метрах?",
        parent=root, minvalue=0.0, maxvalue=2.5)
    if (a!=None): print("Ваш ріст ", a, " м.")
    else:         print("Ви не знаєте, якого Ви зросту?")

    У ході його виконання буде таке.

    1. Виведено вікно діалогу для введення рядка.

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

    2. Виведено вікно діалогу для введення цілого значення.

      При введенні хибних значень буде виведено відповідні повідомлення.





    3. Виведено вікно діалогу для введення запису десяткового дробу.

      При введенні хибних значень буде виведено відповідні повідомлення.





    colorchooserмодуль, що містить такі функцію askcolor, яка надає вікно діалогу для вибору кольору.

    from tkinter import *
    from tkinter.colorchooser import *
    root = Tk()
    c = askcolor(parent=root,initialcolor=(255,255,255))
    print(c)


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

    ((100.390625, 150.5859375, 200.78125), '#6496c8')

    13. Розширення ttk

    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

    Віджет 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 відображає рівень завантаження. Має аргументи:

    Методи Progressbar:

    Приклад:

    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 ()

    14. Приклад: примітивний текстовий редактор.

    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.