Создание, изменение и проверка текста / tkinter 2

869

Скачайте код уроков с GitLab: https://gitlab.com/PythonRu/tkinter-uroki

Создание текстовых элементов

Виджет Entry представляет собой текстовый элемент на одной строке. Вместе с классами Label и Button он является одним из самых используемых в Tkinter.

Как создать текстовый элемент

Следующий пример демонстрирует, как создать форму логина с двумя экземплярами для полей username и password. Каждый символ password отображается в качестве звездочки. Кнопка Войти выводит значения в консоли, а Очистить — удаляет содержимое обоих полей, возвращая фокус в username:

					

import tkinter as tk

class LoginApp(tk.Tk):
def __init__(self):
super().__init__()
self.username = tk.Entry(self)
self.password = tk.Entry(self, show="*")
self.login_btn = tk.Button(self, text="Войти",
command=self.print_login)
self.clear_btn = tk.Button(self, text="Очистить",
command=self.clear_form)
self.username.pack()
self.password.pack()
self.login_btn.pack(fill=tk.BOTH)
self.clear_btn.pack(fill=tk.BOTH)

def print_login(self):
print("Логин: {}".format(self.username.get()))
print("Пароль: {}".format(self.password.get()))

def clear_form(self):
self.username.delete(0, tk.END)
self.password.delete(0, tk.END)
self.username.focus_set()

if __name__ == "__main__":
app = LoginApp()
app.mainloop()

Как создать текстовый элемент

Как работают экземпляры

Экземпляры виджетов Entry создаются в родительском окне или фрейме, будучи переданными в качестве первого аргумента. С помощью опциональных ключевых слов можно задать дополнительные свойства. У username в этом примере таких нет, а у password — аргумент show со строкой «*», который будет выводить каждый символ как звездочку.

С помощью метода get() текущий текст можно будет получить в виде строки. Это используется в методе print_login(), который выводит содержимое Entry в стандартном выводе (stdout).

Метод delete() принимает два аргумента, которые представляют собой диапазон символов для удаления. Важно только помнить, что индексы начинаются с 0 и не включают последний символ. Если передать только один аргумент, то удалится символ на этой позиции.

В методе clear_form() удаляется содержимое от индекса 0 до константы END, в результате чего весь контент очищается. После этого фокус возвращается в поле username.

Содержимое виджета Entry можно модифицировать с помощью метода insert(), который принимает два аргумента:

  • index — позиция, куда нужно вставить текст (индекс первого — 0)
  • string — строка, которая будет вставлена

Стандартный шаблон сброса содержимого на значение по умолчанию — комбинация методов delete() и insert():

Еще один паттерн — добавление текста туда, где находится курсор. Для этого используется константа INSERT:

Как и Button класс Entry также принимает параметры relief и state для изменения стиля контура и состояния. Также стоит отметить, что вызовы delete() и insert() игнорируются, когда состояние равно «disabled» или «readonly».

Отслеживание изменений текста

Переменные Tk позволяют отправлять уведомления приложениям, когда входящие значения меняются. Есть 4 класса переменных в Tkinter: BooleanVar, DoubleVar, IntVar и StringVar. Каждый из них оборачивает значение соответствующего типа Python, который должен соответствовать типу виджета, прикрепленного к переменной.

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

Как отслеживать изменения текста

В следующем примере экземпляр StringVar ассоциирован с Entry, у которого есть параметр textvariable. Такие переменные отслеживают операции записи с помощью метода обратного вызова show_message():

					

import tkinter as tk

class App(tk.Tk):
def __init__(self):
super().__init__()
self.var = tk.StringVar()
self.var.trace("w", self.show_message)
self.entry = tk.Entry(self, textvariable=self.var)
self.btn = tk.Button(self, text="Очистить",
command=lambda: self.var.set(""))
self.label = tk.Label(self)
self.entry.pack()
self.btn.pack()
self.label.pack()

def show_message(self, *args):
value = self.var.get()
text = "Привет, {}!".format(value) if value else ""
self.label.config(text=text)

if __name__ == "__main__":
app = App()
app.mainloop()

Когда что-то вводится в этот виджет, текст метки обновляется на тот, что был составлен с помощью значения переменной Tk. Например, если ввести слово «Мир», то метка выведет Привет, Мир!. Если текст не вводить совсем, то ничего и не будет выводиться. Для демонстрации возможностей интерактивной настройки содержимого переменной была добавлена кнопка, которая очищает поле по нажатию.

Как отслеживать изменения текста

Как работает изменение текста

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

  • w — вызывается, когда переменная пишется
  • r — вызывается, когда переменная читается
  • u (от unset) — вызывается, когда переменная удаляется

При вызове функция обратного вызова получает три аргумента: внутреннее имя переменной, пустую строку (она используется в других типах переменных Tk) и режим, который запустил операцию. При объявлении его с *args эти аргументы становятся опциональными, потому что при обратном вызове значения уже не используются.

Метод get() оберток Tk возвращает текущее значение переменной, а метод set() — обновляет его. Они также уведомляют всех наблюдателей, так что изменение содержимого поля с помощью графического интерфейса, и нажатие кнопки Очистить запускают вызов метода show_message().

Переменные Tk являются опциональными для виджетов поля, но они обязательны для работы других классов виджетов, таких как классы Checkbutton и Radiobutton.

Валидация текста в полях

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

При определенных условиях нужно предотвратить возможность ввода невалидного содержимого в поле текста. Следующие примеры рассмотрят, как реализовать такое поведение с помощью параметров валидации в виджете Entry.

Как валидировать текст

Следующее приложение демонстрирует, как валидировать текст в поле ввода с помощью регулярных выражений:

					

import re
import tkinter as tk

class App(tk.Tk):
def __init__(self):
super().__init__()
self.pattern = re.compile("^\w{0,10}$")
self.label = tk.Label(self, text="Введите логин")
vcmd = (self.register(self.validate_username), "%i", "%P")
self.entry = tk.Entry(self, validate="key",
validatecommand=vcmd,
invalidcommand=self.print_error)
self.label.pack()
self.entry.pack(anchor=tk.W, padx=10, pady=10)

def validate_username(self, index, username):
print("Проверка символа" + index)
return self.pattern.match(username) is not None

def print_error(self):
print("Запрещенный символ в логине")

if __name__ == "__main__":
app = App()
app.mainloop()

Если запустить этот скрипт и ввести не букву алфавита или цифру, то содержимое не изменится, а вместо этого будет выведено сообщение об ошибке в консоль. Это же будет происходить, если попытаться ввести больше 10 символов, поскольку регулярное выражение ограничивает общее количество.

Как работает валидация текста

Когда параметром validate является key, то валидация запускается при любом изменении содержимого. Значение по умолчанию — none, что значит, что валидации не будет.

Также значениями могут быть focusin или focusout, когда валидация выполняется при получении или потере фокуса. Значение focus выполняет проверку в обоих случаях. Во всех ситуациях валидация проходит, если установить значение all.

Функция validatecommand вызывается каждый раз при запуске валидации. Она должна возвращать true, если введенное содержимое прошло проверку. В противном случае — false.

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

vcmd = (self.register(self.validate_username), "%i", "%P")

Можно использовать следующие замещения:

  • %d — тип действия. 1 — добавление, 0 — удаление, -1 — остальное;
  • %i — индекс вставляемой или удаляемой строки;
  • %p — сущность содержимого, если изменение разрешено;
  • %s — строковое содержимое до изменения;
  • %s — строка, которая вставляется или удаляется;
  • %v — тип текущей валидации;
  • %V— тип валидации, которая запускает действие;
  • %w — название виджета Entry.

Параметр invalidcommand принимает функцию, которая вызывается, когда validatecommand возвращает false. Те же замещения могут быть применены и к нему, но в данном примере классу был прямо передан метод print_error().

Документация Tcl/Tk предполагает, что не нужно смешивать параметры validatecommand и textvariable, поскольку невалидое значение переменной Tk вообще отключит проверку. То же самое произойдет, если функция validatecommand не вернет булевое значение.

Подробное введение в регулярные выражения доступно в официальной документации Python по ссылке https://docs.python.org/3.7/howto/regex.html.

Тест на знание python

Что выведет этот код?
Какой код выведет строку — "C:\Common\testString.doc" ?
Что выведет этот код?
Что выведет этот код?
Какой будет результат выполнения этого кода?
Александр
Я создал этот блог в 2018 году, чтобы распространять полезные учебные материалы, документации и уроки на русском. На сайте опубликовано множество статей по основам python и библиотекам, уроков для начинающих и примеров написания программ. Пишу на популярные темы: веб-разработка, работа с базами данных, data sciense и другие...