3

I want to move around objects in python tkinter, specifically polygons. The problem is in is_click function. I can't seem to figure out how to determine if I clicked the object. The code is not 100% complete yet, and moving around needs still need to be finished but I need to figure this out for now. I also have similar class where you can move around Circles and Rectangles, where is_click function is working, but as polygon has from 3 to 4 coordinates it is a bit more complicated. Run The classes for yourself to see what they are doing.

My code for polygons:

import tkinter

class Polygon:

    def __init__(self, ax, ay, bx, by, cx, cy, dx=None, dy=None, color=None):
        self.ax = ax
        self.ay = ay
        self.bx = bx
        self.by = by
        self.cx = cx
        self.cy = cy
        self.dx = dx
        self.dy = dy
        self.color = color

    def is_click(self, event_x, event_y):
        pass

    def paint(self, g):
        self.g = g
        if self.dx is None:
            self.id = self.g.create_polygon(self.ax,self.ay,
                                            self.bx,self.by,
                                            self.cx,self.cy,
                                            fill=self.color)
        else:
            self.id = self.g.create_polygon(self.ax,self.ay,
                                            self.bx,self.by,
                                            self.cx,self.cy,
                                            self.dx,self.dy,
                                            fill=self.color)

    def move(self, d_ax=0, d_ay=0, d_bx=0, d_by=0, d_cx=0, d_cy=0, d_dx=None, d_dy=None):
        if d_dx is None:
            self.ax += d_ax
            self.ay += d_ay
            self.bx += d_bx
            self.by += d_by
            self.cx += d_cx
            self.cy += d_cy
            self.g.move(self.id, d_ax, d_ay, d_bx, d_by, d_cx, d_cy)
        else:
            self.ax += d_ax
            self.ay += d_ay
            self.bx += d_bx
            self.by += d_by
            self.cx += d_cx
            self.cy += d_cy
            self.dx += d_dx
            self.dy += d_dy
            self.g.move(self.id, d_ax, d_ay, d_bx, d_by, d_cx, d_cy, d_dx, d_dy)


class Tangram:

    def __init__(self):
        self.array = []
        self.g = tkinter.Canvas(width=800,height=800)
        self.g.pack()

        #all objects
        self.add(Polygon(500,300,625,175,750,300, color='SeaGreen'))
        self.add(Polygon(750,50,625,175,750,300, color='Tomato'))
        self.add(Polygon(500,175,562.6,237.5,500,300, color='SteelBlue'))
        self.add(Polygon(500,175,562.5,237.5,625,175,562.5,112.5, color='FireBrick'))
        self.add(Polygon(562.5,112.5,625,175,687.5,112.5, color='DarkMagenta'))
        self.add(Polygon(500,50,500,175,625,50, color='Gold'))
        self.add(Polygon(562.5,112.5,687.5,112.5,750,50,625,50, color='DarkTurquoise'))
        #end of all objects

        self.g.bind('<Button-1>', self.event_move_start)

    def add(self, Object):
        self.array.append(Object)
        Object.paint(self.g)

    def event_move_start(self, event):
        ix = len(self.array) - 1
        while ix >= 0 and not self.array[ix].is_click(event.x, event.y):
            ix -= 1
        if ix < 0:
            self.Object = None
            return
        self.Object = self.array[ix]
        self.ex, self.ey = event.x, event.y
        self.g.bind('<B1-Motion>', self.event_move)
        self.g.bind('<ButtonRelease-1>', self.event_release)

    def event_move(self):
        pass

    def event_release(self):
        pass

Tangram()

and code for Circle and Rectangle moving:

import tkinter, random

class Circle:
    def __init__(self, x, y, r, color='red'):
        self.x = x
        self.y = y
        self.r = r
        self.color = color

    def is_click(self, x, y):
        return (self.x-x)**2+(self.y-y)**2 < self.r**2

    def paint(self, g):
        self.g = g
        self.id = self.g.create_oval(self.x-self.r,self.y-self.r,
                      self.x+self.r,self.y+self.r,
                      fill=self.color)

    def move(self, dx=0, dy=0):
        self.g.delete(self.id)
        self.x += dx
        self.y += dy
        self.paint(self.g)

class Rectangle:
    def __init__(self, x, y, width, height, color='red'):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.color = color

    def is_click(self, x, y):
        return self.x<=x<self.x+self.width and self.y<=y<self.y+self.height

    def paint(self, g):
        self.g = g
        self.id = self.g.create_rectangle(self.x,self.y,
                      self.x+self.width,self.y+self.height,
                      fill=self.color)

    def move(self, dx=0, dy=0):
        self.x += dx
        self.y += dy
        self.g.move(self.id, dx, dy)

class Program:
    def __init__(self):
        self.array = []
        self.g = tkinter.Canvas(bg='white', width=400, height=400)
        self.g.pack()
        for i in range(20):
            if random.randrange(2):
                self.add(Circle(random.randint(50, 350),random.randint(50, 350), 20, 'blue'))
            else:
                self.add(Rectangle(random.randint(50, 350),random.randint(50, 350), 40, 30))
        self.g.bind('<Button-1>', self.event_move_start)

    def add(self, Object):
        self.array.append(Object)
        Object.paint(self.g)

    def event_move_start(self, e):
        ix = len(self.array)-1
        while ix >= 0 and not self.array[ix].is_click(e.x, e.y):
            ix -= 1
        if ix < 0:
            self.Object = None
            return
        self.Object = self.array[ix]
        self.ex, self.ey = e.x, e.y
        self.g.bind('<B1-Motion>', self.event_move)
        self.g.bind('<ButtonRelease-1>', self.event_release)

    def event_move(self, e):
        self.Object.move(e.x-self.ex, e.y-self.ey)
        self.ex, self.ey = e.x, e.y

    def event_release(self, e):
        self.g.unbind('<B1-Motion>')
        self.g.unbind('<ButtonRelease-1>')
        self.Object = None

Program()
martineau
  • 119,623
  • 25
  • 170
  • 301
Matis
  • 611
  • 2
  • 11
  • 27
  • 1
    Have you read this answer? http://stackoverflow.com/a/6789351/7432 It shows how to move items on a canvas. I'm not sure if you're asking how to move items on a canvas or if you're asking "why won't my code work?". Also, are you aware that the canvas `move` function only takes two parameters, rather than one for each coordinate? – Bryan Oakley Jan 02 '15 at 00:04
  • @BryanOakley Thank you for you respond ! I am asking how to move items/ polygons on canvas. Will have closer look at the code in the link tomorrow. Yeah, I didn't get it working so I did no even realise that `move` takes only 2 parameters, thanks for that. – Matis Jan 02 '15 at 01:15
  • @BryanOakley I edited the code from the link you posted and it works great! Thanks for that. However, I don't know what to mark as an answer now. – Matis Jan 02 '15 at 15:00
  • @BryanOakley you can post an answer with your link, so I can mark it as a correct one, or this question can be deleted maybe, as there is answer in another question. – Matis Jan 02 '15 at 15:06

1 Answers1

0

This is not the full anwser to your questions, but its too long for a comment. One way, that I would centrally consider, is to change/amend your code so that it uses find_closes method. With this method, you can determine which widget (i.e. polygon) is clicked very easily. As a quick prof of concept, you can make the following changes in the Tangram and ploygon class:

def event_move_start(self, event):
  ix = len(self.array) - 1
  while ix >= 0 and not self.array[ix].is_click(event, self.g, event.x, event.y): # add event and canvas to argumetns

In polygon:

def is_click(self, event, g, event_x, event_y):
    widget_id = event.widget.find_closest(event.x, event.y)
    print(widget_id)
    g.move(widget_id, 1, 1) # just dummy move for a clicked widget
    pass

This will print the ids of clicked widgets/polygons and slightly move the clicked polygon. This can be used to select which object was clicked, and having this id object, you can for moving or whatever.

p.s. the full code that I used to test it is here.

Hope this helps.

Marcin
  • 215,873
  • 14
  • 235
  • 294