blob: 16312b5becc9f1bb112a99decc53becc8b1cf187 [file] [log] [blame]
Benjamin Peterson90f5ba52010-03-11 22:53:45 +00001#! /usr/bin/env python3
Guido van Rossum9a8cb841997-04-03 00:04:51 +00002
Benjamin Petersond6d63f52009-01-04 18:53:28 +00003from tkinter import *
Guido van Rossum9a8cb841997-04-03 00:04:51 +00004
5
Georg Brandl856023a2010-10-25 17:50:20 +00006# Since Canvas.Group is no longer present, the following class reproduces
7# a subset of the old Group class that is used by this app.
Guido van Rossum9a8cb841997-04-03 00:04:51 +00008
Georg Brandl856023a2010-10-25 17:50:20 +00009class Group:
10 def __init__(self, canvas, tag=None):
11 if tag is None:
12 tag = 'Group%d' % id(self)
13
14 self.tag = self.id = tag
15 self.canvas = canvas
16 self.canvas.dtag(self.tag)
17
18 def __str__(self):
19 return self.tag
20
21 def _do(self, cmd, *args):
22 return self.canvas.tk.call(self.canvas._w, cmd, self.tag, *args)
23
24 def addtag_withtag(self, tagOrId):
25 self._do('addtag', 'withtag', tagOrId)
26
27 def bind(self, sequence=None, command=None, add=None):
28 return self.canvas.tag_bind(self.id, sequence, command, add)
29
30 def move(self, x_amount, y_amount):
31 self._do('move', x_amount, y_amount)
32
33 def dtag(self, tagToDelete=None):
34 self._do('dtag', tagToDelete)
35
36 def tkraise(self, aboveThis=None):
37 self._do('raise', aboveThis)
38
Guido van Rossum9a8cb841997-04-03 00:04:51 +000039
40class Object:
41
42 """Base class for composite graphical objects.
43
44 Objects belong to a canvas, and can be moved around on the canvas.
45 They also belong to at most one ``pile'' of objects, and can be
46 transferred between piles (or removed from their pile).
47
48 Objects have a canonical ``x, y'' position which is moved when the
49 object is moved. Where the object is relative to this position
50 depends on the object; for simple objects, it may be their center.
51
52 Objects have mouse sensitivity. They can be clicked, dragged and
53 double-clicked. The behavior may actually determined by the pile
54 they are in.
55
56 All instance attributes are public since the derived class may
57 need them.
Guido van Rossum9a8cb841997-04-03 00:04:51 +000058 """
59
60 def __init__(self, canvas, x=0, y=0, fill='red', text='object'):
Tim Peters182b5ac2004-07-18 06:16:08 +000061 self.canvas = canvas
62 self.x = x
63 self.y = y
64 self.pile = None
65 self.group = Group(self.canvas)
66 self.createitems(fill, text)
Guido van Rossum9a8cb841997-04-03 00:04:51 +000067
68 def __str__(self):
Tim Peters182b5ac2004-07-18 06:16:08 +000069 return str(self.group)
Guido van Rossum9a8cb841997-04-03 00:04:51 +000070
71 def createitems(self, fill, text):
Georg Brandl856023a2010-10-25 17:50:20 +000072 self.__oval = self.canvas.create_oval(self.x - 20, self.y - 10,
73 self.x + 20, self.y + 20, fill=fill, width=3)
Tim Peters182b5ac2004-07-18 06:16:08 +000074 self.group.addtag_withtag(self.__oval)
Georg Brandl856023a2010-10-25 17:50:20 +000075 self.__text = self.canvas.create_text(self.x, self.y, text=text)
Tim Peters182b5ac2004-07-18 06:16:08 +000076 self.group.addtag_withtag(self.__text)
Guido van Rossum9a8cb841997-04-03 00:04:51 +000077
78 def moveby(self, dx, dy):
Tim Peters182b5ac2004-07-18 06:16:08 +000079 if dx == dy == 0:
80 return
81 self.group.move(dx, dy)
82 self.x = self.x + dx
83 self.y = self.y + dy
Guido van Rossum9a8cb841997-04-03 00:04:51 +000084
85 def moveto(self, x, y):
Tim Peters182b5ac2004-07-18 06:16:08 +000086 self.moveby(x - self.x, y - self.y)
Guido van Rossum9a8cb841997-04-03 00:04:51 +000087
88 def transfer(self, pile):
Tim Peters182b5ac2004-07-18 06:16:08 +000089 if self.pile:
90 self.pile.delete(self)
91 self.pile = None
92 self.pile = pile
93 if self.pile:
94 self.pile.add(self)
Guido van Rossum9a8cb841997-04-03 00:04:51 +000095
96 def tkraise(self):
Tim Peters182b5ac2004-07-18 06:16:08 +000097 self.group.tkraise()
Guido van Rossum9a8cb841997-04-03 00:04:51 +000098
99
100class Bottom(Object):
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000101 """An object to serve as the bottom of a pile."""
102
103 def createitems(self, *args):
Georg Brandl856023a2010-10-25 17:50:20 +0000104 self.__oval = self.canvas.create_oval(self.x - 20, self.y - 10,
105 self.x + 20, self.y + 10, fill='gray', outline='')
Tim Peters182b5ac2004-07-18 06:16:08 +0000106 self.group.addtag_withtag(self.__oval)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000107
108
109class Pile:
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000110 """A group of graphical objects."""
111
112 def __init__(self, canvas, x, y, tag=None):
Tim Peters182b5ac2004-07-18 06:16:08 +0000113 self.canvas = canvas
114 self.x = x
115 self.y = y
116 self.objects = []
117 self.bottom = Bottom(self.canvas, self.x, self.y)
118 self.group = Group(self.canvas, tag=tag)
119 self.group.addtag_withtag(self.bottom.group)
120 self.bindhandlers()
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000121
122 def bindhandlers(self):
Tim Peters182b5ac2004-07-18 06:16:08 +0000123 self.group.bind('<1>', self.clickhandler)
124 self.group.bind('<Double-1>', self.doubleclickhandler)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000125
126 def add(self, object):
Tim Peters182b5ac2004-07-18 06:16:08 +0000127 self.objects.append(object)
128 self.group.addtag_withtag(object.group)
129 self.position(object)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000130
131 def delete(self, object):
Tim Peters182b5ac2004-07-18 06:16:08 +0000132 object.group.dtag(self.group)
133 self.objects.remove(object)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000134
135 def position(self, object):
Tim Peters182b5ac2004-07-18 06:16:08 +0000136 object.tkraise()
137 i = self.objects.index(object)
138 object.moveto(self.x + i*4, self.y + i*8)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000139
140 def clickhandler(self, event):
Tim Peters182b5ac2004-07-18 06:16:08 +0000141 pass
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000142
143 def doubleclickhandler(self, event):
Tim Peters182b5ac2004-07-18 06:16:08 +0000144 pass
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000145
146
147class MovingPile(Pile):
148
149 def bindhandlers(self):
Tim Peters182b5ac2004-07-18 06:16:08 +0000150 Pile.bindhandlers(self)
151 self.group.bind('<B1-Motion>', self.motionhandler)
152 self.group.bind('<ButtonRelease-1>', self.releasehandler)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000153
154 movethis = None
155
156 def clickhandler(self, event):
Tim Peters182b5ac2004-07-18 06:16:08 +0000157 tags = self.canvas.gettags('current')
158 for i in range(len(self.objects)):
159 o = self.objects[i]
160 if o.group.tag in tags:
161 break
162 else:
163 self.movethis = None
164 return
165 self.movethis = self.objects[i:]
166 for o in self.movethis:
167 o.tkraise()
168 self.lastx = event.x
169 self.lasty = event.y
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000170
171 doubleclickhandler = clickhandler
172
173 def motionhandler(self, event):
Tim Peters182b5ac2004-07-18 06:16:08 +0000174 if not self.movethis:
175 return
176 dx = event.x - self.lastx
177 dy = event.y - self.lasty
178 self.lastx = event.x
179 self.lasty = event.y
180 for o in self.movethis:
181 o.moveby(dx, dy)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000182
183 def releasehandler(self, event):
Tim Peters182b5ac2004-07-18 06:16:08 +0000184 objects = self.movethis
185 if not objects:
186 return
187 self.movethis = None
188 self.finishmove(objects)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000189
190 def finishmove(self, objects):
Tim Peters182b5ac2004-07-18 06:16:08 +0000191 for o in objects:
192 self.position(o)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000193
194
195class Pile1(MovingPile):
196
197 x = 50
198 y = 50
199 tag = 'p1'
200
201 def __init__(self, demo):
Tim Peters182b5ac2004-07-18 06:16:08 +0000202 self.demo = demo
203 MovingPile.__init__(self, self.demo.canvas, self.x, self.y, self.tag)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000204
205 def doubleclickhandler(self, event):
Tim Peters182b5ac2004-07-18 06:16:08 +0000206 try:
207 o = self.objects[-1]
208 except IndexError:
209 return
210 o.transfer(self.other())
211 MovingPile.doubleclickhandler(self, event)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000212
213 def other(self):
Tim Peters182b5ac2004-07-18 06:16:08 +0000214 return self.demo.p2
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000215
216 def finishmove(self, objects):
Tim Peters182b5ac2004-07-18 06:16:08 +0000217 o = objects[0]
218 p = self.other()
219 x, y = o.x, o.y
220 if (x-p.x)**2 + (y-p.y)**2 < (x-self.x)**2 + (y-self.y)**2:
221 for o in objects:
222 o.transfer(p)
223 else:
224 MovingPile.finishmove(self, objects)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000225
226class Pile2(Pile1):
227
228 x = 150
229 y = 50
230 tag = 'p2'
231
232 def other(self):
Tim Peters182b5ac2004-07-18 06:16:08 +0000233 return self.demo.p1
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000234
235
236class Demo:
237
238 def __init__(self, master):
Tim Peters182b5ac2004-07-18 06:16:08 +0000239 self.master = master
240 self.canvas = Canvas(master,
241 width=200, height=200,
242 background='yellow',
243 relief=SUNKEN, borderwidth=2)
244 self.canvas.pack(expand=1, fill=BOTH)
245 self.p1 = Pile1(self)
246 self.p2 = Pile2(self)
247 o1 = Object(self.canvas, fill='red', text='o1')
248 o2 = Object(self.canvas, fill='green', text='o2')
249 o3 = Object(self.canvas, fill='light blue', text='o3')
250 o1.transfer(self.p1)
251 o2.transfer(self.p1)
252 o3.transfer(self.p2)
Guido van Rossum9a8cb841997-04-03 00:04:51 +0000253
254
255# Main function, run when invoked as a stand-alone Python program.
256
257def main():
258 root = Tk()
259 demo = Demo(root)
260 root.protocol('WM_DELETE_WINDOW', root.quit)
261 root.mainloop()
262
263if __name__ == '__main__':
264 main()