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