Kodomo

Пользователь

Занятие 6-7: Графический интерфейс.

Понятия.

root – корневое окошко.

event – это какое-нибудь событие. Такое, как, например, закрытие окошка, движение мыши над окошком, нажатие на клавишу или на кнопку мыши и т.п.

mainloop – основной цикл программы. В основном цикле программа периодически обращается к операционной системе по мере её выполнения, чтобы узнать не произошли ли какие-нибудь изменения, чтобы среагировать на них должным образом. Например, когда меняется размер окошка, какие-то элементы нужно растянуть (или сжать), какие-то сдвинуть.

widget ("фитюлька") – любой элемент интерфейса. Например, кнопочка, меню, значки, поле ввода и т. д.

Для языка программирования Python такие виджеты включены в специальную библиотеку — Tkinter. Если ее импортировать в программу (скрипт), то можно пользоваться ее компонентами, создавая графический интерфейс.

Шаг первый: минимальная графическая программка.

   1 import Tkinter
   2 root=Tkinter.Tk()
   3 root.mainloop()

У нас появляется пустое окошко. Мы можем менять его размеры и закрывать его.

Шаг второй.

Каждый 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:

   1 hello.pack()
   2 root.mainloop()

Всё, что будет написано после mainloop, будет исполняться после закрытия этого окошка. Как правило, нам это не нужно, поэтому вызов mainloop оказывается последней командой в программе.

Теперь соберём новые команды в единую программу и получим следующее:

Программа 1.

   1 import Tkinter
   2 root=Tkinter.Tk()
   3 root.title('Hello,world')
   4 hello=Tkinter.Button(root, text="Hello")
   5 hello.pack()
   6 root.mainloop()

Эта программа покажет нам следующее: возникнет окошко с названием: "Hello, world" и кнопочкой "Hello" наверху посередине. Сколько бы мы ни нажимали на кнопку, ничего не будет происходить.

Шаг третий.

Но теперь мы хотим, чтобы при нажатии на кнопку что-то происходило. Когда мы создаём кнопку, мы можем передать ей через именованный параметр command действие, которое нужно совершить, когда пользователь нажмёт на кнопку.

   1 import Tkinter
   2 def engreen():
   3     hello.config(background="green")
   4 
   5 root=Tkinter.Tk()
   6 hello=Tkinter.Button(root, text="Hello", command=engreen)
   7 hello.pack()
   8 root.mainloop()

Теперь созданная кнопочка hello будет перекрашиваться в зелёный цвет при нажатии на неё.

Пример:

   1 def a():
   2     print "Hello"
   3     return "World"
   4 b=a    # переложили функцию
   5 b()
   6 c=a()  # положили результат функции

Вопрос на засыпку: что окажется в переменной 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 создает прямоугольник. Аналогично линии в скобках первыми аргументами прописываются четыре числа.

Первые две координаты обозначают верхний левый угол прямоугольника, вторые — правый нижний.

В примере ниже используется немного иной подход.

Он может быть полезен, если начальные координаты объекта могут изменяться, а его размер строго регламентирован.

   1 x = 75
   2 y = 110
   3 canv.create_rectangle(x,y,x+80,y+50,fill="white",outline="blue")

Опция 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

Menubutton

Message

Radiobutton

При нажатии на одну из кнопок, все остальные кнопки автоматически "отскакивают", совсем как кнопки выбора волны на радиоприемнике

Scale

Scrollbar

Text

Анимация.

   1 def f():
   2     print "Hello"
   3     root.after(100,f)
   4 root.after(100,f)
   5 root.mainloop()

Обращаю внимание, что для того, чтобы слово "Hello" печаталось в этой программке через каждые 100 мс, необходимо лишь дважды прописать эту строку, при этом никакие циклы и ещё более хитроумные структуры не требуются! :-)