|
|
Line 24: |
Line 24: |
| There are two ways to get the new plugin | | There are two ways to get the new plugin |
| | | |
− | * If you have subversion installed, | + | * If you have subversion installed, you can always get the latest version via |
| | | |
| <source lang="bash"> | | <source lang="bash"> |
Line 30: |
Line 30: |
| </source> | | </source> |
| | | |
− | * Copy the text directly from http://pymolwiki.org/index.php/User:Mglerner and put it in a file called apbsplugin.py | + | * You can download the zip file from http://pymolwiki.org/index.php/User:Mglerner . Upon unzipping it, you should have a file called apbsplugin.py |
| | | |
| Once you have the plugin, you can install it via PyMOL's plugin installer: Plugin --> Manage Plugins --> Install | | Once you have the plugin, you can install it via PyMOL's plugin installer: Plugin --> Manage Plugins --> Install |
Line 47: |
Line 47: |
| * Gives better diagnostic information so that PyMOL/APBS developers can find bugs more easily | | * Gives better diagnostic information so that PyMOL/APBS developers can find bugs more easily |
| * Lots of internal code cleanup | | * Lots of internal code cleanup |
− |
| |
− | == Plugin code ==
| |
− |
| |
− | <source lang="python">
| |
− | #!/usr/bin/env python
| |
− |
| |
− | # TODO:
| |
− | # - provide diff for pdb2pqr freemol
| |
− | # - use remove_alt to count alternate atom locations and warn the user
| |
− | #
| |
− | # - Note to users that they should remove freemol's pymol.exe on OS X.
| |
− |
| |
− | ### (all) and resn glu and resi 154+157
| |
− | ### flag ignore, atom-selection, clear
| |
− |
| |
− | # APBS TOOLS Copyright Notice
| |
− | # ============================
| |
− | #
| |
− | # The APBS TOOLS source code is copyrighted, but you can freely use and
| |
− | # copy it as long as you don't change or remove any of the copyright
| |
− | # notices.
| |
− | #
| |
− | # ----------------------------------------------------------------------
| |
− | # APBS TOOLS is Copyright (C) 2009 by Michael G. Lerner
| |
− | #
| |
− | # 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 of Michael G. Lerner not be used in advertising or publicity
| |
− | # pertaining to distribution of the software without specific, written
| |
− | # prior permission.
| |
− | #
| |
− | # MICHAEL G. LERNER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
| |
− | # SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
| |
− | # FITNESS. IN NO EVENT SHALL MICHAEL G. LERNER 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.
| |
− | # ----------------------------------------------------------------------
| |
− |
| |
− | """
| |
− |
| |
− | A NOTE TO USERS:
| |
− |
| |
− | You can change the default locations for the APBS and PDB2PQR binaries
| |
− | as well as the default temporary file directory below. If you set them
| |
− | in this file, they will be preserved each time you load up the
| |
− | plugin. Scroll down to the section entitled "Global config variables"
| |
− | (no quotes) below.
| |
− |
| |
− | A NOTE TO OTHER DEVELOPERS:
| |
− |
| |
− | I understand that I'm giving this code away for Free, and that there's
| |
− | not a ton of great sample PyMOL plugin code out there, so you're
| |
− | certainly encouraged to use this as a template. However, please
| |
− | acknowledge me in some way if you do (somewhere in the comments if you
| |
− | only use a little bit of code or something more if you use a lot).
| |
− |
| |
− | Thanks.
| |
− |
| |
− | -Michael
| |
− |
| |
− | Features under consideration:
| |
− |
| |
− | - Use 'acc' to calculate solvent-accessible surface areas. We'll use
| |
− | the PyMOL 'standard' method and store this data as B-factors.
| |
− | - Show the field lines. Relevant code from menu.py:
| |
− |
| |
− | def map_gradient(self_cmd, sele):
| |
− | return [[ 2, 'Gradient:', '' ],
| |
− | [ 1, 'default' , 'cmd.gradient("'+sele+'_grad","'+sele+'");cmd.ramp_new("'+sele+
| |
− | '_grad_ramp","'+sele+'");cmd.color("'+sele+'_grad_ramp","'+sele+'_grad");' ]
| |
− | ]
| |
− |
| |
− | Known hacks:
| |
− |
| |
− |
| |
− | 1. (code in execute() )There's a bug in the version of APBS shipped
| |
− | with Ubuntu 9.10 (version 1.1.0) that causes the name to become
| |
− | foo-PE0.dx instead of foo.dx. This would normally only occur in
| |
− | mg-para calcs or MPI versions of apbs. It's clearly bug-possible,
| |
− | but we will check for foo-PE0 if we can't find foo.dx.
| |
− |
| |
− | 2. We look for dylib errors in the APBS executable that's shipped with
| |
− | freemol on OS X. Code in get_default()'s verify().
| |
− |
| |
− | """
| |
− | from __future__ import division
| |
− | from __future__ import generators
| |
− |
| |
− | global DEBUG
| |
− | DEBUG = 1
| |
− |
| |
− | APBS_DEFAULT=True
| |
− |
| |
− | import os,math,re
| |
− | import string
| |
− | import Tkinter
| |
− | from Tkinter import *
| |
− | import Pmw
| |
− | import distutils.spawn # used for find_executable
| |
− | import traceback
| |
− | import pymol
| |
− |
| |
− | #
| |
− | # Global config variables
| |
− | #
| |
− | # To change the default locations, change these to something like
| |
− | # APBS_BINARY_LOCATION = '/opt/bin/apbs'
| |
− | #
| |
− | APBS_BINARY_LOCATION = None
| |
− | APBS_PSIZE_LOCATION = None
| |
− | APBS_PDB2PQR_LOCATION = None
| |
− | TEMPORARY_FILE_DIR = None
| |
− |
| |
− | apbs_plea = ("IMPORTANT REQUEST: If you have not already done so, please register\n"
| |
− | "your use of the open-source Adaptive Poisson-Boltzmann Solver (APBS) at\n"
| |
− | "-> http://agave.wustl.edu/apbs/download\n"
| |
− | "Such proof of usage is vital in securing funding for APBS development!\n")
| |
− |
| |
− | pdb2pqr_plea = ("IMPORTANT REQUEST: If you have not already done so, please register\n"
| |
− | "your use of the open-source PDB2PQR at\n"
| |
− | "-> http://www.poissonboltzmann.org/pdb2pqr/d\n"
| |
− | "Such proof of usage is vital in securing funding for PDB2PQR development!\n")
| |
− | global apbs_message, pdb2pqr_message
| |
− | apbs_message = """You must have APBS installed on your system."""
| |
− | pdb2pqr_message = """PDB2PQR can be used to generate .PQR files."""
| |
− |
| |
− | def get_default_location(name):
| |
− | """
| |
− | Given the name of an APBS-related binary, look in
| |
− | * pymol path,
| |
− | * freemol path,
| |
− | * user defined places,
| |
− | * the system path,
| |
− | * Known other paths (/usr/local/bin and /opt/local/bin)
| |
− | for the parameter, name.
| |
− |
| |
− | Some programs are verified with additional tests. In particular,
| |
− | some versions of PyMOL ship with a broken apbs.exe, so we verify
| |
− | that it can be run.
| |
− |
| |
− | PARAMS
| |
− | name, (string) the basename of the file we're looking for
| |
− | EXAMPLE
| |
− | get_default_location("apbs.exe")
| |
− | RETURNS
| |
− | (string) path/to/file on success or "" on failure
| |
− | NOTES
| |
− | For any program name <foo>.exe we will also search for
| |
− | <foo>. We'll search for the .exe version first. We do not
| |
− | automatically check for .exe versions of programs when .exe is
| |
− | not specified.
| |
− | """
| |
− | def verify(name,f):
| |
− | if name in 'apbs.exe apbs'.split():
| |
− | # You'd think we could just check the return code, but
| |
− | # APBS doesn't return zero on success. Instead, it returns
| |
− | # things like 3328. It seems to return 5 or -5 in this
| |
− | # particular failure case, but I'm not sure we can depend
| |
− | # on that always.
| |
− | (retcode,prog_out) = run(f,'--version')
| |
− | if 'dyld: Library not loaded' in prog_out:
| |
− | print "Skipping",f,"because it appears to be broken (dyld)"
| |
− | return False
| |
− | return True
| |
− | searchDirs = []
| |
− | if "FREEMOL" in os.environ:
| |
− | searchDirs.append(os.environ["FREEMOL"])
| |
− | if "PYMOL_PATH" in os.environ:
| |
− | searchDirs.append(os.path.join(os.environ["PYMOL_PATH"], "ext", "bin"))
| |
− | searchDirs.append(os.path.join(os.environ["PYMOL_PATH"], "freemol", "bin"))
| |
− | searchDirs.append(os.path.join(os.environ["PYMOL_PATH"], "freemol", "share", "apbs"))
| |
− | searchDirs.append(os.path.join(os.environ["PYMOL_PATH"], "freemol", "share", "pdb2pqr"))
| |
− | for x in (APBS_BINARY_LOCATION, APBS_PSIZE_LOCATION, APBS_PDB2PQR_LOCATION):
| |
− | if x != None and x != "":
| |
− | searchDirs.append(x)
| |
− | if name=="temp":
| |
− | searchDirs = []
| |
− | if TEMPORARY_FILE_DIR != None and TEMPORARY_FILE_DIR != "":
| |
− | searchDirs.append(TEMPORARY_FILE_DIR)
| |
− | searchDirs.append("/tmp")
| |
− | searchDirs.append(".")
| |
− |
| |
− | searchDirs.extend(string.split(os.environ["PATH"], ":"))
| |
− | searchDirs.append(os.path.join("/usr", "local", "bin"))
| |
− | searchDirs.append(os.path.join("/opt", "local", "bin"))
| |
− |
| |
− | print "Search dirs",searchDirs
| |
− |
| |
− | if DEBUG:
| |
− | print "get_default_location will search the following: ", searchDirs
| |
− | for d in searchDirs:
| |
− | if name=="temp":
| |
− | f = d # just search for the directory
| |
− | else:
| |
− | f = os.path.join( d, name ) # make path/name.py
| |
− | print "trying",f
| |
− | if os.path.exists(f) and verify(name,f):
| |
− | return f
| |
− | elif name.endswith('.exe'):
| |
− | f = os.path.join( d, name[:-4] ) # make path/name.py
| |
− | if os.path.exists(f) and verify(name,f):
| |
− | return f
| |
− |
| |
− | print "Could not find default location for file: %s" % name
| |
− | return ""
| |
− |
| |
− | def __init__(self):
| |
− | """
| |
− | Init PyMOL, by adding APBSTools to the GUI under Plugins
| |
− |
| |
− | Creates the APBS widget/notebook. Once the event is received,
| |
− | we create a new instance of APBSTools2 which is a Pmw, which upon
| |
− | creation shows itself.
| |
− | """
| |
− | self.menuBar.addmenuitem('Plugin', 'command',
| |
− | 'Launch APBS Tools2',
| |
− | label='APBS Tools2...',
| |
− | command = lambda s=self: APBSTools2(s))
| |
− |
| |
− | def run(prog,args):
| |
− | '''
| |
− | wrapper to handle spaces on windows.
| |
− | prog is the full path to the program.
| |
− | args is a string that we will split up for you.
| |
− | or a tuple. or a list. your call.
| |
− |
| |
− | return value is (retval,prog_out)
| |
− |
| |
− | e.g.
| |
− |
| |
− | (retval,prog_out) = run("/bin/ls","-al /tmp/myusername")
| |
− | '''
| |
− | import subprocess,tempfile
| |
− |
| |
− | if type(args) == type(''):
| |
− | args = tuple(args.split())
| |
− | elif type(args) in (type([]),type(())):
| |
− | args = tuple(args)
| |
− | args = (prog,) + args
| |
− |
| |
− | try:
| |
− | output_file = tempfile.TemporaryFile(mode="w+") # <-- shouldn't this point to the temp dir
| |
− | except IOError:
| |
− | print "Error opening output_file when trying to run the APBS command."
| |
− |
| |
− | print "Running",args
| |
− | retcode = subprocess.call(args,stdout=output_file.fileno(),stderr=subprocess.STDOUT)
| |
− | output_file.seek(0)
| |
− | #prog_out = output_file.read()
| |
− | prog_out = ''.join(output_file.readlines())
| |
− | output_file.close() #windows doesn't do this automatically
| |
− | if DEBUG:
| |
− | print "Results were:"
| |
− | print "Return value:",retcode
| |
− | print "Output:"
| |
− | print prog_out
| |
− | return (retcode,prog_out)
| |
− |
| |
− | class util:
| |
− | """
| |
− | A quick collection of utility functions.
| |
− | """
| |
− | #@staticmethod
| |
− | def getMolecules():
| |
− | """returns all molecules that PyMOL knows about"""
| |
− | return [i for i in pymol.cmd.get_names() if pymol.cmd.get_type(i)=='object:molecule']
| |
− | getMolecules = staticmethod(getMolecules)
| |
− | #@staticmethod
| |
− | def getMaps():
| |
− | """returns all maps that PyMOL knows about"""
| |
− | return [i for i in pymol.cmd.get_names() if pymol.cmd.get_type(i)=='object:map']
| |
− | getMaps = staticmethod(getMaps)
| |
− | #def hasAlt(sel):
| |
− | # """returns true if non-standard locations (rotamers) are present for this selection"""
| |
− | # return cmd.count_atoms(sel + " and not alt ''")!=0
| |
− | #hasAlt = staticmethod(hasAlt)
| |
− |
| |
− | util = util()
| |
− |
| |
− | ##############################################################################
| |
− | ##############################################################################
| |
− | ### ###
| |
− | ### ApbsInterface ###
| |
− | ### ###
| |
− | ##############################################################################
| |
− | ##############################################################################
| |
− |
| |
− | def getApbsInputFile(pqr_filename,
| |
− | grid_points,
| |
− | cglen,
| |
− | fglen,
| |
− | cent,
| |
− | apbs_mode,
| |
− | bcfl,
| |
− | ion_plus_one_conc,ion_plus_one_rad,
| |
− | ion_minus_one_conc,ion_minus_one_rad,
| |
− | ion_plus_two_conc,ion_plus_two_rad,
| |
− | ion_minus_two_conc,ion_minus_two_rad,
| |
− | interior_dielectric, solvent_dielectric,
| |
− | chgm,
| |
− | srfm,
| |
− | solvent_radius,
| |
− | system_temp,
| |
− | sdens,
| |
− | dx_filename,
| |
− | ):
| |
− | print "Getting APBS input"
| |
− | #print "system_temp",system_temp,type(system_temp)
| |
− | #print "sdens",sdens,type(sdens)
| |
− | #
| |
− | # How shall we set up the grid? We'll use cglen, fglen, cgcent, fgcent
| |
− | # and dime.
| |
− | # This allows people to automate things (e.g. "Alanine scanning")
| |
− | #
| |
− |
| |
− | #
| |
− | # New template using mg-auto
| |
− | # See http://agave.wustl.edu/apbs/doc/api/html/#mg-auto
| |
− | #
| |
− |
| |
− | apbs_template = """#
| |
− | # Note that most of the comments here were taken from sample
| |
− | # input files that came with APBS. You can find APBS at
| |
− | # http://agave.wustl.edu/apbs/
| |
− | # Note that APBS is GPL'd code.
| |
− | #
| |
− | read
| |
− | mol pqr %s # read molecule 1
| |
− | end
| |
− | elec
| |
− | mg-auto
| |
− | dime %d %d %d # number of find grid points
| |
− | # calculated by psize.py
| |
− | cglen %f %f %f # coarse mesh lengths (A)
| |
− | fglen %f %f %f # fine mesh lengths (A)
| |
− | # calculated by psize.py
| |
− | cgcent %f %f %f # (could also give (x,y,z) form psize.py) #known center
| |
− | fgcent %f %f %f # (could also give (x,y,z) form psize.py) #known center
| |
− | %s # solve the full nonlinear PBE with npbe
| |
− | #lpbe # solve the linear PBE with lpbe
| |
− | bcfl %s # Boundary condition flag
| |
− | # 0 => Zero
| |
− | # 1 => Single DH sphere
| |
− | # 2 => Multiple DH spheres
| |
− | # 4 => Focusing
| |
− | #
| |
− | #ion 1 0.000 2.0 # Counterion declaration:
| |
− | ion charge 1 conc %f radius %f # Counterion declaration:
| |
− | ion charge -1 conc %f radius %f # ion <charge> <conc (M)> <radius>
| |
− | ion charge 2 conc %f radius %f # ion <charge> <conc (M)> <radius>
| |
− | ion charge -2 conc %f radius %f # ion <charge> <conc (M)> <radius>
| |
− | pdie %f # Solute dielectric
| |
− | sdie %f # Solvent dielectric
| |
− | chgm %s # Charge disc method
| |
− | # 0 is linear splines
| |
− | # 1 is cubic b-splines
| |
− | mol 1 # which molecule to use
| |
− | srfm smol # Surface calculation method
| |
− | # 0 => Mol surface for epsilon;
| |
− | # inflated VdW for kappa; no
| |
− | # smoothing
| |
− | # 1 => As 0 with harmoinc average
| |
− | # smoothing
| |
− | # 2 => Cubic spline
| |
− | srad %f # Solvent radius (1.4 for water)
| |
− | swin 0.3 # Surface cubic spline window .. default 0.3
| |
− | temp %f # System temperature (298.15 default)
| |
− | sdens %f # Specify the number of grid points per square-angstrom to use in Vacc object. Ignored when srad is 0.0 (see srad) or srfm is spl2 (see srfm). There is a direct correlation between the value used for the Vacc sphere density, the accuracy of the Vacc object, and the APBS calculation time. APBS default value is 10.0.
| |
− | #gamma 0.105 # Surface tension parameter for apolar forces (in kJ/mol/A^2)
| |
− | # only used for force calculations, so we don't care, but
| |
− | # it *used to be* always required, and 0.105 is the default
| |
− | calcenergy no # Energy I/O to stdout
| |
− | # 0 => don't write out energy
| |
− | # 1 => write out total energy
| |
− | # 2 => write out total energy and all
| |
− | # components
| |
− | calcforce no # Atomic forces I/O (to stdout)
| |
− | # 0 => don't write out forces
| |
− | # 1 => write out net forces on molecule
| |
− | # 2 => write out atom-level forces
| |
− | write pot dx %s # What to write .. this says write the potential in dx
| |
− | # format to a file.
| |
− | end
| |
− | quit
| |
− |
| |
− | """
| |
− | return apbs_template % (pqr_filename,
| |
− | grid_points[0], grid_points[1], grid_points[2],
| |
− | cglen[0],cglen[1],cglen[2],
| |
− | fglen[0],fglen[1],fglen[2],
| |
− | cent[0],cent[1],cent[2],
| |
− | cent[0],cent[1],cent[2],
| |
− | apbs_mode,
| |
− | bcfl,
| |
− | ion_plus_one_conc,ion_plus_one_rad,
| |
− | ion_minus_one_conc,ion_minus_one_rad,
| |
− | ion_plus_two_conc,ion_plus_two_rad,
| |
− | ion_minus_two_conc,ion_minus_two_rad,
| |
− | interior_dielectric, solvent_dielectric,
| |
− | chgm,
| |
− | solvent_radius,
| |
− | system_temp,
| |
− | sdens,
| |
− | dx_filename,
| |
− | )
| |
− |
| |
− |
| |
− |
| |
− | ##############################################################################
| |
− | ##############################################################################
| |
− | ### ###
| |
− | ### PluginCode ###
| |
− | ### ###
| |
− | ##############################################################################
| |
− | ##############################################################################
| |
− |
| |
− |
| |
− |
| |
− | class APBSTools2:
| |
− | # The current goal is to factor all of the APBS-specific code into
| |
− | # an ApbsInterface class. The functions defined here before
| |
− | # __init__, as well as the functions defined on the various
| |
− | # Panels/Panes, will call through to self.ApbsInterface.
| |
− | def setPqrFile(self,name):
| |
− | print " APBS Tools: set pqr file to",name
| |
− | self.pqr_to_use.setvalue(name)
| |
− | self.radiobuttons.setvalue('Use another PQR')
| |
− | def getPqrFilename(self):
| |
− | if self.radiobuttons.getvalue() != 'Use another PQR':
| |
− | return self.pymol_generated_pqr_filename.getvalue()
| |
− | else:
| |
− | return self.pqr_to_use.getvalue()
| |
− | def setPsizeLocation(self,value):
| |
− | self.psize.setvalue(value)
| |
− | def setBinaryLocation(self,value):
| |
− | self.binary.setvalue(value)
| |
− | def setPdb2pqrLocation(self,value):
| |
− | self.pdb2pqr.setvalue(value)
| |
− |
| |
− | def setPymolGeneratedPqrFilename(self,value):
| |
− | self.pymol_generated_pqr_filename.setvalue(value)
| |
− | def setPymolGeneratedPdbFilename(self,value):
| |
− | self.pymol_generated_pdb_filename.setvalue(value)
| |
− | def setPymolGeneratedDxFilename(self,value):
| |
− | self.pymol_generated_dx_filename.setvalue(value)
| |
− | def setPymolGeneratedInFilename(self,value):
| |
− | self.pymol_generated_in_filename.setvalue(value)
| |
− |
| |
− |
| |
− | def getPymolGeneratedPqrFilename(self):
| |
− | return self.pymol_generated_pqr_filename.getvalue()
| |
− | def getPymolGeneratedPdbFilename(self):
| |
− | return self.pymol_generated_pdb_filename.getvalue()
| |
− | def getPymolGeneratedDxFilename(self):
| |
− | return self.pymol_generated_dx_filename.getvalue()
| |
− | def getPymolGeneratedInFilename(self):
| |
− | return self.pymol_generated_in_filename.getvalue()
| |
− | defaults = {
| |
− | "interior_dielectric" : 2.0,
| |
− | "solvent_dielectric" : 78.0,
| |
− | "solvent_radius" : 1.4,
| |
− | "system_temp" : 310.0,
| |
− | "apbs_mode" : 'Linearized Poisson-Boltzmann Equation',
| |
− | "ion_plus_one_conc" : 0.15,
| |
− | "ion_plus_one_rad" : 2.0,
| |
− | "ion_plus_two_conc" : 0.0,
| |
− | "ion_plus_two_rad" : 2.0,
| |
− | "ion_minus_one_conc" : 0.15,
| |
− | "ion_minus_one_rad" : 1.8,
| |
− | "ion_minus_two_conc" : 0.0,
| |
− | "ion_minus_two_rad" : 2.0,
| |
− | #"max_mem_allowed" : 400,
| |
− | "max_mem_allowed" : 1500,
| |
− | "potential_at_sas" : 0,
| |
− | "surface_solvent" : 1,
| |
− | "show_surface_for_scanning" : 1,
| |
− | #"grid_buffer" : 0,
| |
− | #"grid_buffer" : 20,
| |
− | "bcfl" : 'Single DH sphere', # Boundary condition flag for APBS
| |
− | "sdens": 10.0, # Specify the number of grid points per
| |
− | # square-angstrom to use in Vacc
| |
− | # object. Ignored when srad is 0.0 (see srad)
| |
− | # or srfm is spl2 (see srfm). There is a direct
| |
− | # correlation between the value used for the
| |
− | # Vacc sphere density, the accuracy of the Vacc
| |
− | # object, and the APBS calculation time. APBS
| |
− | # default value is 10.0.
| |
− | "chgm" : 'Cubic B-splines', # Charge disc method for APBS
| |
− | }
| |
− |
| |
− | def __init__(self,app):
| |
− | self.parent = app.root
| |
− |
| |
− | # Create the dialog.
| |
− | self.dialog = Pmw.Dialog(self.parent,
| |
− | buttons = ('Register APBS Use', 'Register PDB2PQR Use', 'Set grid', 'Run APBS', 'Exit APBS tools'),
| |
− | title = 'PyMOL APBS Tools',
| |
− | command = self.execute)
| |
− | self.dialog.withdraw()
| |
− | Pmw.setbusycursorattributes(self.dialog.component('hull'))
| |
− |
| |
− | w = Tkinter.Label(self.dialog.interior(),
| |
− | text = 'PyMOL APBS Tools\nMichael G. Lerner, Heather A. Carlson, 2009 - http://pymolwiki.org/index.php/APBS\n(incorporates modifications by Warren L. DeLano)',
| |
− | background = 'black',
| |
− | foreground = 'white',
| |
− | #pady = 20,
| |
− | )
| |
− | w.pack(expand = 1, fill = 'both', padx = 4, pady = 4)
| |
− |
| |
− | self.notebook = Pmw.NoteBook(self.dialog.interior())
| |
− | self.notebook.pack(fill='both',expand=1,padx=10,pady=10)
| |
− |
| |
− | # Set up the Main page
| |
− | page = self.notebook.add('Main')
| |
− | group = Pmw.Group(page,tag_text='Main options')
| |
− | group.pack(fill = 'both', expand = 1, padx = 10, pady = 5)
| |
− | self.selection = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_text='Selection to use: ',
| |
− | value='(polymer)',
| |
− | )
| |
− | self.map = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_text='What to call the resulting map: ',
| |
− | value='apbs_map',
| |
− | )
| |
− | self.radiobuttons = Pmw.RadioSelect(group.interior(),
| |
− | buttontype = 'radiobutton',
| |
− | orient = 'vertical',
| |
− | labelpos = 'w',
| |
− | )
| |
− | for text in ('Use PyMOL generated PQR and existing Hydrogens and termini',
| |
− | 'Use PyMOL generated PQR and PyMOL generated Hydrogens and termini',
| |
− | 'Use another PQR',
| |
− | 'Use PDB2PQR',):
| |
− | self.radiobuttons.add(text)
| |
− | self.radiobuttons.setvalue('Use PyMOL generated PQR and PyMOL generated Hydrogens and termini')
| |
− |
| |
− | self.pdb2pqr_options = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_text='pdb2pqr command line options: ',
| |
− | value='--ff=AMBER',
| |
− | )
| |
− | self.pqr_to_use = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_pyclass = FileDialogButtonClassFactory.get(self.setPqrFile,'*.pqr'),
| |
− | label_text='\tChoose Externally Generated PQR:',
| |
− | )
| |
− |
| |
− |
| |
− |
| |
− | for entry in (self.selection,self.map,self.radiobuttons,self.pdb2pqr_options,self.pqr_to_use):
| |
− | #for entry in (self.selection,self.map,self.radiobuttons,):
| |
− | entry.pack(fill='x',padx=4,pady=1) # vertical
| |
− |
| |
− |
| |
− | # Set up the main "Calculation" page
| |
− | page = self.notebook.add('Configuration')
| |
− |
| |
− | group = Pmw.Group(page,tag_text='Dielectric Constants')
| |
− | group.pack(fill = 'both', expand = 1, padx = 4, pady = 5)
| |
− | group.grid(column=0, row=0)
| |
− | self.interior_dielectric = Pmw.EntryField(group.interior(),labelpos='w',
| |
− | label_text = 'Protein Dielectric:',
| |
− | value = str(APBSTools2.defaults['interior_dielectric']),
| |
− | validate = {'validator' : 'real',
| |
− | 'min':0,}
| |
− | )
| |
− | self.solvent_dielectric = Pmw.EntryField(group.interior(),labelpos='w',
| |
− | label_text = 'Solvent Dielectric:',
| |
− | value = str(APBSTools2.defaults['solvent_dielectric']),
| |
− | validate = {'validator' : 'real',
| |
− | 'min':0,}
| |
− | )
| |
− | entries = (self.interior_dielectric,self.solvent_dielectric)
| |
− | for entry in entries:
| |
− | #entry.pack(side='left',fill='both',expand=1,padx=4) # side-by-side
| |
− | entry.pack(fill='x',expand=1,padx=4,pady=1) # vertical
| |
− | group = Pmw.Group(page,tag_text='Other')
| |
− | group.pack(fill='both',expand=1, padx=4, pady=5)
| |
− | group.grid(column=1, row=1,columnspan=4)
| |
− | self.max_mem_allowed = Pmw.EntryField(group.interior(),labelpos='w',
| |
− | label_text = 'Maximum Memory Allowed (MB):',
| |
− | value = str(APBSTools2.defaults['max_mem_allowed']),
| |
− | validate = {'validator' : 'real',
| |
− | 'min':1,}
| |
− | )
| |
− | self.apbs_mode = Pmw.OptionMenu(group.interior(),
| |
− | labelpos = 'w',
| |
− | label_text = 'APBS Mode',
| |
− | items = ('Nonlinear Poisson-Boltzmann Equation','Linearized Poisson-Boltzmann Equation',),
| |
− | initialitem = APBSTools2.defaults['apbs_mode'],
| |
− | )
| |
− | self.apbs_mode.pack(fill='x',expand=1,padx=4)
| |
− | #self.apbs_mode.grid(column=0,row=0,columnspan=3)
| |
− | self.solvent_radius = Pmw.EntryField(group.interior(),
| |
− | labelpos = 'w',
| |
− | label_text = 'Solvent Radius:',
| |
− | validate = {'validator':'real','min':0},
| |
− | value = str(APBSTools2.defaults['solvent_radius']),
| |
− | )
| |
− | self.system_temp = Pmw.EntryField(group.interior(),
| |
− | labelpos = 'w',
| |
− | label_text = 'System Temperature:',
| |
− | validate = {'validator':'real','min':0},
| |
− | value = str(APBSTools2.defaults['system_temp']),
| |
− | )
| |
− | self.sdens = Pmw.EntryField(group.interior(),
| |
− | labelpos = 'w',
| |
− | label_text = 'Vacc sphere density (grid points/A^2)',
| |
− | validate = {'validator':'real','min':0},
| |
− | value = str(APBSTools2.defaults['sdens']),
| |
− | )
| |
− | self.bcfl = Pmw.OptionMenu(group.interior(),
| |
− | labelpos = 'w',
| |
− | label_text = 'Boundary Condition',
| |
− | items = ('Zero','Single DH sphere','Multiple DH spheres',), #'Focusing',),
| |
− | initialitem = APBSTools2.defaults['bcfl'],
| |
− | )
| |
− | self.chgm = Pmw.OptionMenu(group.interior(),
| |
− | labelpos = 'w',
| |
− | label_text = 'Charge disc method',
| |
− | items = ('Linear','Cubic B-splines','Quintic B-splines',),
| |
− | initialitem = APBSTools2.defaults['chgm'],
| |
− | )
| |
− | self.srfm = Pmw.OptionMenu(group.interior(),
| |
− | labelpos = 'w',
| |
− | label_text = 'Surface Calculation Method',
| |
− | items = ('Mol surf for epsilon; inflated VdW for kappa, no smoothing','Same, but with harmonic average smoothing','Cubic spline','Similar to cubic spline, but with 7th order polynomial'),
| |
− | initialitem = 'Same, but with harmonic average smoothing',
| |
− | )
| |
− | #for entry in (self.apbs_mode,self.system_temp,self.solvent_radius,):
| |
− | for entry in (self.max_mem_allowed,self.solvent_radius,self.system_temp,self.sdens,self.apbs_mode,self.bcfl,self.chgm,self.srfm):
| |
− | entry.pack(fill='x',expand=1,padx=4,pady=1) # vertical
| |
− |
| |
− |
| |
− | group = Pmw.Group(page,tag_text='Ions')
| |
− | group.pack(fill='both',expand=1, padx=4, pady=5)
| |
− | group.grid(column=0, row=1, )
| |
− | self.ion_plus_one_conc = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_text='Ion Concentration (M) (+1):',
| |
− | validate={'validator':'real','min':0},
| |
− | value = str(APBSTools2.defaults['ion_plus_one_conc']),
| |
− | )
| |
− | self.ion_plus_one_rad = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_text='Ion Radius (+1):',
| |
− | validate={'validator':'real','min':0},
| |
− | value = str(APBSTools2.defaults['ion_plus_one_rad']),
| |
− | )
| |
− | self.ion_minus_one_conc = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_text='Ion Concentration (M) (-1):',
| |
− | validate={'validator':'real','min':0},
| |
− | value = str(APBSTools2.defaults['ion_minus_one_conc']),
| |
− | )
| |
− | self.ion_minus_one_rad = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_text='Ion Radius (-1):',
| |
− | validate={'validator':'real','min':0},
| |
− | value = str(APBSTools2.defaults['ion_minus_one_rad']),
| |
− | )
| |
− | self.ion_plus_two_conc = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_text='Ion Concentration (M) (+2):',
| |
− | validate={'validator':'real','min':0},
| |
− | value = str(APBSTools2.defaults['ion_plus_two_conc']),
| |
− | )
| |
− | self.ion_plus_two_rad = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_text='Ion Radius (+2):',
| |
− | validate={'validator':'real','min':0},
| |
− | value = str(APBSTools2.defaults['ion_plus_two_rad']),
| |
− | )
| |
− | self.ion_minus_two_conc = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_text='Ion Concentration (M) (-2):',
| |
− | validate={'validator':'real','min':0},
| |
− | value = str(APBSTools2.defaults['ion_minus_two_conc']),
| |
− | )
| |
− | self.ion_minus_two_rad = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_text='Ion Radius (-2):',
| |
− | validate={'validator':'real','min':0},
| |
− | value = str(APBSTools2.defaults['ion_minus_two_rad']),
| |
− | )
| |
− | entries = (self.ion_plus_one_conc,self.ion_plus_one_rad,
| |
− | self.ion_minus_one_conc,self.ion_minus_one_rad,
| |
− | self.ion_plus_two_conc,self.ion_plus_two_rad,
| |
− | self.ion_minus_two_conc,self.ion_minus_two_rad,
| |
− | )
| |
− | for entry in entries:
| |
− | entry.pack(fill='x',expand=1,padx=4)
| |
− |
| |
− | group = Pmw.Group(page,tag_text = 'Coarse Mesh Length')
| |
− | group.pack(fill = 'both', expand = 1, padx = 4, pady = 5)
| |
− | group.grid(column = 1, row = 0)
| |
− | for coord in 'x y z'.split():
| |
− | setattr(self,'grid_coarse_%s'%coord,Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_text=coord,
| |
− | validate={'validator':'real','min':0},
| |
− | value = -1,
| |
− | entry_width=15,
| |
− | )
| |
− | )
| |
− | getattr(self,'grid_coarse_%s'%coord).pack(fill='x', expand=1, padx=4, pady=1)
| |
− |
| |
− |
| |
− | group = Pmw.Group(page,tag_text = 'Fine Mesh Length')
| |
− | group.pack(fill = 'both', expand = 1, padx = 4, pady = 5)
| |
− | group.grid(column = 2, row = 0)
| |
− | for coord in 'x y z'.split():
| |
− | setattr(self,'grid_fine_%s'%coord,Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_text=coord,
| |
− | validate={'validator':'real','min':0},
| |
− | value = -1,
| |
− | entry_width=15,
| |
− | )
| |
− | )
| |
− | getattr(self,'grid_fine_%s'%coord).pack(fill='x', expand=1, padx=4, pady=1)
| |
− |
| |
− |
| |
− | group = Pmw.Group(page,tag_text = 'Grid Center')
| |
− | group.pack(fill = 'both', expand = 1, padx = 4, pady = 5)
| |
− | group.grid(column = 3, row = 0)
| |
− | for coord in 'x y z'.split():
| |
− | setattr(self,'grid_center_%s'%coord,Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_text=coord,
| |
− | validate={'validator':'real'},
| |
− | value = 0,
| |
− | entry_width=10,
| |
− | )
| |
− | )
| |
− | getattr(self,'grid_center_%s'%coord).pack(fill='x', expand=1, padx=4, pady=1)
| |
− |
| |
− | group = Pmw.Group(page,tag_text = 'Grid Points')
| |
− | group.pack(fill = 'both', expand = 1, padx = 4, pady = 5)
| |
− | group.grid(column = 4, row = 0)
| |
− | for coord in 'x y z'.split():
| |
− | setattr(self,'grid_points_%s'%coord,Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_text=coord,
| |
− | validate={'validator':'integer','min':0},
| |
− | value = -1,
| |
− | entry_width=8,
| |
− | )
| |
− | )
| |
− | getattr(self,'grid_points_%s'%coord).pack(fill='x', expand=1, padx=4, pady=1)
| |
− |
| |
− |
| |
− | page.grid_rowconfigure(2,weight=1)
| |
− | page.grid_columnconfigure(5,weight=1)
| |
− | page = self.notebook.add('Program Locations')
| |
− | group = Pmw.Group(page,tag_text='Locations')
| |
− | group.pack(fill = 'both', expand = 1, padx = 10, pady = 5)
| |
− | def quickFileValidation(s):
| |
− | if s == '': return Pmw.PARTIAL
| |
− | elif os.path.isfile(s): return Pmw.OK
| |
− | elif os.path.exists(s): return Pmw.PARTIAL
| |
− | else: return Pmw.PARTIAL
| |
− | def quickFileDirValidation(s):
| |
− | '''
| |
− | assumes s ends in filename
| |
− | '''
| |
− | if os.path.exists(s) and not os.path.isfile(s):
| |
− | return Pmw.PARTIAL
| |
− | if os.path.isdir(os.path.split(s)[0]):
| |
− | return Pmw.OK
| |
− | return Pmw.PARTIAL
| |
− | self.binary = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_pyclass = FileDialogButtonClassFactory.get(self.setBinaryLocation),
| |
− | validate = {'validator':quickFileValidation,},
| |
− | value = get_default_location('apbs.exe'),
| |
− | label_text = 'APBS binary location:',
| |
− | )
| |
− | self.binary.pack(fill = 'x', padx = 20, pady = 10)
| |
− | self.psize = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_pyclass = FileDialogButtonClassFactory.get(self.setPsizeLocation),
| |
− | validate = {'validator':quickFileValidation,},
| |
− | #value = '/usr/local/apbs-0.3.1/tools/manip/psize.py',
| |
− | value = get_default_location('psize.py'),
| |
− | label_text = 'APBS psize.py location:',
| |
− | )
| |
− | self.psize.pack(fill = 'x', padx = 20, pady = 10)
| |
− | self.pdb2pqr = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_pyclass = FileDialogButtonClassFactory.get(self.setPdb2pqrLocation),
| |
− | validate = {'validator':quickFileValidation,},
| |
− | value = get_default_location('pdb2pqr.py'),
| |
− | label_text = 'pdb2pqr location:',
| |
− | )
| |
− | self.pdb2pqr.pack(fill = 'x', padx = 20, pady = 10)
| |
− |
| |
− | label = Tkinter.Label(group.interior(),
| |
− | pady = 10,
| |
− | justify=LEFT,
| |
− | text = """
| |
− | The PyMOL APBS tools can calculate proper grid dimensions and spacing (we attempt to make
| |
− | the fine mesh spacing 0.5A or finer, but we will make it coarser if forced to by the
| |
− | Maximum Grid Points setting in the configuration pane). If you wish to use APBS's psize.py
| |
− | to set up the grid, make sure that the path is set correctly above.
| |
− |
| |
− | PyMOL can generate PQR files using standard protein residues and AMBER charges. If you
| |
− | wish to use PDB2PQR instead, make sure that it is installed and that the path is set
| |
− | correctly above.
| |
− |
| |
− | If PyMOL does not automatically find apbs, psize.py, or pdb2pqr, you may set the environment
| |
− | variables APBS_BINARY, APBS_PSIZE and APBS_PDB2PQR to point to them respectively.
| |
− | """,
| |
− | )
| |
− | label.pack()
| |
− |
| |
− |
| |
− | page = self.notebook.add('Temp File Locations')
| |
− | group = Pmw.Group(page,tag_text='Locations')
| |
− | group.pack(fill = 'both', expand = 1, padx = 10, pady = 5)
| |
− | self.pymol_generated_pqr_filename = Pmw.EntryField(group.interior(),
| |
− | labelpos = 'w',
| |
− | label_pyclass = FileDialogButtonClassFactory.get(self.setPymolGeneratedPqrFilename),
| |
− | validate = {'validator':quickFileDirValidation,},
| |
− | label_text = 'Temporary PQR file: ',
| |
− | value = os.path.join(get_default_location('temp'),'pymol-generated.pqr'),
| |
− | )
| |
− | self.pymol_generated_pqr_filename.pack(fill = 'x', padx = 20, pady = 10)
| |
− |
| |
− | self.pymol_generated_pdb_filename = Pmw.EntryField(group.interior(),
| |
− | labelpos = 'w',
| |
− | label_pyclass = FileDialogButtonClassFactory.get(self.setPymolGeneratedPdbFilename),
| |
− | validate = {'validator':quickFileDirValidation,},
| |
− | label_text = 'Temporary PDB file: ',
| |
− | value = os.path.join(get_default_location('temp'),'pymol-generated.pdb'),
| |
− | )
| |
− | self.pymol_generated_pdb_filename.pack(fill = 'x', padx = 20, pady = 10)
| |
− |
| |
− | self.pymol_generated_dx_filename = Pmw.EntryField(group.interior(),
| |
− | labelpos = 'w',
| |
− | label_pyclass = FileDialogButtonClassFactory.get(self.setPymolGeneratedDxFilename),
| |
− | validate = {'validator':quickFileDirValidation,},
| |
− | label_text = 'Temporary DX file: ',
| |
− | value = os.path.join(get_default_location('temp'),'pymol-generated.dx'),
| |
− | )
| |
− | self.pymol_generated_dx_filename.pack(fill = 'x', padx = 20, pady = 10)
| |
− |
| |
− |
| |
− | self.pymol_generated_in_filename = Pmw.EntryField(group.interior(),
| |
− | labelpos='w',
| |
− | label_pyclass = FileDialogButtonClassFactory.get(self.setPymolGeneratedInFilename),
| |
− | validate = {'validator':quickFileDirValidation,},
| |
− | value = os.path.join(get_default_location('temp'),'pymol-generated.in'),
| |
− | label_text = 'APBS input file:')
| |
− | self.pymol_generated_in_filename.pack(fill = 'x', padx = 20, pady = 10)
| |
− | label = Tkinter.Label(group.interior(),
| |
− | pady = 10,
| |
− | justify=LEFT,
| |
− | text = """You can automatically set the default location of temporary files
| |
− | by setting the environment variable TEMP.
| |
− | """,
| |
− | )
| |
− | label.pack()
| |
− | # Create the visualization pages
| |
− | page = self.notebook.add('Visualization (1)')
| |
− | group = VisualizationGroup(page,tag_text='Visualization',visgroup_num=1)
| |
− | self.visualization_group_1 = group
| |
− | group.pack(fill = 'both', expand = 1, padx = 10, pady = 5)
| |
− |
| |
− | page = self.notebook.add('Visualization (2)')
| |
− | group = VisualizationGroup(page,tag_text='Visualization',visgroup_num=2)
| |
− | self.visualization_group_2 = group
| |
− | group.pack(fill = 'both', expand = 1, padx = 10, pady = 5)
| |
− |
| |
− | # Create a couple of other empty pages
| |
− | page = self.notebook.add('About')
| |
− | group = Pmw.Group(page, tag_text='About PyMOL APBS Tools')
| |
− | group.pack(fill = 'both', expand = 1, padx = 10, pady = 5)
| |
− | text = """This plugin integrates PyMOL (http://PyMOL.org/) with APBS (http://www.poissonboltzmann.org/apbs/).
| |
− |
| |
− | Documentation may be found at
| |
− | http://pymolwiki.org/index.php/APBS
| |
− | and
| |
− | http://apbs.wustl.edu/MediaWiki/index.php/APBS_electrostatics_in_PyMOL
| |
− |
| |
− | It requires APBS version >= 0.5.0.
| |
− |
| |
− | In the simplest case,
| |
− |
| |
− | 1) Load a structure into PyMOL.
| |
− | 2) Start this plugin.
| |
− | 3) Make sure that the path to the APBS binary is correct on the "Program Locations" tab.
| |
− | 4) Click the "Set grid" button to set up the grid.
| |
− | 5) Click the "Run APBS" button.
| |
− |
| |
− | Many thanks to
| |
− | - Warren DeLano and Jason Vertrees for everything involving PyMOL
| |
− | - Nathan Baker, Todd Dolinsky and David Gohara for everything involving APBS
| |
− | - William G. Scott for help with several APBS+PyMOL issues and documentation
| |
− |
| |
− | Created by Michael Lerner (http://pymolwiki.org/index.php/User:Mglerner) mglerner@gmail.com
| |
− | Carlson Group, University of Michigan (http://www.umich.edu/~carlsonh/)
| |
− |
| |
− | Please contact the author and cite this plugin if you use it in a publication.
| |
− |
| |
− | Citation for this plugin:
| |
− | MG Lerner and HA Carlson. APBS plugin for PyMOL, 2006,
| |
− | University of Michigan, Ann Arbor.
| |
− |
| |
− | Citation for PyMOL may be found here:
| |
− | http://pymol.sourceforge.net/faq.html#CITE
| |
− |
| |
− | Citation for APBS:
| |
− | Baker NA, Sept D, Joseph S, Holst MJ, McCammon JA. Electrostatics of
| |
− | nanosystems: application to microtubules and the ribosome. Proc.
| |
− | Natl. Acad. Sci. USA 98, 10037-10041 2001.
| |
− |
| |
− | Citation for PDB2PQR:
| |
− | Dolinsky TJ, Nielsen JE, McCammon JA, Baker NA.
| |
− | PDB2PQR: an automated pipeline for the setup, execution,
| |
− | and analysis of Poisson-Boltzmann electrostatics calculations.
| |
− | Nucleic Acids Research 32 W665-W667 (2004).
| |
− | """
| |
− | #
| |
− | # Add this as text in a scrollable pane.
| |
− | # Code based on Caver plugin
| |
− | # http://loschmidt.chemi.muni.cz/caver/index.php
| |
− | #
| |
− | interior_frame=Frame(group.interior())
| |
− | bar=Scrollbar(interior_frame,)
| |
− | text_holder=Text(interior_frame,yscrollcommand=bar.set,background="#ddddff",font="Times 14")
| |
− | bar.config(command=text_holder.yview)
| |
− | text_holder.insert(END,text)
| |
− | text_holder.pack(side=LEFT,expand="yes",fill="both")
| |
− | bar.pack(side=LEFT,expand="yes",fill="y")
| |
− | interior_frame.pack(expand="yes",fill="both")
| |
− |
| |
− | self.notebook.setnaturalsize()
| |
− | self.showAppModal()
| |
− |
| |
− | def showAppModal(self):
| |
− | #self.dialog.activate() #geometry = 'centerscreenfirst',globalMode = 'nograb')
| |
− | self.dialog.show()
| |
− | def execute(self, result, refocus=True):
| |
− | if result == 'Register APBS Use':
| |
− | import webbrowser
| |
− | webbrowser.open("http://agave.wustl.edu/apbs/download")
| |
− | elif result == 'Register PDB2PQR Use':
| |
− | import webbrowser
| |
− | webbrowser.open("http://www.poissonboltzmann.org/pdb2pqr/d/downloads")
| |
− | elif result == 'Run APBS':
| |
− | good = self.generateApbsInputFile()
| |
− | if not good:
| |
− | if DEBUG:
| |
− | print "ERROR: Something went wrong trying to generate the APBS input file."
| |
− | return False
| |
− | if self.radiobuttons.getvalue() == 'Use another PQR':
| |
− | pass
| |
− | elif self.radiobuttons.getvalue() == 'Use PDB2PQR':
| |
− | if DEBUG: print "GENERATING PQR FILE via PDB2PQR"
| |
− | good = self.generatePdb2pqrPqrFile()
| |
− | if not good:
| |
− | if DEBUG:
| |
− | print "Could not generate PDB2PQR file. generatePdb2pqrPqrFile failed."
| |
− | return False
| |
− | if DEBUG: print "GENERATED"
| |
− | else: # it's one of the pymol-generated options
| |
− | if DEBUG: print "GENERATING PQR FILE via PyMOL"
| |
− | good = self.generatePymolPqrFile()
| |
− | if not good:
| |
− | if DEBUG:
| |
− | print "Could not generate the PyMOL-basd PQR file. generatePyMOLPqrFile failed."
| |
− | return False
| |
− | if DEBUG: print "GENERATED"
| |
− | if os.path.exists(self.pymol_generated_dx_filename.getvalue()):
| |
− | try:
| |
− | os.unlink(self.pymol_generated_dx_filename.getvalue())
| |
− | except:
| |
− | traceback.print_exc()
| |
− | pass
| |
− | #command = "%s %s" % (self.binary.getvalue(),self.pymol_generated_in_filename.getvalue())
| |
− | #os.system(command)
| |
− |
| |
− | #
| |
− | # NOTE: if there are spaces in the directory name that contains pymol_generated_in_filename,
| |
− | # our run command will want to split it up into several arguments if we pass it as a
| |
− | # string. So, we pass it as a tuple.
| |
− | #
| |
− | (retval,progout) = run(self.binary.getvalue(),(self.pymol_generated_in_filename.getvalue(),))
| |
− | if refocus:
| |
− | #
| |
− | # There's a bug in the version of APBS shipped with
| |
− | # Ubuntu 9.10 (version 1.1.0) that causes the name to
| |
− | # become foo-PE0.dx instead of foo.dx. This would
| |
− | # normally only occur in mg-para calcs or MPI versions
| |
− | # of apbs. It's clearly bug-possible, but we will
| |
− | # check for foo-PE0 if we can't find foo.dx.
| |
− | #
| |
− | fname = self.pymol_generated_dx_filename.getvalue()
| |
− | if not os.path.isfile(fname):
| |
− | print "Could not find",fname,"so searching for",
| |
− | fname = '-PE0'.join(os.path.splitext(fname))
| |
− | print fname
| |
− | pymol.cmd.load(fname)
| |
− | self.visualization_group_1.refresh()
| |
− | self.visualization_group_2.refresh()
| |
− | self.notebook.tab('Visualization (1)').focus_set()
| |
− | self.notebook.selectpage('Visualization (1)')
| |
− | elif result == 'Set grid':
| |
− | self.runPsize()
| |
− | else:
| |
− | #
| |
− | # Doing it this way takes care of clicking on the x in the top of the
| |
− | # window, which as result set to None.
| |
− | #
| |
− | global APBS_BINARY_LOCATION, APBS_PSIZE_LOCATION
| |
− | APBS_BINARY_LOCATION = self.binary.getvalue()
| |
− | APBS_PSIZE_LOCATION = self.psize.getvalue()
| |
− | self.quit()
| |
− | def quit(self):
| |
− | self.dialog.destroy() # stops CPU hogging, perhaps fixes Ubuntu bug MGL
| |
− |
| |
− | def runPsize(self):
| |
− | class NoPsize(Exception):
| |
− | pass
| |
− | class NoPDB(Exception):
| |
− | pass
| |
− | try:
| |
− | if not self.psize.valid():
| |
− | raise NoPsize
| |
− | pqr_filename = self.pymol_generated_pqr_filename.getvalue()
| |
− | try:
| |
− | f = file(pqr_filename,'w')
| |
− | f.close()
| |
− | except:
| |
− | raise NoPDB
| |
− | #
| |
− | # Do some magic to load the psize module
| |
− | #
| |
− | import imp
| |
− | f,fname,description = imp.find_module('psize',[os.path.split(self.psize.getvalue())[0]])
| |
− | psize = imp.load_module('psize',f,fname,description)
| |
− | # WLD
| |
− | sel = "((%s) or (neighbor (%s) and hydro))"%(
| |
− | self.selection.getvalue(), self.selection.getvalue())
| |
− |
| |
− | if pymol.cmd.count_atoms( self.selection.getvalue() + " and not alt ''")!=0:
| |
− | print "WARNING: You have alternate locations for some of your atoms!"
| |
− | pymol.cmd.save(pqr_filename,sel)
| |
− | f.close()
| |
− | size = psize.Psize()
| |
− | size.setConstant('gmemceil',int(self.max_mem_allowed.getvalue()))
| |
− | size.runPsize(pqr_filename)
| |
− | coarsedim = size.getCoarseGridDims() # cglen
| |
− | finedim = size.getFineGridDims() # fglen
| |
− | # could use procgrid for multiprocessors
| |
− | finegridpoints = size.getFineGridPoints() # dime
| |
− | center = size.getCenter() # cgcent and fgcent
| |
− | print "APBS's psize.py was used to calculated grid dimensions"
| |
− | except (NoPsize,ImportError):
| |
− | print "This plugin was used to calculated grid dimensions"
| |
− | #
| |
− | # First, we need to get the dimensions of the molecule
| |
− | #
| |
− | # WLD
| |
− | sel = "((%s) or (neighbor (%s) and hydro))"%(
| |
− | self.selection.getvalue(), self.selection.getvalue())
| |
− | model = pymol.cmd.get_model(sel)
| |
− | mins = [None,None,None]
| |
− | maxs = [None,None,None]
| |
− | for a in model.atom:
| |
− | for i in (0,1,2):
| |
− | if mins[i] is None or a.coord[i] < mins[i]:
| |
− | mins[i] = a.coord[i]
| |
− | if maxs[i] is None or a.coord[i] > maxs[i]:
| |
− | maxs[i] = a.coord[i]
| |
− | if None in mins or None in maxs:
| |
− | error_dialog = Pmw.MessageDialog(self.parent,
| |
− | title = 'Error',
| |
− | message_text = "No atoms were in your selection",
| |
− | )
| |
− | junk = error_dialog.activate()
| |
− | return False
| |
− |
| |
− | box_length = [maxs[i] - mins[i] for i in range(3)]
| |
− | center = [(maxs[i] + mins[i])/2.0 for i in range(3)]
| |
− | #
| |
− | # psize expands the molecular dimensions by CFAC (which defaults
| |
− | # to 1.7) for the coarse grid
| |
− | #
| |
− | CFAC = 1.7
| |
− | coarsedim = [length*CFAC for length in box_length]
| |
− |
| |
− | #
| |
− | # psize also does something strange .. it adds a buffer FADD to
| |
− | # the box lengths to get the fine lengths. you'd think it'd also
| |
− | # have FFAC or CADD, but we'll mimic it here. it also has the
| |
− | # requirement that the fine grid lengths must be <= the corase
| |
− | # grid lengths. FADD defaults to 20.
| |
− | #
| |
− | FADD = 20
| |
− | finedim = [min(coarsedim[i],box_length[i] + FADD) for i in range(3)]
| |
− |
| |
− | #
| |
− | # And now the hard part .. setting up the grid points.
| |
− | # From the APBS manual at http://agave.wustl.edu/apbs/doc/html/user-guide/x594.html#dime
| |
− | # we have the formula
| |
− | # n = c*2^(l+1) + 1
| |
− | # where l is the number of levels in the MG hierarchy. The typical
| |
− | # number of levels is 4.
| |
− | #
| |
− | nlev = 4
| |
− | mult_fac = 2**(nlev + 1) # this will typically be 2^5==32
| |
− | # and c must be a non-zero integer
| |
− |
| |
− | # If we didn't have to be c*mult_fac + 1, this is what our grid points
| |
− | # would look like (we use the ceiling to be on the safe side .. it never
| |
− | # hurts to do too much.
| |
− | SPACE = 0.5 # default desired spacing = 0.5A
| |
− | #desired_points = [int(math.ceil(flen / SPACE)) for flen in finedim] # as integers
| |
− | desired_points = [flen / SPACE for flen in finedim] # as floats .. use int(math.ceil(..)) later
| |
− |
| |
− | # Now we set up our cs, taking into account mult_fac
| |
− | # (we use the ceiling to be on the safe side .. it never hurts to do
| |
− | # too much.)
| |
− | cs = [int(math.ceil(dp/mult_fac)) for dp in desired_points]
| |
− |
| |
− | finegridpoints = [mult_fac * c + 1 for c in cs]
| |
− |
| |
− | print "cs",cs
| |
− | print "finedim",finedim
| |
− | print "nlev",nlev
| |
− | print "mult_fac",mult_fac
| |
− | print "finegridpoints",finegridpoints
| |
− |
| |
− | except NoPDB:
| |
− | error_dialog = Pmw.MessageDialog(self.parent,
| |
− | title = 'Error',
| |
− | message_text = "Please set a temporary PDB file location",
| |
− | )
| |
− | junk = error_dialog.activate()
| |
− | return False
| |
− |
| |
− | if (finegridpoints[0]>0) and (finegridpoints[1]>0) and (finegridpoints[2]>0):
| |
− | max_mem_allowed = float(self.max_mem_allowed.getvalue())
| |
− | def memofgrid(finegridpoints):
| |
− | return 200. * float(finegridpoints[0] * finegridpoints[1] * finegridpoints[2]) / 1024. / 1024
| |
− | def gridofmem(mem):
| |
− | return mem * 1024. * 1024. / 200.
| |
− | max_grid_points = gridofmem(max_mem_allowed)
| |
− | print "Estimated memory usage",memofgrid(finegridpoints),'MB out of maximum allowed',max_mem_allowed
| |
− | if memofgrid(finegridpoints) > max_mem_allowed:
| |
− | print "Maximum memory usage exceeded. Old grid dimensions were",finegridpoints
| |
− | product = float(finegridpoints[0] * finegridpoints[1] * finegridpoints[2])
| |
− | factor = pow(max_grid_points/product,0.333333333)
| |
− | finegridpoints[0] = (int(factor*finegridpoints[0]/2))*2+1
| |
− | finegridpoints[1] = (int(factor*finegridpoints[1]/2))*2+1
| |
− | finegridpoints[2] = (int(factor*finegridpoints[2]/2))*2+1
| |
− | print "Fine grid points rounded down from",finegridpoints
| |
− | #
| |
− | # Now we have to make sure that this still fits the equation n = c*2^(l+1) + 1. Here, we'll
| |
− | # just assume nlev == 4, which means that we need to be (some constant times 32) + 1.
| |
− | #
| |
− | # This can be annoying if, e.g., you're trying to set [99, 123, 99] .. it'll get rounded to [99, 127, 99].
| |
− | # First, I'll try to round to the nearest 32*c+1. If that doesn't work, I'll just round down.
| |
− | #
| |
− | new_gp = [0,0,0]
| |
− | for i in 0,1,2:
| |
− | dm = divmod(finegridpoints[i] - 1,32)
| |
− | if dm[1]>16:
| |
− | new_gp[i] = (dm[0]+1)*32+1
| |
− | else:
| |
− | new_gp[i] = (dm[0])*32+1
| |
− | new_prod = new_gp[0]*new_gp[1]*new_gp[2]
| |
− | #print "tried new_prod",new_prod,"max_grid_points",max_grid_points,"small enough?",new_prod <= max_grid_points
| |
− | if new_prod <= max_grid_points:
| |
− | #print "able to round to closest"
| |
− | for i in 0,1,2: finegridpoints[i] = new_gp[i]
| |
− | else:
| |
− | # darn .. have to round down.
| |
− | # Note that this can still fail a little bit .. it can only get you back down to the next multiple <= what was in
| |
− | # finegridpoints. So, if finegridpoints was exactly on a multiple, like (99,129,99), you'll get rounded down to
| |
− | # (99,127,99), which is still just a bit over the default max of 1200000. I think that's ok. It's the rounding error
| |
− | # from int(factor*finegridpoints ..) above, but it'll never be a huge error. If we needed to, we could easily fix this.
| |
− | #
| |
− | #print "rounding down more"
| |
− | for i in 0,1,2:
| |
− | #print finegridpoints[i],divmod(finegridpoints[i] - 1,32),
| |
− | finegridpoints[i] = divmod(finegridpoints[i] - 1,32)[0]*32 + 1
| |
− | print "New grid dimensions are",finegridpoints
| |
− | print " APBS Tools: coarse grid: (%5.3f,%5.3f,%5.3f)"%tuple(coarsedim)
| |
− | self.grid_coarse_x.setvalue(coarsedim[0])
| |
− | self.grid_coarse_y.setvalue(coarsedim[1])
| |
− | self.grid_coarse_z.setvalue(coarsedim[2])
| |
− | print " APBS Tools: fine grid: (%5.3f,%5.3f,%5.3f)"%tuple(finedim)
| |
− | self.grid_fine_x.setvalue(finedim[0])
| |
− | self.grid_fine_y.setvalue(finedim[1])
| |
− | self.grid_fine_z.setvalue(finedim[2])
| |
− | print " APBS Tools: center: (%5.3f,%5.3f,%5.3f)"%tuple(center)
| |
− | self.grid_center_x.setvalue(center[0])
| |
− | self.grid_center_y.setvalue(center[1])
| |
− | self.grid_center_z.setvalue(center[2])
| |
− | print " APBS Tools: fine grid points (%d,%d,%d)"%tuple(finegridpoints)
| |
− | self.grid_points_x.setvalue(finegridpoints[0])
| |
− | self.grid_points_y.setvalue(finegridpoints[1])
| |
− | self.grid_points_z.setvalue(finegridpoints[2])
| |
− |
| |
− | def fixColumns(self,sel):
| |
− | """
| |
− | Make sure that everything fits into the correct columns.
| |
− | This means doing some rounding. It also means getting rid of
| |
− | chain, occupancy and b-factor information.
| |
− | """
| |
− | #pymol.cmd.alter_state(1,'all','(x,y,z)=(int(x*1000)/1000.0, int(y*1000)/1000.0, int(z*1000)/1000.0)')
| |
− | #pymol.cmd.alter_state(1,'all','(x,y,z)=float("%.2f"%x),float("%.2f"%y),float("%.2f"%z)')
| |
− | pymol.cmd.alter_state(1,'all','(x,y,z)=float("%.3f"%x),float("%.3f"%y),float("%.3f"%z)')
| |
− | pymol.cmd.alter(sel,'chain=""')
| |
− | pymol.cmd.alter(sel,'b=0')
| |
− | pymol.cmd.alter(sel,'q=0')
| |
− |
| |
− | def cleanupGeneratedPdbOrPqrFile(self,filename):
| |
− | """
| |
− | More cleanup on PQR files.
| |
− |
| |
− | pdb2pqr will happily write out a file where the coordinate
| |
− | columns overlap if you have -100.something as one of the
| |
− | coordinates, like
| |
− |
| |
− | 90.350 97.230-100.010
| |
− |
| |
− | and so will PyMOL. We can't just assume that it's 0-1
| |
− | because pdb2pqr will debump things and write them out with
| |
− | 3 digits post-decimal. Bleh.
| |
− | """
| |
− | f = file(filename,'r')
| |
− | txt = f.read()
| |
− | f.close()
| |
− | f = file(filename,'w')
| |
− | # APBS accepts whitespace-delimited columns
| |
− | # it doesn't care about non-coord lines, so there's not need to be careful about
| |
− | # where we replace dashes.
| |
− | txt = txt.replace('-',' -')
| |
− | f.write(txt)
| |
− | f.close()
| |
− |
| |
− | def getUnassignedAtomsFromPqr(self,fname):
| |
− | """
| |
− | Here is a comment from Todd Dolinsky via email:
| |
− |
| |
− | There's a couple of different errors which can be printed out via REMARK 5 lines; a good sample is:
| |
− |
| |
− | REMARK 1 PQR file generated by PDB2PQR (Version 1.2.0)
| |
− | REMARK 1
| |
− | REMARK 1 Forcefield Used: charmm
| |
− | REMARK 1
| |
− | REMARK 1 pKas calculated by propka and assigned using pH 7.00
| |
− | REMARK 1
| |
− | REMARK 5 WARNING: Propka determined the following residues to be
| |
− | REMARK 5 in a protonation state not supported by the
| |
− | REMARK 5 charmm forcefield!
| |
− | REMARK 5 All were reset to their standard pH 7.0 state.
| |
− | REMARK 5
| |
− | REMARK 5 CYS 61 2 (negative)
| |
− | REMARK 5 CYS 79 2 (negative)
| |
− | REMARK 5
| |
− | REMARK 5 WARNING: Unable to debump ALA 1 19
| |
− | REMARK 5 WARNING: Unable to debump MET 1 151
| |
− | REMARK 5 WARNING: Unable to debump PRO 1 258
| |
− | REMARK 5 WARNING: Unable to debump GLY 2 8
| |
− | REMARK 5 WARNING: Unable to debump THR 3 118
| |
− | REMARK 5
| |
− | REMARK 5 WARNING: PDB2PQR was unable to assign charges
| |
− | REMARK 5 to the following atoms (omitted below):
| |
− | REMARK 5 6657 O1 in TRS 900
| |
− | REMARK 5 6658 C2 in TRS 900
| |
− | REMARK 5 6659 C3 in TRS 900
| |
− | REMARK 5 6660 C4 in TRS 900
| |
− | REMARK 5 6661 O5 in TRS 900
| |
− | REMARK 5 6662 C6 in TRS 900
| |
− | REMARK 5 6663 O7 in TRS 900
| |
− | REMARK 5 6664 N8 in TRS 900
| |
− | REMARK 5
| |
− | REMARK 6 Total charge on this protein: -1.0000 e
| |
− | REMARK 6
| |
− |
| |
− | If all you care about is the atom number, you can probably regexp match on the 'in' field, something like
| |
− |
| |
− | >>> re.compile('REMARK 5 *(\d)* \w* in').findall(text) # Text contains PQR output string
| |
− | ['6657', '6658', '6659', '6660', '6661', '6662', '6663', '6664']
| |
− |
| |
− | Or you can grab any other useful information - I'd say that using a regular expression like this would be the best option to ensure you don't get false positives.
| |
− | """
| |
− | f = file(fname)
| |
− | unassigned = re.compile('REMARK 5 *(\d+) \w* in').findall(f.read()) # Text contains PQR output string
| |
− | f.close()
| |
− | return '+'.join(unassigned)
| |
− |
| |
− |
| |
− | def generateApbsInputFile(self):
| |
− | if self.checkInput(silent=False):
| |
− | #
| |
− | # set up our variables
| |
− | #
| |
− | pqr_filename = self.getPqrFilename()
| |
− |
| |
− | grid_points = [int(getattr(self,'grid_points_%s'%i).getvalue()) for i in 'x y z'.split()]
| |
− | cglen = [float(getattr(self,'grid_coarse_%s'%i).getvalue()) for i in 'x y z'.split()]
| |
− | fglen = [float(getattr(self,'grid_fine_%s'%i).getvalue()) for i in 'x y z'.split()]
| |
− | cent = [float(getattr(self,'grid_center_%s'%i).getvalue()) for i in 'x y z'.split()]
| |
− |
| |
− | apbs_mode = self.apbs_mode.getvalue()
| |
− | if apbs_mode == 'Nonlinear Poisson-Boltzmann Equation':
| |
− | apbs_mode = 'npbe'
| |
− | else:
| |
− | apbs_mode = 'lpbe'
| |
− |
| |
− | bcflmap = {'Zero': 'zero',
| |
− | 'Single DH sphere': 'sdh',
| |
− | 'Multiple DH spheres': 'mdh',
| |
− | #'Focusing': 'focus',
| |
− | }
| |
− | bcfl = bcflmap[self.bcfl.getvalue()]
| |
− |
| |
− | chgmmap = {'Linear':'spl0',
| |
− | 'Cubic B-splines':'spl2',
| |
− | 'Quintic B-splines':'spl4',
| |
− | }
| |
− | chgm = chgmmap[self.chgm.getvalue()]
| |
− |
| |
− | srfmmap = {'Mol surf for epsilon; inflated VdW for kappa, no smoothing':'mol',
| |
− | 'Same, but with harmonic average smoothing':'smol',
| |
− | 'Cubic spline':'spl2',
| |
− | 'Similar to cubic spline, but with 7th order polynomial':'spl4',}
| |
− |
| |
− | srfm = srfmmap[self.srfm.getvalue()]
| |
− |
| |
− | dx_filename = self.pymol_generated_dx_filename.getvalue()
| |
− | if dx_filename.endswith('.dx'):
| |
− | dx_filename = dx_filename[:-3]
| |
− | apbs_input_text = getApbsInputFile(pqr_filename,
| |
− | grid_points,
| |
− | cglen,
| |
− | fglen,
| |
− | cent,
| |
− | apbs_mode,
| |
− | bcfl,
| |
− | float(self.ion_plus_one_conc.getvalue()), float(self.ion_plus_one_rad.getvalue()),
| |
− | float(self.ion_minus_one_conc.getvalue()), float(self.ion_minus_one_rad.getvalue()),
| |
− | float(self.ion_plus_two_conc.getvalue()), float(self.ion_plus_two_rad.getvalue()),
| |
− | float(self.ion_minus_two_conc.getvalue()), float(self.ion_minus_two_rad.getvalue()),
| |
− | float(self.interior_dielectric.getvalue()), float(self.solvent_dielectric.getvalue()),
| |
− | chgm,
| |
− | srfm,
| |
− | float(self.solvent_radius.getvalue()),
| |
− | float(self.system_temp.getvalue()),
| |
− | float(self.sdens.getvalue()),
| |
− | dx_filename,
| |
− | )
| |
− | if DEBUG:
| |
− | print "GOT THE APBS INPUT FILE"
| |
− |
| |
− | #
| |
− | # write out the input text
| |
− | #
| |
− | try:
| |
− | f = file(self.pymol_generated_in_filename.getvalue(),'w')
| |
− | f.write(apbs_input_text)
| |
− | f.close()
| |
− | except IOError:
| |
− | print "ERROR: Got the input file from APBS, but failed when trying to write to %s" % self.pymol_generated_in_filename.getvalue()
| |
− | return True
| |
− | else:
| |
− | self.checkInput()
| |
− | return False
| |
− |
| |
− | def checkInput(self,silent=False):
| |
− | """If silent is True, we'll just return a True/False value
| |
− | """
| |
− | if not silent:
| |
− | def show_error(message):
| |
− | error_dialog = Pmw.MessageDialog(self.parent,
| |
− | title = 'Error',
| |
− | message_text = message,
| |
− | )
| |
− | junk = error_dialog.activate()
| |
− | else:
| |
− | def show_error(message):
| |
− | pass
| |
− |
| |
− | #
| |
− | # First, check to make sure we have valid locations for apbs and psize
| |
− | #
| |
− | if not self.binary.valid():
| |
− | show_error('Please set the APBS binary location')
| |
− | return False
| |
− | #
| |
− | # If the path to psize is not correct, that's fine .. we'll
| |
− | # do the calculations ourself.
| |
− | #
| |
− |
| |
− | #if not self.psize.valid():
| |
− | # show_error("Please set APBS's psize location")
| |
− | # return False
| |
− |
| |
− | #
| |
− | # Now check the temporary filenames
| |
− | #
| |
− | if self.radiobuttons.getvalue() != 'Use another PQR':
| |
− | if not self.pymol_generated_pqr_filename.getvalue():
| |
− | show_error('Please choose a name for the PyMOL\ngenerated PQR file')
| |
− | return False
| |
− | elif not self.pqr_to_use.valid():
| |
− | show_error('Please select a valid pqr file or tell\nPyMOL to generate one')
| |
− | return False
| |
− | if not self.pymol_generated_pdb_filename.getvalue():
| |
− | show_error('Please choose a name for the PyMOL\ngenerated PDB file')
| |
− | return False
| |
− | if not self.pymol_generated_dx_filename.getvalue():
| |
− | show_error('Please choose a name for the PyMOL\ngenerated DX file')
| |
− | return False
| |
− | if not self.pymol_generated_in_filename.getvalue():
| |
− | show_error('Please choose a name for the PyMOL\ngenerated APBS input file')
| |
− | return False
| |
− | if not self.map.getvalue():
| |
− | show_error('Please choose a name for the generated map.')
| |
− | return False
| |
− |
| |
− |
| |
− | #
| |
− | # Now, the ions
| |
− | #
| |
− | for sign in 'plus minus'.split():
| |
− | for value in 'one two'.split():
| |
− | for parm in 'conc rad'.split():
| |
− | if not getattr(self,'ion_%s_%s_%s'%(sign,value,parm)).valid():
| |
− | show_error('Please correct Ion concentrations and radii')
| |
− | return False
| |
− | #
| |
− | # Now the grid
| |
− | #
| |
− | for grid_type in 'coarse fine points center'.split():
| |
− | for coord in 'x y z'.split():
| |
− | if not getattr(self,'grid_%s_%s'%(grid_type,coord)).valid():
| |
− | show_error('Please correct grid dimensions\nby clicking on the "Set grid" button')
| |
− | return False
| |
− |
| |
− | #
| |
− | # Now other easy things
| |
− | #
| |
− | for (message, thing) in (('solvent dielectric',self.solvent_dielectric),
| |
− | ('protein dielectric',self.interior_dielectric),
| |
− | ('solvent radius',self.solvent_radius),
| |
− | ('system temperature',self.system_temp),
| |
− | ('sdens',self.sdens),
| |
− | ):
| |
− | if not thing.valid():
| |
− | show_error('Please correct %s'%message)
| |
− | return False
| |
− |
| |
− | return True
| |
− |
| |
− |
| |
− | def generatePdb2pqrPqrFile(self,silent=False):
| |
− | """use pdb2pqr to generate a pqr file
| |
− | """
| |
− | if not silent:
| |
− | def show_error(message):
| |
− | error_dialog = Pmw.MessageDialog(self.parent,
| |
− | title = 'Error',
| |
− | message_text = message,
| |
− | )
| |
− | junk = error_dialog.activate()
| |
− | else:
| |
− | def show_error(message):
| |
− | pass
| |
− |
| |
− | #
| |
− | # First, generate a PDB file
| |
− | #
| |
− | pdb_filename = self.pymol_generated_pdb_filename.getvalue()
| |
− | try:
| |
− | f = file(pdb_filename,'w')
| |
− | f.close()
| |
− | except:
| |
− | show_error('Please set a temporary PDB file location that you have permission to edit')
| |
− | return False
| |
− | # copied from WLD code
| |
− | sel = "((%s) or (neighbor (%s) and hydro))"%(
| |
− | self.selection.getvalue(), self.selection.getvalue())
| |
− | self.fixColumns(sel)
| |
− | pymol.cmd.save(pdb_filename,sel)
| |
− | self.cleanupGeneratedPdbOrPqrFile(pdb_filename)
| |
− |
| |
− | #
| |
− | # Now, generate a PQR file
| |
− | #
| |
− | ## command_line = '%s %s %s %s'%(self.pdb2pqr.getvalue(),
| |
− | ## self.pdb2pqr_options.getvalue(),
| |
− | ## pdb_filename,
| |
− | ## self.pymol_generated_pqr_filename.getvalue(),
| |
− | ## )
| |
− | ## print "RAN",command_line
| |
− | ## result = os.system(command_line)
| |
− | #
| |
− | # We have to be a little cute about args, because pdb2pqr_options could have several options in it.
| |
− | args = '%s %s %s' %(self.pdb2pqr_options.getvalue(),
| |
− | pdb_filename,
| |
− | self.pymol_generated_pqr_filename.getvalue(),
| |
− | )
| |
− | (retval,progout) = run(self.pdb2pqr.getvalue(),args)
| |
− |
| |
− |
| |
− | if retval != 0:
| |
− | show_error('Could not run pdb2pqr: %s %s\n\n%s'%(self.pdb2pqr.getvalue(),
| |
− | args,
| |
− | progout)
| |
− | )
| |
− | return False
| |
− | self.cleanupGeneratedPdbOrPqrFile(self.pymol_generated_pqr_filename.getvalue())
| |
− | unassigned_atoms = self.getUnassignedAtomsFromPqr(self.pymol_generated_pqr_filename.getvalue())
| |
− | if unassigned_atoms:
| |
− | pymol.cmd.select('unassigned','ID %s'%unassigned_atoms)
| |
− | message_text = "Unable to assign parameters for the %s atoms in selection 'unassigned'.\nPlease either remove these unassigned atoms and re-start the calculation\nor fix their parameters in the generated PQR file and run the calculation\nusing the modified PQR file (select 'Use another PQR' in 'Main')."%len(unassigned_atoms.split('+'))
| |
− | print "Unassigned atom IDs",unassigned_atoms
| |
− | show_error(message_text)
| |
− | return False
| |
− | return True
| |
− |
| |
− |
| |
− | def generatePymolPqrFile(self):
| |
− | """generate a pqr file from pymol
| |
− |
| |
− | This will also call through to champ to set the Hydrogens and charges
| |
− | if it needs to. If it does that, it may change the value self.selection
| |
− | to take the new Hydrogens into account.
| |
− |
| |
− | To make it worse, APBS seems to freak out when there are chain ids. So,
| |
− | this gets rid of the chain ids.
| |
− |
| |
− | """
| |
− | # CHAMP will break in many cases if retain_order is set. So,
| |
− | # we unset it here and reset it later. Note that it's fine to
| |
− | # reset it before things are written out.
| |
− | ret_order = pymol.cmd.get('retain_order')
| |
− | pymol.cmd.set('retain_order',0)
| |
− |
| |
− | # WLD
| |
− | sel = "((%s) or (neighbor (%s) and hydro))"%(
| |
− | self.selection.getvalue(), self.selection.getvalue())
| |
− |
| |
− | pqr_filename = self.getPqrFilename()
| |
− | try:
| |
− | f = file(pqr_filename,'w')
| |
− | f.close()
| |
− | except:
| |
− | error_dialog = Pmw.MessageDialog(self.parent,
| |
− | title = 'Error',
| |
− | message_text = "Could not write PQR file.\nPlease check that temporary PQR filename is valid.",
| |
− | )
| |
− | junk = error_dialog.activate()
| |
− | return False
| |
− |
| |
− | # PyMOL + champ == pqr
| |
− | from chempy.champ import assign
| |
− | if self.radiobuttons.getvalue() == 'Use PyMOL generated PQR and PyMOL generated Hydrogens and termini':
| |
− | pymol.cmd.remove('hydro and %s'%sel)
| |
− | assign.missing_c_termini(sel)
| |
− | assign.formal_charges(sel)
| |
− | pymol.cmd.h_add(sel)
| |
− | # WLD (code now unnecessary)
| |
− | # new_hydros = '(hydro and neighbor %s)'%sel
| |
− | # sel = '%s or %s' % (sel,new_hydros)
| |
− | assign.amber99(sel)
| |
− | pymol.cmd.set('retain_order',ret_order)
| |
− |
| |
− | # WLD (code now unnecessary)
| |
− | # if not self.selection.getvalue() in '(all) all'.split():
| |
− | # self.selection.setvalue(sel)
| |
− |
| |
− | #
| |
− | # Get rid of chain information
| |
− | #
| |
− | # WLD -- PyMOL now does this automatically with PQR files
| |
− | # pymol.cmd.alter(sel,'chain = ""')
| |
− | self.fixColumns(sel)
| |
− | pymol.cmd.save(pqr_filename,sel)
| |
− | self.cleanupGeneratedPdbOrPqrFile(pqr_filename)
| |
− | missed_count = pymol.cmd.count_atoms("("+sel+") and flag 23")
| |
− | if missed_count > 0:
| |
− | pymol.cmd.select("unassigned","("+sel+") and flag 23")
| |
− | error_dialog = Pmw.MessageDialog(self.parent,
| |
− | title = 'Error',
| |
− | message_text = "Unable to assign parameters for the %s atoms in selection 'unassigned'.\nPlease either remove these unassigned atoms and re-start the calculation\nor fix their parameters in the generated PQR file and run the calculation\nusing the modified PQR file (select 'Use another PQR' in 'Main')."%missed_count,
| |
− | )
| |
− | junk = error_dialog.activate()
| |
− | return False
| |
− | return True
| |
− |
| |
− |
| |
− | ############################################################
| |
− | ############################################################
| |
− | ############################################################
| |
− | ## ##
| |
− | ## PmwExtensions ##
| |
− | ## ##
| |
− | ############################################################
| |
− | ############################################################
| |
− | ############################################################
| |
− |
| |
− | """
| |
− | This contains all of the visualization groups that we'll use for our
| |
− | PMW interface.
| |
− | """
| |
− |
| |
− | #
| |
− | # Generically useful PMW extensions
| |
− |
| |
− | import os,fnmatch,time
| |
− | import Tkinter,Pmw
| |
− | #Pmw.setversion("0.8.5")
| |
− |
| |
− | #
| |
− | # The classes PmwFileDialog and PmwExistingFileDialog and the _errorpop function
| |
− | # are taken from the Pmw contrib directory. The attribution given in that file
| |
− | # is:
| |
− | ################################################################################
| |
− | # Filename dialogs using Pmw
| |
− | #
| |
− | # (C) Rob W.W. Hooft, Nonius BV, 1998
| |
− | #
| |
− | # Modifications:
| |
− | #
| |
− | # J. Willem M. Nissink, Cambridge Crystallographic Data Centre, 8/2002
| |
− | # Added optional information pane at top of dialog; if option
| |
− | # 'info' is specified, the text given will be shown (in blue).
| |
− | # Modified example to show both file and directory-type dialog
| |
− | #
| |
− | # No Guarantees. Distribute Freely.
| |
− | # Please send bug-fixes/patches/features to <r.hooft@euromail.com>
| |
− | #
| |
− | ################################################################################
| |
− |
| |
− | def _errorpop(master,text):
| |
− | d=Pmw.MessageDialog(master,
| |
− | title="Error",
| |
− | message_text=text,
| |
− | buttons=("OK",))
| |
− | d.component('message').pack(ipadx=15,ipady=15)
| |
− | d.activate()
| |
− | d.destroy()
| |
− |
| |
− | class PmwFileDialog(Pmw.Dialog):
| |
− | """File Dialog using Pmw"""
| |
− | def __init__(self, parent = None, **kw):
| |
− | # Define the megawidget options.
| |
− | optiondefs = (
| |
− | ('filter', '*', self.newfilter),
| |
− | ('directory', os.getcwd(), self.newdir),
| |
− | ('filename', '', self.newfilename),
| |
− | ('historylen',10, None),
| |
− | ('command', None, None),
| |
− | ('info', None, None),
| |
− | )
| |
− | self.defineoptions(kw, optiondefs)
| |
− | # Initialise base class (after defining options).
| |
− | Pmw.Dialog.__init__(self, parent)
| |
− |
| |
− | self.withdraw()
| |
− |
| |
− | # Create the components.
| |
− | interior = self.interior()
| |
− |
| |
− | if self['info'] is not None:
| |
− | rowoffset=1
| |
− | dn = self.infotxt()
| |
− | dn.grid(row=0,column=0,columnspan=2,padx=3,pady=3)
| |
− | else:
| |
− | rowoffset=0
| |
− |
| |
− | dn = self.mkdn()
| |
− | dn.grid(row=0+rowoffset,column=0,columnspan=2,padx=3,pady=3)
| |
− | del dn
| |
− |
| |
− | # Create the directory list component.
| |
− | dnb = self.mkdnb()
| |
− | dnb.grid(row=1+rowoffset,column=0,sticky='news',padx=3,pady=3)
| |
− | del dnb
| |
− |
| |
− | # Create the filename list component.
| |
− | fnb = self.mkfnb()
| |
− | fnb.grid(row=1+rowoffset,column=1,sticky='news',padx=3,pady=3)
| |
− | del fnb
| |
− |
| |
− | # Create the filter entry
| |
− | ft = self.mkft()
| |
− | ft.grid(row=2+rowoffset,column=0,columnspan=2,padx=3,pady=3)
| |
− | del ft
| |
− |
| |
− | # Create the filename entry
| |
− | fn = self.mkfn()
| |
− | fn.grid(row=3+rowoffset,column=0,columnspan=2,padx=3,pady=3)
| |
− | fn.bind('<Return>',self.okbutton)
| |
− | del fn
| |
− |
| |
− | # Buttonbox already exists
| |
− | bb=self.component('buttonbox')
| |
− | bb.add('OK',command=self.okbutton)
| |
− | bb.add('Cancel',command=self.cancelbutton)
| |
− | del bb
| |
− |
| |
− | Pmw.alignlabels([self.component('filename'),
| |
− | self.component('filter'),
| |
− | self.component('dirname')])
| |
− |
| |
− | def infotxt(self):
| |
− | """ Make information block component at the top """
| |
− | return self.createcomponent(
| |
− | 'infobox',
| |
− | (), None,
| |
− | Tkinter.Label, (self.interior(),),
| |
− | width=51,
| |
− | relief='groove',
| |
− | foreground='darkblue',
| |
− | justify='left',
| |
− | text=self['info']
| |
− | )
| |
− |
| |
− | def mkdn(self):
| |
− | """Make directory name component"""
| |
− | return self.createcomponent(
| |
− | 'dirname',
| |
− | (), None,
| |
− | Pmw.ComboBox, (self.interior(),),
| |
− | entryfield_value=self['directory'],
| |
− | entryfield_entry_width=40,
| |
− | entryfield_validate=self.dirvalidate,
| |
− | selectioncommand=self.setdir,
| |
− | labelpos='w',
| |
− | label_text='Directory:')
| |
− |
| |
− | def mkdnb(self):
| |
− | """Make directory name box"""
| |
− | return self.createcomponent(
| |
− | 'dirnamebox',
| |
− | (), None,
| |
− | Pmw.ScrolledListBox, (self.interior(),),
| |
− | label_text='directories',
| |
− | labelpos='n',
| |
− | hscrollmode='none',
| |
− | dblclickcommand=self.selectdir)
| |
− |
| |
− | def mkft(self):
| |
− | """Make filter"""
| |
− | return self.createcomponent(
| |
− | 'filter',
| |
− | (), None,
| |
− | Pmw.ComboBox, (self.interior(),),
| |
− | entryfield_value=self['filter'],
| |
− | entryfield_entry_width=40,
| |
− | selectioncommand=self.setfilter,
| |
− | labelpos='w',
| |
− | label_text='Filter:')
| |
− |
| |
− | def mkfnb(self):
| |
− | """Make filename list box"""
| |
− | return self.createcomponent(
| |
− | 'filenamebox',
| |
− | (), None,
| |
− | Pmw.ScrolledListBox, (self.interior(),),
| |
− | label_text='files',
| |
− | labelpos='n',
| |
− | hscrollmode='none',
| |
− | selectioncommand=self.singleselectfile,
| |
− | dblclickcommand=self.selectfile)
| |
− |
| |
− | def mkfn(self):
| |
− | """Make file name entry"""
| |
− | return self.createcomponent(
| |
− | 'filename',
| |
− | (), None,
| |
− | Pmw.ComboBox, (self.interior(),),
| |
− | entryfield_value=self['filename'],
| |
− | entryfield_entry_width=40,
| |
− | entryfield_validate=self.filevalidate,
| |
− | selectioncommand=self.setfilename,
| |
− | labelpos='w',
| |
− | label_text='Filename:')
| |
− |
| |
− | def dirvalidate(self,string):
| |
− | if os.path.isdir(string):
| |
− | return Pmw.OK
| |
− | else:
| |
− | return Pmw.PARTIAL
| |
− |
| |
− | def filevalidate(self,string):
| |
− | if string=='':
| |
− | return Pmw.PARTIAL
| |
− | elif os.path.isfile(string):
| |
− | return Pmw.OK
| |
− | elif os.path.exists(string):
| |
− | return Pmw.PARTIAL
| |
− | else:
| |
− | return Pmw.OK
| |
− |
| |
− | def okbutton(self):
| |
− | """OK action: user thinks he has input valid data and wants to
| |
− | proceed. This is also called by <Return> in the filename entry"""
| |
− | fn=self.component('filename').get()
| |
− | self.setfilename(fn)
| |
− | if self.validate(fn):
| |
− | self.canceled=0
| |
− | self.deactivate()
| |
− |
| |
− | def cancelbutton(self):
| |
− | """Cancel the operation"""
| |
− | self.canceled=1
| |
− | self.deactivate()
| |
− |
| |
− | def tidy(self,w,v):
| |
− | """Insert text v into the entry and at the top of the list of
| |
− | the combobox w, remove duplicates"""
| |
− | if not v:
| |
− | return
| |
− | entry=w.component('entry')
| |
− | entry.delete(0,'end')
| |
− | entry.insert(0,v)
| |
− | list=w.component('scrolledlist')
| |
− | list.insert(0,v)
| |
− | index=1
| |
− | while index<list.index('end'):
| |
− | k=list.get(index)
| |
− | if k==v or index>self['historylen']:
| |
− | list.delete(index)
| |
− | else:
| |
− | index=index+1
| |
− | w.checkentry()
| |
− |
| |
− | def setfilename(self,value):
| |
− | if not value:
| |
− | return
| |
− | value=os.path.join(self['directory'],value)
| |
− | dir,fil=os.path.split(value)
| |
− | self.configure(directory=dir,filename=value)
| |
− |
| |
− | c=self['command']
| |
− | if callable(c):
| |
− | c()
| |
− |
| |
− | def newfilename(self):
| |
− | """Make sure a newly set filename makes it into the combobox list"""
| |
− | self.tidy(self.component('filename'),self['filename'])
| |
− |
| |
− | def setfilter(self,value):
| |
− | self.configure(filter=value)
| |
− |
| |
− | def newfilter(self):
| |
− | """Make sure a newly set filter makes it into the combobox list"""
| |
− | self.tidy(self.component('filter'),self['filter'])
| |
− | self.fillit()
| |
− |
| |
− | def setdir(self,value):
| |
− | self.configure(directory=value)
| |
− |
| |
− | def newdir(self):
| |
− | """Make sure a newly set dirname makes it into the combobox list"""
| |
− | self.tidy(self.component('dirname'),self['directory'])
| |
− | self.fillit()
| |
− |
| |
− | def singleselectfile(self):
| |
− | """Single click in file listbox. Move file to "filename" combobox"""
| |
− | cs=self.component('filenamebox').curselection()
| |
− | if cs!=():
| |
− | value=self.component('filenamebox').get(cs)
| |
− | self.setfilename(value)
| |
− |
| |
− | def selectfile(self):
| |
− | """Take the selected file from the filename, normalize it, and OK"""
| |
− | self.singleselectfile()
| |
− | value=self.component('filename').get()
| |
− | self.setfilename(value)
| |
− | if value:
| |
− | self.okbutton()
| |
− |
| |
− | def selectdir(self):
| |
− | """Take selected directory from the dirnamebox into the dirname"""
| |
− | cs=self.component('dirnamebox').curselection()
| |
− | if cs!=():
| |
− | value=self.component('dirnamebox').get(cs)
| |
− | dir=self['directory']
| |
− | if not dir:
| |
− | dir=os.getcwd()
| |
− | if value:
| |
− | if value=='..':
| |
− | dir=os.path.split(dir)[0]
| |
− | else:
| |
− | dir=os.path.join(dir,value)
| |
− | self.configure(directory=dir)
| |
− | self.fillit()
| |
− |
| |
− | def askfilename(self,directory=None,filter=None):
| |
− | """The actual client function. Activates the dialog, and
| |
− | returns only after a valid filename has been entered
| |
− | (return value is that filename) or when canceled (return
| |
− | value is None)"""
| |
− | if directory!=None:
| |
− | self.configure(directory=directory)
| |
− | if filter!=None:
| |
− | self.configure(filter=filter)
| |
− | self.fillit()
| |
− | self.canceled=1 # Needed for when user kills dialog window
| |
− | self.activate()
| |
− | if self.canceled:
| |
− | return None
| |
− | else:
| |
− | return self.component('filename').get()
| |
− |
| |
− | lastdir=""
| |
− | lastfilter=None
| |
− | lasttime=0
| |
− | def fillit(self):
| |
− | """Get the directory list and show it in the two listboxes"""
| |
− | # Do not run unnecesarily
| |
− | if self.lastdir==self['directory'] and self.lastfilter==self['filter'] and self.lasttime>os.stat(self.lastdir)[8]:
| |
− | return
| |
− | self.lastdir=self['directory']
| |
− | self.lastfilter=self['filter']
| |
− | self.lasttime=time.time()
| |
− | dir=self['directory']
| |
− | if not dir:
| |
− | dir=os.getcwd()
| |
− | dirs=['..']
| |
− | files=[]
| |
− | try:
| |
− | fl=os.listdir(dir)
| |
− | fl.sort()
| |
− | except os.error,arg:
| |
− | if arg[0] in (2,20):
| |
− | return
| |
− | raise
| |
− | for f in fl:
| |
− | if os.path.isdir(os.path.join(dir,f)):
| |
− | dirs.append(f)
| |
− | else:
| |
− | filter=self['filter']
| |
− | if not filter:
| |
− | filter='*'
| |
− | if fnmatch.fnmatch(f,filter):
| |
− | files.append(f)
| |
− | self.component('filenamebox').setlist(files)
| |
− | self.component('dirnamebox').setlist(dirs)
| |
− |
| |
− | def validate(self,filename):
| |
− | """Validation function. Should return 1 if the filename is valid,
| |
− | 0 if invalid. May pop up dialogs to tell user why. Especially
| |
− | suited to subclasses: i.e. only return 1 if the file does/doesn't
| |
− | exist"""
| |
− | return 1
| |
− |
| |
− |
| |
− | class PmwExistingFileDialog(PmwFileDialog):
| |
− | def filevalidate(self,string):
| |
− | if os.path.isfile(string):
| |
− | return Pmw.OK
| |
− | else:
| |
− | return Pmw.PARTIAL
| |
− |
| |
− | def validate(self,filename):
| |
− | if os.path.isfile(filename):
| |
− | return 1
| |
− | elif os.path.exists(filename):
| |
− | _errorpop(self.interior(),"This is not a plain file")
| |
− | return 0
| |
− | else:
| |
− | _errorpop(self.interior(),"Please select an existing file")
| |
− | return 0
| |
− |
| |
− | class FileDialogButtonClassFactory:
| |
− | def get(fn,filter='*'):
| |
− | """This returns a FileDialogButton class that will
| |
− | call the specified function with the resulting file.
| |
− | """
| |
− | class FileDialogButton(Tkinter.Button):
| |
− | # This is just an ordinary button with special colors.
| |
− |
| |
− | def __init__(self, master=None, cnf={}, **kw):
| |
− | '''when we get a file, we call fn(filename)'''
| |
− | self.fn = fn
| |
− | self.__toggle = 0
| |
− | apply(Tkinter.Button.__init__, (self, master, cnf), kw)
| |
− | self.configure(command=self.set)
| |
− | def set(self):
| |
− | fd = PmwFileDialog(self.master,filter=filter)
| |
− | fd.title('Please choose a file')
| |
− | n=fd.askfilename()
| |
− | if n is not None:
| |
− | self.fn(n)
| |
− | return FileDialogButton
| |
− | get = staticmethod(get)
| |
− |
| |
− | ############################################################
| |
− | ############################################################
| |
− | ############################################################
| |
− | ## ##
| |
− | ## PmwGroups ##
| |
− | ## ##
| |
− | ############################################################
| |
− | ############################################################
| |
− | ############################################################
| |
− | class VisualizationGroup(Pmw.Group):
| |
− | def __init__(self,*args,**kwargs):
| |
− | my_options = 'visgroup_num'.split()
| |
− | for option in my_options:
| |
− | # use these options as attributes of this class
| |
− | # and remove them from the kwargs dict before
| |
− | # passing on to Pmw.Group.__init__().
| |
− | setattr(self,option,kwargs.pop(option))
| |
− | kwargs['tag_text'] = kwargs['tag_text'] + ' (%s)'%self.visgroup_num
| |
− | Pmw.Group.__init__(self,*args,**kwargs)
| |
− | self.refresh()
| |
− | self.show_ms = False
| |
− | self.show_pi = False
| |
− | self.show_ni = False
| |
− | def refresh(self):
| |
− | things_to_kill = 'error_label update_buttonbox mm_group ms_group pi_group ni_group'.split()
| |
− | for thing in things_to_kill:
| |
− | try:
| |
− | getattr(self,thing).destroy()
| |
− | #print "destroyed",thing
| |
− | except AttributeError:
| |
− | #print "couldn't destroy",thing
| |
− |
| |
− | # note: this attributeerror will also hit if getattr(self,thing) misses.
| |
− | # another note: both halves of the if/else make an update_buttonbox.
| |
− | # if you rename the one in the top half to something else, you'll cause nasty Pmw errors.
| |
− | pass
| |
− | if [i for i in pymol.cmd.get_names() if pymol.cmd.get_type(i)=='object:map'] and [i for i in pymol.cmd.get_names() if pymol.cmd.get_type(i)=='object:molecule']:
| |
− | self.mm_group = Pmw.Group(self.interior(),tag_text = 'Maps and Molecules')
| |
− | self.map = Pmw.OptionMenu(self.mm_group.interior(),
| |
− | labelpos = 'w',
| |
− | label_text = 'Map',
| |
− | items = [i for i in pymol.cmd.get_names() if pymol.cmd.get_type(i)=='object:map'],
| |
− | )
| |
− | self.map.pack(padx=4,side=LEFT)
| |
− |
| |
− | self.molecule = Pmw.OptionMenu(self.mm_group.interior(),
| |
− | labelpos = 'w',
| |
− | label_text = 'Molecule',
| |
− | items = [i for i in pymol.cmd.get_names() if pymol.cmd.get_type(i)=='object:molecule'],
| |
− | )
| |
− | self.molecule.pack(padx=4,side=LEFT)
| |
− | self.update_buttonbox = Pmw.ButtonBox(self.mm_group.interior(), padx=0)
| |
− | self.update_buttonbox.pack(side=LEFT)
| |
− | self.update_buttonbox.add('Update',command=self.refresh)
| |
− | self.mm_group.pack(fill = 'both', expand = 1, padx = 4, pady = 5, side=TOP)
| |
− |
| |
− | self.ms_group = Pmw.Group(self.interior(),tag_text='Molecular Surface')
| |
− | self.ms_buttonbox = Pmw.ButtonBox(self.ms_group.interior(), padx=0)
| |
− | self.ms_buttonbox.pack()
| |
− | self.ms_buttonbox.add('Show',command=self.showMolSurface)
| |
− | self.ms_buttonbox.add('Hide',command=self.hideMolSurface)
| |
− | self.ms_buttonbox.add('Update',command=self.updateMolSurface)
| |
− | self.ms_buttonbox.alignbuttons()
| |
− | self.surface_solvent = IntVar()
| |
− | self.surface_solvent.set(APBSTools2.defaults['surface_solvent'])
| |
− | self.sol_checkbutton = Checkbutton(self.ms_group.interior(),
| |
− | text = "Solvent accessible surface",
| |
− | variable = self.surface_solvent)
| |
− | self.sol_checkbutton.pack()
| |
− | self.potential_at_sas = IntVar()
| |
− | self.potential_at_sas.set(APBSTools2.defaults['potential_at_sas'])
| |
− | self.pot_checkbutton = Checkbutton(self.ms_group.interior(),
| |
− | text = "Color by potential on sol. acc. surf.",
| |
− | variable = self.potential_at_sas)
| |
− | self.pot_checkbutton.pack()
| |
− | self.mol_surf_low = Pmw.Counter(self.ms_group.interior(),
| |
− | labelpos = 'w',
| |
− | label_text = 'Low',
| |
− | orient = 'vertical',
| |
− | entry_width = 4,
| |
− | entryfield_value = -1,
| |
− | datatype = 'real',
| |
− | entryfield_validate = {'validator' : 'real'},
| |
− | )
| |
− | self.mol_surf_middle = Pmw.Counter(self.ms_group.interior(),
| |
− | labelpos = 'w',
| |
− | label_text = 'Middle',
| |
− | orient = 'vertical',
| |
− | entry_width = 4,
| |
− | entryfield_value = 0,
| |
− | datatype = 'real',
| |
− | entryfield_validate = {'validator' : 'real'}
| |
− | )
| |
− | self.mol_surf_high = Pmw.Counter(self.ms_group.interior(),
| |
− | labelpos = 'w',
| |
− | label_text = 'High',
| |
− | orient = 'vertical',
| |
− | entry_width = 4,
| |
− | entryfield_value = 1,
| |
− | datatype = 'real',
| |
− | entryfield_validate = {'validator' : 'real'}
| |
− | )
| |
− | bars = (self.mol_surf_low,self.mol_surf_middle,self.mol_surf_high)
| |
− | Pmw.alignlabels(bars)
| |
− | for bar in bars: bar.pack(side=LEFT)
| |
− | self.ms_group.pack(fill = 'both', expand = 1, padx = 4, pady = 5, side=LEFT)
| |
− |
| |
− | self.pi_group = Pmw.Group(self.interior(),tag_text='Positive Isosurface')
| |
− | self.pi_buttonbox = Pmw.ButtonBox(self.pi_group.interior(), padx=0)
| |
− | self.pi_buttonbox.pack()
| |
− | self.pi_buttonbox.add('Show',command=self.showPosSurface)
| |
− | self.pi_buttonbox.add('Hide',command=self.hidePosSurface)
| |
− | self.pi_buttonbox.add('Update',command=self.updatePosSurface)
| |
− | self.pi_buttonbox.alignbuttons()
| |
− | self.pos_surf_val = Pmw.Counter(self.pi_group.interior(),
| |
− | labelpos = 'w',
| |
− | label_text = 'Contour (kT/e)',
| |
− | orient = 'vertical',
| |
− | entry_width = 4,
| |
− | entryfield_value = 1,
| |
− | datatype = 'real',
| |
− | entryfield_validate = {'validator' : 'real', 'min':0}
| |
− | )
| |
− | self.pos_surf_val.pack(side=LEFT)
| |
− | self.pi_group.pack(fill = 'both', expand = 1, padx = 4, pady = 5, side=LEFT)
| |
− |
| |
− | self.ni_group = Pmw.Group(self.interior(),tag_text='Negative Isosurface')
| |
− | self.ni_buttonbox = Pmw.ButtonBox(self.ni_group.interior(), padx=0)
| |
− | self.ni_buttonbox.pack()
| |
− | self.ni_buttonbox.add('Show',command=self.showNegSurface)
| |
− | self.ni_buttonbox.add('Hide',command=self.hideNegSurface)
| |
− | self.ni_buttonbox.add('Update',command=self.updateNegSurface)
| |
− | self.ni_buttonbox.alignbuttons()
| |
− | self.neg_surf_val = Pmw.Counter(self.ni_group.interior(),
| |
− | labelpos = 'w',
| |
− | label_text = 'Contour (kT/e)',
| |
− | orient = 'vertical',
| |
− | entry_width = 4,
| |
− | entryfield_value = -1,
| |
− | datatype = 'real',
| |
− | entryfield_validate = {'validator' : 'real', 'max':0}
| |
− | )
| |
− | self.neg_surf_val.pack(side=LEFT)
| |
− | self.ni_group.pack(fill = 'both', expand = 1, padx = 4, pady = 5, side=LEFT)
| |
− |
| |
− | else:
| |
− | self.error_label = Tkinter.Label(self.interior(),
| |
− | pady = 10,
| |
− | justify=LEFT,
| |
− | text = '''You must have at least a molecule and a map loaded.
| |
− | If you have a molecule and a map loaded, please click "Update"''',
| |
− | )
| |
− | self.error_label.pack()
| |
− | self.update_buttonbox = Pmw.ButtonBox(self.interior(), padx=0)
| |
− | self.update_buttonbox.pack()
| |
− | self.update_buttonbox.add('Update',command=self.refresh)
| |
− |
| |
− | def showMolSurface(self):
| |
− | self.updateMolSurface()
| |
− |
| |
− | def hideMolSurface(self):
| |
− | pymol.cmd.hide('surface',self.molecule.getvalue())
| |
− |
| |
− | def getRampName(self):
| |
− | #return 'e_lvl'
| |
− | idx = [i for i in pymol.cmd.get_names() if pymol.cmd.get_type(i)=='object:molecule'].index(self.molecule.getvalue())
| |
− | return '_'.join(('e_lvl',str(idx),str(self.visgroup_num)))
| |
− |
| |
− | def getIsoPosName(self):
| |
− | idx = [i for i in pymol.cmd.get_names() if pymol.cmd.get_type(i)=='object:map'].index(self.map.getvalue())
| |
− | return '_'.join(('iso_pos',str(idx),str(self.visgroup_num)))
| |
− |
| |
− | def getIsoNegName(self):
| |
− | idx = [i for i in pymol.cmd.get_names() if pymol.cmd.get_type(i)=='object:map'].index(self.map.getvalue())
| |
− | return '_'.join(('iso_neg',str(idx),str(self.visgroup_num)))
| |
− |
| |
− | def updateMolSurface(self):
| |
− | molecule_name = self.molecule.getvalue()
| |
− | ramp_name = self.getRampName()
| |
− | map_name = self.map.getvalue()
| |
− | low = float(self.mol_surf_low.getvalue())
| |
− | mid = float(self.mol_surf_middle.getvalue())
| |
− | high = float(self.mol_surf_high.getvalue())
| |
− | range = [low,mid,high]
| |
− | print " APBS Tools: range is",range
| |
− | pymol.cmd.delete(ramp_name)
| |
− | pymol.cmd.ramp_new(ramp_name,map_name,range)
| |
− | pymol.cmd.set('surface_color',ramp_name,molecule_name)
| |
− | if self.surface_solvent.get()==1:
| |
− | pymol.cmd.set('surface_solvent',1,molecule_name)
| |
− | pymol.cmd.set('surface_ramp_above_mode',0,molecule_name)
| |
− | else:
| |
− | pymol.cmd.set('surface_solvent',0,molecule_name)
| |
− | pymol.cmd.set('surface_ramp_above_mode',self.potential_at_sas.get(),molecule_name)
| |
− | pymol.cmd.show('surface',molecule_name)
| |
− | pymol.cmd.refresh()
| |
− | pymol.cmd.recolor(molecule_name)
| |
− |
| |
− | def showPosSurface(self):
| |
− | self.updatePosSurface()
| |
− | def hidePosSurface(self):
| |
− | pymol.cmd.hide('everything',self.getIsoPosName())
| |
− | def updatePosSurface(self):
| |
− | pymol.cmd.delete(self.getIsoPosName())
| |
− | pymol.cmd.isosurface(self.getIsoPosName(),self.map.getvalue(),float(self.pos_surf_val.getvalue()))
| |
− | pymol.cmd.color('blue',self.getIsoPosName())
| |
− | pymol.cmd.show('everything',self.getIsoPosName())
| |
− | def showNegSurface(self):
| |
− | self.updateNegSurface()
| |
− | def hideNegSurface(self):
| |
− | pymol.cmd.hide('everything',self.getIsoNegName())
| |
− | def updateNegSurface(self):
| |
− | pymol.cmd.delete(self.getIsoNegName())
| |
− | pymol.cmd.isosurface(self.getIsoNegName(),self.map.getvalue(),float(self.neg_surf_val.getvalue()))
| |
− | pymol.cmd.color('red',self.getIsoNegName())
| |
− | pymol.cmd.show('everything',self.getIsoNegName())
| |
− |
| |
− |
| |
− | </source>
| |
| | | |
| = APBS Plugin FAQ = | | = APBS Plugin FAQ = |