comparison light8/stage.py @ 6:119369e60da1

tag-heavy selection management is getting hard - about to switch to python lists
author drewp
date Sun, 07 Jul 2002 03:47:18 +0000
parents a76f775bb635
children 1b0266dd233a
comparison
equal deleted inserted replaced
5:a76f775bb635 6:119369e60da1
1 from Tkinter import *
2
3
4 def printevent(ev):
5 for k in dir(ev):
6 if not k.startswith('__'):
7 print k,getattr(ev,k)
8 print ""
9
1 10
2 class Stage(Canvas): 11 class Stage(Canvas):
3 12
4 """a fancy widget that shows light locations (and optionally their 13 """a fancy widget that shows light locations (and optionally their
5 aim locations on an image of the stage. you can select or 14 aim locations on an image of the stage. you can select or
11 re-clicking a light with shift key down toggles whether it's in the selection. 20 re-clicking a light with shift key down toggles whether it's in the selection.
12 ctrl-drag-rectangle deselects the lights in the rectangle, 21 ctrl-drag-rectangle deselects the lights in the rectangle,
13 shift-drag-rectangle selects the lights in the rectangle, 22 shift-drag-rectangle selects the lights in the rectangle,
14 drag-rectangle selects only the lights in the rectangle. 23 drag-rectangle selects only the lights in the rectangle.
15 24
16 a light can be selected on its location point, it's aim point 25 a light can be selected on its location point, its aim point
17 (which may or may not be present), or its name. 26 (which may or may not be present), or its name.
18 27
19 lights should be able to be interactively 'locked', which blocks 28 lights should be able to be interactively 'locked', which blocks
20 them from being selected. 29 them from being selected.
21 30
22 """ 31 """
23 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
24 Canvas.__init__(self,parent,**kw) 39 Canvas.__init__(self,parent,**kw)
25 if 'stageimage' in 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
46 self.bind("<ButtonPress-1>", self.leftpress)
47 self.bind("<B1-Motion>", self.leftmotion)
48 self.bind("<ButtonRelease-1>", self.leftrelease)
49
50 self.halo=11 # search radius for clicked items
51
52 self.lmbstate=None # as you perform with LMB, this goes from None to 'pressed','rectangle','levelchange'
53
54 self.alllights=[]
55 self.selectedlights=[]
56
57 #
58 # selection management
59 #
60 def clearselection(self,dyn=0):
61 if dyn:
62 seltag="dynselection"
63 else:
64 seltag="selection"
65 for o in self.find_withtag(seltag):
66 self.select(o,0,dyn)
67
68 def replacedynselection(self,newlightnames):
69 for o in self.find_withtag('dynselection'):
70 self.select(o,0,1)
71 for o in newlightnames:
72 self.select(o,1,1)
73
74 def select(self,obj,select=1,dyn=0): # select=0 for deselect
75 if dyn:
76 seltag="dynselection"
77 else:
78 seltag="selection"
79 if select:
80 print obj,"into selection"
81 self.addtag_withtag(seltag,obj)
82 for c in self.getlightbboxes(obj):
83 self.create_rectangle(c[0]-2,c[1]-2,c[2]+2,c[3]+2,outline='red',tag="selectbox_%s"%obj)
84 else:
85 print obj,"out of select"
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
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
97 #
98 # LMB click or drag
99 #
100 def leftpress(self,ev):
101
102 self.lmbstate='pressed'
103 self.lmbstart=(ev.x,ev.y)
104
105 shifted=ev.state & 1
106 control=ev.state & 4
107 touching=self.findoverlappinglights((ev.x-self.halo,ev.y-self.halo,ev.x+self.halo,ev.y+self.halo))
108 istouching=len(touching)>0
109
110 if not istouching:
111 # clicked in space
112 if not shifted and not control:
113 # either a deselect (if no motion) or a level change (if motion)
114 self.clearselection()
115 self.lmbstate='rectangle'
116 if shifted or control:
117 # with shift/control, add/subtract lights to selection
118 self.lmbstate='rectangle'
119
120 else:
121 # clicked a selectable object
122 # toggle selection
123 if 'selection' in self.gettags(touching[0]):
124 if shifted:
125 # deselect
126 self.select(touching[0],0)
127 # and do nothing else
128 self.lmbstate=None
129 else:
130 # select only this light
131 self.clearselection()
132 self.select(touching[0])
133 # and adjust its level
134 self.lmbstate='levelchange'
135
136 else:
137 if not shifted:
138 self.clearselection()
139 self.select(touching[0])
140 # and adjust levels now
141 self.lmbstate='levelchange'
142
143
144 def leftmotion(self,ev):
145
146 coords=(ev.x,ev.y)
147
148 shifted=ev.state & 1
149 control=ev.state & 4
150
151 if self.lmbstate=='levelchange':
152 delta = self.lmbstart[1]-ev.y
153 print "change by",delta
154
155 if self.lmbstate=='rectangle':
156 sr = self.find_withtag('selectrect')
157 if not sr:
158 sr=self.create_rectangle( self.lmbstart[0],self.lmbstart[1],coords[0],coords[1],tag='selectrect')
159 # sr=self.create_rectangle( *(self.lmbstart+coords), tag='selectrect' )
160
161 self.coords(sr,*(self.lmbstart+coords))
162
163 # redo the dynselection with the new rectangle
164 self.replacedynselection([o for o in self.findoverlappinglights((self.lmbstart+coords),1)])
165
166 # need to handle ctrl
167
168 def leftrelease(self,ev):
169 if self.lmbstate:
170
171 if self.lmbstate=='rectangle':
172 self.delete('selectrect')
26 173
174 # all items that were in dynselection join the selection
175 self.incorporatedynselection()
176
177 self.lmbstate=None
178
179 def nametag(self,name):
180 "returns a safe version of the name that won't match other names"
181 return name.replace(" ","__")
182
183 def addlight(self,name,location,aim=None):
184
185 tags='light selectable name_%s' % self.nametag(name)
186
187 self.create_oval(location[0]-2,location[1]-2,
188 location[0]+2,location[1]+2,
189 fill='red',tag=tags+" hotspot")
190 self.create_text(location[0],location[1]+5,anchor='n',text=name,tag=tags)
191 self.alllights.append(name)
192
193 def getlightbboxes(self,tag):
194 """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"""
196 bboxes=[]
197 for o in self.find_withtag(tag):
198 if 'hotspot' in self.gettags(o):
199 bboxes.append(self.bbox(o))
200 return bboxes
201
202 def findoverlappinglights(self,box,enclosed=0):
203 "returns all the different name_ tags for lights that are within (or enclosed by) the box"
204 lights=[]
205 if enclosed:
206 candidates = self.find_enclosed(*box)
207 else:
208 candidates = self.find_overlapping(*box)
209 for o in candidates:
210 for t in self.gettags(o):
211 if t.startswith("name_") and t not in lights:
212 lights.append(t)
213 return lights
214
215 root=Tk()
216 s=Stage(root,stageimage='guysanddolls.ppm')
217 s.addlight('drew',(80,80))
218 s.addlight('house',(150,80))
219 s.addlight('barn',(200,80))
220 s.pack()
221
222 root.mainloop()