Mercurial > code > home > repos > light9
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() |