Canvas (холст): методы, идентификаторы и теги. Урок 13

Методическая разработка урока
Элективный курс: Модуль tkinter. Создание графического интерфейса пользователя с помощью языка программирования Python
Уровень: Программирование для начинающих

На прошлом уроке были рассмотрены методы объекта canvas, формирующие на нем геометрические примитивы и текст. Однако это лишь часть методов холста. В другую условную группу можно выделить методы, изменяющие свойства уже существующих объектов холста (например, геометрических фигур). И тут возникает вопрос: как обращаться к уже созданным фигурам? Ведь если при создании было прописано что-то вроде canvas.create_oval(30,10,130,80) и таких овалов, квадратов и др. на холсте очень много, то как к ним обращаться?

Для решения этой проблемы в tkinter для объектов холста можно использовать идентификаторы и теги, которые затем передаются другим методам. У любого объекта может быть как идентификатор, так и тег. Использование идентификаторов и тегов немного различается.

Рассмотрим несколько методов изменения уже существующих объектов с использованием при этом идентификаторов. Для начала создадим холст и три объекта на нем. При создании объекты "возвращают" свои идентификаторы, которые можно связать с переменными (oval, rect и trial в примере ниже) и потом использовать их для обращения к конкретному объекту.

c = Canvas(width=460,height=460,bg='grey80')
c.pack()
oval = c.create_oval(30,10,130,80)
rect = c.create_rectangle(180,10,280,80)
trian = c.create_polygon(330,80,380,10,430,80, fill='grey80', outline="black")

Если вы выполните данный скрипт, то увидите на холсте три фигуры: овал, прямоугольник и треугольник.

Далее можно использовать методы-"модификаторы" указывая в качестве первого аргумента идентификатор объекта. Метод move перемещает объект на по оси X и Y на расстояние указанное в качестве второго и третьего аргументов. Следует понимать, что это не координаты, а смещение, т. е. в примере ниже прямоугольник опустится вниз на 150 пикселей. Метод itemconfig изменяет указанные свойства объектов, coords изменяет координаты (им можно менять и размер объекта).

c.move(rect,0,150)
c.itemconfig(trian,outline="red",width=3)
c.coords(oval,300,200,450,450) 

Если запустить скрипт, содержащий две приведенные части кода (друг за другом), то мы сразу увидим уже изменившуюся картину на холсте: прямоугольник опустится, треугольник приобретет красный контур, а эллипс сместится и сильно увеличится в размерах. Обычно в программах изменения должны наступать при каком-нибудь внешнем воздействии. Пусть по щелчку левой кнопкой мыши прямоугольник передвигается на два пикселя вниз (он будет это делать при каждом щелчке мышью):

def mooove(event):
     c.move(rect,0,2)
...
c.bind('<Button-1>',mooove)

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

oval = c.create_oval(30,10,130,80,tag="group1")
c.create_line(10,100,450,100,tag="group1")def color(event):
     c.itemconfig('group1',fill="red",width=3)
…
c.bind('<Button-3>',color) 

Еще один метод, который стоит рассмотреть, это delete, который удаляет объект по указанному идентификатору или тегу. В tkinter существуют зарезервированные теги: например, all обозначает все объекты холста. Так в примере ниже функция clean просто очищает холст.

def clean(event):
     c.delete('all')
...
c.bind('<Button-2>',clean) 

Метод tag_bind позволяет привязать событие (например, щелчок кнопкой мыши) к определенному объекту. Таким образом, можно реализовать обращение к различным областям холста с помощью одного и того же события. Пример ниже это наглядно иллюстрирует: изменения на холсте зависят от того, где произведен щелчок мышью.

from tkinter import *
 
c = Canvas(width=460,height=100,bg='grey80')
c.pack()
 
oval = c.create_oval(30,10,130,80,fill="orange")
c.create_rectangle(180,10,280,80,tag="rect",fill="lightgreen")
trian = c.create_polygon(330,80,380,10,430,80,fill='white',outline="black")
 
def oval_func(event):
     c.delete(oval)
     c.create_text(30,10,text="Здесь был круг",anchor="w")
def rect_func(event):
     c.delete("rect")
     c.create_text(180,10,text="Здесь был\nпрямоугольник",anchor="nw")
def triangle(event):
     c.create_polygon(350,70,380,20,410,70,fill='yellow',outline="black")
 
c.tag_bind(oval,'<Button-1>',oval_func)
c.tag_bind("rect",'<Button-1>',rect_func)
c.tag_bind(trian,'<Button-1>',triangle)
 
mainloop() 

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

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

Исправьте, пожалуйста,

Исправьте, пожалуйста, опечатки в тексте "trial" вместо "trian" и "Метод move перемещает объект на по оси X и Y" лишнее "на"

для анимации использовал

для анимации использовал функцию обратного вызова after библиотеки tkinter

from tkinter import *
 
def go():
        x,y=1,1
        c.move(krug,x,y)
        c.after(30,go)  #повторный вызов метода через 30 милесикунд
 
root=Tk()
c=Canvas(root,width=400,height=400,bg='#A6BDDC')
c.pack()
 
krug=c.create_oval(60,60,120,120,fill='#FFF70F',width=0)
 
go()
root.mainloop()

Толи получилось, толи нет))

Толи получилось, толи нет)) Не пойму, так как не видно следа движения объекта. Как его замедлить?

from tkinter import *
root=Tk()
 
c=Canvas(root, width=300, height=300, bg="white")
c.pack()
 
rec=c.create_rectangle(2,2,55,55,fill="green", outline="blue")
def mooove(event):
    x=0
    rec1=c.create_rectangle(2,2,55,55,fill="green", outline="blue")
    while x<200:
        c.delete(rec)
        c.move(rec1,1,1)
        x=x+1
c.tag_bind(rec,"<Any-Enter>",mooove)
root.mainloop()

В уроке неправильно

В уроке неправильно сформулировано второе задание (надо использовать либо delete и перерисовку, либо просто move). К тому же без дополнительных знаний данное задание сделать нельзя (данного урока для этого мало).

Хорошо! Тогда понятно! А что

Хорошо! Тогда понятно! А что тогда читать дальше! Читаю Лутца 4 издание, но там 1200+ страниц, а времени мало свободного:(. Может совет?! - как лучше продолжать?

По поводу задания. Вот так

По поводу задания. Вот так задержка работает (цифры выводятся через полсекунды):

import time
for i in range(10):
   print(i)
   time.sleep(0.5)

Но на холсте задержка почему-то не срабатывает так как надо. Сначала вычисляется вся задержка (сумма), после чего идет прорисовка на новом месте (проверено в Linux).

На мой взгляд Лутс хороший выбор. Быстро программирование все-равно изучить нельзя. Здесь большое значение имеет практика: важно не только читать, но и программировать самостоятельно.
Хорошая книга (свободно распространяется в интернете): Россум, Дрейк, Откидач "Язык программирования Python". Однако она старая, в ней описана вторая версия Python.

Я тоже пробовал импортировать

Я тоже пробовал импортировать time и на Windows такое же вычисление - сначала задержка, потом все остальное! Так как я начал с 3.2.2 питона, то думаю, что запутаюсь, если начну паралельно изучать и 2.х. А вот практика - хотелось бы) Но не могу найти никаких заданий, скажем: Для новичков! или что-то подобное! Кстати... на pythonchallenge.com/pc/def/map.html не могу понять, как сделать смещение на 2 каждой буквы. Делал так:

# Присвоил s хаос:)
s="g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj."
#Сделал цифровой список значений букв
s=list(map(ord, s))
#Меняю принадлежность букв по цифрам
for i in list:
    print(chr(i+2))
#Получается, но не так как хотелось бы(((
#Подскажите что делать?!

У меня получилось вот так: #

У меня получилось вот так:

# Присвоил s хаос:)
s="g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj."
#Сделал цифровой список значений букв
s=list(map(ord, s))
#Меняю принадлежность букв по цифрам
for i in s:
    if 97 <= i+2 <= 122: # вывод букв от a до z
        print(chr(i+2),end='')
    elif i+2 == 123: # больше z, это a или б
        print('a',end='')
    elif i+2 == 124:
        print('b',end='')
    else: # точки, пробелы, скобки выводятся как есть
        print(chr(i),end='')
 
""" Получается фраза:
i hope you didnt translate it by hand. (я надеюсь вы не переводили в ручную)
thats what computers are for. (для этого есть компьютеры)
doing it in by hand is inefficient and that's why this text is so long. (вручную неэффективно, текст длинный)
using string.maketrans() is recommended. (рекомендовано использовать string.maketrans())
now apply on the url. (используйте в url)
"""

В модуле string нет функции maketrans().
Что ввести в url, я так и не поняла. Если ввести maketrans.html, то появляется надпись: "that's a handy little function. isn't it?" (это удобная маленькая функция, да?)

Гениально!!! Нужно

Гениально!!! Нужно практиковаться, а то соображалка не работает)) В строке адреса ввести "ocr.html" вместо "map.html". По аналогии с заданием(Шифр Цезаря). Похоже, так как это задание создавалось еще в 2005 году, string.maketrans() отсутствует в питон 3. Спасибо за помощь.

s=list(map(ord, 'map'))
for i in s:
    print(chr(i+2), end='')
    i=i+2

помогите разобрать

Нужно создать окно с 100(10х10) полями,причем если мышкой в левой стороне кликнуть на одно поле,то поле должно поменять окраску с белого на черную, а если в право кликнуть, то должен стать поле серое,и если влево 2 раза кликнуть, то должно опять стать белым.

у меня вообще нет идей..подскажите пожалуйста..

вот я что имела в виду

вот я что имела в виду

from tkinter import*
win=Tk()
 
def maus_rechts(event):
        event.widget.config(bg='grey')
def maus_links(event):
        event.widget.config(bg='black')
def maus_doppelt(event):
        event.widget.config(bg='white')
 
liste=[(x,y) for x in range(10) for y in range(10)]
 
for (x,y) in liste:
        lab = Label(win)
        lab["height"]=2
        lab["width"]=5
        lab["bg"] = "#FAFAFA"
        lab.grid(column=y,row=x)
        lab.bind('<Button-3>',maus_rechts)
        lab.bind('<Button-1>',maus_links)
        lab.bind('<Double-Button-1>',maus_doppelt)
 
 
 
win.mainloop()

Что из себя представляет

Что из себя представляет поле? Может просто холст поделить на 100 частей линиями?

А в Python анимация быстро

А в Python анимация быстро происходит?я не успеваю посмотреть а фигуры уже нет!а когда использую While True: у меня окно зависает!