Das Erwachen

Bioinformatics

"Python looks to me like the illegitimate spawn of C and BASIC, but then I used to program in 6502 machine code so what do I know ..." (Laurence Pearl, PyMOL user)

 
 

Tips for PyMOL from the PyMOL mailing list

Disclaimer: All the contributions displayed on this page were made by subscribers of the PyMOL mailing list at sourceforge.net and Warren L. DeLano himself. Although i have checked most of the things listed here, i cannot guarantee that everythings works well. I cannot even guarantee that i have understood everything written on this page. Please be cautious.

Kristian Rother

Available Subtopics:


Python scripting


   

Launching Python programs

1. Running a Python script from PyMOL, usually the command:

run script.py
Is enough. Of course, the file script.py needs to be in the working directory.
You can also launch Python scripts when starting PyMOL. Asynchronous means, that a new Python thread is started:
pymol example.py     # synchronous, in PyMOL module
pymol -r example.py  # synchronous in __main__ module
pymol -l example.py  # asychronous in a new module
You can also launch python programs from within PyMOL with the commands:
run example.py        # synchronous in pymol module
run example.py,main   # synchronous in __main__ module

spawn example.py        # asychronous in a new module
spawn example.py,global # asychronous in the PyMOL module
spawn example.py,main   # asychronous in the __main__ module

2. Running PyMOL from a Python script requires two commands:

import pymol
pymol.finish_launching()


 

Using the PyMOL commandline

Are you aware that the PyMOL command line is also a Python command line? You can just use PyMOL interactively in that fashion.

PyMOL>print 1+1
2
PyMOL>from random import random
PyMOL>print random()
0.739460642143
The only major difference is that the default namespace for PyMOL is "pymol" not "__main__"
PyMOL>print __name__
pymol

Warren DeLano


 

What is in a selection?

Atom selections aren't directly exposed to Python, but you can have PyMOL build a Python list containing whatever information you need:

Using PyMOL commands:

list=[]
iterate (name ca),list.append((resi,resn))
print list

[('ASP', '1'), ('CYS', '2'), ('ALA', '3'), ('TRP', '4'), ('HIS', '5'), ('LEU',
 '6'), ('GLY', '7'), ('GLU', '8'), ('LEU', '9'), ('VAL', '10'), ('TRP', '11'), 
('CYS', '12'), ('THR', '13')]
or using a Python script (in PyMOL):
from pymol import cmd,stored
stored.list=[]
cmd.iterate("(name ca)","stored.list.append((resi,resn))")
print stored.list

[('1', 'ASP'), ('2', 'CYS'), ('3', 'ALA'), ('4', 'TRP'), ('5', 'HIS'), ('6', '
LEU'), ('7', 'GLY'), ('8', 'GLU'), ('9', 'LEU'), ('10', 'VAL'), ('11', 'TRP'), 
('12', 'CYS'), ('13', 'THR')]

Warren DeLano


 

Does a selection exist?

The function below will return true if the selection is defined.

from pymol import cmd
from types import *

def sele_exists(sele):
    sess = cmd.get_session()
    for i in sess["names"]:
        if type(i) is ListType:
            if sele==i[0]:
                return 1
    return 0

Igor Pechersky


 

Get coordinates from Python

The actual C-langauge arrays aren't exposed, but there are at least three different ways you can modify coordinates from within Python:

  • You can get a python object which contains the molecular information, modify the coordinates in that object, load the modified molecule into PyMOL, update the modified coordinates to the original model, and then delete the modified object. (Example in a python script)
  • Another approach is the "alter_state" function, which can perform the same transformation in a single PyMOL command statement:
    alter_state 1,pept,(x,y)=(-y,x)
    
    Likewise sub-selections can be transformed as well:
    alter_state 1,(pept and name ca),(x,y,z)=(x+5,y,z)
    
  • A third approach is to use alter_state with the global "stored" object: Example in a Python script)
  • Approaches 2 gives the best performance, approach 3 gives more flexibility, and approach 1 gives you a reusable and fully modifiable Python object.
    Warren DeLano


     

    Create objects from PDB strings

    I thought I'd post this example, just in case anyone else cares about this:

    delete all
    cmd.read_pdbstr("""HEADER    CREATED BY CONVERTPROSPECT              27-JAN-02   2tnf  \
    REMARK   1                                                                       \
    ATOM      1  N   PRO A   9       1.895  67.213 -38.182  1.00  0.00           N   \
    ATOM      2  CA  PRO A   9       1.703  68.680 -38.402  1.00  0.00           C   \
    ....
    ATOM   1153  C   GLY A 157       6.927  59.108 -38.901  1.00  6.00           C   \
    ATOM   1154  O   GLY A 157       6.700  59.292 -37.676  1.00  6.00           O   \
    TER    1155      GLY A 157                                                       \
    MASTER                                                                           \
    END                                                                              \
    ""","2tnfa")
    hide all
    show cartoon
    color grey
    ....
    

    Reece Hart


     

    Measure distances from Python

    Use Python (and the run command with .py files).

    from pymol import cmd
    f=open('dist.txt','w')
    dst=cmd.distance('tmp','mol1///25/ha','mol1///26/ha')
    f.write("%8.3f\n"%dst)
    f.close()
    
    You could measure the whole protein this way by putting a loop around the distance command:
    from pymol import cmd
    f=open('dist.txt','w')
    atom = cmd.get_model("mol1////ha").atom
    for i in range(len(atom)-1):
       sele1 = 'mol1///%s/HA'%atom[i].resi
       sele2 = 'mol1///%s/HA'%atom[i+1].resi
       dst=cmd.distance('tmp',sele1,sele2)
       f.write("%14s %14s %8.3f\n"%(sele1,sele2,dst))
    f.close()
    
    The output "dist.txt" would then look like:
       mol1///4/HA    mol1///5/HA    4.748
       mol1///5/HA    mol1///6/HA    4.828
       mol1///6/HA    mol1///7/HA    4.861
       mol1///7/HA    mol1///8/HA    4.784
       mol1///8/HA    mol1///9/HA    4.936
       mol1///9/HA   mol1///10/HA    4.833
      mol1///10/HA   mol1///11/HA    4.933
      mol1///11/HA   mol1///12/HA    4.813
    

    Warren DeLano


     

    Coloring all objects differently

    Q: Is there a simple way to colour each object currently loaded, with a different colour (in the same way that you can colour each chain in a molecule differently)? This would be really useful in visualising a set of superposed structures.

    A: There is a script
    color_obj.py that does the job.
    The script is also available at
    http://www.ebi.ac.uk/~gareth/misc

    USAGE
    
            color_obj(rainbow=0)
    
    	This function colours each object currently in the PyMOL heirarchy
    	with a different colour.  Colours used are either the 22 named
    	colours used by PyMOL (in which case the 23rd object, if it exists,
    	gets the same colour as the first), or are the colours of the rainbow
    

    Gareth Stockwell


     

    List the color of atoms

    To retrieve the color for a residue as identified in an expression, you can either iterate over a selection from the PyMOL command line

    iterate all, print color
    
    Alternatively, this can be done from a
    Python script.
    import pymol
    pymol.color_list = []
    cmd.iterate('all', 'pymol.color_list.append(color)')
    print pymol.color_list
    

    Warren DeLano


     

    List secondary structures

    Secondary structures (both predefined and those calculated with the 'dss' command) can be exported as a long string ('HHHHLLLLSSS') with the following script:

    import pymol
    pymol.stored_ss = []
    cmd.iterate('all', 'pymol.stored_ss.append(string.ljust(ss,1))')
    print string.join(pymol.stored_ss)
    

    Warren DeLano


     

    Process key events from shell

    The following scripts turns the view 30 deg around the y-axis, each time you press the enter key in the python shell (the original shell you started pymol from), just as an example:

    #use "spawn spawn_demo.py, local" to invoke this python script from
    within pymol
    wait=""
    i=0
    while i < 12 and wait!="x":
      cmd.turn("y", 30)
      print "Press enter key to continue or x + enter to terminate"
      wait=raw_input()
      i=i+1
    print "Done"
    

    Markus Meier


     

    Alter key bindings

    It's not GUI, but you could simply bind a function key such as F1 to a command:

    cmd.set_key('F1',lambda :cmd.show('sticks'))
    

    Warren DeLano


     

    Viewing direction vectors

    Create a python script (I call it axes.py):

    # axes.py
    from pymol.cgo import *
    from pymol import cmd
    from pymol.vfont import plain
    
    # create the axes object, draw axes with cylinders coloured red, green,
    #blue for X, Y and Z
    
    obj = [
       CYLINDER, 0., 0., 0., 10., 0., 0., 0.2, 1.0, 1.0, 1.0, 1.0, 0.0, 0.,
       CYLINDER, 0., 0., 0., 0., 10., 0., 0.2, 1.0, 1.0, 1.0, 0., 1.0, 0.,
       CYLINDER, 0., 0., 0., 0., 0., 10., 0.2, 1.0, 1.0, 1.0, 0., 0.0, 1.0,
       ]
    
    # add labels to axes object (requires pymol version 0.8 or greater, I
    # believe
    
    cyl_text(obj,plain,[-5.,-5.,-1],'Origin',0.20,axes=[[3,0,0],[0,3,0],[0,0,3]])
    cyl_text(obj,plain,[10.,0.,0.],'X',0.20,axes=[[3,0,0],[0,3,0],[0,0,3]])
    cyl_text(obj,plain,[0.,10.,0.],'Y',0.20,axes=[[3,0,0],[0,3,0],[0,0,3]])
    cyl_text(obj,plain,[0.,0.,10.],'Z',0.20,axes=[[3,0,0],[0,3,0],[0,0,3]])
    
    # then we load it into PyMOL
    cmd.load_cgo(obj,'axes')
    

    Then you just need to do "run axes.py" from the pymol command line.

    You can modify the "3" in the above description of the text labels to change the size of the labels. If you are running a version of pymol older the 0.8, then you cannot add the text (that's why I included colour coding of the axes). You can also use just lines instead of cylinders if you wish:

    obj = [
       BEGIN, LINES,
       COLOR, 1.0, 0.0, 0.0,
       VERTEX, 0.0, 0.0, 0.0, VERTEX, 10.0, 0.0, 0.0,
       COLOR, 0.0, 1.0, 0.0,
       VERTEX, 0.0, 0.0, 0.0, VERTEX, 0.0, 10.0, 0.0,
       COLOR, 0.0, 0.0, 1.0,
       VERTEX, 0.0, 0.0, 0.0, VERTEX, 0.0, 0.0, 10.0,
       END,
       ]
    

    would work as well to define the axes object.


     

    Ray-traceable labels

    You can use the cgo text (line or cylinder versions) as I mentioned in my reply about drawing the xyz axes, but be warned that the labels rotate with your molecule, so getting them oriented perpendicular to the view may be a pain (unless there is something I've missed).

    Warren's example from a previous reply of his (
    cgotext.py):

    # draw text using cgo
    from pymol import cmd
    from pymol.cgo import *
    from pymol.vfont import plain
    
    cgo = []
    
    axes = [[2.0,0.0,0.0],[0.0,2.0,0.0],[0.0,0.0,2.0]]
    
    pos = [0.0,0.0,0.0]
    wire_text(cgo,plain,pos,'Hello World',axes)
    
    pos = [0.0,-3.0,0.0]
    cyl_text(cgo,plain,pos,'Hello Universe',0.10,axes=axes)
    
    cmd.set("cgo_line_radius",0.03)
    cmd.load_cgo(cgo,'txt')
    cmd.zoom("all",2.0)
    
    Robert Campbell


     

    CGO label orientation

    You could use the cmd.rotate and cmd.translate to position the labels, but it is likely to be somewhat painful. If I'm not mistaken, the rotation will always be about and axis through the origin and so you may need to translate the label into the correct position.

    Thus if you have your label in an object called 'text', you could do,

      cmd.rotate(axis='x',angle=20.,object='text')
    
    and repeat this with different angles, until you get the orientation correct. Then use:
      cmd.translate(vector='[1.,2.,3.]',object='text')
    
    (using the appropriate vector, of course!) to position the label.
    Not ideal, but if it is sufficiently important, it can be done!
    Robert Campbell


     

    3D marks on atoms

    Here's a script graph.py which can be used to put marks on the positions of every atom in a selection. There are several types of marks included now, e.g. cube, rhombic dodecahedron, tetrahedron, plus and star. Using a matrix operator these can be extended a bit, to 2D counterparts for example, or to elongated or rotated shapes. I should still include some documentation but in brief the usage is as follows:

    graph selection = '(all)', shape = 'plus' | 'sphere' | 'star' | 'tetrahedron' | 'dodecahedron', size = 1, r = 0.10, rgb1 = [1,0,0], rgb2 = [0,0,1], mtx = [[1,0,0],[0,1,0],[0,0,1]], name = "graph"
    
    Selection should be obvious, as is shape. Size is a relative indicator for the size, though at present I haven't fixed things such that a size 1 tetrahedron matches a size 1 dodecahedron. r is the radius of the lines. rgb1 and rgb2 are the start and end color for each line. mtx is the matrix operator, which works on the original shape centered around the origin (before translation to the point of the atom from the selection).
    Tserk Wassenaar


     

    Save and load objects

    There is a convenient format: ".pkl", that works like PDB, but is a Pickled copy of the PyMOL Model Class. The main advantage this has over .pdb is that it saves and restores extra properties such as secondary structure code, atom types, van der waals radii, formal and partial charges, etc.

    load myprot.pdb
    dss
    show cartoon
    alter 100-110/, ss='H'
    alter 65-67,ss='L'
    save myprot.pkl
    
    ... quit program, complete your PhD, take a vacation, and come back...
    load myprot.pkl
    show cartoon
    
    will work.

    ".pkl" has the additional advantage of being a simple molecular object useful in straight Python, outside of PyMOL. All you need to do is have a copy of PyMOL's "chempy" module available in your PYTHONPATH

    from chempy import io
    model = io.pkl.fromFile("myprot.pkl")
    for atom in model.atom:
       print atom.name
    

    Warren DeLano


     

    Building ChemPy models

    This is a snap. Just "run" the following Python program from within PyMOL.

    from chempy.models import Indexed
    from chempy import Bond, Atom
    from whrandom import random
    from pymol import cmd
    
    model = Indexed()
    
    # create some atoms
    
    for a in range(1,11):
       at = Atom()
       at.name = "X%02d"%a
       at.coord = [random()*5,random()*5,random()*5]
       model.atom.append(at)
    
    # now create some bonds
    
    for a in range(1,10):
       bd = Bond()
       bd.index = [a-1,a] # zero-based indices!
       model.bond.append(bd)
    
    # now load and label
    
    cmd.load_model(model,"example")
    cmd.label("example","name")
    

     
    Rubor : http://www.rubor.de
    Post an Kristian Rother: kristian.rother@charite.de

    Last modified: Wed Apr 28 07:24:35 CEST 2004