""" python/ui/intersel.py: CCP4MG Molecular Graphics Program Copyright (C) 2001-2008 University of York, CCLRC This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3, modified in accordance with the provisions of the license to address the requirements of UK law. You should have received a copy of the modified GNU Lesser General Public License along with this library. If not, copies may be downloaded from http://www.ccp4.ac.uk/ccp4license.php This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. """ import GUI from dataobj import Dependency import mmut import pygl_coord #----------------------------------------------------------------------- #----------------------------------------------------------------------- #----------------------------------------------------------------------- class CInterSel(Dependency): #----------------------------------------------------------------------- #----------------------------------------------------------------------- #----------------------------------------------------------------------- operator_menu = ['OR: add to current selection', \ 'EXCLude: exclude from selection (=and not)', \ 'AND - atom must be in both selections'] operator_alias = ['or','excl','and'] ifskeleton = 1 skeleton_selection = 'ALL' #---------------------------------------------------------------------- def __init__(self,moldisp=None,CSelection=None): #---------------------------------------------------------------------- self.moldisp = moldisp self.CSelection = CSelection self.skeleton=None self.saved_style_mode = None self.gui_user_sel = '' self.user_sel_range = [] self.operator = 'or' self.not_operator = 0 self.object_type = '' self.interface_mode = 'advanced' self.gui_user_sel_stack = [] self.name = 'CInterSel' Dependency.__init__(self) self.set_skeleton() #---------------------------------------------------------------------- def delete(self): #---------------------------------------------------------------------- #print "Deleting CInterSel",self,self.CSelection if self.skeleton: self.handle_skeleton(mode=-1) self.skeleton = None ATOMPICKING().popup_request( \ mode='vertex',feature='user_sel',delete=1) GUI.GUI.insts.DeleteGUIWindow ( 'mol_userselect'+self.moldisp.name) if self.saved_style_mode and self.CSelection.style == 'FATBONDS': # Beware the CSelection object may have been zapped try: self.CSelection.set_style(style_mode=self.saved_style_mode) except: pass try: self.CSelection.CInterSel=None if hasattr(self.CSelection,'selection_params'): self.CSelection.selection_params['interactive_sele_gui']='off' except: pass Dependency.delete(self) #-------------------------------------------------------------------- def name_string(self): #-------------------------------------------------------------------- return 'model.'+self.CSelection.name_string()+'.CInterSel' #---------------------------------------------------------------------- def user_sel_popup (self,**keywords): #------------------------------------------------------------------------ # Define a portion of the pop-up menu to appear when # the user picks an atom molobj,dispobj,atomptr = ATOMPICKING().get_last_picked(\ 'atom') #print "molobj,dispobj",molobj.name,dispobj.name if molobj != self.CSelection.parent: return ['','',''] range_menu = ['Start residue range','Start atom range','Start chain range'] range_alias = ['start_residue','start_atom','start_chain'] if self.user_sel_range: range_menu.insert(0,'End range from '+self.user_sel_range[1]) range_alias.insert(0,'end') if ['excl'].count(self.operator): exclude = '1' else: exclude = '0' params = [ ['RETURN',self.name_string()+'.handle_user_sel_popup'], ['PARAM','selected_atom', ['TYPE','text'], ['INITIAL','%atomid%']], ['PARAM','exclude', ['TYPE','_logical'], ['INITIAL',exclude], ['LABEL','Exclude next selection']], ['PARAM','add_user_sel', ['TYPEDEF','menu',['atom','side chain','main chain','residue','SSE','chain','all'], ['atom','side','main','residue','SSE','chain','all']], ['INITIAL',''], ['LABEL','Select this..']], ['PARAM','add_user_range', ['TYPEDEF','menu',range_menu,range_alias], ['INITIAL',''], ['LABEL','Atom/res/chain range..']], ['PARAM','add_user_neighb', ['TYPEDEF','menu',['atom','residue','chain'], ['atom','residue','chain']], ['INITIAL',''], ['LABEL',"Neighbhood of this.."]], ['PARAM','undo', ['TYPE','_logical'], ['LABEL','Undo last']], ['PARAM','skeleton_selection' , ['LABEL','Skeleton atoms'], ['TYPEDEF','menu',['no skeleton','all atoms','trace and ligands'], \ ['OFF','ALL','CALIGAND']], ['INITIAL',self.skeleton_selection]] ] display = [ ['lab','Select'], ['cb','exclude'], ['pr','add_user_sel'], ['pr','add_user_neighb'], ['pr','add_user_range']] if self.gui_user_sel_stack: display.append(['com','undo']) display.append(['pr','skeleton_selection']) return ['',params,display] #----------------------------------------------------------------------- def handle_user_sel_popup( self,add_user_sel='',selected_atom='', \ add_user_neighb='', add_user_range='', exclude='', undo='', \ skeleton_selection='', **keywords ): #----------------------------------------------------------------------- #print "handle_user_sel_popup",undo,add_user_sel if exclude: if exclude == '1': self.operator = 'excl' else: self.operator = 'or' GUI.GUI.insts.UpdateGUIWindow ( 'mol_userselect'+self.moldisp.name, \ [['PARAM','operator', ['UPDATE',self.operator]]]) if not selected_atom: from atom_util import inta molobj,dispobj,atomptr = ATOMPICKING().get_last_picked('atom') mask = inta(9) py_mask = [0,1,1,1,1,1,1,1,0] for i in range(0,9): mask[i]=py_mask[i] selected_atom = molobj.molHnd.AtomLabel_mask(atomptr,mask) del mask if add_user_sel: if add_user_sel == 'all': # reset to all or nothing if self.operator == 'excl': sel = '' elif self.operator == 'or': sel = 'all' self.append_sel(sel=sel,mode='reset') return 0 elif add_user_sel == 'atom': sel = selected_atom elif ['side','main'].count(add_user_sel): import mmdb2 as mmdb import string sel = self.CSelection.parent.convertAtomID ( inp = 'atom', \ out = 'residue', atomID = selected_atom) if add_user_sel == 'side': cmd = sel+' and not main' else: cmd = sel+' and main' rv=self.CSelection.parent.parse_selection(command=cmd) if not rv[0]: selAtoms = mmdb.newPPCAtom() selindexp = pygl_coord.intp() #natoms = self.CSelection.parent.molHnd.GetSelIndex(rv[1],selAtoms) selAtoms = mmut.GetAtomSelIndex(self.CSelection.parent.molHnd,rv[1],selindexp) natoms = selindexp.value() for n in range(0,natoms): pcat = mmdb.getPCAtom(selAtoms,n) sel = sel+string.strip(pcat.GetAtomName())+',' sel = sel[0:-1] self.CSelection.parent.molHnd.DeleteSelection(rv[1]) #mmdb.delPPCAtom(selAtoms) elif add_user_sel == 'SSE': molobj,dispobj,atomptr = \ ATOMPICKING().get_last_picked('atom') mask = inta(7) for i in range(0,7): mask[i] = 1 import string sub = string.strip(molobj.molHnd.ListSecStructure(mask,atomptr)) if sub: sel = string.split(sub)[1] else: return else: sel = self.CSelection.parent.convertAtomID ( inp = 'atom', \ out = add_user_sel, atomID = selected_atom) #print "handle_user_sel_popup",sel self.append_sel(sel) return 0 elif add_user_neighb: if add_user_neighb == 'atom': sel = selected_atom else: sel = self.CSelection.parent.convertAtomID ( inp = 'atom', \ out = add_user_neighb, atomID = selected_atom) self.CSelection.define_neighb(selparams={'neighb_sel': sel } , \ gui_offset='mol_userselect'+self.moldisp.name,return_method=self.name_string()+'.handle') elif add_user_range: if add_user_range[0:5] == 'start': import string start,mode = string.split(add_user_range,'_') sel = self.CSelection.parent.convertAtomID ( inp = 'atom', \ out = mode , atomID = selected_atom) self.user_sel_range = [mode,sel] #print "add_user_range",self.user_sel_range elif add_user_range == 'end' and self.user_sel_range: sel = self.CSelection.parent.convertAtomID ( inp = 'atom', \ out =self.user_sel_range[0] , atomID = selected_atom) self.append_sel('range '+ self.user_sel_range[1]+' '+ \ sel+ ' ' + self.user_sel_range[0] ) self.user_sel_range = '' elif undo: self.append_sel(mode='undo') elif skeleton_selection: if skeleton_selection == 'OFF': if self.ifskeleton: self.ifskeleton = 0 self.handle_skeleton(mode=-1) else: if not self.ifskeleton: self.ifskeleton = 1 CInterSel.skeleton_selection = skeleton_selection self.handle_skeleton(mode=1) elif skeleton_selection != self.skeleton_selection: CInterSel.skeleton_selection = skeleton_selection self.handle_skeleton(mode=0) #-------------------------------------------------------------------- def handle_selection ( self,widget='',exit='',**keywords ): #-------------------------------------------------------------------- if exit == 'dismiss' or \ (widget == 'ranges' and ['add','delete'].count(exit)): return retval = self.moldisp.interpret_selection (kw=keywords,widget=widget) if retval[0] or not retval[3]: return status,separams,sel,alias = retval rv,command = self.CSelection.parent.list_to_command(sel) self.append_sel(sel=command) #--------------------------------------------------------------------- def append_sel ( self, sel='',operator = '',not_operator='',mode='append',user_alias='' ): #--------------------------------------------------------------------- import string import re import utils #print "append_sel",sel,mode # self.gui_user_sel = Selection string that appears in GUI - include # line breaks and html style < > etc # sel0 = corrected selection string #print "append_sel",sel,operator,mode #print "gui_user_sel",self.gui_user_sel # Put current selection on stack self.gui_user_sel_stack.append(self.gui_user_sel) if mode == 'append': # sel is a selection to be appended to existing com string if not sel: return if sel: sel = string.strip(sel) if not_operator != '': notop = not_operator else: notop = self.not_operator if notop and not re.match(r'^not',sel): sel = 'not '+sel # convert < and > to standard < > (eg from Property menu) sel = re.sub('&','&',sel) sel = re.sub('<','<',sel) sel = re.sub('>','>',sel) if not self.gui_user_sel: self.gui_user_sel = str(sel) elif operator: string.strip(self.gui_user_sel) self.gui_user_sel = re.sub('
$','',self.gui_user_sel) self.gui_user_sel = self.gui_user_sel+'
'+str(operator)+' '+str(sel) else: self.gui_user_sel = self.gui_user_sel+'
'+self.operator+' '+str(sel) elif mode == 'undo': if len(self.gui_user_sel_stack)<2: return [1] self.gui_user_sel = self.gui_user_sel_stack.pop() self.gui_user_sel = self.gui_user_sel_stack.pop() elif ['update'].count(mode): # Called from MolDisp.set_selection when user has picked the # display table selection menu self.gui_user_sel = string.strip(sel) elif ['reset'].count(mode): # User has editted command in text window # - could be very wrong self.gui_user_sel = re.sub('
','',sel) self.gui_user_sel = re.sub('&','&',self.gui_user_sel) self.gui_user_sel = re.sub('<','<',self.gui_user_sel) self.gui_user_sel = re.sub('>','>',self.gui_user_sel) self.gui_user_sel = re.sub(' or ','
or ',self.gui_user_sel) self.gui_user_sel = re.sub(' and ','
and ',self.gui_user_sel) self.gui_user_sel = re.sub(' excl ','
excl ',self.gui_user_sel) # Update the MolDisp selection params # (not necessary if this call is MolDisp updating CInterSel!) if mode != 'update': sel0 = re.sub('
','',self.gui_user_sel) sel0 = re.sub('&','&',sel0) sel0 = re.sub('<','<',sel0) sel0 = re.sub('>','>',sel0) if not sel0: self.gui_user_sel = '' self.CSelection.set_selection(select='cid',cid='',user_sel=1) self.CSelection.update_selection('','No selection') else: rv = self.CSelection.parent.parse_selection(command=sel0,test=1) #print "rv",rv if rv[0] != 0 : self.gui_user_sel = self.gui_user_sel_stack.pop() GUI.GUI.insts.WarningMessage('Can not interpret selection command') return [1] # Sort out the alias if user_alias: user_alias = utils.oneWord(user_alias,tcl_safe=1) else: user_alias = utils.oneWord(sel0,tcl_safe=1) if len(user_alias) > 20: user_alias = user_alias[0:20] self.CSelection.set_selection(select='cid',cid=sel0,user_sel=1) if len(rv)>2: self.CSelection.update_selection(rv[2],user_alias) else: self.CSelection.update_selection('',user_alias) #print "updating gui gui_user_sel",self.gui_user_sel GUI.GUI.insts.UpdateGUIWindow ( 'mol_userselect'+self.moldisp.name, [ ['OBJECT','user_sel', ['CONFIGURE', ['text', utils.tclSafe(self.gui_user_sel) ]]]]) #-------------------------------------------------------------------- def handle_skeleton(self,mode=0): #-------------------------------------------------------------------- if mode>0: # Create a 'skeleton' MolDisp object style = {} style['style_mode'] = 'THINBONDS' sel = {} sel['select']='all' if self.skeleton_selection == 'CALIGAND': sel['select']="catrace" if self.CSelection.parent.monomers: sel['ligands']= [] for mon in self.CSelection.parent.monomers: sel['ligands'].append(mon) import model self.skeleton = model.MolDisp (self.CSelection.parent.name, \ selparams = sel, styleparams=style, \ master_application=self,display_table_visible=0) elif mode < 0: if self.skeleton: self.skeleton.delete() self.skeleton = None else: if not self.skeleton: return # Update the skeleton display selection if self.skeleton_selection == 'CALIGAND': self.skeleton.set_selection(select="catrace",monomers='all') else: self.skeleton.set_selection(select="all") #-------------------------------------------------------------------- def set_skeleton(self): #-------------------------------------------------------------------- if self.ifskeleton: if self.skeleton: #There is already a skeleton - force it visible self.skeleton.set_visibility(visibility='1') self.set_fatbonds() return else: self.handle_skeleton(mode=1) self.set_fatbonds() # Do not return - allow the current selection to be applied else: if self.skeleton: self.skeleton.set_visibility(visibility='0') self.set_fatbonds(0) return #----------------------------------------------------------------------- def set_fatbonds(self,mode=1): #----------------------------------------------------------------------- # Force the current object style to FATBONDS if it is currently # BONDS or THINBONDS if self.CSelection.object_type != 'MolDisp': return if mode: if self.CSelection.style_mode == 'BONDS' or \ self.CSelection.style_mode == 'THINBONDS': self.saved_style_mode = self.CSelection.style_mode self.CSelection.set_style(style_mode='FATBONDS') else: if self.saved_style_mode: self.CSelection.set_style(style_mode=self.saved_style_mode) self.saved_style_mode = None #------------------------------------------------------------------ def define_userselect (self,interface_mode='',user_sel='', user_alias='',user_old_alias=''): #------------------------------------------------------------------ import utils import re if interface_mode: self.interface_mode =interface_mode if user_sel or user_alias: self.set_user_selection(user_sel=user_sel, \ user_alias=user_alias, \ user_old_alias=user_alias) if utils.oneWord(user_sel) == user_alias: user_alias = '' if user_sel: self.gui_user_sel = re.sub('<','<',user_sel) self.gui_user_sel = re.sub('>','>',self.gui_user_sel) self.gui_user_sel = re.sub('&','&',self.gui_user_sel) self.gui_user_sel = re.sub(' or ','
or ',self.gui_user_sel) self.gui_user_sel = re.sub(' and ','
and ',self.gui_user_sel) self.gui_user_sel = re.sub(' excl ','
excl ',self.gui_user_sel) else: self.gui_user_sel = '' save_mode_menu = ['this model','all models'] save_mode_menu.extend(MODELINFO().list_loaded()) save_mode_alias = ['this','generic'] save_mode_alias.extend(MODELINFO().list_loaded()) if self.interface_mode == 'advanced': desc = [ ['TITLE','Atom Selection for '+self.CSelection.parent.name_label], ['ICON',' Selection'], ['HELP','selection#interactive'], ['RETURN_METHOD','set_user_selection', ['RETURN_LIST','save','select','user_alias', ['user_sel::text',['ALIAS','user_sel']]]], ['PARAM','select', ['TYPEDEF','postmenu',[''],[''],'15'], ['RETURN_METHOD','handle', ['RETURN_LIST', ['user_sel::text',['ALIAS','user_sel']]]], ['INITIAL','selection..'] ], ['PARAM','save', ['TYPEDEF','varbutton','1','0'], ['RETURN_METHOD','handle_save', ['RETURN_LIST','save','user_alias','save_mode', ['user_sel::text',['ALIAS','user_sel']]]], ['LABEL','Save'] ], # ['PARAM','overwrite', # ['TYPE','_logical'], # ['INITIAL','1']], ['PARAM','save_mode', ['TYPEDEF','menu',save_mode_menu,save_mode_alias], ['INITIAL','this']], ['PARAM','user_alias', ['TYPE','_text30'], ['INITIAL',utils.tclSafe(user_alias)]], ['PARAM','operator', ['TYPEDEF','menu',self.operator_menu,self.operator_alias], ['INITIAL',self.operator], ['RETURN_METHOD','set_user_selection', ['RETURN_LIST','operator']]], ['PARAM','not_operator', ['TYPE','_logical'], ['INITIAL',self.not_operator], ['RETURN_METHOD','set_user_selection', ['RETURN_LIST','not_operator']]], ['PARAM','ifskeleton', ['TYPE','_logical'], ['INITIAL',self.ifskeleton], ['RETURN_METHOD','set_user_selection', ['RETURN_LIST','ifskeleton','user_alias','user_old_alias', ['user_sel::text',['ALIAS','user_sel']]]]], ['PARAM','skeleton_selection', ['TYPEDEF','menu',['all atoms','trace and ligands'], \ ['ALL','CALIGAND']], ['INITIAL',self.skeleton_selection], ['RETURN_METHOD','set_user_selection', ['RETURN_LIST','skeleton_selection']]], ['OBJECT','user_sel', ['TYPE','TextEditor'], ['CONFIGURE', ['text',utils.tclSafe(self.gui_user_sel)]]], ['DISPLAY','WINDOW', ['LINE', ['MESSAGE','Show the unselected atoms in the model so they can be picked'], ['WIDGET','ifskeleton'], ['LABEL','Show'], ['MESSAGE','Show CA trace or all atoms'], ['WIDGET','skeleton_selection'], ['LABEL','molecule skeleton for interactive selection']], ['LINE', ['LABEL','Combine next selection by'], ['MESSAGE','Use operator to merge next entered selection'], ['WIDGET','operator'], ['WIDGET','not_operator'], ['LABEL','NOT the next selection']], ['LINE', ['LABEL','Select from menu','-italic'], ['MESSAGE','Choose selection command from the menu'], ['WIDGET','select'], ['LABEL','OR right-click on atom for pop-up menu OR edit text window below','-italic']], ['DRAW','user_sel'], # Beware not using default 'dismiss' button since want all # parameter 'select' returned so set_selection knows that # it is this window which is closing down ['LINE', ['MESSAGE','Save selection command for this model or for general use'], ['LABEL','Save for'], ['WIDGET','save_mode'], ['MESSAGE','Enter a one word identifier for the selection command'], ['LABEL','as named selection:'], ['WIDGET','user_alias'], ['MESSAGE','Save selection command now'], ['WIDGET','save']], # ['LINE', # ['WIDGET','overwrite'], # ['LABEL','Overwrite any any selection with same name']], ['BUTTONS',['ok',['LABEL','Apply'], ['MESSAGE','Apply selection now']], ['undo',['LABEL','Undo last']], ['clear',['LABEL','Clear selection']], ['dismiss',['CLOSE']]] ] ] GUI.GUI.insts.CreateGUIWindow ( 'mol_userselect'+self.moldisp.name,self , desc) elif self.interface_mode == 'simple': GUI.GUI.insts.DeleteGUIWindow ( 'mol_userselect'+self.moldisp.name) # And append options to the atom pick menu ATOMPICKING().popup_request( \ mode='vertex',feature='user_sel', \ module='model', method=self.name_string()+'.user_sel_popup') #---------------------------------------------------------------------- def handle ( self, post_menu='',window='',parent='', _neighb='',_ranges='', _property='',_cid='',widget='', exit='', **keywords): #---------------------------------------------------------------------- if exit == 'dismiss': return if post_menu == 'select': import model guidef,menu = model.define_selection_menu( moldata=self.moldisp.parent, not_required=['not_select','interactive_sele_gui','remove_user','save_current']) guidef.insert(0,['WINDOW_ID',window]) guidef.insert(1,['PARENT',parent]) guidef.insert(2,['RETURN_METHOD','handle', ['RETURN_WIDGET']] ) display = [['DISPLAY','PULLDOWN', menu ]] guidef.extend(display) #print "intersel handle guidef",guidef GUI.GUI.insts.CreateGUIWindow('mol_selection',self,guidef) else: if _neighb=='1': self.CSelection.define_neighb(gui_offset='mol_userselect'+self.moldisp.name,\ return_method=self.name_string()+'.handle_selection' ) elif _ranges=='1': self.CSelection.define_ranges(gui_offset='mol_userselect'+self.moldisp.name,\ return_method=self.name_string()+'.handle_selection') elif _property=='1': self.CSelection.define_property(gui_offset='mol_userselect'+self.moldisp.name, \ return_method=self.name_string()+'.handle_selection') else: retval = self.moldisp.interpret_selection(selparams={},kw=keywords,widget=widget) if retval[0] or not retval[3]: return status,selparams,sel,alias = retval rv,command = self.CSelection.parent.list_to_command(sel) self.append_sel(sel=command) #----------------------------------------------------------------------- def set_user_selection ( self,user_alias='', user_sel='', ifskeleton='',skeleton_selection='', operator='',not_operator='', exit='',widget='',**keywords): #----------------------------------------------------------------------- import re import utils # Deal with the display handling tools if operator: self.operator = operator return if not_operator: self.not_operator = int(not_operator) return if skeleton_selection: CInterSel.skeleton_selection = skeleton_selection self.handle_skeleton(mode=0) return if ifskeleton and int(ifskeleton) != self.ifskeleton: self.ifskeleton = int(ifskeleton) self.set_skeleton() return if exit == 'dismiss': self.delete() del self return elif exit == 'clear': self.append_sel(sel='',mode='reset') return elif exit == 'undo': self.append_sel(mode='undo') return elif exit == 'ok' or user_sel: #print "user_sel",user_sel self.append_sel(sel=user_sel,mode='reset') return #--------------------------------------------------------------------------- def handle_save(self,save='',save_mode='this', \ user_alias='',user_sel='',**keywords): #--------------------------------------------------------------------------- import re if not user_alias: GUI.GUI.insts.WarningMessage('Please enter a short name for the selection before saving',parent = 'mol_userselect'+self.moldisp.name) return if not user_sel: GUI.GUI.insts.WarningMessage('Please enter a selection before saving',parent = 'mol_userselect'+self.moldisp.name) return user_sel = re.sub ('
',' ',user_sel) rv = self.CSelection.parent.parse_selection(command=user_sel,test=1) #print "parse_selection rv",rv if rv[0] != 0: GUI.GUI.insts.WarningMessage('Selection is not interpretable',parent = 'mol_userselect'+self.moldisp.name) return elif rv[1] == 0 or len(rv)<3: alias = user_alias sel = "" else: alias = user_alias sel = rv[2] #print "handle_save alias",alias,'sel',sel if (sel): self.moldisp.save_current_selection (overwrite='1', \ save_mode=save_mode,alias=user_alias,selection=sel) self.moldisp.update_selection(sel,alias)