Changeset - 6540879e336e
[Not reviewed]
default
0 2 0
drewp - 22 years ago 2002-07-07 13:07:31

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
2 files changed with 101 insertions and 57 deletions:
0 comments (0 inline, 0 general)
light8/stage.py
Show inline comments
 
@@ -33,32 +33,39 @@ class Stage(Canvas):
 

	
 
    API:
 
      __init__(parent,**kw)
 
        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("<ButtonPress-1>", self.leftpress)
 
        self.bind("<B1-Motion>", self.leftmotion)
 
        self.bind("<ButtonRelease-1>", self.leftrelease)
 
        self.bind("<ButtonPress>", self.press)
 
        self.bind("<Motion>", self.motion)
 
        self.bind("<ButtonRelease>", self.release)
 
        self.bind("<Control-Key-a>", lambda ev: self.selectall())
 
        self.bind("<Control-Key-A>", lambda ev: self.clearselection())
 
#        self.bind("<Control-Shift-Key-a>",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=[]
 
        self.alllighttags={} # tag: name lookup
 

	
 
        self.subeditor=None
 
@@ -77,72 +84,87 @@ class Stage(Canvas):
 
    #
 
    def updateselectionboxes(self):
 
        "make selection boxes that match self.selectedlights"
 
        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")            
 
               self.create_rectangle(c[0]-2,c[1]-2,c[2]+2,c[3]+2,
 
                                     outline='red',tag="selectbox")            
 
    
 
    def clearselection(self,dyn=0):
 
    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)
 
        elif lightname in self.selectedlights:
 
            self.selectedlights.remove(lightname)
 

	
 
        self.updateselectionboxes()
 
                
 

	
 
    #
 
    # 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 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.lmbstate = 'deselect-or-level'
 
                    self.mode = 'deselect-or-rectangle'
 
            if shifted or control or len(self.selectedlights)==0:
 
                # with shift/control, add/subtract lights to selection
 
                self.lmbstate='rectangle'
 
                    self.startrectangleselect()
 

	
 
        else:
 
            # clicked a selectable object
 
            # toggle selection
 
            if touching[0] in self.selectedlights:
 
                if shifted:
 
                    if shifted or control:
 
                    # deselect
 
                    self.select(touching[0],0)
 
                    # and do nothing else
 
                    self.lmbstate=None
 
                else:
 
                        self.mode=None
 
                    if not shifted:
 
                    # select only this light
 
                    self.clearselection()
 
                    self.select(touching[0])
 
                    # and adjust its level
 
                    self.startlevelchange()
 
                    
 
@@ -151,65 +173,73 @@ class Stage(Canvas):
 
                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
 
    #
 
    def nametag(self,name):
 
        "returns a safe version of the name that won't match other names"
 
        return name.replace(" ","__")
 
@@ -230,14 +260,19 @@ class Stage(Canvas):
 
        if aim:
 
            self.create_oval(aim[0]-2,aim[1]-2,
 
                             aim[0]+2,aim[1]+2,
 
                             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
 

	
 
    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"""
 
@@ -245,13 +280,13 @@ class Stage(Canvas):
 
        for o in self.find_withtag("name_%s" % self.nametag(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"
 
        "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)
 
            
 
@@ -304,14 +339,23 @@ def createlights(s):
 
    s.addlight('hotback',(413, 476),(414, 396))
 

	
 

	
 

	
 
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()
 

	
light8/subediting.py
Show inline comments
 
@@ -58,13 +58,13 @@ class Subediting:
 

	
 
        for l in lightnames:
 
            if l not in self.startlevels:
 
                # 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 
 

	
 
            updatelevels[l] = min(100,max(0,self.startlevels[l]+delta))
 

	
0 comments (0 inline, 0 general)