Когда я впервые изучал этот виджет, моей первой реакцией было: «Зачем вообще здесь нужны переменные tkinter, если можно зарегистрировать обработчики щелчков на виджетах?» На первый взгляд связанные переменные могут показаться излишними, но они упрощают некоторые действия с графическим интерфейсом. Не буду просить принять это на веру, а постараюсь объяснить, почему.
Имейте в виду, что обработчик для флажка, указанный в параметре command, будет выполняться при каждом щелчке — при переключении и в выбранное, и в невыбранное состояние. Поэтому если нужно совершить действие немедленно после щелчка на флажке, как правило, в обработчике события требуется узнать текущее значение флажка. Поскольку у флажка нет метода «get», с помощью которого можно было бы получить текущее его значение, обычно требуется запрашивать ассоциированную переменную, чтобы узнать, включен флажок или выключен.
Кроме того, в некоторых графических интерфейсах пользователям разрешается устанавливать флажки без вызова обработчиков, зарегистрированных с помощью параметра command, и получать значения где-либо позже в программе. В таком сценарии переменные служат для автоматического запоминания состояний флажков. Представителем этого последнего подхода является метод report в сценарии demoCheck.
Конечно, можно и вручную запоминать состояние каждого флажка в обработчиках событий. В примере 8.23 ведется свой список состояний флажков, который вручную обновляется в обработчиках событий, определяемых с помощью параметра command.
Пример 8.23. PP4E\Gui\Tour\demo-check-manual.py
# флажки, сложный способ (без переменных)
from tkinter import *
states = [] # изменение объекта — не имени
def onPress(i): # сохраняет состояния
states[i] = not states[i] # изменяет False->True, True->False
root = Tk()
for i in range(10):
chk = Checkbutton(root, text=str(i), command=(lambda i=i: onPress(i)) ) chk.pack(side=LEFT)
states.append(False)
root.mainloop()
print(states) # при выходе вывести все состояния
Здесь lambda-выражение передает индекс нажатой кнопки в списке states. Иначе для каждой кнопки потребовалось бы создавать отдельный обработчик. Здесь мы снова вынуждены использовать аргумент со значением по умолчанию, чтобы передать переменную цикла lambda-выражению. В противном случае все 10 сгенерированных функций получили бы значение переменной цикла, присвоенное ей в последней итерации цикла (щелчок на любом флажке изменял бы состояние десятого элемента в списке — причины такого поведения описываются в главе 7). При запуске этот сценарий создает окно с 10 флажками, как показано на рис. 8.27.
Рис. 8.27. Окно флажков с изменением состояний, производимым вручную
Состояния флажков, поддерживаемые вручную, обновляются при каждом щелчке на флажках и выводятся при выходе из программы (формально, при возврате из вызова mainloop) — это список логических значений, которые можно было бы представить целыми числами 1 и 0, если бы потребовалось в точности имитировать оригинал:
C:\…\PP4E\Gui\Tour> python demo-check-manual.py
[False, False, True, False, True, False, False, False, True, False]
Такой способ действует, и его не столь уж трудно реализовать. Но связанные переменные tkinter заметно упрощают эту задачу, особенно если до какого-то момента в будущем нет необходимости проверять состояния флажков. Это проиллюстрировано в примере 8.24.
Пример 8.24. PP4E\Gui\Tour\demo—check—auto.py
# проверка состояния флажков, простой способ
from tkinter import *
root = Tk() states = [] for i in range(10): var = IntVar() chk = Checkbutton(root, text=str(i), variable=var) chk.pack(side=LEFT) states.append(var)
root.mainloop() # пусть следит библиотека tkinter
print([var.get() for var in states]) # вывести все состояния при выходе
# (можно также реализовать с помощью
# функции map и lambda-выражение)
Этот сценарий выводит такое же окно и действует точно так же, но здесь мы не передаем обработчики в параметре command, потому что библиотека tkinter автоматически отслеживает изменение состояний:
C:\…\PP4E\Gui\Tour> python demo-check-auto.py [0, 0, 1, 1, 0, 0, 1, 0, 0, 1]
Смысл здесь в том, что необязательно связывать переменные с флажками, но если сделать это, то работать с графическим интерфейсом будет проще. Между прочим, генератор списков в самом конце этого сценария является эквивалентом следующим вызовам функции map со связанным методом или lambda-выражением в качестве аргумента:
print(list(map(IntVar.get, states)))
print(list(map(lambda var: var.get(), states)))
Хотя генераторы списков получили большое распространение в настоящее время, тем не менее то, какая форма наиболее понятна вам, может заметно зависеть от вашего… размера обуви.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011