from Gui import * class Item: """an Item object represents a canvas item. When you create a canvas item, Tkinter returns an integer 'tag' that identifies the new item. To perform an operation on the item, you invoke a method on the canvas and pass the tag as a parameter. The Item class makes this interface more object-oriented: each Item object contains a canvas and a tag. When you invoke methods on the Item, it invokes methods on its canvas. """ def __init__(self, canvas, tag): self.canvas = canvas self.tag = tag self.handles = [] self.bind('', self.select) self.bind('', self.drag) self.bind('', self.release) def __str__(self): return 'Item' + str(self.tag) def show_handles(self): """draw handles for this item (if necessary)""" if self.handles: return coords = self.coords() for i in range(len(coords)): x, y = coords[i] tag = self.canvas.circle(x, y, 5, fill='yellow') handle = Handle(self.canvas, tag, self, i) self.handles.append(handle) # the following are wrappers for canvas methods def bind(self, event, callback): """this method applies bindings to canvas items (not the whole canvas)""" self.canvas.tag_bind(self.tag, event, callback) def config(self, **options): """reconfigure this item with the given options""" self.canvas.itemconfig(self.tag, **options) def coords(self, *args): """get or set the canvas coordinates for this item""" return self.canvas.coords(self.tag, *args) def move(self, dx, dy): """move this item by (dx, dy) in canvas coordinates""" self.canvas.move(self.tag, dx, dy) def move_coord(self, i, dx, dy): """move the ith coordinate by (dx, dy) in canvas coordinates """ coords = self.coords() coords[i][0] += dx coords[i][1] += dy self.coords(coords) def replace_coord(self, i, coord): """replace the ith coordinate with the given coordinate""" coords = self.coords() coords[i] = coord self.coords(coords) # the following event handlers take an event object as a parameter def select(self, event): print 'Item.select', self self.config(fill='red') self.set_drag(event) self.show_handles() def drag(self, event): """move this item (and its handles) using the pixel coordinates in the event object.""" # see how far we have moved dx, dy = self.sub_drag(event, self.drag) # save the current drag coordinates self.set_drag(event) # move the item and its handles self.move(dx, dy) for handle in self.handles: handle.move(dx, dy) return dx, dy def release(self, event): print 'Item.release', self self.config(fill='blue') # the following methods are for dealing with events and drag # coordinates def get_drag(self, event): """get the drag coordinates from this event and translate them into canvas coordinates""" x, y = self.canvas.trans([event.x, event.y]) return x, y def set_drag(self, event): """store the current drag coordinates""" self.drag = self.get_drag(event) def sub_drag(self, event, d2): """subtract d2 from the drag coordinates in event""" d1 = self.get_drag(event) return d1[0] - d2[0], d1[1] - d2[1] class Handle(Item): def __init__(self, canvas, tag, item, index): Item.__init__(self, canvas, tag) self.item = item self.index = index def select(self, event): self.set_drag(event) def drag(self, event): """this method is relatively elegant, but it is based on assumptions about the way the canvas works that turn out not to be true. So it has an annoying behavior for ovals and rectangles. To do this right, you probably have to customize the behavior of different items (this version probably works for polylines, but not ovals and rectangles).""" # move the handle dx, dy = Item.drag(self, event) # move the coordinate that corresponds to this handle self.item.move_coord(self.index, dx, dy) def release(self, event): """override Item.release and do nothing""" class Hello(Gui): def __init__(self): Gui.__init__(self) self.ca_width = 400 self.ca_height = 400 self.setup() def setup(self): # frame 1 self.fr(TOP) self.canvas = self.ca(width=self.ca_width, height=self.ca_height, bg='white') self.canvas.bind('', self.click) self.endfr() # frame 2 self.fr(TOP, fill=BOTH, expand=1) self.fr() self.bu(LEFT, text='Hello', command=self.hello) self.bu(LEFT, text='Circle', command=self.circle) self.bu(LEFT, text='Quit', command=self.quit) self.endfr() self.endfr() def hello(self): font = ('Helvetica', 36) tag = self.canvas.text([0, 0], 'Hello', font=font, fill='blue') item = Item(self.canvas, tag) def circle(self): tag = self.canvas.circle(0, 0, 100, 'blue') item = Item(self.canvas, tag) def click(self, event): """this event handler gets invoked when the user clicks on the canvas.""" print 'Hello.click', event.type, event.num, event.x, event.y if __name__ == '__main__': h = Hello() h.mainloop()