Окно приложения Tk, виджеты Label, Button, Message, PhotoImage, Frame, Toplevel, Canvas, окна диалога.
По умолчанию, в программах с библиотекой tkinter создаётся объект класса Tk. Объект Tk представляет главное («корневое») окно программы – которое открывается вместе с запуском программы. Остальные виджеты являются дочерними к Tk.
from tkinter import * # Импорт всех классов из библиотеки tkinter
tk = Tk() # Создание главного окна приложения
mainloop() # Цикл ожидания событий
Если в Вашем графическом приложении создаются виджеты, явно объект Tk создавать не обязательно.
Label - этикетки
from tkinter import *
Label(None, text='Привет Python tkinter').pack() # Создать и разместить виджет Label
mainloop()
или так:
from tkinter import *
Label(None, {'text': 'Привет Python tkinter', Pack: {'side': 'top'}}).mainloop()
Первый аргумент в конструкторе Label определяет объект родительского виджета, к которому нужно прикрепить новую метку. В данном случае, None означает – прикрепить новый виджет Label к главному окну программы (по умолчанию tk).
Message
Message служит для вывода текста. Он может автоматически и гибко разбивать длинные строки. Обладает большим количеством настраиваемых параметров.
from tkinter import *
msg = Message(text="Oh by the way, which one’s Pink?")
msg.config(bg='pink', font=('times', 16, 'italic'))
msg.pack(fill=X, expand=YES)
mainloop()
Button – кнопки
from tkinter import *
Button(None, text='Привет Python tkinter', command=exit).pack() # Создать и разместить виджет Button
mainloop()
PhotoImage – Объект с изображением
В библиотеке tkinter кнопки, метки, холсты, текстовые виджеты и меню могут выводить изображения путём передачи графических объектов атрибуту image. Виджет PhotoImage поддерживает файлы форматов GIF, PPM и PGM.
from tkinter import *
Label(text='Картинка на кнопке').pack()
img = PhotoImage(file='PenguinLinux.png')
Button(image=img).pack()
print('Картинка : Ширина %(1)s Высота %(2)s' % {'1':img.width(), '2':img.height()})
mainloop()
Картинка : Ширина 100 Высота 100
>>>
Размещение виджетов
Размещение виджетов в tkinter возможно методами pack(), grid() и placer().
from tkinter import *
lab1 = Label(text='Hello GUI world!') # Создать виджет Label
lab1.pack(expand=YES, fill=BOTH) # Разместить виджет Label
mainloop()
side, параметр метода pack позволяет прикреплять виджет к ближайшему объекту слева LEFT, справа RIGHT, сверху TOP, снизу BOTTOM. Прикрепляясь, виджет занимает с этой стороны всё свободное место, от края и до края.
expand=YES, параметр метода pack предлагает менеджеру компоновки выделять виджету все свободное пространство в родительском контейнере, не занятое другими виджетами.
fill, параметр метода pack используется для растяжения виджета, значение BOTH чтобы он занял все выделенное ему пространство, значение X – растянуть по горизонтали, значение Y – растянуть по вертикали.
anchor, параметр метода pack позволяет располагать виджет в одном из 8 желательных направлений в отведённом с помощью параметра side пространстве, если виджет не занимает в этом пространстве всё свободное место. Параметр anchor может принимать значения E, NE, N, NW, W, SW, S, SE и CENTER по умолчанию.
from tkinter import *
Button(text='Ok').pack(expand=YES, anchor=SE) # Выделить для Button всё родительское окно
mainloop()
Frame
Для размещения виджетов удобно использовать фреймы Frame. Frame – виджет, который может быть не видимым и сожет служить контейнером для других виджетов.
from tkinter import *
Frm1 = Frame()
Frm1.pack(expand=YES, fill=BOTH)
Frm2 = Frame()
Frm2.pack(expand=YES, fill=BOTH)
Frm3 = Frame()
Frm3.pack(expand=YES, fill=BOTH)
btn0 = Button(Frm1, text=' ')
btn1 = Button(Frm1, text=' ')
btn2 = Button(Frm1, text=' ')
btn3 = Button(Frm2, text=' ')
btn4 = Button(Frm2, text=' ')
btn5 = Button(Frm2, text=' ')
btn6 = Button(Frm3, text=' ')
btn7 = Button(Frm3, text=' ')
btn8 = Button(Frm3, text=' ')
btn0.pack(side=LEFT, expand=YES, fill=BOTH)
btn1.pack(side=LEFT, expand=YES, fill=BOTH)
btn2.pack(side=LEFT, expand=YES, fill=BOTH)
btn3.pack(side=LEFT, expand=YES, fill=BOTH)
btn4.pack(side=LEFT, expand=YES, fill=BOTH)
btn5.pack(side=LEFT, expand=YES, fill=BOTH)
btn6.pack(side=LEFT, expand=YES, fill=BOTH)
btn7.pack(side=LEFT, expand=YES, fill=BOTH)
btn8.pack(side=LEFT, expand=YES, fill=BOTH)
mainloop()
Toplevel
Toplevel создаёт дополнительные окна и служит для организации многооконного интерфейса.
from tkinter import *
Label(text='Главное окно').pack()
for i in range(0,9):
Toplevel()
mainloop()
Окна Toplevel независимы друг от друга но закрываются все при закрытии главного окна.
Одно из окон можно сделать модальным следующими методами:
from tkinter import *
def clck():
tplvl = Toplevel()
tplvl.focus_set() # Захват фокуса
tplvl.grab_set() # Блокирование доступа к другим окнам
tplvl.wait_window() # Приостановка вызвавшей программы
tk = Tk()
Label(tk, text='Главное окно').pack()
Button(tk, text='Создать окно', command=clck).pack()
tk.mainloop()
Вы можете таким же образом создать несколько окон Tk, каждое из которых можно будет закрывать независимо от других окон.
from tkinter import *
tk = []
for i in range(0,9):
tk.append(Tk())
tk[i].title('Окно №'+str(i))
Label(tk[i],text='Это ткое же Главное окно').pack()
mainloop()
Окна стандартного диалога
from tkinter import *
from tkinter.messagebox import *
from tkinter.simpledialog import *
from tkinter.filedialog import *
from tkinter.colorchooser import *
Directory = askdirectory() # Выбор каталога
youFl = askopenfilename() # Открытие файла
askcolor() # Выбор цвета
askyesno('Проверка', 'Вы хотите выйти?') # Проверка(?) Да / Нет
youCr = askfloat('Для выхода', 'Введите номер кредитной карты') # Ввод десятичного числа
showwarning('Предупреждение', youFl+'\nфайл удалён') # Предупреждение(!) OK
showinfo('Info', 'Оплата картой\n'+str(youCr)+'\nуспешно') # Информация(i) OK
youPh = askinteger('Когда надо, мы Вам позвоним', 'Введите номер телефона без пробелов')
youNm = askstring('Срочно', 'Введите ваше имя')
showerror('Ошибка', youNm + ', извините\n'+str(youPh)+'\nномер забокирован!') # Ошибка(—) OK
mainloop()
Dialog
from tkinter import *
from tkinter.dialog import *
Dialog(title = 'Окно диалога',
text = 'Вы уверены?',
bitmap = 'questhead',
default = 3,
strings = ('Да', 'Нет', 'Не совсем', 'Может быть', 'Другое'))
mainloop()
Изменение параметров виджета
Изменение параметров виджета, чаще всего делают, после его создания с помощью метода config().
from tkinter import *
tk = Tk()
tk.geometry('450x100')
lbl = Label(tk, text='Применение метода config()')
lbl.config(bg='black', fg='yellow') # желтый текст на черном фоне
lbl.config(font=('times', 20, 'bold italic')) # семейство, размер, стиль шрифта
lbl.config(height=3, width=20) # размер метки (строк,символов)
lbl.config(bd=8, relief=RIDGE) # размер и стиль рамки
lbl.config(cursor='watch') # курсор
lbl.config(padx=20, pady=10) # отступы внутри виджета
lbl.pack(expand=YES, fill=BOTH, padx=20, pady=15) # отступы вокруг виджета
tk.mainloop()
Стиль шрифта может принимать значения normal, bold, roman, italic, underline, overstrike и их сочетания.
Стиль рамки может принимать значения FLAT, SUNKEN, RAISED, GROOVE, SOLID или RIDGE.
Некоторые виджеты поддерживают понятие состояния, влияющее на их внешний вид. Параметр state=DISABLED обычно выглядит серым и делается неактивным. Значение NORMAL делает его обычным. Некоторые виджеты поддерживают также состояние READONLY.
Вокруг многих виджетов (кнопок, меток и текста) можно добавить отступы padx=N и pady=N. Интересно, что эти параметры можно определять и в вызовах метода pack(). В одном случае создаётся отступ, а во втором — набивка.
Обработчики событий
Обработчики событий виджета создаются как функции а затем регистрируются в виджете как свойство command.
from tkinter import *
n = 0
def clickBtn():
global n
n += 1
Lbl.config(text = n)
Button(None, text='Упражнение для пальца', command=clickBtn).pack() # Определить обработчик в Button
Lbl = Label(text=0)
Lbl.pack()
mainloop()
Функция обработчика определяется в свойстве command без скобок и, соответственно, передать параметры в эту функцию невозможно. Это ограничение легко преодолевается за счёт использования глобальных переменных.
lambda функция в обработчике событий
from tkinter import *
from tkinter import messagebox
Button(None, text='Привет Python tkinter', command=(lambda:print('Game over') or messagebox.askyesno('Game', 'New game?'))).pack()
mainloop()
lambda функция позволяет передавать параметры в обработчик событий, а оператор or дал нам возможность выполнить две функции одной строкой.
Game over
>>>
from tkinter import *
step = True
Frm = [3]
Btn = [9]
def clck(i):
global step
if step:
Btn[i].config(text="X")
else:
Btn[i].config(text="O")
step = not step
tk = Tk()
tk.geometry('300x300')
for j in range(1,4):
Frm.append(Frame())
Frm[j].pack(expand=YES, fill=BOTH)
for i in range(1,10):
Btn.append(Button(Frm[(i-1)//3+1],text=' ', command = (lambda i=i: clck(i))))
Btn[i].pack(side=LEFT, expand=YES, fill=BOTH)
mainloop()
lambda функция позволяет сохранить параметр существовавший на этапе создания виджета, а на этапе выполнения программы передать это значение в обработчик события. В примере выше, на этапе выполнения программы счётчик цикла for переменная i = 9, но lambda функция будет передавать в обработчик clck() то значение i, которое было на этапе создания Btn.
bind обработчики событий
bind обработчики событий виджета создаются как функции с параметром event а затем регистрируются в методах bind как второй параметр. Первый параметр метода bind – это идентификатор события, например:
- <Button-1> щелчок левой кнопкой мыши
- <Button-3> щелчок правой кнопкой мыши
- <Button-2> щелчок средней кнопкой мыши (может быть двумя кнопками)
- <Double-1> двойной щелчок левой кнопкой мыши
- <B1-Motion> перетаскивание левой кнопкой мыши
- <KeyPress> нажатие клавиши на клавиатуре
- <Up> нажатие клавиши со стрелкой вверх на клавиатуре
- <Down>
- <Left>
- <Right>
- <Return> нажатие клавиши Enter на клавиатуре
from tkinter import *
def hello(event):
print('Двойной щелчок - ВЫХОД') # одиночный щелчок левой кнопкой
def by(event): # двойной щелчок левой кнопкой
print('Приложение закрыто') # event дает виджету, координаты и т.д.
exit()
btn = Button(None, text='Одинарный и двойной щелчок')
btn.pack()
btn.bind('<Button-1>', hello) # привязать обработчик щелчка
btn.bind('<Double-1>', by) # привязать обработчик двойного щелчка
mainloop()
Двойной щелчок - ВЫХОД
Двойной щелчок - ВЫХОД
Двойной щелчок - ВЫХОД
Приложение закрыто
>>>
И ещё несколько идентификаторов событий, которые можно использовать с методом bind():
- <ButtonRelease>
- <ButtonPress>
- <Motion>
- <Enter>
- <Leave>
- <Configure>
- <Destroy>
- <FocusIn>
- <FocusOut>
- <Map>
- <Unmap>
- <Escape>
- <BackSpace>
- <Tab>
- <KeyPress-a>
Entry – поле ввода
from tkinter import *
def fetch():
print('Ввод = "%s"' % ent.get()) # извлечь строку
ent.delete(0, END) # очистить поле ввода
tk = Tk()
ent = Entry(tk)
ent.insert(0, 'Ввод текста') # записать строку начиная с 0 позиции в поле ввода
ent.pack(side=TOP, fill=X)
ent.focus() # передать фокус в поле ввода
ent.bind('<Return>', (lambda event: fetch())) # завершение ввода клавишей Enter
Button(tk, text='OK', command=fetch).pack(side=BOTTOM)
tk.mainloop()
В этом примере поле ввода получает фокус и выводит текст в командную строку после нажатия клавиши Enter или по нажатию кнопки "OK".
Ввод = "Ввод текста"
Ввод = "dfgwhm"
>>>
С полем Entry можно связать следующие классы из библиотеки tkinter:
- StringVar
- IntVar
- DoubleVar
- BooleanVar
После того как переменные будут связаны, операции изменения и получения значения переменной поля Entry можно заменить на те же методы set() и get(), применяя их к объектам класса StringVar, IntVar, DoubleVar или BooleanVar. Замечание, объекты класса StringVar, IntVar, DoubleVar или BooleanVar необходимо создавать после того, как уже создано главное окно программы (root) методом Tk() явно или неявно.
from tkinter import *
Label(None, text='Введите номер телефона в международном формате').pack()
var = StringVar()
ent = Entry()
ent.config(textvariable=var)
ent.bind('<Return>', (lambda event : print('Ваш номер %s ?' % var.get())))
ent.pack()
Button(text='Подсказка', command=(lambda : var.set('+7 9XX XXX-XX-XX'))).pack()
mainloop()
Ваш номер +7 9XX XXX-XX-55 ?
>>>
Checkbutton – переключатель
Checkbutton реализует интерфейс с выбором одного из 2-х состояний виджета.
from tkinter import *
Label(text='Выберите что нибудь').pack()
var1 = BooleanVar()
var2 = BooleanVar()
Checkbutton(text='Это', variable = var1, command=(lambda:print('изменилось Это, var = %s' % var1.get()))).pack(side=LEFT, expand=YES, anchor=NE)
Checkbutton(text='То', variable = var2, command=(lambda:print('изменилось То, var = %s' % var2.get()))).pack(side=LEFT, expand=YES, anchor=NW)
mainloop()
изменилось Это, var = True
изменилось То, var = True
изменилось То, var = False
>>>
Radiobutton – группа переключателей
Radiobutton реализует модель выбора единственного из нескольких взаимоисключающих вариантов. Атрибут variable у Radiobutton служит для связывания кнопок в группу с помощью переменной и получения текущего выбора в произвольный момент времени. Атрибут value, присваивается ассоциированной переменной группы когда производится выбор кнопки.
from tkinter import *
def prnt():
print('Вы выбрали %s' % var.get())
Label(text='Выберите что-то одно:').pack()
var = StringVar()
Radiobutton(text='Это?', variable = var, value='Это', command=prnt).pack(anchor=NW)
Radiobutton(text='Или это?', variable = var, value='Или это', command=prnt).pack(anchor=NW)
Radiobutton(text='Может то?', variable = var, value='Может то', command=prnt).pack(anchor=NW)
var.set('Или это')
mainloop()
Вы выбрали Это
Вы выбрали Может то
Вы выбрали Или это
>>>
Scale – ползунок
Виджет Scale позволяет реализовать интерфейс выбора целого числа в определённом диапазоне с помощью мышки. Делается это путём перетаскивания ползунка.
from tkinter import *
scl = Scale(orient='horizontal',
label='Выберите число в диапазоне от -30 до 30 кратное 3, или 0',
from_=-30, to=30,
tickinterval=6,
resolution=3,
length=600,
showvalue=YES,
command=lambda event: print(event)
)
scl.pack()
Button(text='Выбрано значение', command=lambda: print(scl.get())).pack()
mainloop()
-3
-6
-9
-9
>>>
Canvas – Холст
from tkinter import *
Label(text='Картинка на ходсте').pack()
png = PhotoImage(file='PenguinLinux.png')
can = Canvas()
can.pack(fill=BOTH)
can.create_image(20, 20, image=png, anchor=NW) # координаты x, y
mainloop()
Создание класса виджета
from tkinter import *
class MyButton(Button):
def __init__(self, parent=None, **config):
Button.__init__(self, parent, **config)
self.pack()
self.config(command=self.callback)
def callback(self):
print('Goodbye')
quit()
MyButton(text='MyButton')
mainloop()
Goodbye
Литература:
Марк Лутц. Программирование на Python.
Олег Цилюрик. Разработка GUI-приложений.
Яндекс. Обучение Python GUI уроки по Tkinter.