blob: 1982a5081c0b2666b2ce455560b56d289eac61b8 [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
Martin Panterb44c4522016-05-29 08:13:58 +000027 double-clicked. The behavior may actually be determined by the pile
Guido van Rossum9a8cb841997-04-03 00:04:51 +000028 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()