"""Rect Tracker class for Python Tkinter Canvas""" def groups(glist, numPerGroup=2): result = [] i = 0 cur = [] for item in glist: if not i < numPerGroup: result.append(cur) cur = [] i = 0 cur.append(item) i += 1 if cur: result.append(cur) return result def average(points): aver = [0,0] for point in points: aver[0] += point[0] aver[1] += point[1] return aver[0]/len(points), aver[1]/len(points) class RectTracker: def __init__(self, canvas): self.canvas = canvas self.item = None def draw(self, start, end, **opts): """Draw the rectangle""" return self.canvas.create_rectangle(*(list(start)+list(end)), **opts) def autodraw(self, **opts): """Setup automatic drawing; supports command option""" self.start = None self.canvas.bind("", self.__update, '+') self.canvas.bind("", self.__update, '+') self.canvas.bind("", self.__stop, '+') self._command = opts.pop('command', lambda *args: None) self.rectopts = opts def __update(self, event): if not self.start: self.start = [event.x, event.y] return if self.item is not None: self.canvas.delete(self.item) self.item = self.draw(self.start, (event.x, event.y), **self.rectopts) self._command(self.start, (event.x, event.y)) def __stop(self, event): self.start = None self.canvas.delete(self.item) self.item = None def hit_test(self, start, end, tags=None, ignoretags=None, ignore=[]): """ Check to see if there are items between the start and end """ ignore = set(ignore) ignore.update([self.item]) # first filter all of the items in the canvas if isinstance(tags, str): tags = [tags] if tags: tocheck = [] for tag in tags: tocheck.extend(self.canvas.find_withtag(tag)) else: tocheck = self.canvas.find_all() tocheck = [x for x in tocheck if x != self.item] if ignoretags: if not hasattr(ignoretags, '__iter__'): ignoretags = [ignoretags] tocheck = [x for x in tocheck if x not in self.canvas.find_withtag(it) for it in ignoretags] self.items = tocheck # then figure out the box xlow = min(start[0], end[0]) xhigh = max(start[0], end[0]) ylow = min(start[1], end[1]) yhigh = max(start[1], end[1]) items = [] for item in tocheck: if item not in ignore: x, y = average(groups(self.canvas.coords(item))) if (xlow < x < xhigh) and (ylow < y < yhigh): items.append(item) return items def main(): from random import shuffle canv = Canvas(width=500, height=500) canv.create_rectangle(50, 50, 250, 150, fill='red') canv.pack(fill=BOTH, expand=YES) rect = RectTracker(canv) # draw some base rectangles rect.draw([50,50], [250, 150], fill='red', tags=('red', 'box')) rect.draw([300,300], [400, 450], fill='green', tags=('gre', 'box')) # just for fun x, y = None, None def cool_design(event): global x, y kill_xy() dashes = [3, 2] x = canv.create_line(event.x, 0, event.x, 1000, dash=dashes, tags='no') y = canv.create_line(0, event.y, 1000, event.y, dash=dashes, tags='no') def kill_xy(event=None): canv.delete('no') canv.bind('', cool_design, '+') # command def onDrag(start, end): global x,y items = rect.hit_test(start, end) for x in rect.items: if x not in items: canv.itemconfig(x, fill='grey') else: canv.itemconfig(x, fill='blue') rect.autodraw(fill="", width=2, command=onDrag) mainloop() if __name__ == '__main__': try: from tkinter import * except ImportError: from Tkinter import * main()