Отслеживание каждой точки каждого многоугольника на холсте с помощью tkinter

#python #tkinter #canvas #coordinates #polygon

#python #tkinter #холст #координаты #полигон

Вопрос:

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

Для этого я создал этот класс:

 class Polygon:
    def __init__(self, coords, color, canvas):
        self.coords = coords
        self.color = color
        self.move = False

        canvas.bind('<Button-1>', self.start_movement)
        canvas.bind('<Motion>', self.movement)
        canvas.bind('<ButtonRelease-1>', self.stopMovement)
        canvas.bind('<Button-3>', self.rotate)

        canvas.create_polygon(self.coords, fill=self.color)
 

Каждый полигон создается таким образом :

 medium_triangle = Polygon((0,0, 100*math.sqrt(2),0, 0,100*math.sqrt(2)),
                          'red', drawing_place)
small_triangle_1 = Polygon((0,0 ,100,0, 0,100), 'purple', drawing_place)
[...]
big_triangle_2 = Polygon((0,0, 200,0, 0,200), 'green', drawing_place)
 

Моя главная проблема в том, что, похоже, я могу изменить только coords атрибут последнего Polygon созданного.
Я использую мышь, чтобы перетаскивать фрагменты на моем холсте, и я использую эти методы, чтобы заставить мои полигоны двигаться:

 def start_movement(self, event):
    self.move = True

    # Translate mouse coordinates to canvas coordinate
    self.initi_x = drawing_place.canvasx(event.x)
    self.initi_y = drawing_place.canvasy(event.y)

    self.movingimage = drawing_place.find_closest(self.initi_x, self.initi_y,
                                                  halo=1)  # get canvas object
                                                           # ID of where mouse
                                                           # pointer is.

def movement(self, event):
    if self.move:

        end_x = drawing_place.canvasx(event.x)  # Translate mouse x screen
                                                # coordinate to canvas coordinate.
        end_y = drawing_place.canvasy(event.y)  # Translate mouse y screen
                                                # coordinate to canvas coordinate.

        deltax = end_x - self.initi_x  # Find the difference
        deltay = end_y - self.initi_y  # Find the difference

        self.newPosition(deltax, deltay)

        self.initi_x = end_x  # Update previous current with new location
        self.initi_y = end_y
        drawing_place.move(self.movingimage, deltax, deltay)  # Move object

def stopMovement(self, event):
    self.move = False
    affichage(self)
 

Мне удается добавить к моим начальным координатам смещение, которое было выполнено благодаря моему методу new_position :

 def newPosition(self, deltax, deltay):
    coord = self.coords  # Retrieve object points coordinates
    old_coord = list(coord)  # Tuple to List
    c = []  # New coords
    i = 0  # Cursor on old_coord
    for coordinates in old_coord:

        # check if index of coordinates in range of i and len(old_coord)
        # in old_coord is pair (x coord).
        if (old_coord.index(coordinates, i, len(old_coord)) % 2) == 0:
            c.append(coordinates   deltax)
        else:  # index's impair => y-coord
            c.append(coordinates   deltay)
        i  = 1

    coord2 = tuple(c)  # List to Tuple
    self.set_coords(coord2)

 def set_coords(self, coords):
    self.coords = coords
 

Но, как вы можете видеть прямо здесь, в моей консоли

 just after medium_triangle declaration:
(0, 0, 141.4213562373095, 0, 0, 141.4213562373095)
(0, 0, 200, 0, 0, 200)
(0, 0, 200, 0, 0, 200)
(0, 0, 200, 0, 0, 200)
(0, 0, 200, 0, 0, 200)
(297.0, 61.0, 497.0, 61.0, 297.0, 261.0)
(551.0, 166.0, 751.0, 166.0, 551.0, 366.0)
(951.0, 250.0, 1151.0, 250.0, 951.0, 450.0)
 

Во время объявления моих полигонов я, кажется, могу печатать их координаты с medium_triangle.coords помощью, но после, когда я нажимаю на свой холст, он напрямую отображает координаты последнего объявленного. И когда я перемещаю другой фрагмент на своем холсте, он просто добавляет то же Polygon самое.

Я не совсем разбираюсь в классах, методах и т. Д., Но я думал, что понял, что каждый из моих полигонов был другим экземпляром моего класса, но, несмотря на это, похоже, что я могу получить доступ только к одному экземпляру Polygon .

Надеюсь, моя проблема ясна, действительно ли я создал разные полигоны, и если да, то почему я не могу изменить их отдельно?

Комментарии:

1. Вам необходимо включить определение метода класса. set_coords()

2. Спасибо, что указали на эту проблему, я отредактировал свой пост и поместил метод класса set_coords() в его конец

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

Ответ №1:

 canvas.bind('<Button-1>', self.start_movement)
canvas.bind('<Motion>', self.movement)
canvas.bind('<ButtonRelease-1>', self.stopMovement)
canvas.bind('<Button-3>', self.rotate)
 

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

 canvas.bind('<Button-1>', self.start_movement)
canvas.bind('<Button-1>', lambda e: print("ok"))
 

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

Ответ №2:

Вы не должны использовать canvas.bind(...) Polygon класс inside. Используйте canvas.tag_bind(...) вместо:

 class Polygon:
    def __init__(self, coords, color, canvas):
        self.coords = coords
        self.color = color
        self.move = False
        self.canvas = canvas

        self.id = canvas.create_polygon(self.coords, fill=self.color)

        canvas.tag_bind(self.id, '<Button-1>', self.start_movement)
        canvas.tag_bind(self.id, '<Motion>', self.movement)
        canvas.tag_bind(self.id, '<ButtonRelease-1>', self.stopMovement)
        canvas.tag_bind(self.id, '<Button-3>', self.rotate)
 

Обратите внимание, что я сохранил переданную canvas переменную экземпляра self.canvas . Вы должны заменить все drawing_place на self.canvas внутри других методов класса.

Также вам не нужно вызывать следующую строку внутри start_movement() :

     self.movingimage = drawing_place.find_closest(self.initi_x, self.initi_y,
                                                  halo=1)  # get canvas object
                                                           # ID of where mouse
                                                           # pointer is.
 

As self.id может использоваться вместо результата find_closest() внутри movement() :

     def movement(self, event):
        if self.move:

            end_x = self.canvas.canvasx(event.x)  # Translate mouse x screen
                                                    # coordinate to canvas coordinate.
            end_y = self.canvas.canvasy(event.y)  # Translate mouse y screen
                                                    # coordinate to canvas coordinate.

            deltax = end_x - self.initi_x  # Find the difference
            deltay = end_y - self.initi_y  # Find the difference

            self.newPosition(deltax, deltay)

            self.initi_x = end_x  # Update previous current with new location
            self.initi_y = end_y
            self.canvas.move(self.id, deltax, deltay)  # Move object