Переменные Tkinter

В модуле tkinter есть особый класс Variable и производные от него "зауженные" классы IntVar, DoubleVar, StringVar, BooleanVar.

>>> from tkinter import *
>>> a = [i for i in dir() if i.find('Var') != -1]
>>> a
['BooleanVar', 'DoubleVar', 'IntVar', 'StringVar', 'Variable']
>>> issubclass(StringVar, Variable)
True

Экземпляры класса BooleanVar принимают только значения True или False (при этом можно передать целое число, которое будет преобразовано в булевое значение), IntVar – целые, DoubleVar – дробные, StringVar – строковые.

Объекты, создаваемые от этих классов, – это так называемые переменные Tkinter, по смыслу правильнее говорить "переменные для виджетов". Они используются для упрощения отслеживания определенного состояния того или иного виджета, а также программного изменения какого-то свойства не обязательно того же элемента интерфейса.

Например, можно сделать так, чтобы при вводе символов в текстовое поле они сразу отображаются на кнопке. Если использовать переменные Tkinter, реализовать подобное очень легко:

from tkinter import *

root = Tk()

var = Variable()

ent = Entry(textvariable=var)
ent.pack()

btn = Button(textvariable=var)
btn.pack()

root.mainloop()

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

В данном случае свойство, которому присваивается переменная, и у поля, и у кнопки – это textvariable. Однако у флажков и радиокнопок таким свойством является не только textvariable, но и variable (не путать с классом Variable).

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

Так как "переменная Tkinter" – это объект, связанный при создании с обычной переменной (например, var = Variable() или n = IntVar()), а в классах не перегружен оператор присвоения, мы не можем непосредственно присваивать значение имени переменной. Нельзя сделать так: n = 10. Однако может возникнуть необходимость в программном изменении значения такой переменной. Для таких случаев у объектов переменных Tkinter есть метод set. В приведенном выше примере мы можем добавить такую строку кода:

var.set('Hello')

И тогда при запуске программы в поле и на кнопке будет надпись "Hello". При изменении строки в текстовом поле все будет работать как обычно.

Чтобы установить исходное значение для переменной Tkinter, не обязательно использовать метод set (им пользуются для программного изменения значения). При создании переменной исходное значение можно передать в конструктор с помощью параметра value:

var = StringVar(value='Hello')

Если value не задано, то у переменных Tkinter все равно появляется значение в момент их создания – значение по-умолчанию. У экземпляров Variable и StringVar это пустая строка, у IntVar, DoubleVar и BooleanVar – это 0, 0.0 и False соответственно.

Кроме метода установки значения у переменных Tkinter есть и другие:

>>> [i for i in dir(Variable) if i[0] != '_']
['get', 'initialize', 'set', 'trace', 'trace_add', 'trace_info', 'trace_remove', 'trace_variable', 'trace_vdelete', 'trace_vinfo']

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

from tkinter import *


def concatenate():
    output.delete(0, END)
    output.insert(0, input1.get() + ' ' + input2.get())


root = Tk()
frame = Frame()

input1 = Entry(frame, width=10)
input2 = Entry(frame, width=10)
output = Entry(width=20, bg='lightgreen', justify=CENTER)
button = Button(frame, text='Соединить', command=concatenate)

frame.pack(padx=10, pady=10)
input1.pack(side='left')
button.pack(side='left', padx=10)
input2.pack(side='left')
output.pack(pady=10)

root.mainloop()

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

Однако мы не можем связать свойство textvariable поля вывода сразу с двумя переменными Tkinter. Не будет работать и такой вариант:

var1 = StringVar()
var2 = StringVar()
var3 = StringVar()
var3.set(var1.get() + ' ' + var2.get())

...

output = Entry(width=20, bg='lightgreen', justify=CENTER,
               textvariable=var3)

Потому что значение для var3 устанавливается единожды и потом никак не зависит от var1 и var2.

Стандартным решением подобной задачи является отслеживание определенного события. В данном случае – ввода в текстовые поля. Но тогда переменные Tkinter будут не нужны. Ведь когда вызывается обработчик события (функция), значения из полей можно забирать методом get самого поля, а вставлять с помощью метода insert.

С событиями мы познакомимся позже. Пока рассмотрим решение с помощью переменных Tkinter, что тоже возможно, так как у них есть метод trace_add, назначение которого – следить за своей переменной. Первым аргументом в него передается, что именно отслеживается. Это может быть запись/изменение значения ("write"), получение значения ("read"), удаление переменной ("unset"). Также можно передать комбинацию режимов в виде кортежа или списка.

Вторым аргументом в trace_add передается функция, которая будет выполняться, когда происходит указанное событие (функция обратного вызова – callback).

from tkinter import *


def concatenate(*a):
    var3.set(var1.get() + ' ' + var2.get())


root = Tk()
frame = Frame()

var1 = StringVar()
var2 = StringVar()
var3 = StringVar()

input1 = Entry(frame, width=10, textvariable=var1)
input2 = Entry(frame, width=10, textvariable=var2)
output = Entry(width=20, bg='lightgreen', justify=CENTER,
               textvariable=var3)

var1.trace_add('write', concatenate)
var2.trace_add('write', concatenate)

frame.pack(padx=10, pady=10)
input1.pack(side='left')
input2.pack(side='left')
output.pack(pady=10)

root.mainloop()

Метод trace_add() передает в колбэк три аргумента (var, index, mode). В заголовке функции их можно "распаковывать" через звездочку.

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

Напишите программу, "обратную" приведенной в уроке. Пользователь вводит строку, состоящую из нескольких слов. Первое слово автоматически дублируется в одно поле, остальные – в другое.

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


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




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