Занятие 6-7: Графический интерфейс.
Содержание
Понятия.
root – корневое окошко.
event – это какое-нибудь событие. Такое, как, например, закрытие окошка, движение мыши над окошком, нажатие на клавишу или на кнопку мыши и т.п.
mainloop – основной цикл программы. В основном цикле программа периодически обращается к операционной системе по мере её выполнения, чтобы узнать не произошли ли какие-нибудь изменения, чтобы среагировать на них должным образом. Например, когда меняется размер окошка, какие-то элементы нужно растянуть (или сжать), какие-то сдвинуть.
widget ("фитюлька") – любой элемент интерфейса. Например, кнопочка, меню, значки, поле ввода и т. д.
Для языка программирования Python такие виджеты включены в специальную библиотеку — Tkinter. Если ее импортировать в программу (скрипт), то можно пользоваться ее компонентами, создавая графический интерфейс.
Шаг первый: минимальная графическая программка.
У нас появляется пустое окошко. Мы можем менять его размеры и закрывать его.
Шаг второй.
Каждый widget в Tkinter – это питонский класс.
1 hello=Tkinter.Button(root, text="Hello") #создали объект класса Button
Указывая root первым аргументом Button, мы определяем внутри какого виджета должен появиться этот элемент. Это же верно и для всех типов виджетов в Tkinter.
Но эта кнопка ещё не появится, потому что неизвестно где ей нужно появиться!
Этим занимается Geometry Manager. В Tkinter есть три типа Geometry manager: place, pack, grid. place – ограниченный и неудобный. Чаще всего используется pack (он самый простой) или grid (он самый универсальный). Про них позже.
Пока отобразим кнопочку с помощью метода pack:
Всё, что будет написано после mainloop, будет исполняться после закрытия этого окошка. Как правило, нам это не нужно, поэтому вызов mainloop оказывается последней командой в программе.
Теперь соберём новые команды в единую программу и получим следующее:
Программа 1.
Эта программа покажет нам следующее: возникнет окошко с названием: "Hello, world" и кнопочкой "Hello" наверху посередине. Сколько бы мы ни нажимали на кнопку, ничего не будет происходить.
Шаг третий.
Но теперь мы хотим, чтобы при нажатии на кнопку что-то происходило. Когда мы создаём кнопку, мы можем передать ей через именованный параметр command действие, которое нужно совершить, когда пользователь нажмёт на кнопку.
Теперь созданная кнопочка hello будет перекрашиваться в зелёный цвет при нажатии на неё.
Пример:
Вопрос на засыпку: что окажется в переменной b, что окажется в переменной c и что напечатается на экране?
Программа 2.
Мы хотим написать программку, которая создала бы три кнопочки. Каждой из них присвоен был бы свой цвет при нажатии. Можно было бы написать для каждой кнопки отдельную функцию-обработчик, которая красила бы кнопочку именно в свой цвет. Но это были бы три почти одинаковые функции (а настоящие программисты не любят повторяться). Следовательно, нужно придумать способ, с помощью которого, функция-обработчик получала бы именно её данные. Специального способа в Tkinter не существует.
Применим здесь такой трюк: мы создадим для каждой кнопки объект, в который положим нужные нам даанные (цвет, в который нужно перекрашивать кнопку, и саму кнопку, которую нужно перекрашивать), и передадим в качестве обработчика нажатия кнопки метод этого объекта. Например, так:
1 import Tkinter
2
3 class Colorize(object):
4 def __init__(self, color):
5 self.color = color
6 self.button = Tkinter.Button(root, text=color, command=self.invoke)
7 self.button.pack()
8 def invoke(self):
9 self.button.config(background=self.color)
10 def __repr__(self):
11 return "Colorize(%r)" % (self.color)
12
13 root = Tkinter.Tk()
14 root.title("Colors")
15
16 Colorize("red")
17 Colorize("green")
18 Colorize("blue")
19
20 root.mainloop()
Когда мы передаём конструктору кнопки self.invoke, мы сообщаем ему не только то, что нужно будет вызвать метод invoke класса Colorize, но и говорим ему, какой именно объект нужно будет передать ему в качестве self.
Заодно мы продемонстрировали, какие ещё действия может оказаться полезным выполнить в конструкторе класса: например, создать имеющий отношение к нему виджет и упаковать его.
Графический элемент Canvas.
Canvas (холст) — это достаточно сложный объект библиотеки tkinter.
Он позволяет располагать на самом себе графические примитивы. Это могут быть как геометрические фигуры, узоры, вставленные изображения, так и другие виджеты (например, метки, кнопки, текстовые поля).
И это еще не все. Отображенные на холсте объекты можно изменять и перемещать (при желании) в процессе выполнения скрипта.
Учитывая все это, canvas находит широкое применение: создание рисунков, программируемая анимация и др.
Перед тем как создавать геометрические фигуры на холсте следует разобраться с координатами и единицами измерения расстояния.
Нулевая точка (0,0) для объекта Canvas располагается в верхнем левом углу.
У любой точки первое число — это расстояние от нулевого значения по оси X, второе — по оси Y.
Прямая. Стрелка.
1 canv = Canvas(root, width=500,height=500,bg="lightblue", cursor="pencil") #создаём объект-холст
2 canv.pack() #разместим на главном окне
3 canv.create_line(200,50,300,50,width=3,fill="blue") #линия начинается из точки (200,50), а заканчивается в точке (300,50).Свойство fill позволяет задать цвет линии отличный от черного
4 canv.create_line(0,0,100,100,width=2,arrow=LAST) #линия начинается в точке (0,0), заканчивается — в (100,100). arrow – установливает стрелку (в конце, начале или по обоим концам линии).
Прямоугольник.
Метод create_rectangle создает прямоугольник. Аналогично линии в скобках первыми аргументами прописываются четыре числа.
Первые две координаты обозначают верхний левый угол прямоугольника, вторые — правый нижний.
В примере ниже используется немного иной подход.
Он может быть полезен, если начальные координаты объекта могут изменяться, а его размер строго регламентирован.
Опция outline определяет цвет границы прямоугольника.
Чтобы создать произвольный многоугольник, требуется задать пары координат для каждой его точки.
1 canv.create_polygon([250,100],[200,150],[300,150],fill="yellow")
Квадратные скобки при задании координат используются для удобочитаемости (их можно не использовать).
Эллипс.
При создании эллипса задаются координаты гипотетического прямоугольника, описывающего данный эллипс.
1 canv.create_oval([20,200],[150,300],fill="gray50")
Нарисовать точку - тот же овал!:-)
grid
Ещё один Geometry Manager - grid.
1 root = Tkinter.Tk()
2
3 Tkinter.Button(text="A").grid(row=1, column=1, sticky="news")
4 Tkinter.Button(text="B").grid(row=1, column=2, sticky="news")
5 Tkinter.Button(text="C").grid(row=2, column=1, sticky="news")
6 Tkinter.Button(text="D").grid(row=2, column=2, sticky="news")
7 Tkinter.Button(text="E").grid(row=1, column=3, sticky="news", rowspan=2)
8 root.grid_rowconfigure(1, weight=1)
9 root.grid_rowconfigure(2, weight=1)
10
11 root.mainloop()
Geometry Manager grid располагает виджеты в таблице. Мы задаём для каждого виджета, в какой ячейке он находится (и вполне аналогично html можем задавать, что виджет занимает несколько ячеек). Параметр sticky может состоять из букв n, s, e, w (в соответствии со сторонами света: север, юг, восток, запад) и говорит, к каким из сторон ячейки виджет "прилипает". (Если виджет прилипает к двум противоположным сторонам ячейки, то он растягивается, чтобы занять всю ячейку). По умолчанию виджеты, упакованные grid, не изменяют размера, когда меняется размер родителя (виджета, в котором они упакованы). Если мы хотим, чтобы они меняли размер, мы должны сказать, в какой пропорции изменение размера родителя по данной координате распределяется между ячейками таблицы. Это мы можем сказать с помощью методов grid_rowconfigure и grid_columnconfigure.
Программа 4.
Следующая программа создаёт окошко, в котором по центру располагается большая кнопка,
а ниже неё есть две кнопки "А" и "В", соотвественно "А" - слева, "В" - справа.
1 import Tkinter
2 root=Tkinter.Tk()
3 canvas=Tkinter.Canvas(root, width=400, weight=400)
4 canvas.pack(side="top", fill="both", expand="yes")
5 button=Thinker.Frame(root)
6 button.pack(fill="x")
7 a=Tkinter.Button(buttons, text="A")
8 a.pack(side="left",fill="x",expand="yes")
9 b=Thinker.Button(buttons, text="B")
10 b.pack(sight="right", fill="x", expand="yes")
11 root.mainloop()
fill – позволяет растягивать нам виджет изначально, чтобы он заполнил всё доступное пространство в родителе.
expand – позволяет растягивать виджет в соответствии с родителем. Растягиваешь окошко – растягивается виджет.
Классы виджетов
Toplevel
- Окно верхнего уровня (корневой виджет)
Button
- Кнопка. Простая кнопка для выполнения команды и других действий
Canvas
- Рисунок. Может использоваться для вывода графических примитивов, например, для построения графиков
Checkbutton
- Флажок. Кнопка, имеющая два состояния, при нажатии изменяет состояние на противоположное
Entry
- Поле ввода текста
Frame
- Рамка. Содержит в себе другие визуальные компоненты.
Этот виджет, мы его не видим,но он позволяет в себя вмещать другие виджеты, и использовать другие Geometry Manager.
Label
- Этикетка. Показывает некоторый текст или графическое изображение
Listbox
- Список. Показывает список, из которых пользователь может выделить один или несколько элементов
Menu
- Меню. Служит для организации всплывающих (popup) и ниспадающих (pulldown) меню
Menubutton
- Кнопка-меню. Используется для организации pulldown-меню
Message
- Сообщение. Аналогично Label, но позволяет заворачивать длинные строки и легко меняет свой размер
Radiobutton
- Переключатель. Представляет одно из альтернативных значений некоторой переменной. Обычно действует в группе.
При нажатии на одну из кнопок, все остальные кнопки автоматически "отскакивают", совсем как кнопки выбора волны на радиоприемнике
Scale
- Шкала. Позволяет задать числовое значение путем перемещения движка
Scrollbar
- Полоса прокрутки. Может использоваться вместе с некоторыми другими компонентами для их прокрутки
Text
- Форматированный текст. Позволяет показывать, редактировать и форматировать текст с использованием различных стилей, а также внедрять в текст рисунки и окна.
Анимация.
Обращаю внимание, что для того, чтобы слово "Hello" печаталось в этой программке через каждые 100 мс, необходимо лишь дважды прописать эту строку, при этом никакие циклы и ещё более хитроумные структуры не требуются!