def onAddCmdToViewer(self): if not self.vf.commands.has_key('setICOM'): self.vf.loadCommand('interactiveCommands', 'setICOM', 'Pmv', topCommand=0) if not self.vf.commands.has_key('measureDistance'): self.vf.loadCommand('measureCommands', 'measureDistance', 'Pmv', topCommand=0) self.masterGeom = Geom('measureDistGeom', shape=(0, 0), pickable=0) self.masterGeom.isScalable = 0 if self.vf.hasGui: measure_geoms = check_measure_geoms(self.vf.GUI) self.vf.GUI.VIEWER.AddObject(self.masterGeom, parent=measure_geoms) self.lines = IndexedPolylines('distLine', materials=((1, 1, 0), ), inheritMaterial=0, lineWidth=3., stippleLines=1, pickable=0) self.labels = GlfLabels(name='distLabel', shape=(0, 3), font='arial1.glf', fontStyle='solid3d', fontScales=(.5, .5, .3), inheritMaterial=0, materials=((1, 1, 0), )) self.spheres = Spheres(name='distSpheres', shape=(0, 3), inheritMaterial=0, radii=0.2, quality=15, materials=((1., 1., 0.), )) if self.vf.hasGui: for item in [self.lines, self.labels, self.spheres]: self.vf.GUI.VIEWER.AddObject(item, parent=self.masterGeom)
def onAddObjectToViewer(self, obj): #print "LabelByProperties.onAddObjectToViewer", self.name #if not self.vf.hasGui: # return #from DejaVu.Labels import Labels geomC = obj.geomContainer classNames = ['Atom','Residue','Chain','Protein'] self.level = self.nameDict[self.vf.selectionLevel] #for item in classNames: for i in range(len(classNames)): item = classNames[i] #geomName = eval("'%sLabels'" %item) geomName = '%sLabels'%item for atm in obj.allAtoms: atm.colors[geomName] = (1.0, 1.0, 1.0) atm.opacities[geomName] = 1.0 if not geomC.atoms.has_key(geomName): # DON'T KNOW HOW TO REPLACE THIS EVAL geomC.atoms[geomName]=eval("%sSet()" %item) geomC.atomPropToVertices[geomName] = self.atomPropToVertices if not geomC.geoms.has_key(geomName): j = i + 1 g = GlfLabels(geomName, fontStyle='solid3d', #inheritMaterial=0, fontTranslation=(0,0,3.), # so the label is less hidden by the structure fontScales=(j*.3,j*.3, .1), # different size for atom, res, chain, mol visible=0, pickable=0, ) geomC.addGeom(g, parent=geomC.masterGeom,redo=0) g.applyStrokes() else: g = None if self.vf.hasGui: g = geomC.VIEWER.FindObjectByName(geomC.masterGeom.fullName+'|'+geomName) if g not in self.managedGeometries: self.managedGeometries.append(g)
def onAddCmdToViewer(self): if not hasattr(self.vf, 'GUI'): return self.undoNow = 0 self.save = None from DejaVu.bitPatterns import pat3 from DejaVu.IndexedPolygons import IndexedPolygons if not self.vf.commands.has_key('setICOM'): self.vf.loadCommand('interactiveCommands', 'setICOM', 'Pmv', topCommand=0) if not self.vf.commands.has_key('measureTorsion'): self.vf.loadCommand('measureCommands', 'measureTorsion', 'Pmv', topCommand=0) self.masterGeom = Geom('setTorsionGeom', shape=(0, 0), pickable=0, protected=True) self.masterGeom.isScalable = 0 self.vf.GUI.VIEWER.AddObject(self.masterGeom, parent=self.vf.GUI.miscGeom) self.lines = IndexedPolygons( 'settorsionLine', materials=((0, 1, 1), ), inheritMaterial=0, stipplePolygons=1, protected=True, ) if self.vf.userpref['Sharp Color Boundaries for MSMS'][ 'value'] == 'blur': self.lines.Set( inheritSharpColorBoundaries=False, sharpColorBoundaries=False, ) self.lines.polygonstipple.Set(pattern=pat3) #, tagModified=False) #self.lines.RenderMode(GL.GL_FILL, face=GL.GL_BACK) self.lines.Set(backPolyMode=GL.GL_FILL) self.labels = GlfLabels(name='settorsionLabel', shape=(0, 3), inheritMaterial=0, materials=((0, 1, 1), )) self.spheres = Spheres(name='settorsionSpheres', shape=(0, 3), radii=0.2, quality=15, inheritMaterial=0, materials=((0., 1., 1.), ), protected=True) for item in [self.lines, self.labels, self.spheres]: self.vf.GUI.VIEWER.AddObject(item, parent=self.masterGeom) self.snakeLength = 1 self.oldValue = None self.torsionType = Tkinter.StringVar() self.torsionType.set('1') self.newAngList = Tkinter.StringVar() self.TAHdata = None self.molecule = None #self.bondString = Tkinter.StringVar() self.callbackDict = {} self.callbackDict['measureDistanceGC'] = 'update' self.callbackDict['measureAngleGC'] = 'update' self.callbackDict['measureTorsionGC'] = 'update'
class SetTorsionGUICommand(MeasureTorsionGUICommand): def guiCallback(self): self.save = self.vf.ICmdCaller.commands.value["Shift_L"] self.vf.setICOM(self, modifier="Shift_L", topCommand=0) if not hasattr(self, 'form'): self.buildForm() else: self.form.deiconify() def onAddCmdToViewer(self): if not hasattr(self.vf, 'GUI'): return self.undoNow = 0 self.save = None from DejaVu.bitPatterns import pat3 from DejaVu.IndexedPolygons import IndexedPolygons if not self.vf.commands.has_key('setICOM'): self.vf.loadCommand('interactiveCommands', 'setICOM', 'Pmv', topCommand=0) if not self.vf.commands.has_key('measureTorsion'): self.vf.loadCommand('measureCommands', 'measureTorsion', 'Pmv', topCommand=0) self.masterGeom = Geom('setTorsionGeom', shape=(0, 0), pickable=0, protected=True) self.masterGeom.isScalable = 0 self.vf.GUI.VIEWER.AddObject(self.masterGeom, parent=self.vf.GUI.miscGeom) self.lines = IndexedPolygons( 'settorsionLine', materials=((0, 1, 1), ), inheritMaterial=0, stipplePolygons=1, protected=True, ) if self.vf.userpref['Sharp Color Boundaries for MSMS'][ 'value'] == 'blur': self.lines.Set( inheritSharpColorBoundaries=False, sharpColorBoundaries=False, ) self.lines.polygonstipple.Set(pattern=pat3) #, tagModified=False) #self.lines.RenderMode(GL.GL_FILL, face=GL.GL_BACK) self.lines.Set(backPolyMode=GL.GL_FILL) self.labels = GlfLabels(name='settorsionLabel', shape=(0, 3), inheritMaterial=0, materials=((0, 1, 1), )) self.spheres = Spheres(name='settorsionSpheres', shape=(0, 3), radii=0.2, quality=15, inheritMaterial=0, materials=((0., 1., 1.), ), protected=True) for item in [self.lines, self.labels, self.spheres]: self.vf.GUI.VIEWER.AddObject(item, parent=self.masterGeom) self.snakeLength = 1 self.oldValue = None self.torsionType = Tkinter.StringVar() self.torsionType.set('1') self.newAngList = Tkinter.StringVar() self.TAHdata = None self.molecule = None #self.bondString = Tkinter.StringVar() self.callbackDict = {} self.callbackDict['measureDistanceGC'] = 'update' self.callbackDict['measureAngleGC'] = 'update' self.callbackDict['measureTorsionGC'] = 'update' def onRemoveObjectFromViewer(self, mol): lenAts = len(self.atomList) #if cmd has no atoms on its list, nothing to do if not lenAts: return #remove any atoms which are being deleted from viewer self.atomList = AtomSet(self.atomList) - mol.allAtoms #if some have been removed, do an update if lenAts != len(self.atomList): self.update() self.extslider.set(0) def __call__(self, atoms, angle=None, **kw): """torsion/None<-setTorsionGC(atoms, angle=None) the torsion is returned when the number of atoms is a multiple of 4""" ats = self.vf.expandNodes(atoms) if not len(ats): return 'ERROR' return apply(self.doitWrapper, ( ats, angle, ), kw) def doit(self, ats, angle): for at in ats: lenAts = len(self.atomList) if lenAts and lenAts % 4 != 0 and at == self.atomList[-1]: continue if len(self.atomList) == 0: self.molecule = at.top if at.top == self.molecule: self.atomList.append(at) else: #all atoms in torsion must be in same molecule ...(?) s = at.full_name() + " not in " + self.molecule.full_name() self.warningMsg(s) return 'ERROR' if len(self.atomList) > 4 * self.snakeLength: self.atomList = self.atomList[4:] self.update() if len(self.atomList) == 4: mol = self.atomList[0].top at0, at1, at2, at3 = self.atomList self.mov_atoms = mol.subTree(at1, at2, mol.allAtoms) self.oldValue = self.vf.measureTorsion.doit(at0, at1, at2, at3) self.origValue = self.oldValue self.origCoords = self.mov_atoms.coords if hasattr(self, 'extslider'): self.extslider.set(self.oldValue, update=0) if angle: #angle is what you want to end up with deltaAngle = angle - self.oldValue #print 'deltaAngle=', deltaAngle, 'angle=', angle self.transformCoords(deltaAngle) if hasattr(self, 'extslider'): self.extslider.set(angle, update=0) #s = self.atomList[2].full_name()+'--'+self.atomList[3].full_name() #self.bondString.set(s) self.updateHistory() ##if self.undoNow: raise 'abc' #return float(self.labelStrs[-1]) return def update(self, forward=1, event=None): if not len(self.atomList): self.spheres.Set(vertices=[], tagModified=False) self.labels.Set(vertices=[], tagModified=False) self.lines.Set(vertices=[], tagModified=False) self.vf.GUI.VIEWER.Redraw() return limit = self.snakeLength #each time have to recalculate lineVertices self.lineVertices = [] for at in self.atomList: c1 = self.getTransformedCoords(at) self.lineVertices.append(tuple(c1)) #display spheres: self.spheres.Set(vertices=self.lineVertices, tagModified=False) self.vf.GUI.VIEWER.Redraw() #label with torsion #lines between spheres are only drawn when angle completed #that is, len(ats)%4=0 if len(self.lineVertices) < 4: self.labels.Set(vertices=[], tagModified=False) self.lines.Set(vertices=[], tagModified=False) else: #rebuild labels and polygons each time self.labelCenters = [] self.labelStrs = [] #labelCenters, labelStrs, #this gets done lenATs/4 times numItems = len(self.atomList) / 4 for i in range(numItems): at0, at1, at2, at3 = self.atomList[i * 4:i * 4 + 4] torsion = self.vf.measureTorsion.doit(at0, at1, at2, at3) torsionLabel = '%.3f' % torsion self.labelStrs.append(torsionLabel) c0 = self.getTransformedCoords(at0) c1 = self.getTransformedCoords(at3) newcenter = tuple((c0 + c1) / 2.0) self.labelCenters.append(newcenter) self.vf.GUI.VIEWER.Redraw() items = self.callbackDict.keys() #items = ['measureDistanceGC','measureAngleGC','measureTorsionGC'] #checkout whether measure update needs to be called icomVals = self.vf.ICmdCaller.commands.value.values() for item in items: if not len(icomVals): break if not hasattr(self.vf, item): continue exec('cmd = self.vf.' + item) if cmd in icomVals: #cmd.update() s = self.callbackDict[item] exec('self.vf.' + item + '.' + s + '()') def transformCoords(self, deltaAngle): """ deltaAngle is NOW not final angle wanted but relative""" #mov_coords is the array of the coords of the atoms to be moved, #x2 and x3 are atoms which define the axis of the transformation #by deltaAngle. NB: effect is that mov_atoms.coords #are transformed... if not hasattr(self, 'mov_atoms'): return if not len(self.mov_atoms): return x1, x2, x3, x4 = self.atomList nc = self.vf.setRelativeTorsion.doit(x2, x3, deltaAngle, self.mov_atoms, returnVal=1) mol = x2.top #mov_atoms = mol.subTree(x2, x3, mol.allAtoms) for i in range(len(nc)): at = self.mov_atoms[i] at._coords[at.conformation] = nc[i].tolist() event = EditAtomsEvent('coords', self.mov_atoms) self.vf.dispatchEvent(event) self.update() #####Callback Functions for the Dial: #####slideCallback def slideCallback(self, eventval): #print 'in slideCallback' if len(self.atomList) != 4: return if not hasattr(self, 'oldValue'): return if self.oldValue == None: return #self.setupUndoBefore(self.atomList, self.oldValue) try: newAngle = self.extslider.get() tT = self.torsionType.get() at0, at1, at2, at3 = self.atomList #torsion = self.vf.measureTorsion.doit(at0, at1, at2, at3) torsion = self.oldValue if tT == '1': #NEWdeltaAngle = newAngle deltaAngle = newAngle - torsion else: #NEWdeltaAngle = newAngle + torsion deltaAngle = newAngle self.transformCoords(deltaAngle) #print 'deltaAngle=', deltaAngle self.oldValue = newAngle #self.oldValue = newAngle except ValueError: self.vf.GUI.message("error in slideCallback\n") def rdSet(self, event=None): #"""radiobutton selection of torsionType: #Absolute: initial angle to be displayed in slider/entry #Relative: 0 is displayed """ if self.torsionType.get() == '1': aL = self.atomList if len(aL) == 4: torsion = self.vf.measureTorsion.doit(aL[0], aL[1], aL[2], aL[3]) self.extslider.set(torsion) else: self.extslider.set(0) def setupUndoBefore(self, ats, angle): pass #def setupUndoBefore(self, ats, angle): ##no atoms, <4 atoms, #aSet = AtomSet(self.atomList) #self.undoMenuString = self.name #if len(self.atomList)==0: #undoCmd = 'self.setTorsionGC.atomList=[]; self.setTorsionGC.update()' #elif len(self.atomList)<4: ##need to step back here #undoCmd = 'self.setTorsionGC.atomList=self.setTorsionGC.atomList[:-1]; self.setTorsionGC.update()' #else: ##print 'self.oldValue=', self.oldValue #undoCmd = 'self.setTorsionGC(\''+ aSet.full_name()+ '\',' + str(self.oldValue) + ', topCommand=0)' ##self.oldValue = str(self.extslider.get()) #self.vf.undo.addEntry((undoCmd), (self.name)) def setupUndoAfter(self, ats, angle, **kw): #no atoms, <4 atoms, aSet = AtomSet(self.atomList) self.undoMenuString = self.name if len(self.atomList) == 0: undoCmd = 'self.setTorsionGC.atomList=[]; self.setTorsionGC.update()' elif len(self.atomList) < 4: #need to step back here undoCmd = 'self.setTorsionGC.atomList=self.setTorsionGC.atomList[:-1]; self.setTorsionGC.update()' elif self.origValue == self.oldValue: return else: restoreAngle = self.origValue self.undoNow = 1 undoCmd = 'self.setTorsionGC(\'' + aSet.full_name() + '\',' + str( restoreAngle) + ', topCommand=0)' self.vf.undo.addEntry((undoCmd), (self.name)) def Accept_cb(self): apply(self.setupUndoAfter, (self.atomList, self.oldValue), {}) self.origValue = self.oldValue def Done_cb(self): self.vf.setICOM(self.save, modifier="Shift_L", mode='pick', topCommand=0) self.stopICOM() def startICOM(self): self.vf.setIcomLevel(Atom) if not hasattr(self, 'form'): self.buildForm() else: self.form.deiconify() def stopICOM(self): if hasattr(self, 'form'): self.form.withdraw() self.atomList = [] self.atomCenters = [] self.labelStrs = [] self.labelCenters = [] self.lineVertices = [] self.spheres.Set(vertices=[], tagModified=False) self.lines.Set(vertices=[], faces=[], tagModified=False) self.labels.Set(vertices=[], tagModified=False) self.vf.GUI.VIEWER.Redraw() #when cmd stops being icom, remove callback ## ehm = self.vf.GUI.ehm ## for event in ['<B2-Motion>', '<B3-Motion>']: ## if ehm.eventHandlers.has_key(event) and self.update_cb in \ ## ehm.eventHandlers[event]: ## ehm.RemoveCallback(event, self.update_cb) for event in ['<B2-Motion>', '<B3-Motion>']: self.vf.GUI.removeCameraCallback(event, self.update_cb) def repeat_transTors(self, event=None): deltaAngle = self.extslider.get() if self.torsionType.get() != '1': self.transformCoords(deltaAngle) #this is here in order to create a log message nc = self.vf.setRelativeTorsion(self.atomList[1], self.atomList[2], deltaAngle, self.mov_atoms, returnVal=1) event = EditAtomsEvent('coords', self.mov_atoms) self.vf.dispatchEvent(event) def new_Tors(self, event=0): self.atomList = [] self.update() #when called, most recent 4 atoms are in self.atomList def updateHistory(self): """1: call TorsionHistory.getTorsion: make a new TorsionAngle or add current angle to angleList 2: put TA.name_string into ListBox 3: best if insert a tuple (string to be displayed, item itself) 4: adjust size with self.historyList.configure(height=self.[].size) 5: limit overall size to 4""" #print 'in updateHistory' molecule = self.atomList[-1].top if self.TAHdata is None: self.TAHdata = TorsionHistory(molecule) a1, a2, a3, a4 = self.atomList newone = self.TAHdata.getTorsion(a1, a2, a3, a4) #first check to see if it is in there already??? #need to get info back from getTorsion....(???) if hasattr(self, 'historyList'): if newone.new: self.historyList.insert('end', newone.name_string) if int(self.historyList.cget('height')) < 4: self.historyList.configure(height=self.historyList.size()) if self.historyList.curselection(): self.historyList.select_clear(self.historyList.curselection()) newindex = self.TAHdata.getIndex(newone) self.historyList.select_set(newindex) self.historyList.see(newindex) #set entry to a string ==current TA's angleList newstring = "" for item in newone.angleList: newstring = newstring + " " + "%5.3f" % item self.newAngList.set(newstring) def HLCommand(self, event=None): """double-clicking selection in listbox causes curselection to be picked... 1:self.atomList set to atoms of curselection 2:self.mov_atoms set to atoms of curselection 3:self.selAtom[1-4].Set(vertices=atoms.coords) 4:reset entry +slider and init_bondAngle etc 5.add current angle to selection's.angleList""" #get TA: if self.historyList.get(0) == '': return items = self.historyList.curselection() if type(items) == types.TupleType: items = items[0] try: items = map(int, items) except ValueError: pass thisTA = self.TAHdata.torslist[items[0]] #get currentAngle current = thisTA.getCurrentAngle() if not thisTA.inList(current): thisTA.angleList.append(current) newAts = AtomSet([thisTA.atom1,thisTA.atom2, thisTA.atom3,\ thisTA.atom4]) #reset self.molecule self.atomList = [] self.molecule = newAts[0].top self.doit(newAts, current) #self.setTorsionAngle(thisTA.atom1, thisTA.atom2, thisTA.atom3, thisTA.atom4, current, 'A') #self.drawTransformedAngle() #self.updatespheres(items[0]) #self.update() self.extslider.set(current, 0) newstring = "" for item in thisTA.angleList: newstring = newstring + " " + "%5.3f" % item self.newAngList.set(newstring) def getAngList(self, event=None): items = self.historyList.curselection() try: items = map(int, items) except ValueError: pass thisTA = self.TAHdata.torslist[items[0]] thisTA.angleList = map(float, split(self.newAngList.get())) last = thisTA.angleList[-1] newAts = AtomSet([thisTA.atom1,thisTA.atom2, thisTA.atom3,\ thisTA.atom4]) #reset self.molecule self.atomList = [] self.molecule = newAts[0].top self.doit(newAts, last) #self.doit(thisTA.atom1, thisTA.atom2, thisTA.atom3, thisTA.atom4,last,'A') #self.setTorsionAngle(thisTA.atom1, thisTA.atom2, thisTA.atom3, thisTA.atom4,last,'A') #self.drawTransformedAngle() #self.updatespheres(items[0]) self.extslider.set(last, 0) def stepBack(self, event=None): items = self.historyList.curselection() if len(items) == 0: return try: items = map(int, items) except ValueError: pass thisTA = self.TAHdata.torslist[items[0]] ####last angle is thisTA.angleList[-1] if len(thisTA.angleList) > 1: last = thisTA.angleList[-1] lastIndex = thisTA.angleList.index(last) thisTA.angleList = thisTA.angleList[:lastIndex] last = thisTA.angleList[-1] else: last = thisTA.angleList[0] newAts = AtomSet([thisTA.atom1,thisTA.atom2, thisTA.atom3,\ thisTA.atom4]) #reset self.molecule self.atomList = [] self.molecule = newAts[0].top self.doit(newAts, last) #self.doit(thisTA.atom1, thisTA.atom2, thisTA.atom3, thisTA.atom4,last,'A') #self.setTorsionAngle(thisTA.atom1, thisTA.atom2, thisTA.atom3, thisTA.atom4,last,'A') #self.drawTransformedAngle() #self.updatespheres(items[0]) newstring = "" for item in thisTA.angleList: newstring = newstring + " " + "%5.3f" % item self.newAngList.set(newstring) self.extslider.set(last, 0) #IS THIS ENOUGH in order to create correct log? self.mouseUp() def startOver(self, event=None): items = self.historyList.curselection() if len(items) == 0: return try: items = map(int, items) except ValueError: pass thisTA = self.TAHdata.torslist[items[0]] self.resetAngle(thisTA) def resetAngle(self, thisTA, event=None): #first angle is thisTA.angleList[0] ang = thisTA.angleList[0] newAts = AtomSet([thisTA.atom1,thisTA.atom2, thisTA.atom3,\ thisTA.atom4]) #reset self.molecule self.atomList = [] self.molecule = newAts[0].top self.doit(newAts, ang) #self.doit(thisTA.atom1, thisTA.atom2, thisTA.atom3, thisTA.atom4,ang,'A') #self.setTorsionAngle(thisTA.atom1, thisTA.atom2, thisTA.atom3, thisTA.atom4,ang,'A') if len(thisTA.angleList) > 1: thisTA.angleList = thisTA.angleList[:1] #self.drawTransformedAngle() self.extslider.set(ang, 0) self.mouseUp() self.newAngList.set("%5.3f" % ang) def resetAll(self, event=None): if self.TAHdata == None: return for item in self.TAHdata.torslist: self.resetAngle(item) self.spheres.Set(vertices=[], tagModified=False) self.vf.GUI.VIEWER.Redraw() def buildForm(self): if hasattr(self, 'ifd'): return self.torsionType = Tkinter.StringVar() self.torsionType.set('1') self.ifd = ifd = InputFormDescr(title='Set Torsion Angle') ifd.append({ 'name': 'extLabel', 'widgetType': Tkinter.Label, 'wcfg': { 'text': 'Set Angle:\n(180=trans)' }, 'gridcfg': { 'sticky': Tkinter.W + Tkinter.E, 'columnspan': 2 } }) ifd.append({ 'name': 'extslider', 'widgetType': ExtendedSliderWidget, 'wcfg': { 'label': 'torsion', 'minval': -360., 'maxval': 360., 'width': 150, 'immediate': 1, 'command': self.slideCallback, 'sliderType': 'float', 'entrypackcfg': { 'side': 'bottom' } }, 'gridcfg': { 'sticky': 'we', 'columnspan': 2 } }) ifd.append({ 'name': 'typeLabel', 'widgetType': Tkinter.Label, 'wcfg': { 'text': 'Torsion Type' }, 'gridcfg': { 'sticky': 'we', 'columnspan': 2 } }) ifd.append({ 'name': 'rdbut1', 'widgetType': Tkinter.Radiobutton, 'wcfg': { 'text': 'Absolute', 'variable': self.torsionType, 'value': 1, 'command': self.rdSet }, 'gridcfg': { 'sticky': 'we' } }) ifd.append({ 'name': 'rdbut2', 'widgetType': Tkinter.Radiobutton, 'wcfg': { 'text': 'Relative ', 'variable': self.torsionType, 'value': 0, 'command': self.rdSet }, 'gridcfg': { 'sticky': 'we', 'row': -1, 'column': 1 } }) ifd.append({ 'name': 'historyList', 'widgetType': ListChooser, 'wcfg': { 'title': 'TorsionAngle\nTranformation History', 'mode': 'single', 'command': self.HLCommand, 'lbwcfg': { 'height': 5, 'selectforeground': 'yellow', 'exportselection': 0, 'width': 30 }, }, 'gridcfg': { 'sticky': 'we', 'columnspan': 2 } }) ifd.append({ 'name': 'hbut1', 'widgetType': Tkinter.Button, 'wcfg': { 'text': 'Step Back ', 'command': self.stepBack }, 'gridcfg': { 'sticky': 'we', 'columnspan': 2 } }) ifd.append({ 'name': 'hbut2', 'widgetType': Tkinter.Button, 'wcfg': { 'text': 'Start Over ', 'command': self.startOver }, 'gridcfg': { 'sticky': 'we', 'columnspan': 2 } }) ifd.append({ 'name': 'hbut3', 'widgetType': Tkinter.Button, 'wcfg': { 'text': 'Reset All ', 'command': self.resetAll }, 'gridcfg': { 'sticky': 'we', 'columnspan': 2 } }) ifd.append({ 'name': 'angListEnt', 'widgetType': Tkinter.Entry, 'wcfg': { 'width': 5, 'command': self.getAngList, 'textvariable': self.newAngList }, 'gridcfg': { 'sticky': 'we', 'columnspan': 2 } }) ifd.append({ 'name': 'hbut4', 'widgetType': Tkinter.Button, 'wcfg': { 'text': 'Move', 'command': self.repeat_transTors }, 'gridcfg': { 'sticky': 'we', 'columnspan': 2 } }) ifd.append({ 'name': 'hbut5', 'widgetType': Tkinter.Button, 'wcfg': { 'text': 'New Torsion', 'command': self.new_Tors }, 'gridcfg': { 'sticky': 'we', 'columnspan': 2 } }) #ifd.append({'name':'accept', #'widgetType': Tkinter.Button, #'wcfg':{'text' : 'Accept', #'command': self.Accept_cb}, #'gridcfg':{'sticky':'we'}}) ifd.append({ 'name': 'done', 'widgetType': Tkinter.Button, 'wcfg': { 'text': 'Done', 'command': self.Done_cb }, 'gridcfg': { 'sticky': 'we', 'columnspan': 2 } }) #'gridcfg':{'sticky':'we','column':1, 'row':-1}}) self.form = self.vf.getUserInput(ifd, modal=0, blocking=0) self.form.root.protocol('WM_DELETE_WINDOW', self.Done_cb) self.extslider = self.ifd.entryByName['extslider']['widget'] self.extslider.draw.bind('<ButtonRelease-1>', self.mouseUp, add='+') self.extslider.entry.bind('<Return>', self.mouseUp, add='+') self.historyList = self.ifd.entryByName['historyList']['widget'].lb #self.historyList.bind("<Double-Button-1>",self.HLCommand) self.hbut1 = self.ifd.entryByName['hbut1']['widget'] self.hbut2 = self.ifd.entryByName['hbut2']['widget'] self.hbut3 = self.ifd.entryByName['hbut3']['widget'] self.angListEnt = self.ifd.entryByName['angListEnt']['widget'] def mouseUp(self, event=None): #print "in mouseUp" #fix this: atomList length dependent if len(self.atomList) == 4: at0, at1, at2, at3 = self.atomList angle = self.extslider.get() if self.torsionType.get() == '1': self.vf.setTorsion(at0, at1, at2, at3, angle) else: self.vf.setRelativeTorsion(at1, at2, angle)
def onAddCmdToViewer(self): from DejaVu.bitPatterns import pat3 from DejaVu.IndexedPolygons import IndexedPolygons if not self.vf.commands.has_key('setICOM'): self.vf.loadCommand('interactiveCommands', 'setICOM', 'Pmv', topCommand=0) if not self.vf.commands.has_key('measureAngle'): self.vf.loadCommand('measureCommands', 'measureAngle', 'Pmv', topCommand=0) self.masterGeom = Geom('measureTorsionGeom', shape=(0, 0), pickable=0, protected=True) self.masterGeom.isScalable = 0 if self.vf.hasGui: measure_geoms = check_measure_geoms(self.vf.GUI) self.vf.GUI.VIEWER.AddObject(self.masterGeom, parent=measure_geoms) self.lines = IndexedPolygons('torsionLine', materials=((0, 1, 1), ), culling=GL.GL_NONE, inheritStipplePolygons=0, inheritMaterial=0, stipplePolygons=1, backPolyMode=GL.GL_FILL, frontPolyMode=GL.GL_FILL, protected=True, pickable=0) if self.vf.userpref['Sharp Color Boundaries for MSMS'][ 'value'] == 'blur': self.lines.Set( inheritSharpColorBoundaries=False, sharpColorBoundaries=False, ) self.lines.polygonstipple.Set(pattern=pat3) #self.lines.polygonstipple.Set(pattern=pat3, tagModified=False) #self.lines.RenderMode(GL.GL_FILL) #self.lines.RenderMode(GL.GL_FILL, face=GL.GL_BACK) self.labels = GlfLabels(name='torsionLabel', shape=(0, 3), font='arial1.glf', fontStyle='solid3d', fontScales=(.5, .5, .3), inheritMaterial=0, materials=((0, 1, 1), )) self.spheres = Spheres(name='torsionSpheres', shape=(0, 3), inheritMaterial=0, radii=0.2, quality=15, materials=((0., 1., 1.), ), protected=True) if self.vf.hasGui: for item in [self.lines, self.labels, self.spheres]: self.vf.GUI.VIEWER.AddObject(item, parent=self.masterGeom) doc = """Number of labeled torsions displayed. Valid values are integers>0""" self.vf.userpref.add('Measured Torsions', 4, callbackFunc=[self.setLength_cb], category="Molecules", validateFunc=lambda x: x > 0, doc=doc) #used after startICOM is invoked doc = """Continuous update of torsions if 'transformRoot only' is turned off and viewer's current object is not Root.""" choices = ['yes', 'no'] self.vf.userpref.add('Continuous Update Torsion', 'yes', choices, callbackFunc=[self.continuousUpdate_cb], category="Molecules", doc=doc) self.snakeLength = 4
class MeasureTorsionGUICommand(MeasureGUICommand): """Label torsion between four atoms (color coded cyan) Accumulates picked atoms.Draws polygons and labels showing the torsion angle between groups of 4 selected atoms (color-coded cyan).Userpref 'measureTorsionSL' sets the 'snakeLength' which is how many torsion measureDisplays can be seen at the same time.When more than that number are measured, the first torsion measured is no longer labeled. \nPackage : Pmv \nModule : measureCommands \nClass : MeasureTorsionGUICommand \nCommand : measureTorsionGC \nSynopsis:\n torsion/None<---measureTorsionGC(atoms) \nRequired Argument:\n atoms --- the atom(s) \ntorsion --- returned when the number of atoms is a multiple of 4 """ def __init__(self, func=None): MeasureGUICommand.__init__(self, func=func) self.flag = self.flag | self.objArgOnly def onAddCmdToViewer(self): from DejaVu.bitPatterns import pat3 from DejaVu.IndexedPolygons import IndexedPolygons if not self.vf.commands.has_key('setICOM'): self.vf.loadCommand('interactiveCommands', 'setICOM', 'Pmv', topCommand=0) if not self.vf.commands.has_key('measureAngle'): self.vf.loadCommand('measureCommands', 'measureAngle', 'Pmv', topCommand=0) self.masterGeom = Geom('measureTorsionGeom', shape=(0, 0), pickable=0, protected=True) self.masterGeom.isScalable = 0 if self.vf.hasGui: measure_geoms = check_measure_geoms(self.vf.GUI) self.vf.GUI.VIEWER.AddObject(self.masterGeom, parent=measure_geoms) self.lines = IndexedPolygons('torsionLine', materials=((0, 1, 1), ), culling=GL.GL_NONE, inheritStipplePolygons=0, inheritMaterial=0, stipplePolygons=1, backPolyMode=GL.GL_FILL, frontPolyMode=GL.GL_FILL, protected=True, pickable=0) if self.vf.userpref['Sharp Color Boundaries for MSMS'][ 'value'] == 'blur': self.lines.Set( inheritSharpColorBoundaries=False, sharpColorBoundaries=False, ) self.lines.polygonstipple.Set(pattern=pat3) #self.lines.polygonstipple.Set(pattern=pat3, tagModified=False) #self.lines.RenderMode(GL.GL_FILL) #self.lines.RenderMode(GL.GL_FILL, face=GL.GL_BACK) self.labels = GlfLabels(name='torsionLabel', shape=(0, 3), font='arial1.glf', fontStyle='solid3d', fontScales=(.5, .5, .3), inheritMaterial=0, materials=((0, 1, 1), )) self.spheres = Spheres(name='torsionSpheres', shape=(0, 3), inheritMaterial=0, radii=0.2, quality=15, materials=((0., 1., 1.), ), protected=True) if self.vf.hasGui: for item in [self.lines, self.labels, self.spheres]: self.vf.GUI.VIEWER.AddObject(item, parent=self.masterGeom) doc = """Number of labeled torsions displayed. Valid values are integers>0""" self.vf.userpref.add('Measured Torsions', 4, callbackFunc=[self.setLength_cb], category="Molecules", validateFunc=lambda x: x > 0, doc=doc) #used after startICOM is invoked doc = """Continuous update of torsions if 'transformRoot only' is turned off and viewer's current object is not Root.""" choices = ['yes', 'no'] self.vf.userpref.add('Continuous Update Torsion', 'yes', choices, callbackFunc=[self.continuousUpdate_cb], category="Molecules", doc=doc) self.snakeLength = 4 def __call__(self, atoms, **kw): """torsion/None<-measureTorsionGC(atoms) \natoms --- the atom(s) \ntorsion --- returned when the number of atoms is a multiple of 4""" if type(atoms) is types.StringType: self.nodeLogString = "'" + atoms + "'" ats = self.vf.expandNodes(atoms) if not len(ats): return 'ERROR' return apply(self.doitWrapper, (ats, ), kw) def doit(self, ats): for at in ats: lenAts = len(self.atomList) if lenAts and lenAts % 4 != 0 and at == self.atomList[-1]: continue self.atomList.append(at) if len(self.atomList) > 4 * self.snakeLength: self.atomList = self.atomList[4:] self.update() if len(self.labelStrs) and len(self.atomList) % 4 == 0: return float(self.labelStrs[-1]) def update(self, forward=1, event=None): if not len(self.atomList): self.spheres.Set(vertices=[]) #self.spheres.Set(vertices=[], tagModified=False) self.labels.Set(vertices=[]) #self.labels.Set(vertices=[], tagModified=False) self.lines.Set(vertices=[]) #self.lines.Set(vertices=[], tagModified=False) self.vf.GUI.VIEWER.Redraw() return limit = self.snakeLength #each time have to recalculate lineVertices self.lineVertices = [] for at in self.atomList: c1 = self.getTransformedCoords(at) self.lineVertices.append(tuple(c1)) #display spheres: if len(self.lineVertices) % 4: self.spheres.Set( vertices=self.lineVertices[-(len(self.lineVertices) % 4):]) else: self.spheres.Set(vertices=[]) #self.spheres.Set(vertices=self.lineVertices, tagModified=False) self.vf.GUI.VIEWER.Redraw() #label with torsion #lines between spheres are only drawn when angle completed #that is, len(ats)%4=0 if len(self.lineVertices) < 4: self.labels.Set(vertices=[]) #self.labels.Set(vertices=[], tagModified=False) self.lines.Set(vertices=[]) #self.lines.Set(vertices=[], tagModified=False) else: #rebuild labels and polygons each time self.labelCenters = [] self.labelStrs = [] #labelCenters, labelStrs, #this gets done lenATs/4 times numItems = len(self.atomList) / 4 for i in range(numItems): at0 = self.atomList[i * 4] at1 = self.atomList[i * 4 + 1] at2 = self.atomList[i * 4 + 2] at3 = self.atomList[i * 4 + 3] torsion = self.vf.measureTorsion(at0, at1, at2, at3, topCommand=0) torsionLabel = '%.3f' % torsion self.labelStrs.append(torsionLabel) c0 = self.getTransformedCoords(at0) c1 = self.getTransformedCoords(at3) newcenter = tuple((c0 + c1) / 2.0) self.labelCenters.append(newcenter) #to reset labels, lines and fan, EACH TIME self.labels.Set(vertices=self.labelCenters, labels=self.labelStrs) #tagModified=False) #if len(self.lineVertices)%4!=0: #self.lineVertices = self.lineVertices[:numItems*4] #only draw lines in groups of 4 #numItems*4 if len(self.atomList) % 4 == 0: faces = range(numItems * 4) faces = Numeric.reshape(faces, (-1, 4)) ###FIX THIS ###on undo: if you have just wrapped, undoing the next pt ###breaks because trying to set the vertices uses the old ###faces if not forward: self.lines.Set(vertices=[], faces=[]) #self.lines.Set(vertices=[], faces=[], tagModified=False) self.lines.Set(vertices=self.lineVertices, faces=faces, freshape=1) #freshape=1, tagModified=False) self.vf.GUI.VIEWER.Redraw() else: #this only works going forward: undo breaks here if len(self.lines.faceSet.faces.array) > numItems: faces = range((numItems + 1) * 4) faces = Numeric.reshape(faces, (-1, 4)) if forward: faces = faces[1:] else: faces = faces[:-1] self.lines.Set(faces=faces) self.vf.GUI.VIEWER.Redraw()
class MeasureAngleGUICommand(MeasureGUICommand): """Accumulates picked atoms.Draws fans, lines and labels labelling the angle between trios of selected atoms (color-coded orange).Userpref 'measureAngleSL' sets the 'snakeLength' which is how many angle measureDisplays can be seen at the same time.When more than that number are measured, the first angle measured is no longer labeled. \nPackage : Pmv \nModule : measureCommands \nClass : MeasureAngleGUICommand \nCommand : measureAngleGC \nSynopsis:\n angle/None<---measureAngleGC(atoms) \nRequired Argument:\n atoms --- atom(s) \nangle --- returned when the number of atoms is a multiple of 3 """ def __init__(self, func=None): MeasureGUICommand.__init__(self, func=func) self.flag = self.flag | self.objArgOnly def onAddCmdToViewer(self): from DejaVu.Arcs3D import Fan3D from DejaVu.bitPatterns import pat3 self.arcNormals = [] self.arcVectors = [] self.arcCenters = [] if not self.vf.commands.has_key('setICOM'): self.vf.loadCommand('interactiveCommands', 'setICOM', 'Pmv', topCommand=0) if not self.vf.commands.has_key('measureAngle'): self.vf.loadCommand('measureCommands', 'measureAngle', 'Pmv', topCommand=0) self.masterGeom = Geom('measureAngleGeom', shape=(0, 0), pickable=0, protected=True) self.masterGeom.isScalable = 0 if self.vf.hasGui: measure_geoms = check_measure_geoms(self.vf.GUI) self.vf.GUI.VIEWER.AddObject(self.masterGeom, parent=measure_geoms) self.lines = IndexedPolylines('angleLine', materials=((1, .5, 0), ), inheritMaterial=0, lineWidth=3, stippleLines=1, protected=True, pickable=0) self.fan = Fan3D('angles', materials=((1, .5, 0), ), culling=GL.GL_NONE, inheritMaterial=0, stipplePolygons=1, radii=(1., ), inheritStipplePolygons=0, backPolyMode=GL.GL_FILL, pickable=0) self.fan.polygonstipple.Set(pattern=pat3) #self.fan.polygonstipple.Set(pattern=pat3, tagModified=False) #self.fan.RenderMode(GL.GL_FILL) #self.fan.RenderMode(GL.GL_FILL, face=GL.GL_BACK) self.labels = GlfLabels(name='angleLabel', shape=(0, 3), font='arial1.glf', fontStyle='solid3d', fontScales=(.5, .5, .3), inheritMaterial=0, materials=((1., .5, 0.), )) self.spheres = Spheres(name='angleSpheres', shape=(0, 3), inheritMaterial=0, radii=0.2, quality=15, materials=((1., .5, 0.), ), protected=True) if self.vf.hasGui: for item in [self.lines, self.labels, self.spheres, self.fan]: self.vf.GUI.VIEWER.AddObject(item, parent=self.masterGeom) doc = """Number of labeled angles displayed. Valid values are integers>0""" self.vf.userpref.add('Number of Measure Angles', 4, callbackFunc=[self.setLength_cb], category="Molecules", validateFunc=lambda x: x > 0, doc=doc) #used after startICOM is invoked doc = """Continuous update of angles if 'transformRoot only' is turned off and viewer's current object is not Root.""" choices = ['yes', 'no'] self.vf.userpref.add('Continuous Update Angle', 'yes', choices, callbackFunc=[self.continuousUpdate_cb], category="Molecules", doc=doc) self.snakeLength = 4 def __call__(self, atoms, **kw): """angle/None<---measureAngleGC(atoms) \natoms --- atom(s) \nangle --- returned when the number of atoms is a multiple of 3""" if type(atoms) is types.StringType: self.nodeLogString = "'" + atoms + "'" ats = self.vf.expandNodes(atoms) if not len(ats): return 'ERROR' return apply(self.doitWrapper, (ats, ), kw) def doit(self, ats): for at in ats: lenAts = len(self.atomList) if lenAts and lenAts % 3 != 0 and at == self.atomList[-1]: continue self.atomList.append(at) l = len(self.atomList) #for this command, reset after every 3 #wrap when len(atoms)=3*self.snakeLength+1 if l > 3 * self.snakeLength: self.atomList = self.atomList[3:] self.update() if len(self.labelStrs) and len(self.atomList) % 3 == 0: return float(self.labelStrs[-1]) def update(self, forward=1, event=None): if not len(self.atomList): self.spheres.Set(vertices=[]) #self.spheres.Set(vertices=[], tagModified=False) self.labels.Set(vertices=[]) #self.labels.Set(vertices=[], tagModified=False) self.lines.Set(vertices=[]) #self.lines.Set(vertices=[], tagModified=False) self.vf.GUI.VIEWER.Redraw() return limit = self.snakeLength #each time have to recalculate lineVertices self.lineVertices = [] for at in self.atomList: c1 = self.getTransformedCoords(at) self.lineVertices.append(tuple(c1)) #display spheres: if len(self.lineVertices) % 3: self.spheres.Set( vertices=self.lineVertices[-(len(self.lineVertices) % 3):]) else: self.spheres.Set(vertices=[]) #self.spheres.Set(vertices=self.lineVertices, tagModified=False) self.vf.GUI.VIEWER.Redraw() #label with angle #lines between spheres are only drawn when angle completed #that is, len(ats)%3=0 if len(self.lineVertices) < 3: self.labels.Set(vertices=[]) #self.labels.Set(vertices=[], tagModified=False) self.fan.Set(vertices=[]) #self.fan.Set(vertices=[], tagModified=False) self.lines.Set(vertices=[]) #self.lines.Set(vertices=[], tagModified=False) else: #should all of these be reset? self.arcNormals = [] self.arcVectors = [] self.arcCenters = [] self.labelCenters = [] self.labelStrs = [] #rebuild arcNormals, arcVectors, arcCenters #labelCenters, labelStrs, #this gets done lenATs/3 times numItems = len(self.atomList) / 3 for i in range(numItems): at0 = self.atomList[i * 3] at1 = self.atomList[i * 3 + 1] at2 = self.atomList[i * 3 + 2] ang = self.vf.measureAngle(at0, at1, at2, topCommand=0) v, n = self.normal(at0, at1, at2) self.arcNormals.append(n) #self.arcNormals = self.arcNormals[-limit:] self.arcVectors.append(v) #self.arcVectors = self.arcVectors[-limit:] self.arcCenters.append(self.getTransformedCoords(at1)) #self.arcCenters = self.arcCenters[-limit:] angLabel = '%.3f' % ang self.labelStrs.append(angLabel) c0 = self.getTransformedCoords(at0) c1 = self.getTransformedCoords(at2) newcenter = tuple((c0 + c1) / 2.0) self.labelCenters.append(newcenter) #to reset labels, lines and fan, EACH TIME self.labels.Set(vertices=self.labelCenters, labels=self.labelStrs) #tagModified=False) faces = range(numItems * 3) faces = Numeric.reshape(faces, (-1, 3)) self.lines.Set(vertices=self.lineVertices, type=GL.GL_LINE_STRIP, faces=faces, freshape=1) #faces=faces, freshape=1, tagModified=False) self.fan.angles = map(float, self.labelStrs) self.fan.vectors = self.arcVectors self.fan.Set(vertices=self.arcCenters, vnormals=self.arcNormals) #tagModified=False) def normal(self, at0, at1, at2): c0 = self.getTransformedCoords(at0) c1 = self.getTransformedCoords(at1) c2 = self.getTransformedCoords(at2) v1 = c1 - c0 v2 = c1 - c2 l1 = math.sqrt(Numeric.sum(v1 * v1)) l2 = math.sqrt(Numeric.sum(v2 * v2)) #FIXME #protect against divide by 0 n = self.vvmult(v1 / l1, v2 / l2) n = n / math.sqrt(Numeric.sum(n * n)) return -v2 / l2, n.astype('f')
class MeasureDistanceGUICommand(MeasureGUICommand): """ This command measures distance between atoms. Lines are drawn between pairs of consecutively picked atoms and labels are display showing the distance. \nPackage : Pmv \nModule : measureCommands \nClass : MeasureDistanceGUICommand \nCommand : measureDistanceGC \nSynopsis:\n distance/None<---measureDistanceGC(atoms) \nRequired Argument:\n atoms --- atom(s) """ def __init__(self, func=None): MeasureGUICommand.__init__(self, func=func) self.flag = self.flag | self.objArgOnly def guiCallback(self, event=None): MeasureGUICommand.guiCallback(self, event) self.vf.setICOM(self.vf.measureDistGUI, modifier='Control_L', topCommand=0) def onAddCmdToViewer(self): if not self.vf.commands.has_key('setICOM'): self.vf.loadCommand('interactiveCommands', 'setICOM', 'Pmv', topCommand=0) if not self.vf.commands.has_key('measureDistance'): self.vf.loadCommand('measureCommands', 'measureDistance', 'Pmv', topCommand=0) self.masterGeom = Geom('measureDistGeom', shape=(0, 0), pickable=0) self.masterGeom.isScalable = 0 if self.vf.hasGui: measure_geoms = check_measure_geoms(self.vf.GUI) self.vf.GUI.VIEWER.AddObject(self.masterGeom, parent=measure_geoms) self.lines = IndexedPolylines('distLine', materials=((1, 1, 0), ), inheritMaterial=0, lineWidth=3., stippleLines=1, pickable=0) self.labels = GlfLabels(name='distLabel', shape=(0, 3), font='arial1.glf', fontStyle='solid3d', fontScales=(.5, .5, .3), inheritMaterial=0, materials=((1, 1, 0), )) self.spheres = Spheres(name='distSpheres', shape=(0, 3), inheritMaterial=0, radii=0.2, quality=15, materials=((1., 1., 0.), )) if self.vf.hasGui: for item in [self.lines, self.labels, self.spheres]: self.vf.GUI.VIEWER.AddObject(item, parent=self.masterGeom) def __call__(self, atoms, **kw): """distance/None<---measureDistanceGC(atoms) \natoms --- atom(s)""" if type(atoms) is types.StringType: self.nodeLogString = "'" + atoms + "'" ats = self.vf.expandNodes(atoms) if not len(ats): return 'ERROR' return self.doitWrapper(*(ats, ), **kw) def doit(self, ats): for at in ats: lenAts = len(self.atomList) if lenAts and at == self.atomList[-1]: continue self.atomList.append(at) self.update() if len(self.labelStrs): return float(self.labelStrs[-1]) def update(self, forward=1, event=None): if not len(self.atomList): self.spheres.Set(vertices=[]) #self.spheres.Set(vertices=[], tagModified=False) self.labels.Set(vertices=[]) #self.labels.Set(vertices=[], tagModified=False) self.lines.Set(vertices=[]) #self.lines.Set(vertices=[], tagModified=False) return self.lineVertices = [] #each time have to recalculate lineVertices for at in self.atomList: c1 = self.getTransformedCoords(at) self.lineVertices.append(tuple(c1)) if len(self.lineVertices) % 2: self.spheres.Set(vertices=[self.lineVertices[-1]]) else: self.spheres.Set(vertices=[]) #self.spheres.Set(vertices=self.lineVertices, tagModified=False) #setting spheres doesn't trigger redraw so do it explicitly self.vf.GUI.VIEWER.Redraw() #each time have to recalculate labelCenters and labelStrs if len(self.lineVertices) > 1: self.labelCenters = [] self.labelStrs = [] self.faces = [] numLabels = len(self.lineVertices) - 1 for i in range(0, numLabels, 2): c0 = Numeric.array(self.lineVertices[i]) c1 = Numeric.array(self.lineVertices[i + 1]) newCenter = tuple((c1 + c0) / 2.0) self.labelCenters.append(newCenter) at1 = self.atomList[i] at2 = self.atomList[i + 1] d = self.vf.measureDistance(at1, at2, topCommand=0) dLabel = '%.3f' % d self.labelStrs.append(dLabel) self.faces.append([i, i + 1]) #correct length of labels here self.labels.Set(vertices=self.labelCenters, labels=self.labelStrs) #tagModified=False) self.lines.Set(vertices=self.lineVertices, type=GL.GL_LINE_STRIP, faces=self.faces, freshape=1) #tagModified=False) elif len(self.lineVertices) == 1 and len(self.labelCenters) == 1: #this fixes case of stepping back over 1st label self.labels.Set(vertices=[]) #self.labels.Set(vertices=[], tagModified=False) self.lines.Set(vertices=[])