bp_surface.py(Toggle Plain Text)
# -*- coding: utf-8 -*- # ------------------------------------------------------------------------------- # bp_surface.py - Binding pocket surface generator for Pymol version 2.0 # ------------------------------------------------------------------------------- # Copyright (C) 2008 by Lightnir - lightnir@gmail.com # This script is free software; you can redistribute it and#or modify # it under the terms of the GNU Library General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # This script 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 General Public License for more details. # You should have received a copy of the GNU Library General Public # License along with this program; if not, write to the # Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. from pymol import cmd from Tkinter import * from pymol.cgo import * from math import * import Pmw import tkColorChooser BPSFrame = None def __init__(self): self.menuBar.addcascademenu('Plugin', 'LightnirsPlugins', 'Lightnirs Plugins', label='Lightnirs Plugins') self.menuBar.addmenuitem('LightnirsPlugins', 'command', 'BPsurface', label='BP surface', command = lambda s=self: BPSurface(s)) class BPSurface: # Class variables selections = [] cv = IntVar() pp = IntVar() cp = IntVar() rv = IntVar() colorize = IntVar() Default_Color = None res_states = [['Hydrophobic','Gray',0], ['Aromatic','White',0], ['Polar','Green',0], ['Positive','Red',0], ['Negative','Blue',0], ['Cysteine','Yellow',0], ['Proline','Magenta',0]] def __init__(self,app): global BPSFrame if BPSFrame==None: BPSFrame = Toplevel(width=400, height=300) # create a root window BPSFrame.title("BP surface editor") # Set window title BPSFrame.resizable(0, 0) # Disable window resize BPSFrame.geometry('-40+40') # Set window placement self.get_selections() self.Default_Color = BPSFrame.cget("bg") # Create Ligand selection Combobox BPSFrame.cb1 = Pmw.ComboBox(BPSFrame, label_text = 'Ligand:', labelpos = 'nw', scrolledlist_items = BPSurface.selections ) BPSFrame.cb1.grid(row=0, column=0,columnspan=2) # Create binding pocket Combobox BPSFrame.cb2 = Pmw.ComboBox(BPSFrame, label_text = 'Binding pocket:', labelpos = 'nw', scrolledlist_items = BPSurface.selections ) BPSFrame.cb2.grid(row=1, column=0,columnspan=2) # Create Z axis Slider BPSFrame.slider1 = Scale(BPSFrame, from_=-20, to=20, command=self.scaling, tickinterval=10, resolution=0.5, label='Z axis cutoff', orient=HORIZONTAL) BPSFrame.slider1.grid(row=2, column=0, columnspan=2, sticky=N+W+S+E,) # Create X axis Slider BPSFrame.slider2 = Scale(BPSFrame, from_=-90, to=90, command=self.scaling, tickinterval=30, resolution=1, label='X axis rotation',orient=HORIZONTAL) # Create Y axis Slider BPSFrame.slider3 = Scale(BPSFrame, from_=-90, to=90, command=self.scaling, tickinterval=30, resolution=1, label='Y axis rotation', orient=HORIZONTAL) # Add Checkboxes BPSurface.cv.set(1) cbx = Checkbutton(BPSFrame, text="Create new object", command=None, variable=BPSurface.cv, onvalue="1", offvalue="0") cbx.grid(row=5,column=0, columnspan=2, sticky=W) self.CreatePlane() BPSurface.pp.set(1) cbx2 = Checkbutton(BPSFrame, text="Plane preview", command=self.TogglePlane, variable=BPSurface.pp, onvalue="1", offvalue="0") cbx2.grid(row=6,column=0, columnspan=2, sticky=W) BPSurface.cp.set(0) cbx3 = Checkbutton(BPSFrame, text="Use custom plane", command=self.ToggleCustomPlane, variable=BPSurface.cp, onvalue="1", offvalue="0") cbx3.grid(row=7,column=0, columnspan=2, sticky=W) BPSurface.rv.set(0) cbx4 = Checkbutton(BPSFrame, text="Make residue selections", command=self.ToggleResidueSelect, variable=BPSurface.rv, onvalue="1", offvalue="0") cbx4.grid(row=8,column=0, columnspan=2, sticky=W) # Add some buttons btn1=Button(BPSFrame, text='Refresh', padx=0, pady=0, command = self.refresh_list) btn1.grid(row=9, column=0, sticky=N+W+S+E, padx=0, pady=0,ipadx=0,ipady=0) btn2=Button(BPSFrame, text='Clear', padx=0, pady=0, command = lambda r=1,d=1: self.clear_flags(r,d)) btn2.grid(row=9, column=1, sticky=N+W+S+E, padx=0, pady=0,ipadx=0,ipady=0) btn3=Button(BPSFrame, text='Apply', padx=0, pady=0, command = self.ok) btn3.grid(row=10, column=0, sticky=N+W+S+E,) btn4=Button(BPSFrame, text='Close', padx=0, pady=0, command = self.myHide) btn4.grid(row=10, column=1, sticky=N+W+S+E) # Residue selection column # Create frame for residue content BPSFrame.Res = Frame(BPSFrame) # Create Colorize button self.colorize.set(1) col_cbx = Checkbutton(BPSFrame.Res, text="Colorize residues", command=self.ToggleColor, variable=self.colorize, onvalue=1, offvalue=0) col_cbx.grid(row=0,column=0, sticky=N+W) # Create group widget BPSFrame.gr = Pmw.Group(BPSFrame.Res, tag_text='Residues') BPSFrame.gr.grid(row=1, column=0, padx=3, pady=3, sticky=N+W+E) # Create residues buttons and checkboxes BPSFrame.ccb = [] for i in range(0,len(self.res_states)): var = IntVar() var.set(1) BPSFrame.ccb.append(Button(BPSFrame.gr.interior(), text='', bg=self.res_states[i][1], activebackground=self.res_states[i][1], relief=RAISED, overrelief=RAISED, height=1, width=2, padx=0, pady=0, command=lambda nr=i: self.changecolor(nr))) BPSFrame.ccb[i].grid(row=i,column=0, sticky=N+W) rs_cbx = Checkbutton(BPSFrame.gr.interior(), text=self.res_states[i][0], variable=var, onvalue="1", offvalue="0") rs_cbx.grid(row=i,column=1, sticky=N+W) self.res_states[i][2]=var undo1 = Button(BPSFrame.Res, text='Undo all', padx=0, pady=0, command = lambda u="all":self.undo(u)) undo1.grid(row=2,column=0, sticky=N+W+E) undo2 = Button(BPSFrame.Res, text='Undo last', padx=0, pady=0, command = lambda u="last":self.undo(u)) undo2.grid(row=3,column=0, sticky=N+W+E) # Define own colors self.create_bp_colors() # Make a backup session cmd.save('bp_undoall.pse', '(all)') # create callback to prevent window kill BPSFrame.protocol("WM_DELETE_WINDOW", self.myHide) BPSFrame.mainloop() else: if BPSFrame.state() == "normal": self.myHide() elif BPSFrame.state() == "withdrawn": self.myShow() def ToggleColor(self): if self.colorize.get()==1: for i in range(0,len(self.res_states)): BPSFrame.ccb[i].config(state=ACTIVE, bg=self.res_states[i][1], activebackground=self.res_states[i][1], relief=RAISED, overrelief=RAISED) else: for i in range(0,len(self.res_states)): BPSFrame.ccb[i].config(state=DISABLED, bg=self.Default_Color , activebackground=self.Default_Color, relief=RAISED, overrelief=RAISED) def changecolor(self,nr): new_color = tkColorChooser.askcolor(self.res_states[nr][1]) rgb_color = map(lambda color: '%.4f'%float(color/float(65536)) ,new_color[0]) self.res_states[nr][1]=new_color[1] print rgb_color,new_color[1] cmd.set_color('bp_plugin_color_'+str(nr),new_color[0],0) BPSFrame.ccb[nr].config(bg=new_color[1], activebackground=new_color[1]) def create_bp_colors(self): for i in range(0,len(self.res_states)): rgb_color = map(lambda color: '%.4f'%float(color/float(65536)) ,BPSFrame.winfo_rgb(self.res_states[i][1])) cmd.set_color('bp_plugin_color_'+str(i),rgb_color) def state(self): return map(lambda var: var[2].get(), self.res_states) def undo(self,u): if u=="all": cmd.load('bp_undoall.pse') if u=="last": cmd.load('bp_undolast.pse') self.TogglePlane() def scaling(self,v): self.TogglePlane() def myShow(self): self.refresh_list() BPSFrame.deiconify() def myHide(self): cmd.delete("plane_preview") BPSFrame.withdraw() def get_selections(self): BPSurface.selections = [] for item in cmd.get_names("all"): if cmd.get_type(item)=="object:molecule": BPSurface.selections.append(item) if cmd.get_type(item)=="selection": if item[0]<>"_": BPSurface.selections.append(item) def refresh_list(self): if BPSurface.pp.get()==1: self.CreatePlane() # Save old values old = BPSurface.selections try: old_cb1=BPSFrame.cb1.getvalue()[0] except: old_cb1='' try: old_cb2=BPSFrame.cb2.getvalue()[0] except: old_cb2='' self.get_selections() # Update selectionlist if needed if not(old==BPSurface.selections): BPSFrame.cb1.setlist(BPSurface.selections) BPSFrame.cb2.setlist(BPSurface.selections) # Is ligand selection still available? if not(old_cb1 in BPSurface.selections): # No - clear the combobox entry BPSFrame.cb1.component('entryfield').clear() else: # Yes - set the old value BPSFrame.cb1.selectitem(old_cb1,setentry=1) # Is binding pocket selection still available? if not(old_cb2 in BPSurface.selections): # No - clear the combobox entry BPSFrame.cb2.component('entryfield').clear() else: # Yes - set the old value BPSFrame.cb2.selectitem(old_cb2,setentry=1) def clear_flags(self,r,d): cmd.flag('ignore','all','clear') if r==1: cmd.rebuild('all') if d==1: cmd.delete('bp_cutoff') cmd.delete('bp_surface') cmd.delete('bp_hydrophobic') cmd.delete('bp_aromatic') cmd.delete('bp_negative') cmd.delete('bp_positive') cmd.delete('bp_proline') cmd.delete('bp_cysteine') cmd.delete('bp_polar') def CreatePlane(self): view = cmd.get_view() # choose the size of the plane plane_size = abs(view[11])/4.0 obj = [] # Create a plane in camera space plane = [ [ -plane_size, plane_size, BPSFrame.slider1.get() ], [ plane_size, plane_size, BPSFrame.slider1.get() ], [ -plane_size, -plane_size, BPSFrame.slider1.get() ], [ plane_size, -plane_size, BPSFrame.slider1.get() ]] # Using custom plane? if BPSurface.cp.get()==1: sinx = sin(pi*BPSFrame.slider2.get()/180) siny = sin(pi*BPSFrame.slider3.get()/180) cosx = cos(pi*BPSFrame.slider2.get()/180) cosy = cos(pi*BPSFrame.slider3.get()/180) # Map XY axis rotation # cosY sinX*sinY -cosX*sinY # 0 cosX sinX # sinY -sinX*cosY cosX*cosY plane = map( lambda p: [ cosy*p[0] + sinx*siny*p[1] + (-cosx*siny*p[2]), cosx * p[1] + sinx* p[2], siny * p[0] + (-sinx*cosy*p[1]) + cosx*cosy*p[2] ], plane ) normal = [ 0.0, 0.0, 1.0 ] # Transform plane coordinates into model space plane = map( lambda p,v=view: [ v[0] * p[0] + v[1] * p[1] + v[2]* p[2], v[3] * p[0] + v[4] * p[1] + v[5]* p[2], v[6] * p[0] + v[7] * p[1] + v[8]* p[2] ], plane ) normal = apply( lambda p,v=view:[ v[0] * p[0] + v[1] * p[1] + v[2]* p[2], v[3] * p[0] + v[4] * p[1] + v[5]* p[2], v[6] * p[0] + v[7] * p[1] + v[8]* p[2] ], (normal,) ) # Transform position relative to the camera plane = map( lambda p,v=view: [ p[0] + v[9 ] + v[12], p[1] + v[10] + v[13], p[2] + + v[14], ], plane ) obj.extend( [ COLOR, 0.5, 0.5, 1.0 ] ) # blueish obj.extend( [ BEGIN, TRIANGLE_STRIP ] ) obj.append( NORMAL ) obj.extend( normal ) # draw the plane for a in plane: obj.append( VERTEX) obj.extend(a) obj.append( END ) # delete existing object (if any) cmd.delete("plane_preview") # now load the new object without zooming cmd.set('auto_zoom', 0, quiet=1) auto_zoom = cmd.get('auto_zoom') cmd.load_cgo(obj,'plane_preview') cmd.set('auto_zoom', auto_zoom, quiet=1) def TogglePlane(self): if BPSurface.pp.get()==0: cmd.delete("plane_preview") if BPSurface.pp.get()==1: self.CreatePlane() def ToggleCustomPlane(self): self.TogglePlane() if BPSurface.cp.get()==0: BPSFrame.slider2.grid_remove() BPSFrame.slider3.grid_remove() if BPSurface.cp.get()==1: BPSFrame.slider2.grid(row=3, column=0, columnspan=2, sticky=N+W+S+E,) BPSFrame.slider3.grid(row=4, column=0, columnspan=2, sticky=N+W+S+E,) def ToggleResidueSelect(self): if BPSurface.rv.get()==1: BPSFrame.Res.grid(row=0, column=2, rowspan=10, sticky=N+W+E) if BPSurface.rv.get()==0: BPSFrame.Res.grid_remove() def ok(self): # Has cb1 a value? sel1 = BPSFrame.cb1.getvalue() if len(sel1) == 0: dialog = Pmw.MessageDialog(BPSFrame, title = 'Error', defaultbutton = 0, buttons = ('OK',), message_text = 'ERROR: No ligand selection. Please\n choose one from the dropdownlist.' ) dialog.iconname('Selection Error') dialog.bell() dialog.activate() return 0 # Has cb2 a value? sel2 = BPSFrame.cb2.getvalue() if len(sel2) == 0: dialog = Pmw.MessageDialog(BPSFrame, title = 'Error', defaultbutton = 0, buttons = ('OK',), message_text = 'ERROR: No binding pocket selection. Please\n choose one from the dropdownlist.' ) dialog.iconname('Selection Error') dialog.bell() dialog.activate() return 0 # Are cb1 and cb2 the same? if sel1[0]==sel2[0]: dialog = Pmw.MessageDialog(BPSFrame, title = 'Error', defaultbutton = 0, buttons = ('OK',), message_text = 'ERROR: Ligand and binding pocket must have different selections' ) dialog.iconname('Selection Error') dialog.bell() dialog.activate() return 0 # Set orgin and normal vector cmd.origin(sel1[0]) vector = [[ 0, 0, 1]] # Using custom plane? if BPSurface.cp.get()==1: sinx = sin(pi*BPSFrame.slider2.get()/180) siny = sin(pi*BPSFrame.slider3.get()/180) cosx = cos(pi*BPSFrame.slider2.get()/180) cosy = cos(pi*BPSFrame.slider3.get()/180) # Remap the normal vector to custom vector = map( lambda p: [ cosy*p[0] + sinx*siny*p[1] + (-cosx*siny*p[2]), cosx * p[1] + sinx* p[2], siny * p[0] + (-sinx*cosy*p[1]) + cosx*cosy*p[2] ], vector ) # Remap the normal vector to the model space d=cmd.get_view(0) vector = map( lambda p,v=d: [ v[0] * p[0] + v[1] * p[1] + v[2]* p[2], v[3] * p[0] + v[4] * p[1] + v[5]* p[2], v[6] * p[0] + v[7] * p[1] + v[8]* p[2] ], vector ) # Calculate plane coefficients # Ax + By + Cz + D = 0 # D = -Ax -By -Cz A = vector[0][0] B = vector[0][1] C = vector[0][2] D = -vector[0][0]*d[12] - vector[0][1]*d[13] - vector[0][2]*d[14] atoms = cmd.get_model(sel2[0]) # Surface stuff self.clear_flags(0,1) for at in atoms.atom: if (A*(at.coord[0])+B*at.coord[1]+C*at.coord[2]+D-BPSFrame.slider1.get()>0): if not('bp_cutoff' in cmd.get_names("selections")): cmd.select('bp_cutoff','index '+str(at.index)) else: cmd.select('bp_cutoff','bp_cutoff or index '+str(at.index)) if ('bp_cutoff' in cmd.get_names("selections")): if BPSurface.cv.get()==0: cmd.flag('ignore','bp_cutoff','reset') cmd.delete('indicate') cmd.deselect() cmd.show('surface',sel2[0]) else: self.clear_flags(0,0) cmd.hide('surface',sel2[0]) cmd.set('auto_zoom','off') cmd.create('bp_surface',sel2[0]+' and not bp_cutoff') cmd.set('auto_zoom','on') cmd.show('surface','bp_surface') cmd.rebuild(sel2[0]) # Selections stuff if BPSurface.rv.get()==1: # make residue selections if self.state()[0]==1: # select hydrophobic cmd.select('bp_hydrophobic', 'byres ('+sel2[0]+' and resn ala+gly+ile+leu+met+val)') if self.state()[1]==1: # select aromatic cmd.select('bp_aromatic', 'byres ('+sel2[0]+' and resn phe+trp+tyr)') if self.state()[2]==1: # select polar cmd.select('bp_polar', 'byres ('+sel2[0]+' and resn his+asn+gln+ser+thr)') if self.state()[3]==1: # select positive cmd.select('bp_positive', 'byres ('+sel2[0]+' and resn lys+arg)') if self.state()[4]==1: # select negative cmd.select('bp_negative', 'byres ('+sel2[0]+' and resn asp+glu)') if self.state()[5]==1: # select cysteine cmd.select('bp_cysteine', 'byres ('+sel2[0]+' and resn cys)') if self.state()[6]==1: # select proline cmd.select('bp_proline', 'byres ('+sel2[0]+' and resn pro)') cmd.deselect() cmd.save('bp_undolast.pse', '(all)') if (BPSurface.rv.get()==1 and BPSurface.colorize.get()==1): # colorize? if self.state()[0]==1: # hydrophobic cmd.color('bp_plugin_color_0','bp_hydrophobic') if self.state()[1]==1: # select aromatic cmd.color('bp_plugin_color_1','bp_aromatic') if self.state()[2]==1: # select polar cmd.color('bp_plugin_color_2','bp_polar') if self.state()[3]==1: # select positive cmd.color('bp_plugin_color_3','bp_positive') if self.state()[4]==1: # select negative cmd.color('bp_plugin_color_4','bp_negative') if self.state()[5]==1: # select cysteine cmd.color('bp_plugin_color_5','bp_cysteine') if self.state()[6]==1: # select proline cmd.color('bp_plugin_color_6','bp_proline')
The major updates of bp_surface.py version 2.0 are:
- Ligand and binding site can now be either a selection or an object
- The orientation of the cutting plane now can be customized
- Binding site residues can be selected by type: hydrophobic, aromatic, polar, charged positive or negative, proline, cysteine
- Selected residues can be colorized
- If you screw up the color settings you can undo the last action or recover the state of the binding site when the script was started the first time (use it with caution)
Same molecule (Dexamethasone) but this time hydrophobic residues were colored orange. Notice that large parts of the binding site surface are hydrophobic, which isn't a surprise since the bounded ligand is a steroid.
I hope this tool will be helpful ]:)