| 
									
										
										
										
											2010-03-11 22:53:45 +00:00
										 |  |  | #! /usr/bin/env python3 | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-04 18:53:28 +00:00
										 |  |  | from tkinter import * | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | from Canvas import Oval, Group, CanvasText | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Fix a bug in Canvas.Group as distributed in Python 1.4.  The | 
					
						
							|  |  |  | # distributed bind() method is broken.  This is what should be used: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Group(Group): | 
					
						
							|  |  |  |     def bind(self, sequence=None, command=None): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         return self.canvas.tag_bind(self.id, sequence, command) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Object: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Base class for composite graphical objects.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Objects belong to a canvas, and can be moved around on the canvas. | 
					
						
							|  |  |  |     They also belong to at most one ``pile'' of objects, and can be | 
					
						
							|  |  |  |     transferred between piles (or removed from their pile). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Objects have a canonical ``x, y'' position which is moved when the | 
					
						
							|  |  |  |     object is moved.  Where the object is relative to this position | 
					
						
							|  |  |  |     depends on the object; for simple objects, it may be their center. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Objects have mouse sensitivity.  They can be clicked, dragged and | 
					
						
							|  |  |  |     double-clicked.  The behavior may actually determined by the pile | 
					
						
							|  |  |  |     they are in. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     All instance attributes are public since the derived class may | 
					
						
							|  |  |  |     need them. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, canvas, x=0, y=0, fill='red', text='object'): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.canvas = canvas | 
					
						
							|  |  |  |         self.x = x | 
					
						
							|  |  |  |         self.y = y | 
					
						
							|  |  |  |         self.pile = None | 
					
						
							|  |  |  |         self.group = Group(self.canvas) | 
					
						
							|  |  |  |         self.createitems(fill, text) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __str__(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         return str(self.group) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def createitems(self, fill, text): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.__oval = Oval(self.canvas, | 
					
						
							|  |  |  |                            self.x-20, self.y-10, self.x+20, self.y+10, | 
					
						
							|  |  |  |                            fill=fill, width=3) | 
					
						
							|  |  |  |         self.group.addtag_withtag(self.__oval) | 
					
						
							|  |  |  |         self.__text = CanvasText(self.canvas, | 
					
						
							|  |  |  |                            self.x, self.y, text=text) | 
					
						
							|  |  |  |         self.group.addtag_withtag(self.__text) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def moveby(self, dx, dy): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         if dx == dy == 0: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         self.group.move(dx, dy) | 
					
						
							|  |  |  |         self.x = self.x + dx | 
					
						
							|  |  |  |         self.y = self.y + dy | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def moveto(self, x, y): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.moveby(x - self.x, y - self.y) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def transfer(self, pile): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         if self.pile: | 
					
						
							|  |  |  |             self.pile.delete(self) | 
					
						
							|  |  |  |             self.pile = None | 
					
						
							|  |  |  |         self.pile = pile | 
					
						
							|  |  |  |         if self.pile: | 
					
						
							|  |  |  |             self.pile.add(self) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def tkraise(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.group.tkraise() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Bottom(Object): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """An object to serve as the bottom of a pile.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def createitems(self, *args): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.__oval = Oval(self.canvas, | 
					
						
							|  |  |  |                            self.x-20, self.y-10, self.x+20, self.y+10, | 
					
						
							|  |  |  |                            fill='gray', outline='') | 
					
						
							|  |  |  |         self.group.addtag_withtag(self.__oval) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Pile: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """A group of graphical objects.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, canvas, x, y, tag=None): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.canvas = canvas | 
					
						
							|  |  |  |         self.x = x | 
					
						
							|  |  |  |         self.y = y | 
					
						
							|  |  |  |         self.objects = [] | 
					
						
							|  |  |  |         self.bottom = Bottom(self.canvas, self.x, self.y) | 
					
						
							|  |  |  |         self.group = Group(self.canvas, tag=tag) | 
					
						
							|  |  |  |         self.group.addtag_withtag(self.bottom.group) | 
					
						
							|  |  |  |         self.bindhandlers() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def bindhandlers(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.group.bind('<1>', self.clickhandler) | 
					
						
							|  |  |  |         self.group.bind('<Double-1>', self.doubleclickhandler) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def add(self, object): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.objects.append(object) | 
					
						
							|  |  |  |         self.group.addtag_withtag(object.group) | 
					
						
							|  |  |  |         self.position(object) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def delete(self, object): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         object.group.dtag(self.group) | 
					
						
							|  |  |  |         self.objects.remove(object) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def position(self, object): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         object.tkraise() | 
					
						
							|  |  |  |         i = self.objects.index(object) | 
					
						
							|  |  |  |         object.moveto(self.x + i*4, self.y + i*8) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def clickhandler(self, event): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         pass | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def doubleclickhandler(self, event): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         pass | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MovingPile(Pile): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def bindhandlers(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         Pile.bindhandlers(self) | 
					
						
							|  |  |  |         self.group.bind('<B1-Motion>', self.motionhandler) | 
					
						
							|  |  |  |         self.group.bind('<ButtonRelease-1>', self.releasehandler) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     movethis = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def clickhandler(self, event): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         tags = self.canvas.gettags('current') | 
					
						
							|  |  |  |         for i in range(len(self.objects)): | 
					
						
							|  |  |  |             o = self.objects[i] | 
					
						
							|  |  |  |             if o.group.tag in tags: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.movethis = None | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         self.movethis = self.objects[i:] | 
					
						
							|  |  |  |         for o in self.movethis: | 
					
						
							|  |  |  |             o.tkraise() | 
					
						
							|  |  |  |         self.lastx = event.x | 
					
						
							|  |  |  |         self.lasty = event.y | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     doubleclickhandler = clickhandler | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def motionhandler(self, event): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         if not self.movethis: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         dx = event.x - self.lastx | 
					
						
							|  |  |  |         dy = event.y - self.lasty | 
					
						
							|  |  |  |         self.lastx = event.x | 
					
						
							|  |  |  |         self.lasty = event.y | 
					
						
							|  |  |  |         for o in self.movethis: | 
					
						
							|  |  |  |             o.moveby(dx, dy) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def releasehandler(self, event): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         objects = self.movethis | 
					
						
							|  |  |  |         if not objects: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         self.movethis = None | 
					
						
							|  |  |  |         self.finishmove(objects) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def finishmove(self, objects): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         for o in objects: | 
					
						
							|  |  |  |             self.position(o) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Pile1(MovingPile): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     x = 50 | 
					
						
							|  |  |  |     y = 50 | 
					
						
							|  |  |  |     tag = 'p1' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, demo): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.demo = demo | 
					
						
							|  |  |  |         MovingPile.__init__(self, self.demo.canvas, self.x, self.y, self.tag) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def doubleclickhandler(self, event): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             o = self.objects[-1] | 
					
						
							|  |  |  |         except IndexError: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         o.transfer(self.other()) | 
					
						
							|  |  |  |         MovingPile.doubleclickhandler(self, event) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def other(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         return self.demo.p2 | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def finishmove(self, objects): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         o = objects[0] | 
					
						
							|  |  |  |         p = self.other() | 
					
						
							|  |  |  |         x, y = o.x, o.y | 
					
						
							|  |  |  |         if (x-p.x)**2 + (y-p.y)**2 < (x-self.x)**2 + (y-self.y)**2: | 
					
						
							|  |  |  |             for o in objects: | 
					
						
							|  |  |  |                 o.transfer(p) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             MovingPile.finishmove(self, objects) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Pile2(Pile1): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     x = 150 | 
					
						
							|  |  |  |     y = 50 | 
					
						
							|  |  |  |     tag = 'p2' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def other(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         return self.demo.p1 | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Demo: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, master): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.master = master | 
					
						
							|  |  |  |         self.canvas = Canvas(master, | 
					
						
							|  |  |  |                              width=200, height=200, | 
					
						
							|  |  |  |                              background='yellow', | 
					
						
							|  |  |  |                              relief=SUNKEN, borderwidth=2) | 
					
						
							|  |  |  |         self.canvas.pack(expand=1, fill=BOTH) | 
					
						
							|  |  |  |         self.p1 = Pile1(self) | 
					
						
							|  |  |  |         self.p2 = Pile2(self) | 
					
						
							|  |  |  |         o1 = Object(self.canvas, fill='red', text='o1') | 
					
						
							|  |  |  |         o2 = Object(self.canvas, fill='green', text='o2') | 
					
						
							|  |  |  |         o3 = Object(self.canvas, fill='light blue', text='o3') | 
					
						
							|  |  |  |         o1.transfer(self.p1) | 
					
						
							|  |  |  |         o2.transfer(self.p1) | 
					
						
							|  |  |  |         o3.transfer(self.p2) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Main function, run when invoked as a stand-alone Python program. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def main(): | 
					
						
							|  |  |  |     root = Tk() | 
					
						
							|  |  |  |     demo = Demo(root) | 
					
						
							|  |  |  |     root.protocol('WM_DELETE_WINDOW', root.quit) | 
					
						
							|  |  |  |     root.mainloop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     main() |