comparison light8/stage.py @ 7:1b0266dd233a

picking and dragging works pretty well (no ctrl yet)
author drewp
date Sun, 07 Jul 2002 04:52:48 +0000
parents 119369e60da1
children 6faae180d1c5
comparison
equal deleted inserted replaced
6:119369e60da1 7:1b0266dd233a
28 lights should be able to be interactively 'locked', which blocks 28 lights should be able to be interactively 'locked', which blocks
29 them from being selected. 29 them from being selected.
30 30
31 """ 31 """
32 def __init__(self,parent,**kw): 32 def __init__(self,parent,**kw):
33 if 'stageimage' in kw:
34 stageimage=kw['stageimage']
35 del kw['stageimage']
36 else:
37 stageimage=None
38
39 Canvas.__init__(self,parent,**kw) 33 Canvas.__init__(self,parent,**kw)
40
41 if stageimage:
42 img = Image('photo',stageimage)
43 self.create_image(0,0,anchor='nw',image=img)
44 self.create_rectangle(5,5,50,50)
45 34
46 self.bind("<ButtonPress-1>", self.leftpress) 35 self.bind("<ButtonPress-1>", self.leftpress)
47 self.bind("<B1-Motion>", self.leftmotion) 36 self.bind("<B1-Motion>", self.leftmotion)
48 self.bind("<ButtonRelease-1>", self.leftrelease) 37 self.bind("<ButtonRelease-1>", self.leftrelease)
49 38
51 40
52 self.lmbstate=None # as you perform with LMB, this goes from None to 'pressed','rectangle','levelchange' 41 self.lmbstate=None # as you perform with LMB, this goes from None to 'pressed','rectangle','levelchange'
53 42
54 self.alllights=[] 43 self.alllights=[]
55 self.selectedlights=[] 44 self.selectedlights=[]
56 45 self.alllighttags={} # tag: name lookup
46
47
48 def setimage(self,stageimage):
49 img = Image('photo',file=stageimage)
50 self.img=img # can't lose this!
51 print img.width()
52 self.create_image(0,0,anchor='nw',image=img)
53 self.config(width=img.width(),height=img.height())
54
57 # 55 #
58 # selection management 56 # selection management
59 # 57 #
58 def updateselectionboxes(self):
59 "make selection boxes that match self.selectedlights"
60 self.delete("selectbox")
61 for l in self.selectedlights:
62 for c in self.getlightbboxes(l):
63 self.create_rectangle(c[0]-2,c[1]-2,c[2]+2,c[3]+2,outline='red',tag="selectbox")
64
60 def clearselection(self,dyn=0): 65 def clearselection(self,dyn=0):
61 if dyn: 66 self.selectedlights=[]
62 seltag="dynselection" 67 self.updateselectionboxes()
63 else: 68
64 seltag="selection" 69 def markfordynselection(self):
65 for o in self.find_withtag(seltag): 70 """call this before calls to replacedynselection"""
66 self.select(o,0,dyn) 71 self.origselection = self.selectedlights
67 72
68 def replacedynselection(self,newlightnames): 73 def replacedynselection(self,newlightnames):
69 for o in self.find_withtag('dynselection'): 74 """as a dynamic selection changes, keep calling this function with the
70 self.select(o,0,1) 75 names of the lights in the dynamic selection. the original selection (at the time
71 for o in newlightnames: 76 of markfordynselection) will be shown along with any new lights"""
72 self.select(o,1,1) 77 self.selectedlights = self.origselection + [l for l in newlightnames if l not in self.origselection]
73 78 self.updateselectionboxes()
74 def select(self,obj,select=1,dyn=0): # select=0 for deselect 79
75 if dyn: 80 def select(self,lightname,select=1,dyn=0): # select=0 for deselect
76 seltag="dynselection"
77 else:
78 seltag="selection"
79 if select: 81 if select:
80 print obj,"into selection" 82 if lightname not in self.selectedlights:
81 self.addtag_withtag(seltag,obj) 83 self.selectedlights.append(lightname)
82 for c in self.getlightbboxes(obj): 84 elif lightname in self.selectedlights:
83 self.create_rectangle(c[0]-2,c[1]-2,c[2]+2,c[3]+2,outline='red',tag="selectbox_%s"%obj) 85 self.selectedlights.remove(lightname)
84 else: 86
85 print obj,"out of select" 87 self.updateselectionboxes()
86 self.dtag(obj,seltag)
87 if 'selection' not in self.gettags(obj) and 'dynselection' not in self.gettags(obj):
88 self.delete("selectbox_%s"%obj)
89 88
90 def incorporatedynselection(self):
91 "put all dynselected objects in the regular selection"
92 for o in self.find_withtag('dynselection'):
93 self.dtag(o,'dynselection')
94 self.addtag_withtag('selection',o)
95 # no change for the graphics
96 89
97 # 90 #
98 # LMB click or drag 91 # LMB click or drag
99 # 92 #
100 def leftpress(self,ev): 93 def leftpress(self,ev):
107 touching=self.findoverlappinglights((ev.x-self.halo,ev.y-self.halo,ev.x+self.halo,ev.y+self.halo)) 100 touching=self.findoverlappinglights((ev.x-self.halo,ev.y-self.halo,ev.x+self.halo,ev.y+self.halo))
108 istouching=len(touching)>0 101 istouching=len(touching)>0
109 102
110 if not istouching: 103 if not istouching:
111 # clicked in space 104 # clicked in space
112 if not shifted and not control: 105 if not shifted and not control and len(self.selectedlights)>0:
113 # either a deselect (if no motion) or a level change (if motion) 106 # either a deselect (if no motion) or a level change (if motion)
114 self.clearselection() 107 self.lmbstate = 'deselect-or-level'
115 self.lmbstate='rectangle' 108 if shifted or control or len(self.selectedlights)==0:
116 if shifted or control:
117 # with shift/control, add/subtract lights to selection 109 # with shift/control, add/subtract lights to selection
118 self.lmbstate='rectangle' 110 self.lmbstate='rectangle'
119 111
120 else: 112 else:
121 # clicked a selectable object 113 # clicked a selectable object
122 # toggle selection 114 # toggle selection
123 if 'selection' in self.gettags(touching[0]): 115 if touching[0] in self.selectedlights:
124 if shifted: 116 if shifted:
125 # deselect 117 # deselect
126 self.select(touching[0],0) 118 self.select(touching[0],0)
127 # and do nothing else 119 # and do nothing else
128 self.lmbstate=None 120 self.lmbstate=None
138 self.clearselection() 130 self.clearselection()
139 self.select(touching[0]) 131 self.select(touching[0])
140 # and adjust levels now 132 # and adjust levels now
141 self.lmbstate='levelchange' 133 self.lmbstate='levelchange'
142 134
143 135 if self.lmbstate=='rectangle':
136 self.markfordynselection()
144 def leftmotion(self,ev): 137 def leftmotion(self,ev):
145 138
146 coords=(ev.x,ev.y) 139 coords=(ev.x,ev.y)
147 140
148 shifted=ev.state & 1 141 shifted=ev.state & 1
149 control=ev.state & 4 142 control=ev.state & 4
143
144 if self.lmbstate=='deselect-or-level':
145 if (coords[0]-self.lmbstart[0])**2+(coords[1]-self.lmbstart[1])**2>self.halo**2:
146 # they moved enough, it's a level change
147 self.lmbstate='levelchange'
150 148
151 if self.lmbstate=='levelchange': 149 if self.lmbstate=='levelchange':
152 delta = self.lmbstart[1]-ev.y 150 delta = self.lmbstart[1]-ev.y
153 print "change by",delta 151 print "change by",delta
154 152
168 def leftrelease(self,ev): 166 def leftrelease(self,ev):
169 if self.lmbstate: 167 if self.lmbstate:
170 168
171 if self.lmbstate=='rectangle': 169 if self.lmbstate=='rectangle':
172 self.delete('selectrect') 170 self.delete('selectrect')
171
172 if self.lmbstate=='deselect-or-level':
173 # they didn't move enough to promote the mode to level, so it's a deselect click
174 self.clearselection()
173 175
174 # all items that were in dynselection join the selection 176 # all items that were in dynselection join the selection
175 self.incorporatedynselection() 177 # self.incorporatedynselection()
176 178
177 self.lmbstate=None 179 self.lmbstate=None
178 180
181 #
182 # light names vs. canvas object tags
183 #
179 def nametag(self,name): 184 def nametag(self,name):
180 "returns a safe version of the name that won't match other names" 185 "returns a safe version of the name that won't match other names"
181 return name.replace(" ","__") 186 return name.replace(" ","__")
182 187
188 def tagtoname(self,tag):
189 "finds the real light name for a tag written by nametag()"
190 return self.alllighttags[tag]
191
192 #
193 # light methods
194 #
183 def addlight(self,name,location,aim=None): 195 def addlight(self,name,location,aim=None):
184
185 tags='light selectable name_%s' % self.nametag(name) 196 tags='light selectable name_%s' % self.nametag(name)
186 197
187 self.create_oval(location[0]-2,location[1]-2, 198 self.create_oval(location[0]-2,location[1]-2,
188 location[0]+2,location[1]+2, 199 location[0]+2,location[1]+2,
189 fill='red',tag=tags+" hotspot") 200 fill='red',tag=tags+" hotspot")
201 if aim:
202 self.create_oval(aim[0]-2,aim[1]-2,
203 aim[0]+2,aim[1]+2,
204 fill='red',tag=tags+" hotspot")
205 self.create_line(location[0],location[1],aim[0],aim[1],stipple='gray50',
206 arrow='last',arrowshape="9 15 6",tag='light')
190 self.create_text(location[0],location[1]+5,anchor='n',text=name,tag=tags) 207 self.create_text(location[0],location[1]+5,anchor='n',text=name,tag=tags)
191 self.alllights.append(name) 208 self.alllights.append(name)
209 self.alllighttags[self.nametag(name)]=name
192 210
193 def getlightbboxes(self,tag): 211 def getlightbboxes(self,tag):
194 """returns a list of bboxes for a light with a given name_ tag. the selection 212 """returns a list of bboxes for a light with a given name_ tag. the selection
195 mechanism draws around these bboxes to show that a light is selected""" 213 mechanism draws around these bboxes to show that a light is selected"""
196 bboxes=[] 214 bboxes=[]
197 for o in self.find_withtag(tag): 215 for o in self.find_withtag("name_%s" % self.nametag(tag)):
198 if 'hotspot' in self.gettags(o): 216 if 'hotspot' in self.gettags(o):
199 bboxes.append(self.bbox(o)) 217 bboxes.append(self.bbox(o))
200 return bboxes 218 return bboxes
201 219
202 def findoverlappinglights(self,box,enclosed=0): 220 def findoverlappinglights(self,box,enclosed=0):
204 lights=[] 222 lights=[]
205 if enclosed: 223 if enclosed:
206 candidates = self.find_enclosed(*box) 224 candidates = self.find_enclosed(*box)
207 else: 225 else:
208 candidates = self.find_overlapping(*box) 226 candidates = self.find_overlapping(*box)
227
209 for o in candidates: 228 for o in candidates:
210 for t in self.gettags(o): 229 for t in self.gettags(o):
211 if t.startswith("name_") and t not in lights: 230 if t.startswith("name_"):
212 lights.append(t) 231 n = self.tagtoname(t[5:])
232 if n and (n not in lights):
233 lights.append(n)
213 return lights 234 return lights
214 235
215 root=Tk() 236 root=Tk()
216 s=Stage(root,stageimage='guysanddolls.ppm') 237 root.wm_geometry("+376+330")
217 s.addlight('drew',(80,80)) 238 s=Stage(root)
218 s.addlight('house',(150,80)) 239 s.setimage('guysanddolls.ppm')
219 s.addlight('barn',(200,80))
220 s.pack() 240 s.pack()
241 s.addlight('drew',(330,640),(90,20))
242 s.addlight('house',(360,640))
243 s.addlight('barn',(390,640))
221 244
222 root.mainloop() 245 root.mainloop()