# HG changeset patch # User drewp # Date 2002-07-07 13:07:31 # Node ID 6540879e336eaa8b55e01d3d2f15a148974549be # Parent 2cd759c2b3c706045cbfbef0c3aedf0309be2d33 fixed Stage a lot: ctrl-drag subtracts from selection; ctrl-a/ctrl-A select all/none; fixed Stage a lot: ctrl-drag subtracts from selection; ctrl-a/ctrl-A select all/none; rmb adjusts levels diff --git a/light8/stage.py b/light8/stage.py --- a/light8/stage.py +++ b/light8/stage.py @@ -36,26 +36,33 @@ class Stage(Canvas): put pass any canvas options you want setimage(stageimage) - sets image to given filename (ppm, gif, etc) and resizes the canvas to the image size + sets image to given filename (ppm, gif, etc) and resizes the + canvas to the image size addlight(name, location, aim=None) - location and aim are pixel coord tuples. name will be passed back to you in the callback (see below) + location and aim are pixel coord tuples. name will be passed + back to you in the callback (see below) - setlightchangecb(cb) - give a function which will be called like this: cb(list_of_light_names, delta) + setsubediting(se) + give a subediting object to receive the 'startlevelchange' and + 'levelchange' messages """ def __init__(self,parent,**kw): Canvas.__init__(self,parent,**kw) - self.bind("", self.leftpress) - self.bind("", self.leftmotion) - self.bind("", self.leftrelease) + self.bind("", self.press) + self.bind("", self.motion) + self.bind("", self.release) + self.bind("", lambda ev: self.selectall()) + self.bind("", lambda ev: self.clearselection()) +# self.bind("",self.handlecontrol_a) 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.mode=None # as you perform with the mouse, this goes + # from None to 'pressed','rectangle','levelchange', etc self.alllights=[] self.selectedlights=[] @@ -80,24 +87,36 @@ class Stage(Canvas): self.delete("selectbox") for l in self.selectedlights: for c in self.getlightbboxes(l): - self.create_rectangle(c[0]-2,c[1]-2,c[2]+2,c[3]+2,outline='red',tag="selectbox") - - def clearselection(self,dyn=0): + self.create_rectangle(c[0]-2,c[1]-2,c[2]+2,c[3]+2, + outline='red',tag="selectbox") + + def selectall(self): + self.selectedlights= self.alllights[:] + self.updateselectionboxes() + def clearselection(self): self.selectedlights=[] self.updateselectionboxes() def markfordynselection(self): """call this before calls to replacedynselection""" - self.origselection = self.selectedlights + self.origselection = self.selectedlights[:] - def replacedynselection(self,newlightnames): - """as a dynamic selection changes, keep calling this function with the - names of the lights in the dynamic selection. the original selection (at the time - of markfordynselection) will be shown along with any new lights""" - self.selectedlights = self.origselection + [l for l in newlightnames if l not in self.origselection] + def replacedynselection(self,newlightnames,subtract=0): + """as a dynamic selection changes, keep calling this function + with the names of the lights in the dynamic selection. the + original selection (at the time of markfordynselection) will + be shown along with any new lights. if subtract=1, the selection will + be shown MINUS the newlights.""" + if subtract==0: + # orig selection plus any newlights that weren't in the orig selection + self.selectedlights = self.origselection[:] + [l for l in newlightnames if l not in self.origselection] + else: + # orig selection lights except those that are in the newlightnames list + self.selectedlights = [l for l in self.origselection if l not in newlightnames] self.updateselectionboxes() - def select(self,lightname,select=1,dyn=0): # select=0 for deselect + def select(self,lightname,select=1): # select=0 for deselect + """select or deselect (select=0) a light by name""" if select: if lightname not in self.selectedlights: self.selectedlights.append(lightname) @@ -108,105 +127,116 @@ class Stage(Canvas): # - # LMB click or drag + # mouse handling # - def leftpress(self,ev): + def press(self,ev): - self.lmbstate='pressed' - self.lmbstart=(ev.x,ev.y) - print "click at",self.lmbstart + self.mode='pressed' + self.mousedownpos=(ev.x,ev.y) + print "click at",self.mousedownpos + button=ev.num 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)) + 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 and len(self.selectedlights)>0: - # either a deselect (if no motion) or a level change (if motion) - self.lmbstate = 'deselect-or-level' - if shifted or control or len(self.selectedlights)==0: - # with shift/control, add/subtract lights to selection - self.lmbstate='rectangle' + if button==1: + if not istouching: + # clicked in space + if not shifted and not control and len(self.selectedlights)>0: + # either a deselect (if no motion) or a level change (if motion) + self.mode = 'deselect-or-rectangle' + if shifted or control or len(self.selectedlights)==0: + # with shift/control, add/subtract lights to selection + self.startrectangleselect() - else: - # clicked a selectable object - # toggle selection - if touching[0] in self.selectedlights: - if shifted: - # deselect - self.select(touching[0],0) - # and do nothing else - self.lmbstate=None + else: + # clicked a selectable object + # toggle selection + if touching[0] in self.selectedlights: + if shifted or control: + # deselect + self.select(touching[0],0) + # and do nothing else + self.mode=None + if not shifted: + # select only this light + self.clearselection() + self.select(touching[0]) + # and adjust its level + self.startlevelchange() + else: - # select only this light - self.clearselection() + # clicked a light that wasn't selected + if not shifted: + self.clearselection() self.select(touching[0]) - # and adjust its level + # and adjust levels now self.startlevelchange() - - else: - # clicked a light that wasn't selected - if not shifted: - self.clearselection() - self.select(touching[0]) - # and adjust levels now - self.startlevelchange() - - if self.lmbstate=='rectangle': - self.markfordynselection() - def leftmotion(self,ev): + + if button==3: + self.startlevelchange() + + def motion(self,ev): coords=(ev.x,ev.y) shifted=ev.state & 1 control=ev.state & 4 - if self.lmbstate=='deselect-or-level': - if (coords[0]-self.lmbstart[0])**2+(coords[1]-self.lmbstart[1])**2>self.halo**2: + if self.mode=='deselect-or-rectangle': + if (coords[0]-self.mousedownpos[0])**2+(coords[1]-self.mousedownpos[1])**2>self.halo**2: # they moved enough, it's a level change - self.startlevelchange() + self.startrectangleselect() + - if self.lmbstate=='levelchange': - delta = (self.lmbstart[1]-ev.y) + if self.mode=='levelchange': + delta = (self.mousedownpos[1]-ev.y) if self.subeditor: self.subeditor.levelchange(self.selectedlights,delta) - if self.lmbstate=='rectangle': + if self.mode=='rectangle': sr = self.find_withtag('selectrect') if not sr: - sr=self.create_rectangle( self.lmbstart[0],self.lmbstart[1],coords[0],coords[1], - outlinestipple='gray50',outline='yellow', - tag='selectrect') + # make the selection rectangle + sr=self.create_rectangle( self.mousedownpos[0],self.mousedownpos[1],coords[0],coords[1], + outlinestipple='gray50',outline='yellow',tag='selectrect') # move rectangle with mouse - self.coords(sr,*(self.lmbstart+coords)) + self.coords(sr,*(self.mousedownpos+coords)) # redo the dynselection with the new rectangle - self.replacedynselection([o for o in self.findoverlappinglights((self.lmbstart+coords),1)]) - - # need to handle ctrl + self.replacedynselection(self.findoverlappinglights((self.mousedownpos+coords),1), + subtract=control) - def leftrelease(self,ev): - if self.lmbstate: - - if self.lmbstate=='rectangle': + def release(self,ev): + if self.mode: + if self.mode=='rectangle': self.delete('selectrect') - if self.lmbstate=='deselect-or-level': + if self.mode=='deselect-or-rectangle': # they didn't move enough to promote the mode to level, so it's a deselect click self.clearselection() - # all items that were in dynselection join the selection -# self.incorporatedynselection() + self.mode=None + + # + # + # - self.lmbstate=None def startlevelchange(self): - self.lmbstate='levelchange' + """sets mode to levelchange AND notifies subeditor. this + should be done exactly once (per mouse drag), when you first + decide the mode is levelchange""" + self.mode='levelchange' if self.subeditor: self.subeditor.startlevelchange() - + def startrectangleselect(self): + """sets mode to rectangle AND checkpoints the current selection""" + self.mode='rectangle' + self.markfordynselection() # # light names vs. canvas object tags # @@ -233,8 +263,13 @@ class Stage(Canvas): fill='red',tag=tags+" hotspot") self.create_line(location[0],location[1],aim[0],aim[1],fill='lightblue', arrow='last',arrowshape="9 15 6",tag='light') - self.create_text(location[0]-1,location[1]+6,anchor='n',text=name,fill='black',tag=tags,**dict([(k,v) for k,v in textstyle.items() if k!='fill'])) + # shadow + self.create_text(location[0]-1,location[1]+6, + anchor='n',text=name,fill='black', + tag=tags,**dict([(k,v) for k,v in textstyle.items() if k!='fill'])) + # text self.create_text(location[0],location[1]+5,anchor='n',text=name,tag=tags,**textstyle) + self.alllights.append(name) self.alllighttags[self.nametag(name)]=name @@ -248,13 +283,13 @@ class Stage(Canvas): return bboxes def findoverlappinglights(self,box,enclosed=0): - "returns all the different name_ tags for lights that are within (or enclosed by) the box" + "returns all the different names 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_"): @@ -307,11 +342,20 @@ def createlights(s): if __name__=='__main__': root=Tk() + root.tk_focusFollowsMouse() root.wm_geometry("+376+330") s=Stage(root) s.setimage('guysanddolls.gif') s.pack() createlights(s) + + class subediting_standin: + def startlevelchange(self): + print "start lev change" + def levelchange(self,lights,delta): + print "change",lights,delta + s.setsubediting(subediting_standin()) + root.mainloop() diff --git a/light8/subediting.py b/light8/subediting.py --- a/light8/subediting.py +++ b/light8/subediting.py @@ -61,7 +61,7 @@ class Subediting: # level was not in the sub cl = self.getcurrentlevel(l) if cl is None: - print "light isn't even in the patch! skipping" + print "light '%s' isn't even in the patch! skipping" % l return print "copying current light level",cl,"into the sub" self.startlevels[l] = cl