# HG changeset patch # User drewp # Date 2002-07-07 03:47:18 # Node ID 119369e60da1d77b7f8feb951f2a09fc811fb99f # Parent a76f775bb6358837ac66c6d8a7c31aba477f7669 tag-heavy selection management is getting hard - about to switch to python lists diff --git a/light8/stage.py b/light8/stage.py --- a/light8/stage.py +++ b/light8/stage.py @@ -1,3 +1,12 @@ +from Tkinter import * + + +def printevent(ev): + for k in dir(ev): + if not k.startswith('__'): + print k,getattr(ev,k) + print "" + class Stage(Canvas): @@ -13,7 +22,7 @@ class Stage(Canvas): shift-drag-rectangle selects the lights in the rectangle, drag-rectangle selects only the lights in the rectangle. - a light can be selected on its location point, it's aim point + a light can be selected on its location point, its aim point (which may or may not be present), or its name. lights should be able to be interactively 'locked', which blocks @@ -21,6 +30,193 @@ class Stage(Canvas): """ def __init__(self,parent,**kw): + if 'stageimage' in kw: + stageimage=kw['stageimage'] + del kw['stageimage'] + else: + stageimage=None + Canvas.__init__(self,parent,**kw) - if 'stageimage' in kw: + + if stageimage: + img = Image('photo',stageimage) + self.create_image(0,0,anchor='nw',image=img) + self.create_rectangle(5,5,50,50) + + self.bind("", self.leftpress) + self.bind("", self.leftmotion) + self.bind("", self.leftrelease) + + self.halo=11 # search radius for clicked items + + self.lmbstate=None # as you perform with LMB, this goes from None to 'pressed','rectangle','levelchange' + + self.alllights=[] + self.selectedlights=[] + + # + # selection management + # + def clearselection(self,dyn=0): + if dyn: + seltag="dynselection" + else: + seltag="selection" + for o in self.find_withtag(seltag): + self.select(o,0,dyn) + + def replacedynselection(self,newlightnames): + for o in self.find_withtag('dynselection'): + self.select(o,0,1) + for o in newlightnames: + self.select(o,1,1) + + def select(self,obj,select=1,dyn=0): # select=0 for deselect + if dyn: + seltag="dynselection" + else: + seltag="selection" + if select: + print obj,"into selection" + self.addtag_withtag(seltag,obj) + for c in self.getlightbboxes(obj): + self.create_rectangle(c[0]-2,c[1]-2,c[2]+2,c[3]+2,outline='red',tag="selectbox_%s"%obj) + else: + print obj,"out of select" + self.dtag(obj,seltag) + if 'selection' not in self.gettags(obj) and 'dynselection' not in self.gettags(obj): + self.delete("selectbox_%s"%obj) + + def incorporatedynselection(self): + "put all dynselected objects in the regular selection" + for o in self.find_withtag('dynselection'): + self.dtag(o,'dynselection') + self.addtag_withtag('selection',o) + # no change for the graphics + + # + # LMB click or drag + # + def leftpress(self,ev): + + self.lmbstate='pressed' + self.lmbstart=(ev.x,ev.y) + + shifted=ev.state & 1 + control=ev.state & 4 + touching=self.findoverlappinglights((ev.x-self.halo,ev.y-self.halo,ev.x+self.halo,ev.y+self.halo)) + istouching=len(touching)>0 + + if not istouching: + # clicked in space + if not shifted and not control: + # either a deselect (if no motion) or a level change (if motion) + self.clearselection() + self.lmbstate='rectangle' + if shifted or control: + # with shift/control, add/subtract lights to selection + self.lmbstate='rectangle' + + else: + # clicked a selectable object + # toggle selection + if 'selection' in self.gettags(touching[0]): + if shifted: + # deselect + self.select(touching[0],0) + # and do nothing else + self.lmbstate=None + else: + # select only this light + self.clearselection() + self.select(touching[0]) + # and adjust its level + self.lmbstate='levelchange' + + else: + if not shifted: + self.clearselection() + self.select(touching[0]) + # and adjust levels now + self.lmbstate='levelchange' + + + def leftmotion(self,ev): + + coords=(ev.x,ev.y) + + shifted=ev.state & 1 + control=ev.state & 4 + + if self.lmbstate=='levelchange': + delta = self.lmbstart[1]-ev.y + print "change by",delta + + if self.lmbstate=='rectangle': + sr = self.find_withtag('selectrect') + if not sr: + sr=self.create_rectangle( self.lmbstart[0],self.lmbstart[1],coords[0],coords[1],tag='selectrect') +# sr=self.create_rectangle( *(self.lmbstart+coords), tag='selectrect' ) + + self.coords(sr,*(self.lmbstart+coords)) + + # redo the dynselection with the new rectangle + self.replacedynselection([o for o in self.findoverlappinglights((self.lmbstart+coords),1)]) + + # need to handle ctrl + + def leftrelease(self,ev): + if self.lmbstate: + + if self.lmbstate=='rectangle': + self.delete('selectrect') + # all items that were in dynselection join the selection + self.incorporatedynselection() + + self.lmbstate=None + + def nametag(self,name): + "returns a safe version of the name that won't match other names" + return name.replace(" ","__") + + def addlight(self,name,location,aim=None): + + tags='light selectable name_%s' % self.nametag(name) + + self.create_oval(location[0]-2,location[1]-2, + location[0]+2,location[1]+2, + fill='red',tag=tags+" hotspot") + self.create_text(location[0],location[1]+5,anchor='n',text=name,tag=tags) + self.alllights.append(name) + + def getlightbboxes(self,tag): + """returns a list of bboxes for a light with a given name_ tag. the selection + mechanism draws around these bboxes to show that a light is selected""" + bboxes=[] + for o in self.find_withtag(tag): + if 'hotspot' in self.gettags(o): + bboxes.append(self.bbox(o)) + return bboxes + + def findoverlappinglights(self,box,enclosed=0): + "returns all the different name_ tags for lights that are within (or enclosed by) the box" + lights=[] + if enclosed: + candidates = self.find_enclosed(*box) + else: + candidates = self.find_overlapping(*box) + for o in candidates: + for t in self.gettags(o): + if t.startswith("name_") and t not in lights: + lights.append(t) + return lights + +root=Tk() +s=Stage(root,stageimage='guysanddolls.ppm') +s.addlight('drew',(80,80)) +s.addlight('house',(150,80)) +s.addlight('barn',(200,80)) +s.pack() + +root.mainloop()