def buildChainMenu(self,event=None):
     if not hasattr(self,'chainVar'): self.chainVar={}
     if not hasattr(self,'oldchainVar'): self.oldchainVar={}
     chMols = MoleculeSet([])
     if len(self.molSet):
         chMols=MoleculeSet(filter(lambda x: Chain in x.levels, self.molSet))
     chainIDList = []
     if len(chMols):
         chains=chMols.findType(Chain)
         if chains==None: return
         for i in chains:
             chainIDList.append(i.full_name())
     self.buildMenu(self.chainMB,chainIDList,self.chainVar,self.oldchainVar,self.getChainVal)
 def buildChainMenu(self, event=None):
     if not hasattr(self, 'chainVar'): self.chainVar = {}
     if not hasattr(self, 'oldchainVar'): self.oldchainVar = {}
     chMols = MoleculeSet([])
     if len(self.molSet):
         chMols = MoleculeSet(
             filter(lambda x: Chain in x.levels, self.molSet))
     chainIDList = []
     if len(chMols):
         chains = chMols.findType(Chain)
         if chains == None: return
         for i in chains:
             chainIDList.append(i.full_name())
     self.buildMenu(self.chainMB, chainIDList, self.chainVar,
                    self.oldchainVar, self.getChainVal)
 def getObjects(self, column):
     # return a list of objects associated with this node and possibly
     # other seleted nodes.  For selection we return a list for each type
     # ( i.e. Atom, Residue, etc..)
     # if the node is selected, collect object from all other selected nodes
     resultAtoms = AtomSet([])
     resultResidues = ResidueSet([])
     resultChains = ChainSet([])
     resultMolecules = MoleculeSet([])
     buttonValue = self.chkbtVar[column].get()
     if self.selected:
         for node in self.tree.list_selected:
             node.chkbtVar[column].set(buttonValue)
             result = node.getNodes(column)
             obj = result[0]
             if isinstance(obj, Atom):
                 resultAtoms += result
             elif isinstance(obj, Residue):
                 resultResidues += result
             elif isinstance(obj, Chain):
                 resultChains += result
             elif isinstance(obj, Molecule) or isinstance(obj, Protein):
                 resultMolecules += result
         result = []
         if len(resultAtoms): result.append(resultAtoms)
         if len(resultResidues): result.append(resultResidues)
         if len(resultChains): result.append(resultChains)
         if len(resultMolecules): result.append(resultMolecules)
         return result
     else:
         return [self.getNodes(column)]
Exemple #4
0
    def doit(self, Klass, KlassSet=None):
        if type(Klass) == types.StringType:
            if Klass in self.levelDict.keys():
                Klass = self.levelDict[Klass]
            else:
                msg = Klass + "string does not map to a valid level"
                self.warningMsg(msg)
                return "ERROR"
        if Klass is Protein:
            Klass = Molecule

        if len(self.vf.selection):
            self.vf.selection = self.vf.selection.findType(Klass).uniq()
        else:
            if Klass == Molecule: self.vf.selection = MoleculeSet([])
            elif Klass == Chain: self.vf.selection = ChainSet([])
            elif Klass == Residue: self.vf.selection = ResidueSet([])
            elif Klass == Atom: self.vf.selection = AtomSet([])

        self.vf.selectionLevel = Klass
        self.vf.ICmdCaller.setLevel(Klass, KlassSet=None)
        if self.vf.hasGui:
            col = self.vf.ICmdCaller.levelColors[Klass.__name__]
            c = (col[0] / 1.5, col[1] / 1.5, col[2] / 1.5)
            self.vf.GUI.pickLabel.configure(background=TkColor(c))
            msg = '%d %s(s)' % (len(
                self.vf.selection), self.vf.selectionLevel.__name__)
            self.vf.GUI.pickLabel.configure(text=msg)
            if hasattr(self.vf, 'select'):
                self.vf.select.updateSelectionIcons()
            self.levelVar.set(self.vf.selectionLevel.__name__)
 def addSetLine(self, name, set):
     """
     add a line to the dashboard with a given  molecular fragment
     """
     from MolKit.molecule import MoleculeSet
     node = MoleculeSet()
     node.setSetAttribute('_set', set)
     node.setSetAttribute('name', name)
     node.setSetAttribute('treeNodeClass', SetWithButtons)
     self.onAddObjectToViewer(node)
 def addSetLine(self, name, set):
     """
     add a line to the dashboard with a given  molecular fragment
     """
     from MolKit.molecule import MoleculeSet
     node = MoleculeSet()
     node.setSetAttribute('_set', set)
     node.setSetAttribute('name', name)
     node.setSetAttribute('treeNodeClass', SetWithButtons)
     self.onAddObjectToViewer(node)
    def __init__(self,
                 master,
                 check=0,
                 molSet=MoleculeSet([]),
                 userPref='cS',
                 vf=None,
                 all=1,
                 crColor=(0., 1., 0),
                 clearButton=True,
                 showButton=True,
                 sets=None,
                 **kw):

        if not kw.get('packCfg'):
            self.packCfg = {'side': 'top', 'anchor': 'w', 'fill': 'x'}

        self.sets = sets
        #all is a shortcut to use all of default values
        if 'all' in kw.keys():
            all = 1
        elif __debug__:
            if check:
                apply(checkKeywords, ('stringSelector', self.entryKeywords),
                      kw)

        Tkinter.Frame.__init__(self, master)
        ##????
        Tkinter.Pack.config(self, side='left', anchor='w')

        #to make a stringSelector need vf, moleculeSet, selString
        self.master = master
        self.molSet = molSet
        self.residueSelector = ResidueSetSelector()
        self.atomSelector = AtomSetSelector()
        self.userPref = userPref
        self.vf = vf
        if not self.vf is None:
            self.addGeom(crColor)
        else:
            self.showCross = None
        self.molCts = {}
        self.chainCts = {}

        # optsDict includes defaults for all possible widgets
        # in practice
        # user can specify which widgets to put into gui
        d = self.optsDict = {}

        #first check for menubuttons:

        llists = [self.entryKeywords, self.menuKeywords, self.buttonKeywords]
        if all:
            if not clearButton:
                self.buttonKeywords.remove("clearBut")
            if not showButton:
                self.buttonKeywords.remove("showBut")
            for l in llists:
                for key in l:
                    d[key] = 1

        else:
            for l in llists:
                for key in l:
                    d[key] = kw.get(key)

        self.flexChildren = {
            'mol': ['molLabel', 'molEntry'],
            'chain': ['chainLabel', 'chainEntry'],
            'res': ['resLabel', 'resEntry'],
            'atom': ['atomLabel', 'atomEntry'],
        }

        for w in ['molWids', 'chainWids', 'resWids', 'atomWids']:
            if kw.get(w):
                lab = w[:-4]
                s = lab + 'Label'
                d[s] = 1
                s1 = lab + 'Entry'
                d[s1] = 1

        # instantiate all Tkinter variables (?)
        #DON"T HAVE ANY???
        self.buildTkVars()

        # only build requested widgets(?)
        self.buildifdDict()

        # build commandDictionary
        self.buildCmdDict()

        self.buildGUI()
    def go(self):
        msgStr = None
        if self.moleculeSet:
            self.molSet = self.getMolecules(self.moleculeSet, self.selList[0])
        else:
            self.molSet = None
            return []
            # return [], msgStr

        # SPLIT here if mol.children==Atoms
        # possibly: self.Mols4levels=filter(lambda x: x.childrenSetClass == ChainSet, self.molSet)
        # then process the others separately....?????????
        # eg self.Mols2levels=filter(lambda x: x.childrenSetClass == AtomSet, self.molSet)
        # self.Mols2levels would get fed to self.getAtoms and two results ???merged???
        if not self.molSet:
            self.chainSet = None
            msgStr = str(self.selList[0]) + " selected no molecules"
            return []
            # return [], msgStr

        noChainMols = MoleculeSet(
            [x for x in self.molSet if Chain not in x.levels])
        haveChainMols = MoleculeSet(
            [x for x in self.molSet if Chain in x.levels])

        # build the residues belonging to molecules w/ no Chains (!WEIRD!)
        ncrs = ResidueSet()
        for item in noChainMols:
            if Residue in item.levels:
                itemRes = item.allAtoms.parent.uniq()
                ncrs = ncrs + itemRes

        if ncrs:
            noChainResSet = self.getResidues(ncrs, self.selList[2])
            if not noChainResSet: noChainResSet = ResidueSet()
        else:
            noChainResSet = ResidueSet()

        if len(haveChainMols):
            self.chainSet = self.getChains(haveChainMols.findType(Chain),
                                           self.selList[1])
        if self.chainSet:
            haveChainResSet = self.getResidues(self.chainSet.findType(Residue),
                                               self.selList[2])
            # also test noChainMols for residues
            if haveChainResSet:
                self.resSet = haveChainResSet + noChainResSet
            else:
                self.resSet = noChainResSet
        else:
            self.resSet = noChainResSet
            ##don't return unless selList[2]!==['']
            if self.selList[1] != ['']:
                msgStr = str(self.selList[1]) + " selected no chains"
                return []
                # return [], msgStr

        # now: if self.selList for Chain and Residue level was empty, get the Atoms from noChains
        if self.selList[1] == [''] and self.selList[2] == ['']:
            tla = AtomSet()
            for item in noChainMols:
                if Residue not in item.levels:
                    tla = tla + item.allAtoms
            twoLevelAtoms = tla
        else:
            twoLevelAtoms = AtomSet()
        if self.resSet:
            resAts = self.resSet.findType(Atom)
            if twoLevelAtoms: resAts = resAts + twoLevelAtoms
            self.atomSet = self.getAtoms(resAts, self.selList[3])
        else:
            if self.selList[2] != ['']:
                msgStr = str(self.selList[2]) + " selected no residues"
                return []
                # return [], msgStr
            else:
                self.atomSet = self.getAtoms(twoLevelAtoms, self.selList[3])

        selNodes = self.atomSet
        # find correct levelType to return
        # need to split atomSet into two parts:
        if self.atomSet:
            haveChainAtoms = AtomSet(
                [x for x in self.atomSet if x.top != x.parent])
            haveNoChainAtoms = self.atomSet - haveChainAtoms
            if self.selList[3] == ['']:
                # change atoms to residues
                if haveChainAtoms:
                    selNodes = haveChainAtoms.parent.uniq()
                else:
                    selNodes = ResidueSet()
                if self.selList[2] == ['']:
                    # change residues to chains
                    if len(selNodes):
                        selNodes = selNodes.parent.uniq()
                    if self.selList[1] == ['']:
                        # change chains to molecules
                        if haveNoChainAtoms:
                            noChainTops = haveNoChainAtoms.top.uniq()
                        else:
                            noChainTops = ProteinSet()
                        if selNodes:
                            selTops = selNodes.top.uniq()
                        else:
                            selTops = ProteinSet()
                        selNodes = selTops + noChainTops
                        if self.selList[0] == ['']:
                            # change molecules to molecules(?)in the case of no strs
                            if selNodes.__class__ != MoleculeSet:
                                selNodes = MoleculeSet(selNodes.top.uniq())
        else:
            msgStr = str(self.selList[3]) + " selected no atoms"
        for item in ['moleculeSet', 'molSet', 'chainSet', 'resSet', 'atomSet']:
            if hasattr(self, item):
                delattr(self, item)
        #                 exec('del self.'+item)
        return selNodes
 def __init__(self, objects=None, stringRepr=None, comments="", keywords=[]):
     MoleculeSet.__init__(self, objects, stringRepr, comments=comments,
                             keywords=keywords)
     self.elementType = Protein
    def go(self):
        msgStr = None
        if self.moleculeSet: 
            self.molSet = self.getMolecules(self.moleculeSet, self.selList[0])
        else: 
            self.molSet = None
            return []
            #return [], msgStr

        #SPLIT here if mol.children==Atoms
        #possibly: self.Mols4levels=filter(lambda x: x.childrenSetClass == ChainSet, self.molSet)
        #then process the others separately....?????????
        #eg self.Mols2levels=filter(lambda x: x.childrenSetClass == AtomSet, self.molSet)
        #self.Mols2levels would get fed to self.getAtoms and two results ???merged???
        if not self.molSet:
            self.chainSet = None
            msgStr = str(self.selList[0])+ " selected no molecules"
            return []
            #return [], msgStr

        noChainMols=MoleculeSet(filter(lambda x: Chain not in x.levels, self.molSet))
        haveChainMols=MoleculeSet(filter(lambda x: Chain in x.levels, self.molSet))

        #build the residues belonging to molecules w/ no Chains (!WEIRD!)
        ncrs=ResidueSet()
        for item in noChainMols:
            if Residue in item.levels:
                 itemRes=item.allAtoms.parent.uniq()
                 ncrs= ncrs + itemRes

        if ncrs:
            noChainResSet=self.getResidues(ncrs,self.selList[2]) 
            if not noChainResSet: noChainResSet=ResidueSet()
        else:
            noChainResSet=ResidueSet()

        if len(haveChainMols):
            self.chainSet = self.getChains(haveChainMols.findType(Chain), self.selList[1])
        if self.chainSet:
            haveChainResSet=self.getResidues(self.chainSet.findType(Residue), self.selList[2])
            #also test noChainMols for residues
            if haveChainResSet:
                self.resSet=haveChainResSet+noChainResSet
            else:
                self.resSet = noChainResSet
        else: 
            self.resSet = noChainResSet
            ##don't return unless selList[2]!==['']
            if self.selList[1]!=['']:
                msgStr = str(self.selList[1])+ " selected no chains"
                return []
                #return [], msgStr

        #now: if self.selList for Chain and Residue level was empty, get the Atoms from noChains
        if self.selList[1]==[''] and self.selList[2]==['']:
            tla=AtomSet()
            for item in noChainMols:
                if Residue not in item.levels:
                    tla=tla + item.allAtoms
            twoLevelAtoms = tla
        else:
            twoLevelAtoms=AtomSet()
        if self.resSet:
            resAts=self.resSet.findType(Atom)
            if twoLevelAtoms:resAts=resAts+twoLevelAtoms
            self.atomSet = self.getAtoms(resAts, self.selList[3])
        else: 
            if self.selList[2]!=['']:
                msgStr = str(self.selList[2])+ " selected no residues"
                return []
                #return [], msgStr
            else: self.atomSet = self.getAtoms(twoLevelAtoms, self.selList[3])

        selNodes = self.atomSet
        #find correct levelType to return
        #need to split atomSet into two parts:
        if self.atomSet:
            haveChainAtoms = AtomSet(filter(lambda x: x.top!=x.parent,self.atomSet))
            haveNoChainAtoms=self.atomSet-haveChainAtoms
            if self.selList[3]==['']:
                #change atoms to residues
                if haveChainAtoms: selNodes = haveChainAtoms.parent.uniq()
                else: selNodes=ResidueSet()
                if self.selList[2]==['']:
                    #change residues to chains
                    if len(selNodes): 
                        selNodes = selNodes.parent.uniq()
                    if self.selList[1]==['']:
                        #change chains to molecules
                        if haveNoChainAtoms: noChainTops=haveNoChainAtoms.top.uniq()
                        else: noChainTops=ProteinSet()
                        if selNodes: selTops= selNodes.top.uniq()
                        else: selTops=ProteinSet()
                        selNodes = selTops+noChainTops
                        if self.selList[0]==['']:
                            #change molecules to molecules(?)in the case of no strs
                            if selNodes.__class__!=MoleculeSet:
                                selNodes = MoleculeSet(selNodes.top.uniq())
        else:
            msgStr = str(self.selList[3])+ " selected no atoms"
        for item in ['moleculeSet','molSet','chainSet','resSet','atomSet']:
             if hasattr(self, item): 
                 delattr(self, item)
#                 exec('del self.'+item)
        return selNodes
Exemple #11
0
 msg = rec_file + " not found"
 assert os.path.exists(rec_file), msg
 recs = Read(rec_file)
 rec = recs[0]
 # locate the receptor atom
 css = CompoundStringSelector()
 rec_ats = css.select(recs, ratom_name)[0]
 msg = ratom_name + " did not match exactly 1 atom in " + rec_file
 assert len(rec_ats) == 1, msg
 rec_at = rec_ats[0]
 rec_at_coords = rec_at.coords
 if verbose:
     print("found rec_at = ", rec_at.full_name(), end=' ')
     print("with initial coords = ", rec_at.coords)
 # locate the ligand atom
 lig_ats = css.select(MoleculeSet([d.ligMol]), latom_name)[0]
 assert len(lig_ats) == 1
 lig_at = lig_ats[0]
 print("found lig_at =>", lig_at.full_name())
 print("initial coords=", lig_at.coords)
 init_coords = lig_at.coords
 #open the outputfile
 if os.path.exists(outputfilename):
     fptr = open(outputfilename, 'a')
 else:
     fptr = open(outputfilename, 'w')
     ostr = "run     rec_at coords             lig_at coords        distance \n"
     fptr.write(ostr)
 print(" opened output file:", outputfilename)
 # set the pose to Run 1
 ctr = 1
    def __init__(self, title="Molecule Viewer", logMode='no',
                 libraries=[], gui=1, resourceFile = '_pmvrc',
                 customizer = None, master=None, guiVisible=1,
                 withShell=1, verbose=True, trapExceptions=True):
        """
        * title:
          string used as a title. 
        * logMode:
          string specifying the mode of logging of mv.
            'no': for no loging of commands at all
            'overwrite': the log files overwrite the one from the previous
                         session the log files = mvAll.log.py
            'unique': the log file name include the date and time

        * libraries:
          list of the Python packages containing modules and commands
          that can be loaded in the application. Such a package needs the
          following files : cmdlib.py and modlib.py
        * gui :
          Flag specifying whether or not to run the application with a gui.
        * resourceFile:
          file sourced at startup and where userpreference  made as default
          are saved (default: '.pmvrc')
        * customizer :
          file when specified is sourced at startup instead of the resourceFile
        * master:
          can be specified to run PMV withing another GUI application.
        * guiVisible:
          Flag to specify whether or not to show the GUI.
        - trapExceptions should be set to False when creating a ViewerFramework
          for testing, such that exception are seen by the testing framework
        """
        libraries = ['Pmv', 'Volume','AutoDockTools'] + libraries
        _pmvrc = Find_pmvrc(resourceFile)
        if _pmvrc:
            resourceFile = _pmvrc
        if withShell:
            from traceback import print_exception
            
            def print_exception_modified(etype, value, tb, limit=None, file=None):
                """
                Modified version of traceback.print_exception
                Deiconifies pyshell when Traceback is printed
                """
                print_exception(etype, value, tb, limit, file)
                if hasattr(self, 'GUI'):
                    self.GUI.pyshell.top.deiconify()
                if not 'Pmv' in tb.tb_frame.f_code.co_filename:
                    return
                if etype == ImportError:
                    if hasattr(value,'message'):
                        package = value.message.split()[-1]
                        print "Please install " +package + " to fix this problem."
                elif etype == AssertionError:
                    pass
                else:
                    print "Please include this Traceback in your bug report. Help --> Report a Bug in PMV/ADT."
            import traceback 
            
            traceback.print_exception = print_exception_modified
        ViewerFramework.__init__(self, title, logMode, libraries, gui,
                                 resourceFile, master=master,
                                 guiVisible=guiVisible, withShell=withShell,
                                 verbose=verbose, trapExceptions=trapExceptions)
        #if sys.platform == 'win32': #this needed to account for camera size
        #    geometry = '%dx%d+%d+%d' % (800,600, 30, 30)
        #else:
        #    geometry = '%dx%d+%d+%d' % (800,200, 30, 30)    
        #self.GUI.ROOT.geometry(geometry)

        # Establish interface to Visual Programming environment.
        if self.visionAPI is not None:
            # add Molecule, Pmv, Viewer to lookup table
            from Pmv.VisionInterface.PmvNodes import PmvMolecule, PmvNode, \
                 PmvViewer, PmvSetNode, PmvVolume
            self.visionAPI.addToLookup(Protein, PmvMolecule, "Molecules")
            self.visionAPI.addToLookup(MoleculeViewer, PmvNode, "PMV")
            from DejaVu import Viewer
            self.visionAPI.addToLookup(Viewer, PmvViewer, "PMV")
            self.visionAPI.addToLookup(TreeNodeSet, PmvSetNode, "Sets")

            # Note: Molecules are added to the interface in addMolecule() below
            
            # put Pmv instance into list of objects to be added to Vision
            self.visionAPI.add(self, "Pmv", kw={
                'vf':self,
                'constrkw':{'vf':'masterNet.editor.vf'} } )
            # put Pmv Viewer instance in list of objects to be added to Vision
            if self.hasGui:
                self.visionAPI.add(self.GUI.VIEWER, "Pmv Viewer", kw={
                'viewer':self.GUI.VIEWER,
                'constrkw':{'viewer':'masterNet.editor.vf.GUI.VIEWER'} } )
            
        self.selection = MoleculeSet()  # store current selection
        # replace interactive command caller by MVInteractiveCmdCaller
        # we need the ICmdCaller even if there is no GUI because it has
        # the level variable used byu selection commands
        self.ICmdCaller = MVInteractiveCmdCaller( self )
        from mvCommand import MVSetIcomLevel
        self.addCommand( MVSetIcomLevel(), 'setIcomLevel', None )
        from mvCommand import MVSetSelectionLevel, MVSetSelectionLevelGUI
        self.addCommand( MVSetSelectionLevel(), 'setSelectionLevel',  MVSetSelectionLevelGUI)

        self.setSelectionLevel(Molecule, topCommand = 0) #should this be Protein?
        from Pmv.displayCommands import BindGeomToMolecularFragment
        from Pmv.displayCommands import BindGeomToMolecularFragmentGUI

        self.addCommand( BindGeomToMolecularFragment(),
                         'bindGeomToMolecularFragment',
                         BindGeomToMolecularFragmentGUI )

#        if self.hasGui:
#            from Pmv.mvCommand import MVPrintNodeNames, MVCenterOnNodes
#            self.addCommand( MVPrintNodeNames(), 'printNodeNames ', None )
#            self.addCommand( MVCenterOnNodes(), 'centerOnNodes', None )

            # load out default interactive command which prints out object
            # names
            #self.ICmdCaller.setCommands( self.printNodeNames )

        self.ICmdCaller.go()
        self.addMVBasicMenus()

        # load out default interactive command
        self.ICmdCaller.setCommands( self.printNodeNames, modifier=None )
        self.ICmdCaller.setCommands( self.select, modifier='Shift_L' )
        self.ICmdCaller.setCommands( self.centerOnNodes, modifier='Control_L' )
        self.ICmdCaller.setCommands( self.deselect, modifier='Alt_L' )

        #self.setIcomLevel(Molecule, topCommand = 0)
        self.setIcomLevel(Atom, topCommand = 0)

        self.Mols = MoleculeSet() # store the molecules read in
        self.objects = self.Mols
        from MolKit.sets import Sets
        self.sets = Sets()  # store user-defined sets in this dict

        # lock needs to be acquired before volume can be added
        self.volumesLock = thread.allocate_lock()

        self.Vols = [] # list of Grid3D objects storing volumetric data
        if self.visionAPI is not None:
            from Volume.Grid3D import Grid3D
            self.visionAPI.addToLookup(Grid3D, PmvVolume, "Volumes")

        self.allAtoms = AtomSet() # set of all atoms (across molecules)

        #if self.hasGui:
        #    from Pmv.controlPanelCommands import ControlPanel,ControlPanel_GUI
        #    self.addCommand(ControlPanel(), "controlPanel",ControlPanel_GUI)
        choices = ['caseSensitive', 'caseInsensitive',
                   'caseInsensWithEscapedChars']

        self.userpref.add('selectStringMatchMode', 'caseSensitive', validValues=choices,
                          doc = """When set to caseSensitive the string match
mode will be case sensitive the other possibility is to be case insensitive or
case insensitive with escaped characters.
""")
        choices = [1,0]
        self.userpref.add('showSelectionSpheres', 1, validValues=choices,
                          doc = """When set to 1 the selection visual feedback
which are the little yellow crosses will be displayed.""")
        choices = [1,0]
        self.userpref.add('raiseExceptionForMissingKey', 1, validValues=choices,
                          callbackFunc = [self.setRaiseException],
                          doc = """When set to 1 an exception will be raised
is a a key is not found in a dictionnary.
""")
        
        choices = [1, 0]
        self.userpref.add('expandNodeLogString', 0, validValues=choices,
                          doc = """When set to 1 the log string representing
the node argument of a doit will be expanded to the full name of each element
of the TreeNodeSet, when set to 0 the log string representing the node argument
of a doit will be 'self.getSelection()'. In the last case the command log will
depend on the current selection.""")

        # overwrite firstObject only with firstMoleculeOnly
        self.userpref['centerScene']['validValues'][0] = 'firstMoleculeOnly'
        self.userpref.set('centerScene', 'firstMoleculeOnly')
        
        choices = ['yes','no']
        self.userpref.add('useDepthCueing', 'yes', validValues=choices,
                          doc = """ When set to 'yes' the depthCueing is
turned on by default""")

        doc = """When set to yes a warning message is displayed when an empty
selection is about to be expanded to all the  molecules loaded in the 
application"""
        self.userpref.add('warnOnEmptySelection', 'no', validValues=choices, doc=doc)

        if self.hasGui:
            self.GUI.VIEWER.suspendRedraw = True
    
            self.GUI.drop_cb = self.drop_cb
            self.GUI.pickLabel.bind("<Button-1>",self.setSelectionLevel.guiCallback)
            if self.userpref['useDepthCueing']['value']=='yes':
                self.GUI.VIEWER.currentCamera.fog.Set(enabled=1,
                                                      tagModified=False)

            if title != 'AutoDockTools':
                toolbarDict = {}
                toolbarDict['name'] = 'ADT'
                toolbarDict['type'] = 'Checkbutton'
                toolbarDict['icon1'] = 'adt.png'
                toolbarDict['balloonhelp'] = 'AutoDock Tools'
                toolbarDict['icon_dir'] = ICONPATH
                toolbarDict['index'] = 7
                toolbarDict['cmdcb'] = self.Add_ADT
                toolbarDict['variable'] = None
                self.GUI.toolbarList.append(toolbarDict)
            self.GUI.configureToolBar(self.GUI.iconsize)
            # overwrite unsollicited picking with a version that prints atom names
            #self.GUI.VIEWER.RemovePickingCallback("unsolicitedPick")
            #self.GUI.VIEWER.AddPickingCallback(self.unsolicitedPick)

            top = self.GUI.ROOT.winfo_toplevel()
            geom = top.geometry()
            geom = geom.split('x')
            self.GUI.menuBars['Toolbar']._frame.update_idletasks()
            winfo_width = self.GUI.menuBars['Toolbar']._frame.winfo_width()        
            if int(geom[0]) < winfo_width + 10:
                geom[0] = str(winfo_width + 10)
            top.geometry(geom[0]+'x'+geom[1])   
            if not trapExceptions and customizer == './.empty':
                top.update_idletasks()
                top.deiconify()  
                #self.GUI.vwrCanvasFloating.deiconify()
                self.GUI.naturalSize()
            from Pmv.updateCommands import Update, UpdateGUI
            self.addCommand( Update(), 'update', UpdateGUI )
    
            from Pmv.aboutCommands import About, AboutGUI
            self.addCommand( About(), 'about', AboutGUI )
    
            self.GUI.VIEWER.suspendRedraw = False
            self.browseCommands('deleteCommands',package='Pmv', topCommand=0)
            self.GUI.ROOT.bind('<Delete>', self.deleteAtomSet.guiCallback)
            self.GUI.vwrCanvasFloating.bind('<Delete>', self.deleteAtomSet.guiCallback)
            self.browseCommands ('dashboardCommands', package='Pmv', topCommand=0)
        self.customize(customizer)
        if self.hasGui:
            try:
                import grid3DCommands
                self.browseCommands("grid3DCommands",package="Pmv", topCommand=0)
            except ImportError:
                print "UTpackages are not installed. Disabling grid3DCommands..."
        
        rcFile = getResourceFolderWithVersion()
        if rcFile:
            rcFile += os.sep + 'Pmv' + os.sep + "recent.pkl"
            
        if self.hasGui:
            fileMenu = self.GUI.menuBars['menuRoot'].menubuttons['File'].menu
    
            self.recentFiles = RecentFiles(self, fileMenu, filePath=rcFile, 
                                           menuLabel = 'Recent Files')
            try:
                from DejaVu.Camera import RecordableCamera
                if isinstance(self.GUI.VIEWER.cameras[0], RecordableCamera):
                    from Pmv.videoCommands import VideoCommand, VideoCommandGUI 
                    self.addCommand(VideoCommand(), 'videoCommand', VideoCommandGUI)
            except:
                pass
                #print "Recordable camera is not available"
            if len(self.dashboard.tree.columns)==0:
                # this warning is wrong, it appears during test_pmvscript
                #print "WARNING: update your _pmvrc file to load the dashboard commands"
                from Pmv.dashboard import loadAllColunms
                loadAllColunms(self)
class MoleculeViewer(ViewerFramework):
    """
    package    : Pmv
    module     : moleculeViewer
    class      : MoleculeViewer
    description:
       Class derived from the ViewerFramework base class. It provides a 3D
       molecular viewer.
    """
    

    def getSelLev(self):
        return self.selection.elementType


    def setSelLev(self, value):
        if value==Protein: value = Molecule
        assert value in [Molecule, Chain, Residue, Atom]
        self.setSelectionLevel(value)

    selectionLevel = property(getSelLev, setSelLev)

    def __init__(self, title="Molecule Viewer", logMode='no',
                 libraries=[], gui=1, resourceFile = '_pmvrc',
                 customizer = None, master=None, guiVisible=1,
                 withShell=1, verbose=True, trapExceptions=True):
        """
        * title:
          string used as a title. 
        * logMode:
          string specifying the mode of logging of mv.
            'no': for no loging of commands at all
            'overwrite': the log files overwrite the one from the previous
                         session the log files = mvAll.log.py
            'unique': the log file name include the date and time

        * libraries:
          list of the Python packages containing modules and commands
          that can be loaded in the application. Such a package needs the
          following files : cmdlib.py and modlib.py
        * gui :
          Flag specifying whether or not to run the application with a gui.
        * resourceFile:
          file sourced at startup and where userpreference  made as default
          are saved (default: '.pmvrc')
        * customizer :
          file when specified is sourced at startup instead of the resourceFile
        * master:
          can be specified to run PMV withing another GUI application.
        * guiVisible:
          Flag to specify whether or not to show the GUI.
        - trapExceptions should be set to False when creating a ViewerFramework
          for testing, such that exception are seen by the testing framework
        """
        libraries = ['Pmv', 'Volume','AutoDockTools'] + libraries
        _pmvrc = Find_pmvrc(resourceFile)
        if _pmvrc:
            resourceFile = _pmvrc
        if withShell:
            from traceback import print_exception
            
            def print_exception_modified(etype, value, tb, limit=None, file=None):
                """
                Modified version of traceback.print_exception
                Deiconifies pyshell when Traceback is printed
                """
                print_exception(etype, value, tb, limit, file)
                if hasattr(self, 'GUI'):
                    self.GUI.pyshell.top.deiconify()
                if not 'Pmv' in tb.tb_frame.f_code.co_filename:
                    return
                if etype == ImportError:
                    if hasattr(value,'message'):
                        package = value.message.split()[-1]
                        print "Please install " +package + " to fix this problem."
                elif etype == AssertionError:
                    pass
                else:
                    print "Please include this Traceback in your bug report. Help --> Report a Bug in PMV/ADT."
            import traceback 
            
            traceback.print_exception = print_exception_modified
        ViewerFramework.__init__(self, title, logMode, libraries, gui,
                                 resourceFile, master=master,
                                 guiVisible=guiVisible, withShell=withShell,
                                 verbose=verbose, trapExceptions=trapExceptions)
        #if sys.platform == 'win32': #this needed to account for camera size
        #    geometry = '%dx%d+%d+%d' % (800,600, 30, 30)
        #else:
        #    geometry = '%dx%d+%d+%d' % (800,200, 30, 30)    
        #self.GUI.ROOT.geometry(geometry)

        # Establish interface to Visual Programming environment.
        if self.visionAPI is not None:
            # add Molecule, Pmv, Viewer to lookup table
            from Pmv.VisionInterface.PmvNodes import PmvMolecule, PmvNode, \
                 PmvViewer, PmvSetNode, PmvVolume
            self.visionAPI.addToLookup(Protein, PmvMolecule, "Molecules")
            self.visionAPI.addToLookup(MoleculeViewer, PmvNode, "PMV")
            from DejaVu import Viewer
            self.visionAPI.addToLookup(Viewer, PmvViewer, "PMV")
            self.visionAPI.addToLookup(TreeNodeSet, PmvSetNode, "Sets")

            # Note: Molecules are added to the interface in addMolecule() below
            
            # put Pmv instance into list of objects to be added to Vision
            self.visionAPI.add(self, "Pmv", kw={
                'vf':self,
                'constrkw':{'vf':'masterNet.editor.vf'} } )
            # put Pmv Viewer instance in list of objects to be added to Vision
            if self.hasGui:
                self.visionAPI.add(self.GUI.VIEWER, "Pmv Viewer", kw={
                'viewer':self.GUI.VIEWER,
                'constrkw':{'viewer':'masterNet.editor.vf.GUI.VIEWER'} } )
            
        self.selection = MoleculeSet()  # store current selection
        # replace interactive command caller by MVInteractiveCmdCaller
        # we need the ICmdCaller even if there is no GUI because it has
        # the level variable used byu selection commands
        self.ICmdCaller = MVInteractiveCmdCaller( self )
        from mvCommand import MVSetIcomLevel
        self.addCommand( MVSetIcomLevel(), 'setIcomLevel', None )
        from mvCommand import MVSetSelectionLevel, MVSetSelectionLevelGUI
        self.addCommand( MVSetSelectionLevel(), 'setSelectionLevel',  MVSetSelectionLevelGUI)

        self.setSelectionLevel(Molecule, topCommand = 0) #should this be Protein?
        from Pmv.displayCommands import BindGeomToMolecularFragment
        from Pmv.displayCommands import BindGeomToMolecularFragmentGUI

        self.addCommand( BindGeomToMolecularFragment(),
                         'bindGeomToMolecularFragment',
                         BindGeomToMolecularFragmentGUI )

#        if self.hasGui:
#            from Pmv.mvCommand import MVPrintNodeNames, MVCenterOnNodes
#            self.addCommand( MVPrintNodeNames(), 'printNodeNames ', None )
#            self.addCommand( MVCenterOnNodes(), 'centerOnNodes', None )

            # load out default interactive command which prints out object
            # names
            #self.ICmdCaller.setCommands( self.printNodeNames )

        self.ICmdCaller.go()
        self.addMVBasicMenus()

        # load out default interactive command
        self.ICmdCaller.setCommands( self.printNodeNames, modifier=None )
        self.ICmdCaller.setCommands( self.select, modifier='Shift_L' )
        self.ICmdCaller.setCommands( self.centerOnNodes, modifier='Control_L' )
        self.ICmdCaller.setCommands( self.deselect, modifier='Alt_L' )

        #self.setIcomLevel(Molecule, topCommand = 0)
        self.setIcomLevel(Atom, topCommand = 0)

        self.Mols = MoleculeSet() # store the molecules read in
        self.objects = self.Mols
        from MolKit.sets import Sets
        self.sets = Sets()  # store user-defined sets in this dict

        # lock needs to be acquired before volume can be added
        self.volumesLock = thread.allocate_lock()

        self.Vols = [] # list of Grid3D objects storing volumetric data
        if self.visionAPI is not None:
            from Volume.Grid3D import Grid3D
            self.visionAPI.addToLookup(Grid3D, PmvVolume, "Volumes")

        self.allAtoms = AtomSet() # set of all atoms (across molecules)

        #if self.hasGui:
        #    from Pmv.controlPanelCommands import ControlPanel,ControlPanel_GUI
        #    self.addCommand(ControlPanel(), "controlPanel",ControlPanel_GUI)
        choices = ['caseSensitive', 'caseInsensitive',
                   'caseInsensWithEscapedChars']

        self.userpref.add('selectStringMatchMode', 'caseSensitive', validValues=choices,
                          doc = """When set to caseSensitive the string match
mode will be case sensitive the other possibility is to be case insensitive or
case insensitive with escaped characters.
""")
        choices = [1,0]
        self.userpref.add('showSelectionSpheres', 1, validValues=choices,
                          doc = """When set to 1 the selection visual feedback
which are the little yellow crosses will be displayed.""")
        choices = [1,0]
        self.userpref.add('raiseExceptionForMissingKey', 1, validValues=choices,
                          callbackFunc = [self.setRaiseException],
                          doc = """When set to 1 an exception will be raised
is a a key is not found in a dictionnary.
""")
        
        choices = [1, 0]
        self.userpref.add('expandNodeLogString', 0, validValues=choices,
                          doc = """When set to 1 the log string representing
the node argument of a doit will be expanded to the full name of each element
of the TreeNodeSet, when set to 0 the log string representing the node argument
of a doit will be 'self.getSelection()'. In the last case the command log will
depend on the current selection.""")

        # overwrite firstObject only with firstMoleculeOnly
        self.userpref['centerScene']['validValues'][0] = 'firstMoleculeOnly'
        self.userpref.set('centerScene', 'firstMoleculeOnly')
        
        choices = ['yes','no']
        self.userpref.add('useDepthCueing', 'yes', validValues=choices,
                          doc = """ When set to 'yes' the depthCueing is
turned on by default""")

        doc = """When set to yes a warning message is displayed when an empty
selection is about to be expanded to all the  molecules loaded in the 
application"""
        self.userpref.add('warnOnEmptySelection', 'no', validValues=choices, doc=doc)

        if self.hasGui:
            self.GUI.VIEWER.suspendRedraw = True
    
            self.GUI.drop_cb = self.drop_cb
            self.GUI.pickLabel.bind("<Button-1>",self.setSelectionLevel.guiCallback)
            if self.userpref['useDepthCueing']['value']=='yes':
                self.GUI.VIEWER.currentCamera.fog.Set(enabled=1,
                                                      tagModified=False)

            if title != 'AutoDockTools':
                toolbarDict = {}
                toolbarDict['name'] = 'ADT'
                toolbarDict['type'] = 'Checkbutton'
                toolbarDict['icon1'] = 'adt.png'
                toolbarDict['balloonhelp'] = 'AutoDock Tools'
                toolbarDict['icon_dir'] = ICONPATH
                toolbarDict['index'] = 7
                toolbarDict['cmdcb'] = self.Add_ADT
                toolbarDict['variable'] = None
                self.GUI.toolbarList.append(toolbarDict)
            self.GUI.configureToolBar(self.GUI.iconsize)
            # overwrite unsollicited picking with a version that prints atom names
            #self.GUI.VIEWER.RemovePickingCallback("unsolicitedPick")
            #self.GUI.VIEWER.AddPickingCallback(self.unsolicitedPick)

            top = self.GUI.ROOT.winfo_toplevel()
            geom = top.geometry()
            geom = geom.split('x')
            self.GUI.menuBars['Toolbar']._frame.update_idletasks()
            winfo_width = self.GUI.menuBars['Toolbar']._frame.winfo_width()        
            if int(geom[0]) < winfo_width + 10:
                geom[0] = str(winfo_width + 10)
            top.geometry(geom[0]+'x'+geom[1])   
            if not trapExceptions and customizer == './.empty':
                top.update_idletasks()
                top.deiconify()  
                #self.GUI.vwrCanvasFloating.deiconify()
                self.GUI.naturalSize()
            from Pmv.updateCommands import Update, UpdateGUI
            self.addCommand( Update(), 'update', UpdateGUI )
    
            from Pmv.aboutCommands import About, AboutGUI
            self.addCommand( About(), 'about', AboutGUI )
    
            self.GUI.VIEWER.suspendRedraw = False
            self.browseCommands('deleteCommands',package='Pmv', topCommand=0)
            self.GUI.ROOT.bind('<Delete>', self.deleteAtomSet.guiCallback)
            self.GUI.vwrCanvasFloating.bind('<Delete>', self.deleteAtomSet.guiCallback)
            self.browseCommands ('dashboardCommands', package='Pmv', topCommand=0)
        self.customize(customizer)
        if self.hasGui:
            try:
                import grid3DCommands
                self.browseCommands("grid3DCommands",package="Pmv", topCommand=0)
            except ImportError:
                print "UTpackages are not installed. Disabling grid3DCommands..."
        
        rcFile = getResourceFolderWithVersion()
        if rcFile:
            rcFile += os.sep + 'Pmv' + os.sep + "recent.pkl"
            
        if self.hasGui:
            fileMenu = self.GUI.menuBars['menuRoot'].menubuttons['File'].menu
    
            self.recentFiles = RecentFiles(self, fileMenu, filePath=rcFile, 
                                           menuLabel = 'Recent Files')
            try:
                from DejaVu.Camera import RecordableCamera
                if isinstance(self.GUI.VIEWER.cameras[0], RecordableCamera):
                    from Pmv.videoCommands import VideoCommand, VideoCommandGUI 
                    self.addCommand(VideoCommand(), 'videoCommand', VideoCommandGUI)
            except:
                pass
                #print "Recordable camera is not available"
            if len(self.dashboard.tree.columns)==0:
                # this warning is wrong, it appears during test_pmvscript
                #print "WARNING: update your _pmvrc file to load the dashboard commands"
                from Pmv.dashboard import loadAllColunms
                loadAllColunms(self)


    def drop_cb(self, files):
        for file in files:
            self.readMolecule(file)
        
    #def getSelectionLevel(self):
    #    return self.selection.elementType
            

    def getMolFromName(self, name):
        mols = filter(lambda x: x.name == name, self.Mols)
        if len(mols):
            mol = mols[0]
        else:
            mol = None
        return mol

    def setRaiseException(self, name, oldval, val):
        import MolKit.molecule
        MolKit.molecule.raiseExceptionForMissingKey = val


    def unsolicitedPick(self, pick):
        """treat an unsollicited picking event"""
        
        if pick is None: return
        vi = self.GUI.VIEWER
        if vi.isShift() or vi.isControl():
            vi.unsolicitedPick(pick)
        else:
            atom = self.findPickedAtoms(pick)
            if atom:
                level = self.ICmdCaller.level.value
                if level == Molecule: level = Protein
                node = atom.findType(level)
                for n in node:
                    self.message( n.full_name() )


    def loadMoleculeIfNeeded(self, filename):
        """load a molecule only if it doesn't exist yet in Pmv, else it
        aborts silent"""

        if not os.path.exists(filename):
            print 'Error! %s not found!'%filename
            return
        
        # find what name would be
        name = os.path.split(filename)[-1]
        try:
            spl = split(name, '.')
        except:
            spl = [name]
        name = spl[0]

        # ask if name already used
        if self.Mols:
            for mol in self.Mols.data:
                if name == mol.name:
                    # break and return mol
                    return mol
        
        # else load molecule
        if not hasattr(self, 'readMolecule'):
            self.browseCommands(
                'fileCommands',
                commands=['readMolecule'], package='Pmv', topCommand=0)

        mol = self.readMolecule(filename)
        return mol
        
        
    def addMolecule(self, newmol, ask=1):
        """
        Add a molecule to this viewer
        """
        #IN ANY CASE: change any special characters in name to '-'

        from MolKit.molecule import Molecule
        if self.hasGui:
            Molecule.configureProgressBar = self.GUI.progressBarConf
            Molecule.updateProgressBar = self.GUI.progressBarUpd
        
        spChar=['?','*','.','$','#',':','-',',']        
##         spChar=['?','*','.','$','#',':','_',',']        
        for item in spChar:
            newmol.name = replace(newmol.name,item,'_')
##             newmol.name = replace(newmol.name,item,'-')
        if len(self.Mols) > 0:
            if newmol.name in self.Mols.name:
                if ask==1: 
                    from mglutil.gui.InputForm.Tk.gui import InputFormDescr
                    idf = self.ifd = InputFormDescr(title = '')
                    idf.append({'widgetType':Pmw.EntryField,
                                'name':'newmol',
                                'required':1,
                                'wcfg':{'labelpos':'w',
                                        'label_text':'New Name: ',
                                        'validate':None,
                                        'value':'%s-%d'%(newmol.name,
                                                         len(self.Mols))},
                                'gridcfg':{'sticky':'we'}})

                    vals = self.getUserInput(idf)
                    if len(vals)>0:
                        assert not vals['newmol'] in self.Mols.name
                        newmol.name = vals['newmol']
                    else:
                        return None
                else:
                    newmol.name='%s_%d'%(newmol.name,len(self.Mols))

        newmol.allAtoms.setStringRepr(newmol.full_name()+':::')
        
        # provide hook for progress bar
        # old code: newmol.allAtoms._bndIndex_ = range(len(newmol.allAtoms))

        allAtomsLen = len(newmol.allAtoms)
        if allAtomsLen == 0:
            import warnings
            warnings.warn("%s is empty molecule cannot add it to the viewer"%newmol.name)
            return None
        if self.hasGui:
            self.GUI.configureProgressBar(init=1, mode='increment',
                                      max=allAtomsLen,
                                      labeltext='add molecule to viewer')
        i = 0
        for a in newmol.allAtoms:
            a._bndIndex_ = i
            if self.hasGui:
                self.GUI.updateProgressBar()
            i = i + 1

        g = None
        if self.hasGui:
            g = MolGeomContainer( newmol, self )
        # addObject calls updateProgressBar on its own
        self.addObject('mol%s'%len(self.Mols), newmol, g)

        self.Mols.setStringRepr(self.Mols.full_name())

        # add object to visionAPI (to add them as nodes to Vision library)
        if self.visionAPI:
            self.visionAPI.add(newmol, newmol.name, kw={
                'molecule':newmol,
                'constrkw':{
                    'molecule':
                    'masterNet.editor.vf.expandNodes("%s")[0]'%newmol.name} } )

        self.allAtoms = self.allAtoms + newmol.allAtoms
        self.allAtoms.setStringRepr(self.Mols.full_name()+':::')
        
        #used by cpk command to decide whether or not to compute radii
        newmol.unitedRadii = None # set to None to force initial radii assignment
        return newmol


    def addVolume(self, name, grid):
        # FIXME we need to check for name unicity and have a repalcement policy

        #self.volumesLock.acquire()
        self.Vols.append(grid)
        grid.name = name
        #self.volumesLock.release()

        if self.visionAPI:
            self.visionAPI.add(grid, name, kw={
                'grid':grid,
                'constrkw':{
                    'grid':
                    'masterNet.editor.vf.gridFromName("%s")[0]'%grid.name} } )

    
    def getSelection(self):
        # FIXME why not return self.Mols always on empty selection ??
        # this should speed thing up
        #ICmdCallerLevel = self.ICmdCaller.level.value
        selLevel = self.selectionLevel

        if len(self.selection)==0:
            # empty selection
            if self.userpref['warnOnEmptySelection']['value']=='yes':
                if self.askOkCancelMsg('expand empty selection to all molecules?'):
                    #selection = self.Mols.findType(selLevel)#, uniq=1)
                    return self.Mols
                    #try:
                    #    selection = self.Mols.findType(selLevel)#, uniq=1)
                    #except:
                    #    if selLevel==Molecule:
                    #        selection = self.Mols.findType(Protein)
                    #return selection
                else:
                    #selection = self.Mols.findType(selLevel)#, uniq=1)
                    return self.Mols
                    #try:
                    #    selection = self.Mols.findType(selLevel)#, uniq=1)
                    #except:
                    #    if selLevel==Molecule:
                    #        selection = self.Mols.findType(Protein)
                    #return selection
            else:
                #selection = self.Mols.findType(selLevel)#, uniq=1)
                return self.Mols
                #try:
                #    selection = self.Mols.findType(selLevel)#, uniq=1)
                #except:
                #    if selLevel==Molecule:
                #        selection = self.Mols.findType(Protein)
                #return selection
                
        else:
            # not empty select
            #try:
            #    selection = self.selection.findType(selLevel, uniq=1)
            #except:
            #    if selLevel==Molecule:
            #        selection = self.selection.findType(Protein)
            return self.selection
            #selection = self.selection.findType(selLevel, uniq=1)
            #return selection

##             if self.userpref['warnOnEmptySelection']['value']=='yes':
##                 if self.askOkCancelMsg('expand empty selection to all molecules?'):
##                     selection = self.Mols.findType(selLevel)#, uniq=1)
##                     #try:
##                     #    selection = self.Mols.findType(selLevel)#, uniq=1)
##                     #except:
##                     #    if selLevel==Molecule:
##                     #        selection = self.Mols.findType(Protein)
##                     return selection
##                 else:
##                     selection = self.Mols.findType(selLevel)#, uniq=1)
##                     #try:
##                     #    selection = self.Mols.findType(selLevel)#, uniq=1)
##                     #except:
##                     #    if selLevel==Molecule:
##                     #        selection = self.Mols.findType(Protein)
##                     return selection
##             else:
##                 selection = self.Mols.findType(selLevel)#, uniq=1)
##                 #try:
##                 #    selection = self.Mols.findType(selLevel)#, uniq=1)
##                 #except:
##                 #    if selLevel==Molecule:
##                 #        selection = self.Mols.findType(Protein)
##                 return selection
                
##         else:
##             # not empty select
##             #try:
##             #    selection = self.selection.findType(selLevel, uniq=1)
##             #except:
##             #    if selLevel==Molecule:
##             #        selection = self.selection.findType(Protein)
##             selection = self.selection.findType(selLevel, uniq=1)
##             return selection
            

    def getItems(self, selString=""):
        """Takes a string and returns a TreeNodeSet
The string  can contain a series of set descriptors with operators
separated by / characters.  There is always a first set, followed by pairs of
operators and sets.  All sets have to describe nodes of the same level.
example:
    '1crn:::CA*/+/1crn:::O*' describes the union of all CA ans all O in 1crn
    '1crn:::CA*/+/1crn:::O*/-/1crn::TYR29:' 
"""
        assert type(selString)==StringType
        return self.expandNodes(selString)
            
        
    def expandNodes(self, nodes):
        """Takes nodes as string or TreeNode or TreeNodeSet and returns
a TreeNodeSet
If nodes is a string it can contain a series of set descriptors with operators
separated by / characters.  There is always a first set, followed by pairs of
operators and sets.  All sets ahve to describe nodes of the same level.

example:
    '1crn:::CA*/+/1crn:::O*' describes the union of all CA ans all O in 1crn
    '1crn:::CA*/+/1crn:::O*/-/1crn::TYR29:' 
"""
        if isinstance(nodes,TreeNode):
            result = nodes.setClass([nodes])
            result.setStringRepr(nodes.full_name())

        elif type(nodes)==StringType:
            stringRepr = nodes
            css = CompoundStringSelector()
            result = css.select(self.Mols, stringRepr)[0]
##            setsStrings = stringRepr.split('/')
##            getSet = self.Mols.NodesFromName
##            result = getSet(setsStrings[0])
##            for i in range(1, len(setsStrings), 2):
##                op = setsStrings[i]
##                arg = setsStrings[i+1]
##                if op=='|': # or
##                    result += getSet(arg)
##                elif op=='-': # subtract
##                    result -= getSet(arg)
##                elif op=='&': # intersection
##                    result &= getSet(arg)
##                elif op=='^': # xor
##                    result ^= getSet(arg)
##                elif op=='s': # sub select (i.e. select from previous result)
##                    result = result.get(arg)
##                else:
##                    raise ValueError, '%s bad operation in selection string'%op
##            result.setStringRepr(stringRepr)

        elif isinstance(nodes,TreeNodeSet):
            result = nodes
        else:
            raise ValueError, 'Could not expand nodes %s\n'%str(nodes)
        
        return result

        
    def getNodesByMolecule(self, nodes, nodeType=None):
        """ moleculeSet, [nodeSet, nodeSet] <- getNodesByMolecule(nodes, nodeType=None)
        nodes can be either: a string, a TreeNode or a TreeNodeSet.
        This method returns a molecule set and for each molecule a TreeNodeSet
        of the nodes belonging to this molecule.
        'nodeType' enables a desired type of nodes to be returned for each
        molecule
        """

        # special case list of complete molecules to be expanded to atoms
        # this also covers the case where nothing is selected
        if isinstance(nodes, MoleculeSet) or isinstance(nodes, ProteinSet):
            if nodeType is Atom:
                atms = []
                for mol in nodes:
                    atms.append(mol.allAtoms)
                return nodes, atms
            elif (nodeType is Protein) or (nodeType is Molecule):
                return nodes, nodes
        
        # if it is a string, get a bunch of nodes from the string
        if type(nodes)==StringType:
            nodes = self.expandNodes(nodes)

        assert issubclass(nodes.__class__, TreeNode) or \
               issubclass(nodes.__class__, TreeNodeSet)

        # if nodes is a single TreeNode make it a singleton TreeNodeSet
        if issubclass(nodes.__class__, TreeNode):
            nodes = nodes.setClass([nodes])
            nodes.setStringRepr(nodes.full_name())

        if len(nodes)==0: return MoleculeSet([]), []

        # catch the case when nodes is already a MoleculeSet
        if nodes.elementType in [Molecule, Protein]:
            molecules = nodes
        else: # get the set of molecules
            molecules = nodes.top.uniq()

        # build the set of nodes for each molecule
        nodeSets = []

        # find out the type of the nodes we want to return
        searchType=0
        if nodeType is None:
            Klass = nodes.elementType # class of objects in that set
        else:
            assert issubclass(nodeType, TreeNode)
            Klass = nodeType
            if Klass != nodes.elementType:
                searchType=1

        for mol in molecules:
            # get set of nodes for this molecule
            mol_nodes = nodes.get(lambda x, mol=mol: x.top==mol)

            # get the required types of nodes
            if searchType:
                if Klass == Atom and hasattr(mol_nodes, 'allAtoms'):
                    mol_nodes = mol_nodes.allAtoms
                else:
                    mol_nodes = mol_nodes.findType( Klass ).uniq()

            stringRepr = nodes.getStringRepr()
            if stringRepr:
                if ':' in stringRepr:
                    mol_nodes.setStringRepr(
                        mol.name+stringRepr[stringRepr.index(':'):])
                else:
                    mol_nodes.setStringRepr(stringRepr)

            nodeSets.append( mol_nodes )

        return molecules, nodeSets


    def addMVBasicMenus(self):
        from Pmv.selectionCommands import MVSelectCommand, MVDeSelectCommand
        from Pmv.mvCommand import MVPrintNodeNames, MVCenterOnNodes
        self.addCommand( MVPrintNodeNames(), 'printNodeNames' )
        self.addCommand( MVSelectCommand(), 'select' )
        self.addCommand( MVDeSelectCommand(), 'deselect' )
        self.addCommand( MVCenterOnNodes(), 'centerOnNodes' )


    def findPickedAtoms(self, pick):
        """
given a PickObject this function finds all corresponding atoms.
Each atom in the returned set has its attribute pickedInstances set to a list
of 2-tuples [(geom, instance),...].
"""

        allatoms = AtomSet( [] )
        # loop over object, i.e. geometry objects
        for obj, values in pick.hits.items():

            # build a list of vertices and list of instances
            instances = map(lambda x: x[1], values)
            vertInds = map(lambda x: x[0], values)

            # only geometry bound to molecules is packable in PMV
            if not hasattr(obj, 'mol') or len(vertInds)<1:
                continue

            # only vertices of geometry have a mapping to atoms
            # for other types we return an empty atom set
            if pick.type!='vertices':
                return allatoms

            g = obj.mol.geomContainer

            # convert vertex indices into atoms
            if g.geomPickToAtoms.has_key(obj.name):
                # the geometry obj has a function to convert to atoms
                # specified it he geomContainer[obj], e.g. MSMS surface
                func = g.geomPickToAtoms[obj.name]
                if func:
                    atList = func(obj, vertInds)
                else:
                    atlist = []
            else:
                # we assume a 1 to 1 mapping of vertices with atoms
                # e.g. the lines geometry
                atList = []
                allAtoms = g.atoms[obj.name]
                for i in vertInds:
                    atList.append(allAtoms[int(i)])

            # build a dict of atoms used to set the pickedAtomInstance
            # attribute for the last picking operation
            pickedAtoms = {}

            # update the pickedAtoms dict
            for i, atom in enumerate(atList):
                atomInstList = pickedAtoms.get(atom, None)
                if atomInstList:
                    atomInstList.append( (obj, instances[i]) )
                else:
                    pickedAtoms[atom] = [ (obj, instances[i]) ]

            # FIXME atoms might appear multiple times because they were picked
            # in several geometries OR be cause they correspond to different
            # instances.  In the first case (i.e. multiple geometries)
            # duplicates should be removed, in the latter (multiple instances)
            # duplicate should be kept
            #
            # Apparently we do not get duplication for multiple geoemtry objects!
            allatoms = allatoms + AtomSet( atList )

            # loop over picked atoms and write the instance list into the atom
            for atom, instances in pickedAtoms.items():
                atom.pickedInstances = instances

        #print allAtoms
        return allatoms


    def findPickedBonds(self, pick):
        """do a pick operation and return a 2-tuple holding (the picked bond,
        the picked geometry)"""

        allbonds = BondSet( [] )
        for o, val in pick.hits.items(): #loop over geometries
            # loop over list of (vertices, instance) (e.g. (45, [0,0,2,0]))
            for instvert in val:
                primInd = instvert[0]
                if not hasattr(o, 'mol'): continue
                g = o.mol.geomContainer
                if g.geomPickToBonds.has_key(o.name):
                    func = g.geomPickToBonds[o.name]
                    if func: allbonds = allbonds + func(o, primInd)
                else:
                    l = []
                    bonds = g.atoms[o.name].bonds[0]
                    for i in range(len(primInd)):
                        l.append(bonds[int(primInd[i])])
                    allbonds = allbonds + BondSet(l)

        return allbonds


    def transformedCoordinatesWithInstances(self, nodes):
        """ for a nodeset, this function returns transformed coordinates.
This function will use the pickedInstance attribute if found.
"""
        # nodes is a list of atoms, residues, chains, etc. where each member
        # has a pickedInstances attribute which is a list of 2-tuples
        # (object, [i,j,..])
        vt = []
        for node in nodes:
            #find all atoms and their coordinates
            coords = nodes.findType(Atom).coords
            if hasattr(node, 'pickedInstances'):
                # loop over the pickedInstances of this node
                for inst in node.pickedInstances:
                    geom, instance = inst # inst is a tuple (object, [i,j,..])
                    M = geom.GetMatrix(geom.LastParentBeforeRoot(), instance[1:])
                    for pt in coords:
                        ptx = M[0][0]*pt[0]+M[0][1]*pt[1]+M[0][2]*pt[2]+M[0][3]
                        pty = M[1][0]*pt[0]+M[1][1]*pt[1]+M[1][2]*pt[2]+M[1][3]
                        ptz = M[2][0]*pt[0]+M[2][1]*pt[1]+M[2][2]*pt[2]+M[2][3]
                        vt.append( (ptx, pty, ptz) )
            else:
                # no picking ==> no list of instances ==> use [0,0,0,...] 
                g = nodes[0].top.geomContainer.geoms['master']
                M = g.GetMatrix(g.LastParentBeforeRoot())
                for pt in coords:
                    ptx = M[0][0]*pt[0]+M[0][1]*pt[1]+M[0][2]*pt[2]+M[0][3]
                    pty = M[1][0]*pt[0]+M[1][1]*pt[1]+M[1][2]*pt[2]+M[1][3]
                    ptz = M[2][0]*pt[0]+M[2][1]*pt[1]+M[2][2]*pt[2]+M[2][3]
                    vt.append( (ptx, pty, ptz) )
                
        return vt
        
    def Add_ADT(self):
        """Adds AutoToolsBar"""
        if self.GUI.toolbarCheckbuttons['ADT']['Variable'].get():
            #if self.GUI.menuBars.has_key('AutoTools4Bar'):
            #    self.GUI.menuBars['AutoTools4Bar'].pack(fill='x',expand=1)
            if hasattr(self.GUI, 'currentADTBar'):
                self.GUI.menuBars[self.GUI.currentADTBar].pack(fill='x',expand=1)
            else:
                self.browseCommands('autotors4Commands', commands = None, 
                                        package = 'AutoDockTools')
                self.browseCommands('autoflex4Commands', commands = None, 
                                     package = 'AutoDockTools')
                self.browseCommands('autogpf4Commands', commands = None, 
                                        package = 'AutoDockTools')
                self.browseCommands('autodpf4Commands', commands = None, 
                                        package = 'AutoDockTools')
                self.browseCommands('autostart4Commands', commands = None, 
                                        package = 'AutoDockTools')
                self.browseCommands('autoanalyze4Commands', commands = None, 
                                        package = 'AutoDockTools')
                self.GUI.currentADTBar = 'AutoTools4Bar'
                from AutoDockTools import setADTmode
                setADTmode('AD4.0', self)
                self.GUI.adt4ModeLabel.bind("<Double-Button-1>", self.ADTSetMode.guiCallback)
        else:
            #self.GUI.menuBars['AutoToolsBar'].pack_forget()
            self.GUI.menuBars[self.GUI.currentADTBar].pack_forget()