Radiobutton и Checkbutton. Переменные Tkinter

В Tkinter от класса Radiobutton создаются радиокнопки, от класса Checkbutton – флажки.

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

Экземпляры Checkbutton также могут быть визуально оформлены в группу, но каждый флажок независим от остальных. Каждый может быть в состоянии "установлен" или "снят", независимо от состояний других флажков. Другими словами, в группе Checkbutton можно сделать множественный выбор, в группе Radiobutton – нет.

Radiobutton – радиокнопка

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

Эти переключатели никак не связаны друг с другом. Кроме того для них не указано исходное значение, должны ли они быть в состоянии "вкл" или "выкл". По-умолчанию они включены.

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

В Tkinter нельзя использовать любую переменную для хранения состояний виджетов. Для этих целей предусмотрены специальные классы-переменные пакета tkinterBooleanVar, IntVar, DoubleVar, StringVar. Первый класс позволяет принимать своим экземплярам только булевы значения (0 или 1 и True или False), второй – целые, третий – дробные, четвертый – строковые.

r_var = BooleanVar()
r_var.set(0)
r1 = Radiobutton(text='First',
                 variable=r_var, value=0)
r2 = Radiobutton(text='Second',
                 variable=r_var, value=1)

Здесь переменной r_var присваивается объект типа BooleanVar. С помощью метода set он устанавливается в значение 0.

При запуске программы включенной окажется первая радиокнопка, так как значение ее опции value совпадает с текущим значением переменной r_var. Если кликнуть по второй радиокнопке, то она включится, а первая выключится. При этом значение r_var станет равным 1.

В программном коде обычно требуется "снять" данные о том, какая из двух кнопок включена. Делается это с помощью метода get экземпляров переменных Tkinter.

from tkinter import *
 
 
def change():
    if var.get() == 0:
        label['bg'] = 'red'
    elif var.get() == 1:
        label['bg'] = 'green'
    elif var.get() == 2:
        label['bg'] = 'blue'
 
 
root = Tk()
 
var = IntVar()
var.set(0)
red = Radiobutton(text="Red",
                  variable=var, value=0)
green = Radiobutton(text="Green",
                    variable=var, value=1)
blue = Radiobutton(text="Blue",
                   variable=var, value=2)
button = Button(text="Изменить",
                command=change)
label = Label(width=20, height=10)
red.pack()
green.pack()
blue.pack()
button.pack()
label.pack()
 
root.mainloop()
 

В функции change в зависимости от считанного значения переменной var ход выполнения программы идет по одной из трех веток.

Мы можем избавиться от кнопки "Изменить", связав функцию change или любую другую со свойством command радиокнопок. При этом не обязательно, чтобы радиокнопки, объединенные в одну группу, вызывали одну и ту же функцию.

from tkinter import *
 
 
def red_label():
    label['bg'] = 'red'
 
 
def green_label():
    label['bg'] = 'green'
 
 
def blue_label():
    label['bg'] = 'blue'
 
 
root = Tk()
 
var = IntVar()
var.set(0)
Radiobutton(text="Red", command=red_label,
            variable=var, value=0).pack()
Radiobutton(text="Green", command=green_label,
            variable=var, value=1).pack()
Radiobutton(text="Blue", command=blue_label,
            variable=var, value=2).pack()
label = Label(width=20, height=10, bg='red')
label.pack()
 
root.mainloop()

Здесь метка будет менять цвет при клике по радиокнопкам.

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

from tkinter import *
 
 
def paint(color):
    label['bg'] = color
 
 
class RBColor:
    def __init__(self, color, val):
        Radiobutton(
            text=color.capitalize(),
            command=lambda i=color: paint(i),
            variable=var, value=val).pack()
 
 
root = Tk()
 
var = IntVar()
var.set(0)
RBColor('red', 0)
RBColor('green', 1)
RBColor('blue', 2)
label = Label(width=20, height=10, bg='red')
label.pack()
 
root.mainloop()

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

Checkbutton – флажок

Флажки не требуют установки между собой связи, поэтому может возникнуть вопрос, а нужны ли тут переменные Tkinter? Они нужны, чтобы снимать сведения о состоянии флажков. По значению связанной с Checkbutton переменной можно определить, установлен ли флажок или снят, что в свою очередь повлияет на ход выполнения программы.

У каждого флажка должна быть своя переменная Tkinter. Иначе при включении одного флажка, другой будет выключаться, так как значение общей tkinter-переменной изменится и не будет равно значению опции onvalue первого флажка.

from tkinter import *
root = Tk()
 
 
def show():
    s = f'{var1.get()}, ' \
        f'{var2.get()}'
    lab['text'] = s
 
 
frame = Frame()
frame.pack(side=LEFT)
 
var1 = BooleanVar()
var1.set(0)
c1 = Checkbutton(frame, text="First",
                 variable=var1,
                 onvalue=1, offvalue=0,
                 command=show)
c1.pack(anchor=W, padx=10)
 
var2 = IntVar()
var2.set(-1)
c2 = Checkbutton(frame, text="Second",
                 variable=var2,
                 onvalue=1, offvalue=0,
                 command=show)
c2.pack(anchor=W, padx=10)
 
lab = Label(width=25, height=5, bg="lightblue")
lab.pack(side=RIGHT)
 
root.mainloop()

С помощью опции onvalue устанавливается значение, которое принимает связанная переменная при включенном флажке. С помощью свойства offvalue – при выключенном. В данном случае оба флажка при запуске программы будут выключены, так как методом set были установлены отличные от onvalue значения.

Опцию offvalue можно не указывать. Однако при ее наличии можно отследить, выключался ли флажок.

С помощью методов select и deselect флажков можно их программно включать и выключать. То же самое относится к радиокнопкам.

from tkinter import *
 
 
class CheckButton:
    def __init__(self, master, title):
        self.var = BooleanVar()
        self.var.set(0)
        self.title = title
        self.cb = Checkbutton(
            master, text=title, variable=self.var,
            onvalue=1, offvalue=0)
        self.cb.pack(side=LEFT)
 
 
def ch_on():
    for ch in checks:
        ch.cb.select()
 
 
def ch_off():
    for ch in checks:
        ch.cb.deselect()
 
 
root = Tk()
 
f1 = Frame()
f1.pack(padx=10, pady=10)
checks = []
for i in range(10):
    checks.append(CheckButton(f1, i))
 
f2 = Frame()
f2.pack()
button_on = Button(f2, text="Все ВКЛ",
                   command=ch_on)
button_on.pack(side=LEFT)
button_off = Button(f2, text="Все ВЫКЛ",
                    command=ch_off)
button_off.pack(side=LEFT)
 
root.mainloop()

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

Виджеты Radiobatton и Checkbutton поддерживают большинство свойств оформления внешнего вида, которые есть у других элементов графического интерфейса. При этом у Radiobutton есть особое свойство indicatoron. По-умолчанию он равен единице, в этом случае радиокнопка выглядит как нормальная радиокнопка. Однако если присвоить этой опции ноль, то виджет Radiobutton становится похожим на обычную кнопку по внешнему виду. Но не по смыслу.

Напишите программу, в которой имеется несколько объединенных в группу радиокнопок, индикатор которых выключен (indicatoron=0). Если какая-нибудь кнопка включается, то в метке должна отображаться соответствующая ей информация. Обычных кнопок в окне быть не должно.

Помните, что свойство command есть не только у виджетов класса Button.

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

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