20-й час. Функции рисования библиотеки Tk, I

20-й час

Функции рисования библиотеки Tk, I

  В предыдущих двух главах Вы познакомились с основными графическими объектами библиотеки Tkinter. В этой главе мы продолжим тему использования базовых графических объектов при создании интерфейса GUI и перейдем к работе с функциями рисования Tkinter. Изучив материал данной главы, Вы сможете использовать новые типы меню (всплывающие и динамические); объединять списки и полосы прокрутки в функциональные конструкции; добавлять в объекты Tk готовые графические изображения; различать графику разных типов в объекте холст.

Новые меню

  В главе 18 Вы узнали об объекте меню, который является контейнером для объектов кнопок меню. Но мы познакомились лишь с двумя стандартными типами меню: сострокой меню (которая располагается в верхней части окон большинства приложений) и с меню опций (может появляться в любом месте окна приложения). Прежде чем перейти к работе с графикой, нам нужно рассмотреть следующие дополнительные типы меню:

  • всплывающее (или контекстное) меню, которое открывается после щелчка правой кнопки мыши;

  • динамическое меню с обновляемым набором опций.

  Всплывающие меню используются во многих приложениях. В листинге 20.1 показан пример кода такого меню.

Листинг 20.1. Программа tkpopup.py

from Tkinter import *
import string
import sys

def die(event=None):
  sys.exit(0)

def getxy(something):
  s=something.geometry()
  return map(int, string.splitfields(s[1+string.find(s,"+"):],'+'))

def new_file(event=None):
 
print "Opening new file"

def open_file(event=None):
 
print "Opening existing file"

def activate_menu(event=None):
  x, y = getxy(root)
  menu.tk_popup(x+event.x, y+event.y)

root = Tk()
root.canvas = Canvas(root, height=100, width=100)
root.canvas.pack()
menu = Menu(root)
menu.add_command(label="New...", underline=0, command=new_file)
menu.add_command(label="Open...", underline=0, command=open_file)
menu.add_separator()
menu.add_command(label="Exit...", underline=0, command=die)
menu['tearoff'] = 0

root.canvas.bind("<Button-3>", activate_menu)

root.mainloop()

  Пожалуй, только одна строка в этом листинге требует объяснения, поскольку со всеми остальными инструкциями кода мы уже встречались в предыдущих главах. Но выражение в строке 12 нарушает традиционную простоту кода и может вызвать у Вас затруднение. В строке 11 инструкция s=something.geometry() возвращает геометрические пропорции основного окна (root). Данный метод возвращает текстовую строку в формате "100x100+64+64"— стандартная запись, используемая в X Windows. Компонент 100x100 представляет ширину и высоту окна, в данном случае 100 на 100 пикселей. Компонент +64+64 указывает координаты х и у, опять-таки, в пикселях, от левого и верхнего краев экрана. Чтобы оперировать этими данными, необходимо преобразовать части строки в соответствующие числовые значения. Для показа всплывающего меню, что происходит в строке 22, все, что нам нужно, — это координаты х и у. Функция string.find() отыскивает в возвращенной строке первое вхождение символа +. Мы прибавляем к полученному значению единицу (поскольку искомое значение следует за символом +), после чего вырезаем последовательность символов от цифры за знаком + и до конца строки. В нашем примере будет возвращена строка 64+64 . Затем мы преобразуем полученную строку в список строк, вновь используя символ + в качестве разделителя. Таким образом, функция string.splitfields() возвратит значение [64, 64], которое передаётся в функцию mар(). Данная функция преобразовывает строки в соответствующие целочисленные значения с помощью вызова функции int(). И наконец, инструкция return принимает список целых чисел и возвращает его по месту вызова функции getxy(), где возвращённые значения присваиваются переменным х и у. К этим переменным прибавляются соответствующие значения event.х и event.у, в результате чего получаются экранные координаты точки щелчка мыши (нам нужны именно эти координаты, а не координаты окна root). Полученные значения используются в функции menu.tk_popup().

  Обычно во всплывающих меню не предлагаются такие опции, как "Закрыть...", так как во время работы со всплывающим меню гораздо проще допустить ошибку. Эта команда более характерна для основной строки меню, но в нашем простеньком примере, показанном на рис. 20.1, такая конструкция меню вполне допустима.

Рис. 20.1. Всплывающее меню, созданное программой tkpopup.py

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

Листинг 20.2. Программа tkcascadingmenu.py

from Tkinter import *
import os
import string

img=None

def die():
  sys.exit(0)

def listgifs(d="."):
  l=os.listdir(d)
  rl=[]
 
for i in l:
    t=string.lower(i)
    g=string.rfind(t,".gif")
   
if g>=0:
      rl.append(i)
 
if len(rl)<1:
    rl=None
 
else:
    rl.sort()
 
return rl

def setimage(s):
 
global elements
 
global lb
 
global img
 
if s in elements:
    img=PhotoImage(file=s)
    lb["image"]=img

def main():
 
global elements
 
global lb
  elements=listgifs()
 
if not elements:
   
print "No gifs"
  n=
len(elements)
  nm=n/10
  no=n%10
 
if no:
    nm=nm+1
 
print "For %d files, I'll make %d menus" % ( n, nm )
 

  root=Tk()
  mb=Menu(root)
  cb=Menu(mb)
  cb.add_command(label="Exit",command=die)

  gm=Menu(mb)
 
for i in range(nm):
    tm=Menu(gm)
   
if i==nm-1 and no!=0:
      lim=no
   
else:
      lim=10
   
for j in range(lim):
      ne=(10*i)+j
      tm.add_command(label=elements[ne],
        command=lambda m=elements[ne]:setimage(m))
      gm.add_cascade(label="List gifs %d" % (i),menu=tm)

  mb.add_cascade(label="File", menu=cb)
  mb.add_cascade(label="Gifs", menu=gm)

  lb=Label(root,text="No gif")
  lb.pack()
  root.config(menu=mb)
  root.mainloop()

if __name__ == "__main__":
  main()

  Функция listgifs(), показанная в строках 12-25, возвращает с помощью метода os.listdir() отсортированный список всех файлов GIF в текущем каталоге. Поскольку в Python отсутствует нечувствительная к регистру встроенная функция сравнения строк, нам пришлось создать собственную. С этой целью для каждого элемента списка, полученного с помощью функции listdir(), применяется метод string.lower(). Полученная строка из символов в нижнем регистре сравнивается с искомым суффиксом. В качестве альтернативы можно использовать модуль fnmatch. который предоставляет зависимые от платформы средства локализации файлов. Например, альтернативный код функции listgifs() показан в листинге 20.3.

Листинг 20.3. Использование модуля fnmatch в функции listgifs()

def listgifs(d="."):

  l=os.listdir(d)

  rl=[]

  for i in 1:

    if fnmatch.fnmatch(i,"*.gif"):

      rl.append(i)

    if len(rl)<1:

      rl=None

    else:

      r1.sort()

  return rl

  Вторым параметром в функцию f nmatch() передаются джокерные, или подстановочные, символы, обычные для системы UNIX. Аналогичные подстановочные символы знакомы пользователям системы DOS. В табл. 20.1 описывается назначение различных подстановочных символов, используемых в модуле fnmatch.

Таблица 20.1. Подстановочные символы системы UNIX

Символ Значение
* Любой набор символов или их отсутствие
; Любой одиночный символ
[abc] Любые символы, заключённые в квадратные скобки
[a-z] или [0-9] Любой одиночный символ, заключенный в заданном диапазоне
[!seg] Любой одиночный символ, не указанный в seq

Формат. GIF (Graphic Interchange Format — формат обмена графическими данными) изначально разрабатывался в качестве формата графических файлов для использования только в CompuServe. Но в настоящее время — это основной формат графики в Internet. Полемика вокруг использования данного формата возникала в связи с непредсказуемой политикой компании Unisys — владельца патента на метод сжатия данных LZW (Lempel-Ziv-Welch), используемого для эффективного уменьшения размеров графических файлов. Далее на примерах Вы узнаете больше о формате GIF и особенностях его использования. Учтите только, что Tkinter не поддерживает использование метода write() для файлов GIF.

  За использование файлов GIF в нашей программе отвечает функция setimage() - строки 26-32. Для считывания файлов и приведения изображений к формату, понятному для всех графических объектов Tkinter, используется встроенная функция Photolmage(). Переменная elements хранит список файлов GIF, обнаруженных в текущем каталоге. Данный список возвращается функцией listgifs(). Переменная lЬ представляет объект ярлыка. Обратите внимание, что переменная img, которая представляет графическое изображение, должна быть глобальной. В противном случае после завершения выполнения функции setimage() переменная испарится и графический объект, которому было передано изображение, потеряет его.

  В этой программе также продемонстрирован стиль программирования, альтернативный тому, которого мы придерживались раньше. Программы на языке С всегда содержат функцию main(), которая определяет точку начала выполнения программы при её запуске. В данном примере имитируется стиль языка С, хотя в действительности выполнение программы на языке Python принципиально отличается от языка С. В Python основная часть программы начинается за выражением if __name__== "__main__":. В листинге, показанном выше, в этом блоке просто происходит вызов функции main(), что некоторым образом имитирует язык С. Я предпочитаю не использовать этот стиль хотя бы потому, что Python действительно сильно отличается от С или PINC, но поскольку подобная запись может повстречаться Вам на практике, следует уметь в ней разобраться.

  В строках 37-50 создаётся список файлов GIF, который затем присваивается глобальной переменной, определяется число вложенных меню; после чего создаётся меню Файл. В строках 51-63 показан традиционный подход к конструированию вложенных меню: создаётся объект меню, добавляется 10 имён файлов GIF в качестве опций меню, после чего вызывается функция add_cascade(), которая добавляет объекты кнопок меню в объект-контейнер. После завершения цикла в строках 64, 65 вся конструкция вложенных меню добавляется в строку меню. Любопытное решение представлено в строках 60, 61, где создаётся объект кнопки меню, а в качестве функции обработки события назначаем анонимную функцию lambda.

  Нам нужно, чтобы при выборе пользователем опции меню открывался соответствующий список графических файлов. Было бы здорово, если бы объект кнопки меню был настолько умным, что мог отслеживать названия опций по мере их выбора. Но, к сожалению, этого делать он не умеет, следовательно, умными придётся быть нам. Мы не можем напрямую использовать функцию setimage(), так как в неё невозможно передать имя выбранной опции. Проблема с анонимной функцией lambda состоит в том, что в ней нельзя использовать оператор присвоения. Чтобы обойти эти ограничения, нам следует воспользоваться приёмом с аргументом, заданным по умолчанию. Рассмотрим отдельно выражение с инструкцией lambda:

lambda m=elements[ne]: setimage(m)

  На момент создания подменю имя файла изображения уже должно быть известно, и оно определяется как elements[nе]. Для передачи этого имени в lambda используется инструкция m=elements[ne], где переменной m присваивается действительное имя файла. Затем эта переменная передаётся в функцию setimage(). Дальше все очень просто. На рис. 20.2 показан результат выполнения программы tkcascadingmenu.py.

Рис. 20.2. Динамическое меню

  Примите к сведению, что функция PhotoImage() различает только два графических формата: GIF и РРМ. Пользователи Windows редко встречаются с файлами в формате РРМ, но они обычны для систем Linux/UNIX. Поэтому нашу программу можно усовершенствовать, чтобы она отображала не только файлы GIF, но и РРМ, если есть вероятность, что файлы этого формата могут быть представлены в каталогах предполагаемого пользователя.

Формат РРМ (Portable Pixmap — переносимый массив пикселей) был разработан несколько лет назад Джефом Посканзером (Jeff Poskanzer) в качестве низкоуровневого общего формата для всех цветных графических изображений. Предполагалось, что этот формат заменит собой устаревший формат РВМ (Portable Bitmap — переносимый массив битов), который поддерживал только черно-белые изображения. Данный формат не очень подходит для хранения изображений (хотя иногда используется в этих целях), поскольку он крайне не экономичен. Сохранение изображения в этом формате потребует значительно больше памяти по сравнению с традиционными графическими форматами. Чтобы ближе познакомиться с файлами РРМ, обратитесь к разделу "Примеры и задания" в конце этой главы.

  Любое меню может содержать подменю, причём нескольких уровней. Обычно создание многоуровневых меню — это не самое удачное решение по упрощению работы пользователя. Но иногда, при определённых обстоятельствах, создание подменю может оказаться весьма полезным, так как позволит сократить число опций меню. Слишком длинный список опций может раздражать пользователя не меньше, чем необходимость блуждать по опциям многоуровневого меню. Необходимость сокращения числа опций меню становится тем более очевидной, если список опций не умещается на экране целиком. Также следует учитывать, что разрешение экрана пользователя может быть меньше, чем у разработчика. Например, многие разработчики приложений устанавливают разрешение экрана 1280x1024. (Один мой друг предпочитает разрешение 1600x1280. Он говорит, что при таком разрешении со стороны трудно определить, работает он или спит за компьютером. Если бы существовала возможность установить ещё большее разрешение, не сомневаюсь, что он воспользовался бы этой возможностью.) В то же время у пользователя может быть установлено разрешение экрана 640x480, в результате чего меню, великолепно выглядевшие на экране разработчика, могут вылезти за экран пользователя.

Прокручиваемые списки

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

Листинг 20.4. Программа tkscrolledlistbox.py

from Tkinter import *
import sys
import os
import string

def setn(event):
 
global elements
 
global listbox
 
global lb
 
global img
  x=listbox.curselection()
  n=string.atoi(x[0])
  img=PhotoImage(file=elements[n])
  lb["image"]=img

def listgifs(d="."):
  l=os.listdir(d)
  rl=[]
 
for i in l:
    t=string.lower(i)
    g=string.rfind(t,".gif")
   
if g>=0:
      rl.append(i)
 
if len(rl)<1:
    rl=None
 
else:
    rl.sort()
 
return rl

def die(event):
  sys.exit(0)

root=Tk()
button=Button(root)
button["text"]="Quit"
button.bind("<Button>",die)
button.pack()
labelx=Label(root)
labelx["height"] = 1
labelx.pack()

elements=listgifs()

frame=Frame(root,bd=2,relief=SUNKEN)
frame.pack(expand=1,fill=BOTH)
scrollbar=Scrollbar(frame, orient=VERTICAL)
listbox=Listbox(frame,exportselection=0,height=10,
yscrollcommand=scrollbar.set)
listbox.bind("<Double-Button-1>",setn)
for i in elements:
  listbox.insert(END, i)
scrollbar.config(command=listbox.yview)
listbox.pack(side=LEFT)
scrollbar.pack(side=LEFT, fill=Y)
listbox.select_set(0)
listbox.see(0)
lb=Label(root,text="No gif")
lb.pack()

root.mainloop()

  В основу кода программы tkcascadingraenu.py были положены многие средства, с которыми мы уже познакомились в предыдущей программе. Функция listgifs() осталась той же, а функция setn() напоминает setimage(). Поскольку в этой программе мы обошлись без анонимной функции lambda, функцию setn() можно было вызывать напрямую. В строке 51 мы связали вызов этой функции с событием двойного щелчка на опции списка, в результате чего загружается новое изображение. Новые инструкции представлены в строках 49-59, где создаётся объект списка listbox. Свойству yscrollcommand этого объекта присваивается метод scrollbar.set(). В свою очередь, в строке 54 свойству command объекта полосы прокрутки scrollbar присваивается встроенный метод списка yview(). Этих двух условий достаточно для того, чтобы два базовых графических объекта, список и полоса прокрутки, работали) как одно целое. В строке 57 устанавливается выбор первого элемента списка по умолчанию, а в строке 58 — показ выбранного изображения на экране. Чтобы загрузить другое изображение, достаточно дважды щелкнуть мышью на имени файла в списке, как показано на рис. 20.3.

Рис. 20.3. Выполнение программы tkcascadingmenu.py

  Если Вы модифицировали предыдущую программу tkcascadingmenu.py для показа файлов РРМ, то сейчас можете просто перенести и вставить измененный код функции listgifs() в программу tkcascadingmenu.py.

Библиотека средств поддержания графики в Python

  Если Вы хотите использовать дополнительные форматы графических файлов, загрузите с Web-страницы (http://www.pythonware.com) и установите на свой компьютер приложение Python Imaging Library. Ниже приведена инструкция по инсталляции программы в Windows.

  1. Загрузите программный пакет, находящийся по адресу http://www.pythonware.com/.

  2. Разархивируйте содержимое в папку Temp. В этой папке Вы затем обнаружите новую папку с вложенными папками PIL, Scripts и файлом tkinter.dll.

  3. Скопируйте все файлы из папки PIL в папку Python\Lib на Вашем компьютере (например, в C:\Python\Lib).

  4. Скопируйте все файлы из папки Scripts в папку Python\Tools\Scripts.

  5. Скопируйте файл tkinter.dll в папку Python.

  После выполнения этой процедуры Вы получите возможность использовать другие графические форматы. Для установки программы на машинах с Linux или UNIX необходимо скомпилировать файлы в соответствии с инструкцией, прилагаемой к установочному пакету (в том же каталоге на ftp-сервере). После установки использование программы не представляет труда, как Вы в этом убедитесь на приведённых ниже примерах. Только в дополнение к модулю Tkinter в программу следует импортировать ещё два модуля: import Image, ImageTk. При таком импортировании вызовы функций img=Photolmage(...) следует заменить на img=ImageTk.Photolmage(...). Теперь Вы получили доступ к дополнительным графическим файлам. Пример такой программы приведён в листинге 20.5.

Листинг 20.5. Программа tkpil.py

  *Прим. В. Шипкова: этот файл всё равно работать не будет без указанной библиотеки, поэтому код программы не привожу, кому надо можете скачать файл.

  Почти всё в этой программе Вам уже знакомо. Код создания меню был перенесен в функцию buildmenu(). Для смены текущего каталога добавлены диалоговое окно открытия файла (функция ls() в строке 115) и ещё одно небольшое диалоговое окно, опредёленное в строках 47-67. Последнее диалоговое окно вызывается функцией cd() в строке 69, но все его свойства задаются в собственном коде, а именно в функции ok() в строках 57-67. Основная нагрузка ложится на строки 62—67, где происходит вызов функции os.chdir(), активизируется новый каталог, создаётся список файлов и т.д. Диалоговое окно для ввода пути к новому каталогу показано на рис. 20.4.

Рис. 20.4. Диалоговое окно Введите новый каталог приложения tkpil.py

  В меню "Файл" была добавлена опция "Сохранить как...", с помощью которой любое открытое изображение можно сохранить в формате РРМ. (Класс ImageTk предлагает для записи только этот формат. Другие форматы представлены в библиотеке PIL, но в этом случае следует использовать альтернативные пути загрузки файлов, описанные в документации, прилагаемой к PIL.) На рис. 20.5 показано диалоговое окно, открываемое функцией askopenfilename(). Аналогичное окно открывается также функцией asksaveasfilename().

Рис. 20.5. Диалоговое окно Открытие файла приложения tkpil.py

  *Прим. В. Шипкова: Это окно внешне отличается в Windows XP. Причина тому - изменившаяся системная библиотека, которая отвечает за прорисовку этого окна (хотя называется оно также).

  Если Вы всерьез хотите заняться созданием графического редактора, то установка Python Imaging Library не окажется для Вас пустой тратой времени. С помощью средств этой библиотеки Вы сможете создавать собственные графические изображения и сохранять их в разных форматах, а не только в GIF. Хотя не надейтесь, что все проблемы решатся сами собой. В частности, некоторые сложности возникнут с использованием графических форматов ХВМ и ХРМ, распространенных в среде UNIX. Но это уже детали, требующие отдельного разбирательства.

  В следующем разделе мы начнём работу над созданием графических изображении и рисунков с помощью объекта холст.

Графический объект холст

  Графический объект холст библиотеки Tkinter создан специально для работы с графикой и содержит множество разнообразных методов. Документация с описанием методов разных графических объектов представлена на Web-странице по адресу http://www.python.com/, но она все ещё не завершена и несколько фрагментарна Прежде чем приступить к использованию холста, следует уяснить некоторые общие принципы процесса рисования. Большинство инструментов рисования обычно не применяется для настройки цвета отдельных пикселей. Вместо этого создаётся некоторая структура данных, которая с помощью опредёленной функции передаётся окну или графическому объекту, внутренний код которого выполняет за Вас всю остальную работу. Если раньше Вам приходилось работать с другими инструментами рисования, то набор методов объекта холст может показаться довольно бедным. Тем не менее путём комбинирования имеющихся базовых функций можно создать практически любое изображение. Например, холст не содержит команды установки цвета отдельного пикселя, но это задачу можно выполнить с помощью метода Rectangle() или Line(). Мы хорошо попрактикуемся с комбинированием базовых методов рисования при создании приложения Mandelbrotset в главе 23.

  Принципы, положенные в основу рисования в холсте Tkinter, несколько отличаются от принципов вывода изображений в других объектах GUI. В большинстве систем графического редактирования для рисования на экране можно использовать точки, линии и другие графические примитивы, причём результаты рисования тут же появятся на экране. В случае с холстом Tkinter происходит предварительное описание графического элемента, такого, например, как прямоугольник, после чего он помещается в холсте в список элементов, которые следует нарисовать. Элементы в списке не появятся на экране до тех пор, пока не поступит команда на обновление холста. После выполнения обновления все элементы из списка появятся на экране поверх ранее нарисованных элементов. Также можно дать команду на удаление нарисованного элемента, и он исчезнет после следующего обновления. Независимо от сложности нарисованного элемента, его можно легко перемещать по экрану с помощью простых команд. Для упрощения работы пользователей можно запрограммировать выделение на экране наборов элементов, которые будут перемещены или удалены. Позже, на примере графического редактора Mandelbrot, мы рассмотрим на практике, как все это делается. Недостаток данного подхода рисования состоит в том, что объект холста сохраняет в себе все созданные графические элементы, число которых может превышать тысячу. Для редакторов растровой графики, где учитывается только цвет отдельных пикселей, усложнение рисунка не влияет на его размер. Напротив, с добавлением каждого нового элемента объект холста требует все больше памяти, а время его обновления катастрофически возрастает. Впрочем, ресурсов современных компьютеров обычно вполне хватает на то, чтобы удовлетворить запросы в графике большинства пользователей.

  В следующих разделах будут описаны основные графические элементы, поддерживаемые объектом холст библиотеки Tkinter. В каждом случае также будут рассматриваться специальные методы, используемые в этом объекте для создания и редактирования данных элементов. По возможности всегда используйте встроенные методы, хотя можно создавать собственные, основываясь на базовых методах объекта холст: create_rectangle() и create_line().

  Учтите, что для использования специальных методов их следует импортировать из модуля Canvas.

Прямоугольник

  Прямоугольники задаются четырьмя координатами: х-у — для верхнего левого угла и x1-y1 — для нижнего правого угла. Нулевые координаты х-у соответствуют верхнему левому углу холста. Но чтобы пиксель с координатами 0,0 был виден на экране, нужно обнулить свойства холста borderwidth и highlightthickness. Если этого не сделать, то рамка текущего фокуса будет прочерчена по верхнему краю холста и скроет часть крайних пикселей. Чтобы очертить рамку вокруг холста, лучше всего вставить объект холста в объект рамки и установить для него соответствующее свойство границы.

  Синтаксис вызова метода Rectangle() для рисования прямоугольника следующий:

Rectangle (cv, х, у, xl, у1, опции...)

Например: Rectangle(cv, 100, 100, 200, 200, fill="red")

  В результате в объекте холста cv будет нарисован квадрат со стороной, равной 100 пикселям, залитый красным цветом с координатами верхнего левого угла 100 пикселей от верхнего края холста и 100 пикселей от левого края (в терминологии Tk этот угол называется NW — northwest — северо-западный). Ту же операцию можно выполнить с помощью следующего базового метода:

cv.create_rectangle(x, y, xl, yl, опции...)

  Вместо того чтобы перечислять здесь все опции, среди которых много весьма полезных, лучше загрузите из Internet демонстрационную программу, представленную по адресу http://www.pauahtun.org/TYPython/CanvasDemo.html. Прилагаемые инструкции и экранные подсказки помогут Вам разобраться со всеми опциями графических элементов, поддерживаемых объектом холст библиотеки Tkinter.

Линия

  Линии, как и прямоугольники, задаются с помощью четырех координат. Координаты х и у указывают исходную точку линии, a xl и yl — конечную точку. Используется следующий синтаксис:

Line (cv, х, у, xl, у1, опции...}.

Например:

Line(cv,100,100,200,100,fill="blue").

Дуга

  Следует отличать дуги, создаваемые с помощью метода Arc, от окружностей, для создания которых используется метод Oval (подробно об этом - в следующем разделе). На рис. 20.6 показана копия экрана демонстрационной программы CanvasDemo.py, которую можно загрузить из Internet. Дуги в верхней части рисунка называются секторами, в среднем ряду — собственно дугами, а в нижнем ряду — хордами. Так выглядят вызовы методов для построения дуг всех трёх типов:

Arc(cv, x, у, xl, yl, extent=90, fill="#808080",\

    style="pieslice")

Arc(cv, x, у, xl, yl, extent=90, fill="# 808080",\

   style="arc")

Arc(cv, x, у, xl, yl, extent=90, fill="#1808080",\

   style="chord")

  Как видите, различные типы дуг задаются только параметрами. С помошью метода Arc также можно построить замкнутую окружность, но гораздо эффективнее для этого воспользоваться методом Oval.

Рис. 20.6. Создание дуг с помощью метода Arc

Окружность и овал

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

Oval(cv, x, y, x1, y1, fill="#808080")

  Следует помнить, что ни окружность, ни сектор и ни овал не рисуются от центра (к тому же в овале два фокальных центра). Все эти фигуры задаются прямоугольником, в который они вписаны. Центр фигуры не указывается в параметрах. Его можно только рассчитать, если в этом возникнет необходимость. Оперировать прямоугольником значительно проще. Так, на рис. 20.7 показано, как можно из совершенной окружности получить совершенный эллипс, изменяя стороны прямоугольника, вписанные в этот прямоугольник.

Рис. 20.7. Изменяя стороны прямоугольника, можно легко менять форму вписанного в него эллипса

Многоугольник

  Многоугольником называется закрытая фигура, построенная из линейных сегментов. Можно использовать неограниченное число сегментов, но не меньше трёх. Невозможно построить замкнутую фигуру, содержащую меньше трёх сторон. Многоугольники со сторонами равной длины и равными углами называются правильными. Для любого числа сегментов можно создать правильный многоугольник: треугольник, квадрат, пятиугольник и т.д. Правильный многоугольник, состоящий из огромного числа граней и углов, будет не отличим от окружности. Это возможный путь создания окружности, но не самый эффективный. Гораздо проще воспользоваться методом Oval. Простейшим многоугольником является треугольник. На практике гораздо чаще используются неправильные треугольники, чем правильные, что, впрочем, справедливо и для других многоугольников. Метод Polygon можно использовать для рисования как правильных, так и неправильных многоугольников. Но если Вам нужно построить правильный многоугольник, следует воспользоваться программой расчёта координат вершин правильной фигуры. Ряд таких программ можно бесплатно загрузить из Internet, но среди них нет написанных на Python. (Дополнительную информацию о построении правильных фигур Вы найдёте в разделе "Примеры и задания" в конце главы.)

Вершиной фигуры называется точка, где сходятся две её грани. Правда, если две грани сходятся под углом 180°, то вершина не получится. Это будет просто сплошная линия.

  В случае с методом Polygon объект холст не выполняет за Вас никаких вычислений, как это было при рисовании окружностей, дуг и эллипсов. В объект просто передаётся список координат (х,у), и программа прочерчивает линии между этими вершинами. Если нужно построить девятиугольник, то в коде следует указать координаты всех девяти вершин, но, в отличие от многих других графических систем, Вам не нужно в конце списка повторно указывать первую вершину, чтобы замкнуть фигуру. Программа Tkinter автоматически прочерчивает линию между первой и последней вершинами. Ниже показан код создания четырехугольника:

 Polygon(cv, 130, 130, 130, 258, 258, 258, 130, 30, fill="#FFFFFF")

  В этом примере предполагается, что объект холст имеет размер 400x400 пикселей с цветом фона, отличным от белого (обычно оттенки серого). Подробнее о рисовании многоугольников мы поговорим позже.

Рисунок

  Побитовые изображения называют ещё однослойными. Для поддержания цветовой информации требуется несколько слоев, поэтому однослойные побитовые изображения всегда монохромные. Рисунок создаётся из мозаики пикселей, которые могут быть только белыми или черными (включен/выключен). Обычным форматом для сохранения таких изображений является широко используемый в X Windows формат ХВМ - X Bitmap. Как Вы уже видели раньше, библиотека Tkinter поддерживает использование изображений такого типа с помощью объекта ImageTk. При работе с побитовыми изображениями можно начинать не с создания самого изображения, а с объекта, в котором следует определить файл источника и установить цвета переднего и заднего плана. Пример такого кода показан ниже,

bmpn = "@GVI.xbm"

item = Bitmap(cv, 368, 368, bitmap=bmpn, foreground="#808080")

  Символ @ указывает, что функции Bitmap() следует искать файл, а не встроенное побитовое изображение. Вновь предполагается, что объект холста имеет размер 400x400 пикселей. Результат выполнения этих инструкций показан на рис. 20.8.

Рис. 20.8. Побитовый рисунок

Изображение

  Метод Imageltem используется так же, как и метод Bitmap, но для представления не только черно-белых, но и цветных рисунков. Метод Photolmage поддерживает изображения только в форматах GIF и РРМ. Как Вы узнали в этой главе, для использования других форматов необходимо установить библиотеку PIL. Следующий код отобразит цветное изображение в том же холсте, что и предыдущее побитовое изображение:

img=PhotoImage(file="GVI.ppm")

item=Imageltem(cv, 368, 368, image=img)

На рис. 20.9 показано полученное изображение.

Рис. 20.9. Графическое изображение Image

Текст

  Для добавления в холст текста используется метод CanvasText. В параметрах метода нужно задать координаты текста, сам текст и цвет шрифта. Ниже представлен пример вывода текста на экран в объекте холста,

item = CanvasText(cv, 368, 368+48, fill="#007090", text="Бог индейцев Майя")

Результат показан на рис. 20.10.

Рис. 20.10. Текст и графическое изображение, добавленные в холст

  Обратите внимание, что по умолчанию надпись выравнивается по центру относительно заданных координат. Чтобы выровнять надпись по левому краю, следует установить параметр anchor=NW.

Метод Window

  Метод Window используется для того, чтобы разместить различные графические объекты Tkinter внутри холста. В следующем коде показан пример добавления в холст кнопки:

btn=Button(cv, text="Показать рисунок", command=drawImage)

item=Window(cv, 368, 368, anchor=SW, window=btn)

На рис. 20.11 показан холст, содержащий кнопку.

Рис. 20.11. Кнопка, добавленная с помощью метода Window (это указывает на тот факт, что все кнопки на экране также являются окнами)

Резюме

  Мы рассмотрели разные типы меню, научились создавать всплывающие меню и списки с полосой прокрутки, узнали о базовых средствах рисования пакета Tkinter (включая Python Imaging Library), получили общие представления о методах редактирования изображений в объекте холста и обсудили различные базовые графические элементы, которые можно использовать в холсте.

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

Практикум

Вопросы и ответы

  Можно ли создавать игры с помощью Python?

  Безусловно. Правда, если весь код написать только с помощью Python, программа будет не очень быстрой. Хотя бы основные блоки следует создать с помощью языка С. На 7-й международной конференции по языку Python, которая проходила в Хьюстоне (Houston) в 1998 г., был представлен имитатор Virtual World, полностью написанный на языке Python. Более подробную информацию о нем Вы найдёте на Web-странице по адресу http://www.foretec.com/python/workshops/1998-ll/proceedings/papers/asbahr/asbahr.html.

  *Прим. В. Шипкова: на самом деле на конец 2005 года существует по крайней мере один пакет для профессиональной работы с 3D-графикой, один пакет для создания игр, и плагин написанный на Python для одной весьма популярной программы для работы с 3D-графикой.

  На рис. 20.6 представлено много интересных графических объектов. Почему мы не рассмотрели их подробно?

  Это объекты Python MegaWidgets, рассмотрение которых выходит за рамки данной книги. Обратитесь к Web-странице PMW по адресу http://www.dscpl.com.au/pmw/.

Контрольные вопросы

  1. Как добавить в список полосу прокрутки?

    а) Нужно разработать отдельные функции для полосы прокрутки и для списка.

    б) Нужно создать только функцию для полосы прокрутки.

    в) Необходимо сконструировать для списка собственную полосу прокрутки.

    г) Можно просто воспользоваться встроенными функциями объектов полосы прокрутки и списка.

  2. Какая разница между методами Bitmap и ImageItem?

    а) Нет никакой принципиальной разницы.

    б) С помощью метода Imageltem можно добавлять цветные изображения, а с помощью Bitmap — только монохромные.

    в) Это два альтернативных метода использования рисунков в приложениях,

    г) Методы отличаются только скоростью выполнения.

Ответы

  1. г. Все функции, необходимые для прокручивания списков, уже встроены в объекты списка и полосы прокрутки. Нужно только присвоить их соответствующим параметрам.

  2. 6. Действительно, метод Imageltem отличается только способностью обрабатывать цветные изображения. Каждый пиксель в объекте Bitmap представлен только одним битом данных, тогда как для сохранения информации о цвете пикселя в объекте Image требуется по крайней мере 8 бит.

Примеры и задания

  Дополнительную информацию об использовании анонимных функций можно получить на домашней странице International Obfuscated С Code Contest (Международный диспут по неявному программированию на С) по адресу http://www.ioccc.org/. И хотя основное внимание уделено программированию на языке С, в действительности неявные (анонимные) коды можно создавать на любом языке. Python, пожалуй, меньше всего подходит для этих целей, но и ему по силам создавать подобные коды для чётко детерминированных несложных приложений.

  Больше сведений о графическом формате GIF и связанных с ним проблемах можно найти на Web-странице Burn АН Gifs (Первоисточник файлов GIF) по адресу http://burnallgifs.org/. Эрик Раймонд (Eric S. Raymond) опубликовал ряд интересных статей по этой теме на своей Web-странице, её адрес — http://www.tuxedo.org/~esr/.

  За информацией о netpbm и других графических форматах зайдите на страницу Graphics Muse (Муза графики) по адресу http://www.graphics-muse.org/.

  Совсем недавно Тим Мидлетон (Tim Middleton) открыл новую страницу на сервере Vauls of Parnassus по адресу http://www.vex.net/pernassus/. Было заявлено, что на этой странице будет собрана одна из лучших подборок программных модулей на Python.

  *Прим. В. Шипкова: Товарищ, надо признать, постарался. Хоть на этом сайте и не самые новые версии программ, но общее представление о той нише, которой занимает Питон в мире программирования - получить можно. ;)

  Действительно, здесь можно найти отличные программные продукты, которые великолепно иллюстрируют применение на практике всех средств программирования, рассмотренных в этой книге. Данная страница задумывалась как эквивалент архивов CPAN, где собраны стандартные модули на языке Perl. (Между прочим, Парнассус (Parnassus) — это гора, в расщелинах которой, согласно древнегреческой мифологии, скрывался Питон.)

  Чтобы научиться создавать правильные многоугольники, посетите Web-страницу по адресу http://www.scienceu.com/geometry/articles/tiling/.