Changeset - 1b0266dd233a
[Not reviewed]
default
0 1 0
drewp - 22 years ago 2002-07-07 04:52:48

picking and dragging works pretty well (no ctrl yet)
1 file changed with 79 insertions and 56 deletions:
0 comments (0 inline, 0 general)
light8/stage.py
Show inline comments
 
@@ -27,75 +27,68 @@ class Stage(Canvas):
 

	
 
    lights should be able to be interactively 'locked', which blocks
 
    them from being selected. 
 

	
 
    """
 
    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:
 
            img = Image('photo',stageimage)
 
            self.create_image(0,0,anchor='nw',image=img)
 
        self.create_rectangle(5,5,50,50)
 

	
 
        self.bind("<ButtonPress-1>", self.leftpress)
 
        self.bind("<B1-Motion>", self.leftmotion)
 
        self.bind("<ButtonRelease-1>", 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=[]
 
        self.alllighttags={} # tag: name lookup
 

	
 

	
 
    def setimage(self,stageimage):
 
        img = Image('photo',file=stageimage)
 
        self.img=img # can't lose this!
 
        print img.width()
 
        self.create_image(0,0,anchor='nw',image=img)
 
        self.config(width=img.width(),height=img.height())
 
        
 
    #
 
    # selection management
 
    #
 
    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")            
 
    
 
    def clearselection(self,dyn=0):
 
        if dyn:
 
            seltag="dynselection"
 
        else:
 
            seltag="selection"       
 
        for o in self.find_withtag(seltag):
 
            self.select(o,0,dyn)
 
        self.selectedlights=[]
 
        self.updateselectionboxes()
 

	
 
    def markfordynselection(self):
 
        """call this before calls to replacedynselection"""
 
        self.origselection = self.selectedlights
 

	
 
    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)
 
        """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]
 
        self.updateselectionboxes()
 

	
 
    def select(self,obj,select=1,dyn=0): # select=0 for deselect
 
        if dyn:
 
            seltag="dynselection"
 
        else:
 
            seltag="selection"
 
    def select(self,lightname,select=1,dyn=0): # select=0 for deselect
 
        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)
 
            if lightname not in self.selectedlights:
 
                self.selectedlights.append(lightname)
 
        elif lightname in self.selectedlights:
 
            self.selectedlights.remove(lightname)
 

	
 
        self.updateselectionboxes()
 
                
 
    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):
 
        
 
@@ -106,24 +99,23 @@ class Stage(Canvas):
 
        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:
 
            if not shifted and not control and len(self.selectedlights)>0:
 
                # either a deselect (if no motion) or a level change (if motion)
 
                self.clearselection()
 
                self.lmbstate='rectangle'
 
            if shifted or control:
 
                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'
 

	
 
        else:
 
            # clicked a selectable object
 
            # toggle selection
 
            if 'selection' in self.gettags(touching[0]):
 
            if touching[0] in self.selectedlights:
 
                if shifted:
 
                    # deselect
 
                    self.select(touching[0],0)
 
                    # and do nothing else
 
                    self.lmbstate=None
 
                else:
 
@@ -137,20 +129,26 @@ class Stage(Canvas):
 
                if not shifted:
 
                    self.clearselection()
 
                self.select(touching[0])
 
                # and adjust levels now
 
                self.lmbstate='levelchange'
 
                
 
        
 
        if self.lmbstate=='rectangle':
 
            self.markfordynselection()
 
    def leftmotion(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:
 
                # they moved enough, it's a level change
 
                self.lmbstate='levelchange'
 

	
 
        if self.lmbstate=='levelchange':
 
            delta = self.lmbstart[1]-ev.y
 
            print "change by",delta
 

	
 
        if self.lmbstate=='rectangle':
 
            sr = self.find_withtag('selectrect')
 
@@ -167,56 +165,81 @@ class Stage(Canvas):
 

	
 
    def leftrelease(self,ev):
 
        if self.lmbstate:
 

	
 
            if self.lmbstate=='rectangle':
 
                self.delete('selectrect')
 

	
 
            if self.lmbstate=='deselect-or-level':
 
                # 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.incorporatedynselection()
 
            
 
        self.lmbstate=None
 

	
 
    #
 
    # 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(" ","__")
 

	
 
    def tagtoname(self,tag):
 
        "finds the real light name for a tag written by nametag()"
 
        return self.alllighttags[tag]
 

	
 
    #
 
    # light methods
 
    #
 
    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")
 
        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],stipple='gray50',
 
                             arrow='last',arrowshape="9 15 6",tag='light')
 
        self.create_text(location[0],location[1]+5,anchor='n',text=name,tag=tags)
 
        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"""
 
        bboxes=[]
 
        for o in self.find_withtag(tag):
 
        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"
 
        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)
 
                if t.startswith("name_"):
 
                    n = self.tagtoname(t[5:])
 
                    if n and (n not in lights):
 
                        lights.append(n)
 
        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))
 
root.wm_geometry("+376+330")
 
s=Stage(root)
 
s.setimage('guysanddolls.ppm')
 
s.pack()
 
s.addlight('drew',(330,640),(90,20))
 
s.addlight('house',(360,640))
 
s.addlight('barn',(390,640))
 

	
 
root.mainloop()
0 comments (0 inline, 0 general)