blob: 74ed76f61b963ae2099cb8c45b8a116192eb6423 [file] [log] [blame]
Guido van Rossum9a8cb841997-04-03 00:04:51 +00001#! /usr/bin/env python
2
3from Tkinter import *
4from Canvas import Oval, Group, CanvasText
5
6
7# Fix a bug in Canvas.Group as distributed in Python 1.4. The
8# distributed bind() method is broken. This is what should be used:
9
10class Group(Group):
11 def bind(self, sequence=None, command=None):
Tim Peters182b5ac2004-07-18 06:16:08 +000012 return self.canvas.tag_bind(self.id, sequence, command)
Guido van Rossum9a8cb841997-04-03 00:04:51 +000013
14class Object:
15
16 """Base class for composite graphical objects.
17
18 Objects belong to a canvas, and can be moved around on the canvas.
19 They also belong to at most one ``pile'' of objects, and can be
20 transferred between piles (or removed from their pile).
21
22 Objects have a canonical ``x, y'' position which is moved when the
23 object is moved. Where the object is relative to this position
24 depends on the object; for simple objects, it may be their center.
25
26 Objects have mouse sensitivity. They can be clicked, dragged and
27 double-clicked. The behavior may actually determined by the pile
28 they are in.
29
30 All instance attributes are public since the derived class may
31 need them.
32
33 """
34
35 def __init__(self, canvas, x=0, y=0, fill='red', text='object'):
Tim Peters182b5ac2004-07-18 06:16:08 +000036 self.canvas = canvas
37 self.x = x
38 self.y = y
39 self.pile = None
40 self.group = Group(self.canvas)
41 self.createitems(fill, text)
Guido van Rossum9a8cb841997-04-03 00:04:51 +000042
43 def __str__(self):
Tim Peters182b5ac2004-07-18 06:16:08 +000044 return str(self.group)
Guido van Rossum9a8cb841997-04-03 00:04:51 +000045
46 def createitems(self, fill, text):
Tim Peters182b5ac2004-07-18 06:16:08 +000047 self.__oval = Oval(self.canvas,
48 self.x-20, self.y-10, self.x+20, self.y+10,
49 fill=fill, width=3)
50 self.group.addtag_withtag(self.__oval)
51 self.__text = CanvasText(self.canvas,
52 self.x, self.y, text=text)
53 self.group.addtag_withtag(self.__text)
Guido van Rossum9a8cb841997-04-03 00:04:51 +000054
55 def moveby(self, dx, dy):
Tim Peters182b5ac2004-07-18 06:16:08 +000056 if dx == dy == 0:
57 return
58 self.group.move(dx, dy)
59 self.x = self.x + dx
60 self.y = self.y + dy
Guido van Rossum9a8cb841997-04-03 00:04:51 +000061
62 def moveto(self, x, y):
Tim Peters182b5ac2004-07-18 06:16:08 +000063 self.moveby(x - self.x, y - self.y)
Guido van Rossum9a8cb841997-04-03 00:04:51 +000064
65 def transfer(self, pile):
Tim Peters182b5ac2004-07-18 06:16:08 +000066 if self.pile:
67 self.pile.delete(self)
68 self.pile = None
69 self.pile = pile
70 if self.pile:
71 self.pile.add(self)
Guido van Rossum9a8cb841997-04-03 00:04:51 +000072
73 def tkraise(self):
Tim Peters182b5ac2004-07-18 06:16:08 +000074 self.group.tkraise()
Guido van Rossum9a8cb841997-04-03 00:04:51 +000075
76
77class Bottom(Object):
78
79 """An object to serve as the bottom of a pile."""
80
81 def createitems(self, *args):
Tim Peters182b5ac2004-07-18 06:16:08 +000082 self.__oval = Oval(self.canvas,
83 self.x-20, self.y-10, self.x+20, self.y+10,
84 fill='gray', outline='')
85 self.group.addtag_withtag(self.__oval)
Guido van Rossum9a8cb841997-04-03 00:04:51 +000086
87
88class Pile:
89
90 """A group of graphical objects."""
91
92 def __init__(self, canvas, x, y, tag=None):
Tim Peters182b5ac2004-07-18 06:16:08 +000093 self.canvas = canvas
94 self.x = x
95 self.y = y
96 self.objects = []
97 self.bottom = Bottom(self.canvas, self.x, self.y)
98 self.group = Group(self.canvas, tag=tag)
99 self.group.addtag_withtag(self.bottom.group)
100 self.bindhandlers()
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000101
102 def bindhandlers(self):
Tim Peters182b5ac2004-07-18 06:16:08 +0000103 self.group.bind('<1>', self.clickhandler)
104 self.group.bind('<Double-1>', self.doubleclickhandler)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000105
106 def add(self, object):
Tim Peters182b5ac2004-07-18 06:16:08 +0000107 self.objects.append(object)
108 self.group.addtag_withtag(object.group)
109 self.position(object)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000110
111 def delete(self, object):
Tim Peters182b5ac2004-07-18 06:16:08 +0000112 object.group.dtag(self.group)
113 self.objects.remove(object)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000114
115 def position(self, object):
Tim Peters182b5ac2004-07-18 06:16:08 +0000116 object.tkraise()
117 i = self.objects.index(object)
118 object.moveto(self.x + i*4, self.y + i*8)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000119
120 def clickhandler(self, event):
Tim Peters182b5ac2004-07-18 06:16:08 +0000121 pass
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000122
123 def doubleclickhandler(self, event):
Tim Peters182b5ac2004-07-18 06:16:08 +0000124 pass
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000125
126
127class MovingPile(Pile):
128
129 def bindhandlers(self):
Tim Peters182b5ac2004-07-18 06:16:08 +0000130 Pile.bindhandlers(self)
131 self.group.bind('<B1-Motion>', self.motionhandler)
132 self.group.bind('<ButtonRelease-1>', self.releasehandler)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000133
134 movethis = None
135
136 def clickhandler(self, event):
Tim Peters182b5ac2004-07-18 06:16:08 +0000137 tags = self.canvas.gettags('current')
138 for i in range(len(self.objects)):
139 o = self.objects[i]
140 if o.group.tag in tags:
141 break
142 else:
143 self.movethis = None
144 return
145 self.movethis = self.objects[i:]
146 for o in self.movethis:
147 o.tkraise()
148 self.lastx = event.x
149 self.lasty = event.y
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000150
151 doubleclickhandler = clickhandler
152
153 def motionhandler(self, event):
Tim Peters182b5ac2004-07-18 06:16:08 +0000154 if not self.movethis:
155 return
156 dx = event.x - self.lastx
157 dy = event.y - self.lasty
158 self.lastx = event.x
159 self.lasty = event.y
160 for o in self.movethis:
161 o.moveby(dx, dy)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000162
163 def releasehandler(self, event):
Tim Peters182b5ac2004-07-18 06:16:08 +0000164 objects = self.movethis
165 if not objects:
166 return
167 self.movethis = None
168 self.finishmove(objects)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000169
170 def finishmove(self, objects):
Tim Peters182b5ac2004-07-18 06:16:08 +0000171 for o in objects:
172 self.position(o)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000173
174
175class Pile1(MovingPile):
176
177 x = 50
178 y = 50
179 tag = 'p1'
180
181 def __init__(self, demo):
Tim Peters182b5ac2004-07-18 06:16:08 +0000182 self.demo = demo
183 MovingPile.__init__(self, self.demo.canvas, self.x, self.y, self.tag)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000184
185 def doubleclickhandler(self, event):
Tim Peters182b5ac2004-07-18 06:16:08 +0000186 try:
187 o = self.objects[-1]
188 except IndexError:
189 return
190 o.transfer(self.other())
191 MovingPile.doubleclickhandler(self, event)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000192
193 def other(self):
Tim Peters182b5ac2004-07-18 06:16:08 +0000194 return self.demo.p2
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000195
196 def finishmove(self, objects):
Tim Peters182b5ac2004-07-18 06:16:08 +0000197 o = objects[0]
198 p = self.other()
199 x, y = o.x, o.y
200 if (x-p.x)**2 + (y-p.y)**2 < (x-self.x)**2 + (y-self.y)**2:
201 for o in objects:
202 o.transfer(p)
203 else:
204 MovingPile.finishmove(self, objects)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000205
206class Pile2(Pile1):
207
208 x = 150
209 y = 50
210 tag = 'p2'
211
212 def other(self):
Tim Peters182b5ac2004-07-18 06:16:08 +0000213 return self.demo.p1
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000214
215
216class Demo:
217
218 def __init__(self, master):
Tim Peters182b5ac2004-07-18 06:16:08 +0000219 self.master = master
220 self.canvas = Canvas(master,
221 width=200, height=200,
222 background='yellow',
223 relief=SUNKEN, borderwidth=2)
224 self.canvas.pack(expand=1, fill=BOTH)
225 self.p1 = Pile1(self)
226 self.p2 = Pile2(self)
227 o1 = Object(self.canvas, fill='red', text='o1')
228 o2 = Object(self.canvas, fill='green', text='o2')
229 o3 = Object(self.canvas, fill='light blue', text='o3')
230 o1.transfer(self.p1)
231 o2.transfer(self.p1)
232 o3.transfer(self.p2)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000233
234
235# Main function, run when invoked as a stand-alone Python program.
236
237def main():
238 root = Tk()
239 demo = Demo(root)
240 root.protocol('WM_DELETE_WINDOW', root.quit)
241 root.mainloop()
242
243if __name__ == '__main__':
244 main()