Difference between revisions of "Plugins Tutorial"

From PyMOLWiki
Jump to: navigation, search
m (mediawiki formatted the code with a space on the left side, which makes copying and pasting the code annoying)
Line 56: Line 56:
  
 
<source lang="python">
 
<source lang="python">
# Copyright Notice
+
# Copyright Notice
# ================
+
# ================
#  
+
#  
# The PyMOL Plugin source code in this file is copyrighted, but you can
+
# The PyMOL Plugin source code in this file is copyrighted, but you can
# freely use and copy it as long as you don't change or remove any of
+
# freely use and copy it as long as you don't change or remove any of
# the copyright notices.
+
# the copyright notices.
#  
+
#  
# ----------------------------------------------------------------------
+
# ----------------------------------------------------------------------
# This PyMOL Plugin is Copyright (C) 2004 by Charles Moad <cmoad@indiana.edu>
+
# This PyMOL Plugin is Copyright (C) 2004 by Charles Moad <cmoad@indiana.edu>
#  
+
#  
#                        All Rights Reserved
+
#                        All Rights Reserved
#  
+
#  
# Permission to use, copy, modify, distribute, and distribute modified
+
# Permission to use, copy, modify, distribute, and distribute modified
# versions of this software and its documentation for any purpose and
+
# versions of this software and its documentation for any purpose and
# without fee is hereby granted, provided that the above copyright
+
# without fee is hereby granted, provided that the above copyright
# notice appear in all copies and that both the copyright notice and
+
# notice appear in all copies and that both the copyright notice and
# this permission notice appear in supporting documentation, and that
+
# this permission notice appear in supporting documentation, and that
# the name(s) of the author(s) not be used in advertising or publicity
+
# the name(s) of the author(s) not be used in advertising or publicity
# pertaining to distribution of the software without specific, written
+
# pertaining to distribution of the software without specific, written
# prior permission.
+
# prior permission.
#  
+
#  
# THE AUTHOR(S) DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+
# THE AUTHOR(S) DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN
+
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN
# NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+
# NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
# USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+
# USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
+
# PERFORMANCE OF THIS SOFTWARE.
# ----------------------------------------------------------------------
+
# ----------------------------------------------------------------------
+
 
import tkSimpleDialog
+
import tkSimpleDialog
import tkMessageBox
+
import tkMessageBox
from pymol import cmd
+
from pymol import cmd
import sys, urllib2, zlib
+
import sys, urllib2, zlib
+
 
def __init__(self):
+
def __init__(self):
    self.menuBar.addmenuitem('Plugin', 'command',
+
  self.menuBar.addmenuitem('Plugin', 'command',
                            'PDB Loader Service',
+
                            'PDB Loader Service',
                            label = 'PDB Loader Service',
+
                            label = 'PDB Loader Service',
                            command = lambda s=self : fetchPDBDialog(s))
+
                            command = lambda s=self : fetchPDBDialog(s))
+
 
def remote(pdbCode):
+
def remote(pdbCode):
    pdbCode = pdbCode.upper()
+
  pdbCode = pdbCode.upper()
    try:
+
  try:
      pdbFile = urllib2.urlopen('<nowiki>http://www.rcsb.org/pdb/cgi/export.cgi/</nowiki>' +
+
      pdbFile = urllib2.urlopen('http://www.rcsb.org/pdb/cgi/export.cgi/' +
                                pdbCode + '.pdb.gz?format=PDB&pdbId=' +
+
                                pdbCode + '.pdb.gz?format=PDB&pdbId=' +
                                pdbCode + '&compression=gz')
+
                                pdbCode + '&compression=gz')
      cmd.read_pdbstr(zlib.decompress(pdbFile.read()[22:], -zlib.MAX_WBITS), pdbCode)
+
      cmd.read_pdbstr(zlib.decompress(pdbFile.read()[22:], -zlib.MAX_WBITS), pdbCode)
    except:
+
  except:
      print "Unexpected error:", sys.exc_info()[0]
+
      print "Unexpected error:", sys.exc_info()[0]
      tkMessageBox.showerror('Invalid Code',
+
      tkMessageBox.showerror('Invalid Code',
                              'You entered an invalid pdb code:' + pdbCode)
+
                            'You entered an invalid pdb code:' + pdbCode)
+
 
def fetchPDBDialog(app):
+
def fetchPDBDialog(app):
    pdbCode = tkSimpleDialog.askstring('PDB Loader Service',
+
  pdbCode = tkSimpleDialog.askstring('PDB Loader Service',
                                      'Please enter a 4-digit pdb code:',
+
                                      'Please enter a 4-digit pdb code:',
                                      parent=app.root)
+
                                      parent=app.root)
   
+
 
    remote(pdbCode)
+
  remote(pdbCode)
+
 
cmd.extend('remote', remote)
+
cmd.extend('remote', remote)
 
</source>
 
</source>
  

Revision as of 13:21, 29 July 2008

Learn By Example

This tutorial is a more sophisticated version of the PDB Loader Service plugin that is bundled with PyMol. It is a relatively simple plugin, but should prove to be a good starting point for developing new plugins.

Registering your Plugin

First you must add your plugin to the Plugins menu. This is most easily done in the __init__ method of your plugin. A callback, fetchPDBDialog, is added. It is passed a reference to the main Tk app. It will need this in order to create new content in the main Tkinter loop. It is not strictly required in this example, but is needed in more complex interfaces.

 def __init__(self):
    self.menuBar.addmenuitem('Plugin', 'command',
                             'PDB Loader Service',
                             label = 'PDB Loader Service',
                             command = lambda s=self : fetchPDBDialog(s))

Adding Functionality

For this example we are going to dynamically download and load a pdb structure into the pymol interface. Here is a simple method which does that given a pdbCode.

 def remote(pdbCode):
    pdbCode = pdbCode.upper()
    try:
       pdbFile = urllib2.urlopen('<nowiki>http://www.rcsb.org/pdb/cgi/export.cgi/</nowiki>' +
                                 pdbCode + '.pdb.gz?format=PDB&pdbId=' +
                                 pdbCode + '&compression=gz')
       cmd.read_pdbstr(zlib.decompress(pdbFile.read()[22:], -zlib.MAX_WBITS), pdbCode)
    except:
       print "Unexpected error:", sys.exc_info()[0]
       tkMessageBox.showerror('Invalid Code',
                              'You entered an invalid pdb code:' + pdbCode)

Creating the Interface

Now we need to write the callback method, fetchPDBDialog, which creates the interface.

 def fetchPDBDialog(app):
    pdbCode = tkSimpleDialog.askstring('PDB Loader Service',
                                       'Please enter a 4-digit pdb code:',
                                       parent=app.root)
    remote(pdbCode)

Pretty simple! As of now you have a fully functional plugin that will prompt the user for a pdb code, download it from RCSB, and load it into the PyMol interface.

Extending Plugins to the Command Line

Opening the dialog can be tedious, so let's add a command line callback to make use of this new functionality.

 cmd.extend('remote', remote)

Now you can type remote 1di9, for example, to load the the corresponding pdb.

Full Source

Here is the complete example. You can save this file and load it into PyMol with the Plugin -> Install Plugin... menu item. I also include the sample license that Warren provides for plugin developers. It is optional, of course.

# Copyright Notice
# ================
# 
# The PyMOL Plugin source code in this file is copyrighted, but you can
# freely use and copy it as long as you don't change or remove any of
# the copyright notices.
# 
# ----------------------------------------------------------------------
# This PyMOL Plugin is Copyright (C) 2004 by Charles Moad <cmoad@indiana.edu>
# 
#                        All Rights Reserved
# 
# Permission to use, copy, modify, distribute, and distribute modified
# versions of this software and its documentation for any purpose and
# without fee is hereby granted, provided that the above copyright
# notice appear in all copies and that both the copyright notice and
# this permission notice appear in supporting documentation, and that
# the name(s) of the author(s) not be used in advertising or publicity
# pertaining to distribution of the software without specific, written
# prior permission.
# 
# THE AUTHOR(S) DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN
# NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
# USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# ----------------------------------------------------------------------

import tkSimpleDialog
import tkMessageBox
from pymol import cmd
import sys, urllib2, zlib

def __init__(self):
   self.menuBar.addmenuitem('Plugin', 'command',
                            'PDB Loader Service',
                            label = 'PDB Loader Service',
                            command = lambda s=self : fetchPDBDialog(s))

def remote(pdbCode):
   pdbCode = pdbCode.upper()
   try:
      pdbFile = urllib2.urlopen('http://www.rcsb.org/pdb/cgi/export.cgi/' +
                                pdbCode + '.pdb.gz?format=PDB&pdbId=' +
                                pdbCode + '&compression=gz')
      cmd.read_pdbstr(zlib.decompress(pdbFile.read()[22:], -zlib.MAX_WBITS), pdbCode)
   except:
      print "Unexpected error:", sys.exc_info()[0]
      tkMessageBox.showerror('Invalid Code',
                             'You entered an invalid pdb code:' + pdbCode)

def fetchPDBDialog(app):
   pdbCode = tkSimpleDialog.askstring('PDB Loader Service',
                                      'Please enter a 4-digit pdb code:',
                                      parent=app.root)
   
   remote(pdbCode)

cmd.extend('remote', remote)