Что такое Tkinter

Tkinter – это пакет для Python, предназначенный для работы с библиотекой Tk. Библиотека Tk содержит компоненты графического интерфейса пользователя (graphical user interface – GUI). Эта библиотека написана на языке программирования Tcl.

Под графическим интерфейсом пользователя (GUI) подразумеваются все те окна, кнопки, текстовые поля для ввода, скроллеры, списки, радиокнопки, флажки и другие элементы, которые вы видите на экране, открывая то или иное приложение. С их помощью вы взаимодействуете с программой, управляете ее поведением. Все эти элементы интерфейса будем называть виджетами (widgets – штуковины).

В настоящее время почти все приложения, которые создаются для конечного пользователя, имеют GUI. Редкие программы, подразумевающие взаимодействие с человеком, остаются консольными. В предыдущих двух курсах мы писали только консольные программы.

Существует множество библиотек GUI, среди которых Tk не самый популярный инструмент. Однако он надежный (устойчивый), быстрый и независимый от платформы (работает на многих ОС). Поэтому был выбран для включения в стандартную библиотеку Python посредством Tkinter. Скорее всего чаще на Tkinter пишут небольшие приложения для внутренних нужд организаций. Установочный файл интерпретатора Питона обычно уже включает пакет tkinter. Хотя в Ubuntu его надо будет установить дополнительно.

Слово "tkinter" происходит от фразы "Tk interface". Tkinter – это объектно-ориентированная надстройка языка Python над Tcl/Tk. Его можно представить как переводчик с Python на язык Tcl. Вы пишете программу на Python, а код модуля tkinter переводит ваши инструкции на язык Tcl, который понимает библиотека Tk.

Программы с графическим интерфейсом пользователя событийно-ориентированные. Вы уже должны иметь представление о структурном и желательно объектно-ориентированном программировании. В событийно-ориентированном на первый план выходят события. То есть та или иная часть программного кода начинает выполняться только тогда, когда что-то произошло, было выполнено определенное действие.

Событийно-ориентированное программирование базируется на объектно-ориентированном. Даже если мы не будем разрабатывать собственные классы, мы обязательно будем пользоваться теми, что есть в tkinter. Все виджеты – это объекты-экземпляры, создаваемые от встроенных классов.

События бывают разными. Сработал временной фактор, завершилась загрузка, пришло сообщение, кто-то кликнул мышкой или нажал Enter, начал вводить текст, переключил радиокнопки, прокрутил страницу вниз и так далее. Когда случается что-либо подобное, то, если был создан обработчик для соответствующего события, происходит выполнение определенной части программы, что приводит к какому-либо результату.

Tkinter импортируется как и все модули Python любым из способов:

В данном курсе мы будем использовать последний вариант, так как несмотря на то, что подобный импорт не рекомендуется, из-за уникальности имен классов виджетов риск перекрытия пространств имен минимальный.

Также можно импортировать отдельные классы. Например:

from tkinter import Tk, Button, Label

Если необходимо, узнать установленную версию Tk можно через константу TkVersion:

>>> from tkinter import *
>>> TkVersion
8.6

Чтобы написать GUI-программу, надо выполнить приблизительно следующее:

  1. Создать главное окно.

  2. Создать виджеты и выполнить конфигурацию их свойств (опций).

  3. Определить события, то есть то, на что будет реагировать программа.

  4. Описать обработчики событий, то есть то, что будет делать программа.

  5. Расположить виджеты в главном окне.

  6. Запустить цикл обработки событий.

Последовательность не обязательно такая, но первый и последний пункты всегда остаются на своих местах. Посмотрим все это в действии.

В современных операционных системах любое пользовательское приложение заключено в окно, которое можно назвать главным, так как в нем располагаются все остальные виджеты. Объект окна верхнего уровня создается от класса Tk модуля tkinter. Переменную, связываемую с объектом, часто называют root (корень):

root = Tk()

Пусть в окне приложения располагаются текстовое поле, метка и кнопка. Данные объекты создаются от классов Entry, Label и Button модуля tkinter. Сразу выполним конфигурацию некоторые их свойства с помощью передачи аргументов конструкторам этих классов:

ent = Entry(root, width=20)
but = Button(root, text='Преобразовать')
lab = Label(root, width=20, bg='black', fg='white')

Устанавливать свойства объектов не обязательно при их создании. Существуют еще пара способов, с помощью которых это можно сделать после.

Первым аргументом в конструктор виджета передается виджет-хозяин, то есть тот, на котором будет располагаться создаваемый. В случае, когда элементы GUI помещаются непосредственно на главное окно, родителя можно не указывать. То есть в нашем примере мы можем убрать root:

ent = Entry(width=20)
but = Button(text='Преобразовать')
lab = Label(width=20, bg='black', fg='white')

Однако виджеты не обязательно располагаются на root'е. Они могут находиться на других виджетах, и тогда указывать "мастера" необходимо.

Пусть в программе текст, введенный человеком в поле, при нажатии на кнопку разбивается на список слов, слова сортируются по алфавиту и выводятся в метке. Выполняющий все это код надо поместить в функцию:

def sort_words(event):
    s = ent.get()
    s = s.split()
    s.sort()
    lab['text'] = ' '.join(s)

У функций, которые вызываются при наступлении события с помощью метода bind, должен быть один параметр. Обычно его называют event, то есть "событие".

В теле функции с помощью метода get из поля забирается текст, представляющий собой строку. Она преобразуется в список слов с помощью метода split. Потом список сортируется. В конце изменяется свойство text метки. Ему присваивается строка, полученная из списка с помощью строкового метода join.

Теперь необходимо связать вызов функции с событием:

but.bind('<Button-1>', sort_words)

В данном случае это делается с помощью метода bind. Ему передается событие и функция-обработчик. Событие будет передано в функцию и присвоено параметру event. Здесь событием является щелчок левой кнопкой мыши, что обозначается строкой '<Button-1>'.

В любом приложении виджеты не разбросаны по окну как попало, а организованы, интерфейс продуман до мелочей и обычно подчинен определенным стандартам. Пока расположим элементы друг под другом с помощью наиболее простого менеджера геометрии Tkinter – метода pack:

ent.pack()
but.pack()
lab.pack()

Метод mainloop экземпляра Tk запускает главный цикл обработки событий, что в том числе приводит к отображению главного окна со всеми "упакованными" на нем виджетами:

root.mainloop()

Полный код программы:

from tkinter import *


def sort_words(event):
    s = ent.get()
    s = s.split()
    s.sort()
    lab['text'] = ' '.join(s)


root = Tk()

ent = Entry(width=20)
but = Button(text='Преобразовать')
lab = Label(width=20, bg='black', fg='white')

but.bind('<Button-1>', sort_words)

ent.pack()
but.pack()
lab.pack()
root.mainloop()

В результате выполнения данного скрипта появляется окно, в текстовое поле которого можно ввести список слов, нажать кнопку и получить его отсортированный вариант:

Попробуем теперь реализовать в нашей программе объектно-ориентированный подход. Это необязательно, но позволяет связать/объединить вместе (вспомните про инкапсуляцию в ООП) виджет и функции, которые обрабатывают связанные с ним события, что концептуально более верно. В этом случае функции становятся методами.

Пусть группа из метки, кнопки и поля представляет собой один объект, порождаемый от класса Block. Тогда в основной ветке программы будет главное окно, объект типа Block и запуск окна. Поскольку блок должен быть привязан к главному окну, то неплохо бы передать в конструктор класса окно-родитель:

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

Теперь напишем сам класс Block:

class Block:
    def __init__(self, master):
        self.ent = Entry(master, width=20)
        self.but = Button(master, text='Преобразовать')
        self.lab = Label(master, width=20, bg='black', fg='white')
        self.but['command'] = self.sort_words
        self.ent.pack()
        self.but.pack()
        self.lab.pack()

    def sort_words(self):
        s = self.ent.get().split()
        s.sort()
        self.lab['text'] = ' '.join(s)

Здесь виджеты являются значениями полей объекта типа Block, функция-обработчик события нажатия на кнопку устанавливается не с помощью метода bind, а с помощью свойства кнопки command. В этом случае в вызываемой функции не требуется параметр event. В метод мы передаем только сам объект.

Больший смысл в определении собственного класса появляется, когда требуется несколько или множество похожих объектов-блоков. Допустим, нам нужно несколько блоков, состоящих из метки, кнопки, поля. Причем у кнопки каждой группы будет своя функция-обработчик клика.

Тогда можно передавать значения для свойства command в конструктор. Значение будет представлять собой привязываемую к кнопке функцию-обработчик события. Полный код программы:

from tkinter import *


class Block:
    def __init__(self, master, func):
        self.ent = Entry(master, width=20)
        self.but = Button(master, text='Преобразовать')
        self.lab = Label(master, width=20, bg='black', fg='white')
        self.but['command'] = getattr(self, func)
        self.ent.pack()
        self.but.pack()
        self.lab.pack()

    def sort_words(self):
        s = self.ent.get().split()
        s.sort()
        self.lab['text'] = ' '.join(s)

    def reverse_words(self):
        s = self.ent.get().split()
        s.reverse()
        self.lab['text'] = ' '.join(s)


root = Tk()

first_block = Block(root, 'sort_words')
second_block = Block(root, 'reverse_words')

root.mainloop()

Выражение getattr(self, func), где вместо func подставляется строка 'sort_words' или 'reverse_words', преобразуется в выражение self.sort_words или self.reverse_words. Можно прочитать подробнее про встроенную функцию getattr языка Python.

При выполнения этого кода в окне будут выведены два однотипных блока, кнопки которых выполняют разные действия.

Класс можно сделать более гибким, если жестко не задавать свойства виджетов, а передавать значения как аргументы в конструктор, после чего присваивать их соответствующим опциям при создании объектов.

Практическая работа

Напишите простейший калькулятор, состоящий из двух текстовых полей, куда пользователь вводит числа, и четырех кнопок "+", "-", "*", "/". Результат вычисления должен отображаться в метке. Если арифметическое действие выполнить невозможно (например, если были введены буквы, а не числа), то в метке должно появляться слово "ошибка".

Курс с примерами решений практических работ: pdf-версия


Tkinter. Программирование GUI на Python




Все разделы сайта