Поля выбора значений / tkinter 3

341

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

Выбор числовых значений

В предыдущем материале речь шла о работе с вводом текста, но иногда есть необходимость ограничить поле для работы исключительно с числовыми значениями. Это нужно для классов Spinbox и Scale, которые позволяют пользователям выбирать числовое значение из диапазона или списка валидных вариантов. Однако есть отличия в том, как они отображаются и настраиваются.

У этой программы есть Spinbox и Scale для выбора целого числа от 0 до 5:

					

import tkinter as tk

class App(tk.Tk):
def __init__(self):
super().__init__()
self.spinbox = tk.Spinbox(self, from_=0, to=5)
self.scale = tk.Scale(self, from_=0, to=5,
orient=tk.HORIZONTAL)
self.btn = tk.Button(self, text="Вывести значения",
command=self.print_values)
self.spinbox.pack()
self.scale.pack()
self.btn.pack()

def print_values(self):
print("Spinbox: {}".format(self.spinbox.get()))
print("Scale: {}".format(self.scale.get()))

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

Для отладки также была добавлена кнопка, которая выводит значение при нажатии:

Выбор числовых значений

Как работает выбор значений

Оба класса принимают параметры from_ и to, которые обозначают диапазон подходящих значений — нижнее подчеркивание в конце является обязательным, потому что параметр from изначально определен в Tck/Tk, хотя является зарезервированным ключевым словом в Python.

Удобная особенность класса Scale — параметр resolution, который настраивает точность округления. Например, значение «0.2» позволит выбирать такие: 0.0, 0.2, 0.4 и так далее. По умолчанию установлено значение 1, поэтому виджет округляет все введенные числа до ближайшего целого.

Также значение каждого виджета можно получить с помощью метода get(). Важное отличие в том, что Spinbox возвращает число в виде строки, а Scale — целое число или число с плавающей точкой, если округление принимает десятичные значения.

Класс Spinbox имеет настройки, которые похожи на те, что есть у Entry: параметры textvariable и validate. Разница лишь в том, что правила будут ограничены числовыми значениями.

Создание полей с радиокнопками (переключателями)

С помощью виджета Radiobutton можно разрешить пользователю выбирать среди нескольких вариантов. Это работает для относительно небольшого количества взаимоисключающих вариантов.

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

В следующем примере создаются три кнопки для параметров Red, Green и Blue. При каждом нажатии выводится название соответствующего цвета в нижнем регистре:

					

import tkinter as tk

COLORS = [("Red", "red"), ("Green", "green"), ("Blue", "blue")]

class ChoiceApp(tk.Tk):
def __init__(self):
super().__init__()
self.var = tk.StringVar()
self.var.set("red")
self.buttons = [self.create_radio(c) for c in COLORS]
for button in self.buttons:
button.pack(anchor=tk.W, padx=10, pady=5)

def create_radio(self, option):
text, value = option
return tk.Radiobutton(self, text=text, value=value,
command=self.print_option,
variable=self.var)

def print_option(self):
print(self.var.get())

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

Если запустить скрипт, он покажет приложение, где вариант Red уже выбран.

Создание полей с радиокнопками

Как работают радиокнопки

Чтобы не повторять код для инициализации Radiobutton, нужно определить служебный метод, вызываемый из сгенерированного списка. Так, значения каждого кортежа в списке COLORS распаковываются, а локальные переменные передаются в качестве опций в Radiobutton. Очень важно при возможности избегать повторений.

Поскольку StringVar является общим для всех Radiobutton, они автоматически соединяются, а пользователь может выбрать лишь один вариант.

Значением по умолчанию в программе является «red». Но что произойдет, если эту строку пропустить, а значение StringVar не будет соответствовать значению ни одной из кнопок? В таком случае оно будет совпадать со значением по умолчанию опции tristatevalue, то есть, пустой строкой. Из-за этого виджет отображается в неопределенном режиме «tri-state». Это можно изменить с помощью метода config(), но еще лучше — задавать правильное значение по умолчанию, чтобы переменная инициализировалась в валидном состоянии.

Реализация чекбоксов

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

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

					

import tkinter as tk

class SwitchApp(tk.Tk):
def __init__(self):
super().__init__()
self.var = tk.IntVar()
self.cb = tk.Checkbutton(self, text="Активно?",
variable=self.var,
command=self.print_value)
self.cb.pack()

def print_value(self):
print(self.var.get())

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

В этом примере значение виджета просто выводится каждый раз при нажатии.

Реализация чекбоксов

Как работают чекбоксы

По аналогии с Button Checkbutton принимает параметры Command и text.

С помощью опций onvalue и offvalue можно определить значения для отмеченного и пустого чекбоксов. Используется целочисленная переменная, потому что значения по умолчанию — это 1 и 0. Но это могут быть любые другие целые числа.

С Checkbuttons можно использовать даже другие типы переменных:

					

var = tk.StringVar()
var.set("OFF")
checkbutton_active = tk.Checkbutton(master, text="Активно?", variable=self.var,
onvalue="ON", offvalue="OFF",
command=update_value)

Единственное ограничение в том, что onvalue и offvalue должны совпадать с типом переменной Tkinter. В таком случае, поскольку «ON» и «OFF» — это строки, то и переменная должна быть StringVar. В противном случае интерпретатор Tcl вернет ошибку при попытке задать соответствующее значение другому типу.

Отображение списка элементов

Виджет Listbox содержит текстовые элементы, которые пользователь может выбрать с помощью мыши или клавиатуры. Есть возможность настроить, будут ли для выбора доступны один или несколько элементов.

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

					

import tkinter as tk

DAYS = ["Понедельник", "Вторник", "Среда", "Четверг",
"Пятница", "Суббота", "Воскресенье"]
MODES = [tk.SINGLE, tk.BROWSE, tk.MULTIPLE, tk.EXTENDED]

class ListApp(tk.Tk):
def __init__(self):
super().__init__()
self.list = tk.Listbox(self)
self.list.insert(0, *DAYS)
self.print_btn = tk.Button(self, text="Вывести выбор",
command=self.print_selection)
self.btns = [self.create_btn(m) for m in MODES]

self.list.pack()
self.print_btn.pack(fill=tk.BOTH)
for btn in self.btns:
btn.pack(side=tk.LEFT)

def create_btn(self, mode):
cmd = lambda: self.list.config(selectmode=mode)
return tk.Button(self, command=cmd,
text=mode.capitalize())

def print_selection(self):
selection = self.list.curselection()
print([self.list.get(i) for i in selection])

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

Попробуйте менять режимы и смотреть на вывод:

Отображение списка элементов

Как работает выбор элементов из списка

Можно создать пустой объект Listbox и добавить все элементы с помощью метода insert(). Индекс 0 обозначает, что элементы должны добавляться в начале списка. В следующей строке список DAYS распаковывается, но отдельные элементы можно добавить в конец с помощью константы END:

self.list.insert(tk.END, "Новый пункт")

Текущая выборка извлекается с помощью метода curselection(). Он возвращает индексы выбранных элементов. А для последующей трансформации их в соответствующие текстовые элементы для каждого элемента в списке вызывается метод get(). В итоге список выводится в STDOUT для отладки.

В этом примере параметр selectmode можно изменить для получения разного поведения:

  • SINGLE — один вариант;
  • BROWSE — один вариант, который можно перемещать с помощью клавиш со стрелками;
  • MULTIPLE — несколько вариантов;
  • EXTENDED — несколько вариантов с диапазонами, которые выбираются кнопками Shift и Ctrl.

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

					

def __init__(self):
self.frame = tk.Frame(self)
self.scroll = tk.Scrollbar(self.frame, orient=tk.VERTICAL)
self.list = tk.Listbox(self.frame, yscrollcommand=self.scroll.set)
self.scroll.config(command=self.list.yview)
# ...
self.frame.pack()
self.list.pack(side=tk.LEFT)
self.scroll.pack(side=tk.LEFT, fill=tk.Y)

Также существует параметр xscrollcommand для горизонтальной оси.

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

Что выведет этот код?
Какой будет результат выполнения кода — print(type(1J)) ?
Что выведет этот код?
Если выполнить код ниже, каков будет результат?
Какой будет результат выполнения кода — print('Monty' + 'Python') ?
Александр
Я создал этот блог в 2018 году, чтобы распространять полезные учебные материалы, документации и уроки на русском. На сайте опубликовано множество статей по основам python и библиотекам, уроков для начинающих и примеров написания программ. Пишу на популярные темы: веб-разработка, работа с базами данных, data sciense и другие...