1019 lines
33 KiB
Python
1019 lines
33 KiB
Python
#A* -------------------------------------------------------------------
|
|
#B* This file contains source code for the PyMOL computer program
|
|
#C* Copyright (c) Schrodinger, LLC.
|
|
#D* -------------------------------------------------------------------
|
|
#E* It is unlawful to modify or remove this copyright notice.
|
|
#F* -------------------------------------------------------------------
|
|
#G* Please see the accompanying LICENSE file for further information.
|
|
#H* -------------------------------------------------------------------
|
|
#I* Additional authors of this source file include:
|
|
#-*
|
|
#-*
|
|
#-*
|
|
#Z* -------------------------------------------------------------------
|
|
|
|
from . import colorprinting
|
|
|
|
if True:
|
|
import os
|
|
import sys
|
|
import _thread as thread
|
|
from . import selector
|
|
import re
|
|
import copy
|
|
|
|
import pymol
|
|
cmd = sys.modules["pymol.cmd"]
|
|
from .cmd import _cmd,Shortcut,QuietException
|
|
from chempy import io
|
|
from chempy.pkl import cPickle
|
|
from .cmd import _feedback,fb_module,fb_mask, \
|
|
DEFAULT_ERROR, DEFAULT_SUCCESS, is_error, \
|
|
is_list, is_dict, is_tuple, loadable
|
|
|
|
def copy_image(quiet=1, *, _self=cmd): # incentive feature / proprietary
|
|
_self._call_in_gui_thread(lambda: _self._copy_image(_self, int(quiet)))
|
|
|
|
cache_action_dict = {
|
|
'enable' : 0,
|
|
'disable' : 1,
|
|
'read_only' : 2,
|
|
'clear' : 3,
|
|
'optimize' : 4,
|
|
}
|
|
|
|
cache_action_sc = Shortcut(cache_action_dict.keys())
|
|
|
|
def cache(action='optimize', scenes='',state=-1, quiet=1, *, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"cache" manages storage of precomputed results, such as
|
|
molecular surfaces.
|
|
|
|
USAGE
|
|
|
|
cache action [, scenes [, state ]]
|
|
|
|
ARGUMENTS
|
|
|
|
action = string: enable, disable, read_only, clear, or optimize
|
|
|
|
scenes = string: a space-separated list of scene names (default: '')
|
|
|
|
state = integer: state index (default: -1)
|
|
|
|
EXAMPLES
|
|
|
|
cache enable
|
|
cache optimize
|
|
cache optimize, F1 F2 F5
|
|
|
|
NOTES
|
|
|
|
"cache optimize" will iterate through the list of scenes provided
|
|
(or all defined scenes), compute any missing surfaces, and store
|
|
them in the cache for later reuse.
|
|
|
|
PYMOL API
|
|
|
|
cmd.cache(string action, string scenes, int state, int quiet)
|
|
|
|
'''
|
|
|
|
action = cache_action_dict[cache_action_sc.auto_err(str(action),'action')]
|
|
quiet = int(quiet)
|
|
if action == 0: # enable
|
|
_self.set('cache_mode', 2, quiet=quiet)
|
|
elif action == 1: # disable
|
|
_self.set('cache_mode', 0, quiet=quiet)
|
|
elif action == 2: # read_only
|
|
_self.set('cache_mode', 1, quiet=quiet)
|
|
elif action == 3: # clear
|
|
_self._cache_clear()
|
|
elif action == 4: # optimize
|
|
_self._cache_mark()
|
|
cur_scene = _self.get('scene_current_name')
|
|
cache_max = _self.get_setting_int('cache_max')
|
|
if cache_max>0:
|
|
# allow double memory for an optimized cache
|
|
_self.set('cache_max',cache_max*2)
|
|
scenes = str(scenes)
|
|
scene_list = scenes.split()
|
|
cache_mode = int(_self.get('cache_mode'))
|
|
_self.set('cache_mode',2)
|
|
if not len(scene_list):
|
|
scene_list = _self.get_scene_list()
|
|
for scene in scene_list:
|
|
scene = scene.strip()
|
|
if not quiet:
|
|
print(" cache: optimizing scene '%s'."%scene)
|
|
_self.scene(scene,animate=0)
|
|
_self.rebuild()
|
|
_self.refresh()
|
|
if len(cur_scene):
|
|
_self.scene(cur_scene,animate=0)
|
|
else:
|
|
scene_list = _self.get_scene_list()
|
|
if len(scene_list):
|
|
_self.scene(scene_list[0],animate=0)
|
|
else:
|
|
if not quiet:
|
|
print(" cache: no scenes defined -- optimizing current display.")
|
|
_self.rebuild()
|
|
_self.refresh()
|
|
usage = _self._cache_purge(-1)
|
|
if cache_mode:
|
|
_self.set('cache_mode',cache_mode)
|
|
else:
|
|
_self.set('cache_mode',2) # hmm... could use 1 here instead.
|
|
_self.set('cache_max',cache_max) # restore previous limits
|
|
if not quiet:
|
|
print(" cache: optimization complete (~%0.1f MB)."%(usage*4/1000000.0))
|
|
else:
|
|
raise ValueError('action')
|
|
|
|
_resn_to_aa = {
|
|
'ALA' : 'A',
|
|
'CYS' : 'C',
|
|
'ASP' : 'D',
|
|
'GLU' : 'E',
|
|
'PHE' : 'F',
|
|
'GLY' : 'G',
|
|
'HIS' : 'H',
|
|
'ILE' : 'I',
|
|
'LYS' : 'K',
|
|
'LEU' : 'L',
|
|
'MET' : 'M',
|
|
'ASN' : 'N',
|
|
'PRO' : 'P',
|
|
'GLN' : 'Q',
|
|
'ARG' : 'R',
|
|
'SER' : 'S',
|
|
'THR' : 'T',
|
|
'VAL' : 'V',
|
|
'TRP' : 'W',
|
|
'TYR' : 'Y',
|
|
# RNA
|
|
'A' : 'A',
|
|
'U' : 'U',
|
|
'G' : 'G',
|
|
'C' : 'C',
|
|
# DNA
|
|
'DA' : 'A',
|
|
'DT' : 'T',
|
|
'DG' : 'G',
|
|
'DC' : 'C',
|
|
}
|
|
|
|
def get_fastastr(selection="all", state=-1, quiet=1, key='', *, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
API only. Get protein and nucleic acid sequences in fasta format.
|
|
|
|
Used for saving:
|
|
PyMOL> save foo.fasta
|
|
|
|
New in PyMOL 2.2:
|
|
- chain specific keys (key argument)
|
|
- nucleic acid support
|
|
|
|
ARGUMENTS
|
|
|
|
selection = str: atom selection (reduced to "guide & alt +A") {default: all}
|
|
|
|
state = int: (only used if state > 0)
|
|
|
|
quiet = 0/1: UNUSED
|
|
|
|
key = str: python expression {default: model + "_" + chain}
|
|
Use key=model to get the old (non-chain specific) behavior
|
|
'''
|
|
import textwrap
|
|
import collections
|
|
|
|
seq = collections.OrderedDict()
|
|
lines = []
|
|
|
|
selection = "(" + selection + ") & guide & alt +A"
|
|
if int(state) > 0:
|
|
selection += ' & state {}'.format(state)
|
|
|
|
if not key:
|
|
key = 'model + "_" + chain'
|
|
# for discrete objects: state specific keys
|
|
key += ' + (":{}".format(state) if state else "")'
|
|
|
|
_self.iterate(selection,
|
|
"seq.setdefault((" + key + "),[]).append(resn)",
|
|
space={'seq': seq, 'str': str})
|
|
|
|
for key, resn_list in seq.items():
|
|
cur_seq = ''.join(_resn_to_aa.get(x, '?') for x in resn_list)
|
|
lines.append(">{}".format(key))
|
|
lines.extend(textwrap.wrap(cur_seq, 70))
|
|
|
|
if lines:
|
|
lines.append('') # final newline
|
|
return '\n'.join(lines)
|
|
|
|
def get_pdbstr(selection="all", state=-1, ref='', ref_state=-1, quiet=1, *, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"get_pdbstr" in an API-only function which returns a pdb
|
|
corresponding to the atoms in the selection provided and that are
|
|
present in the indicated state
|
|
|
|
PYMOL API
|
|
|
|
cmd.get_pdbstr(string selection, int state)
|
|
|
|
NOTES
|
|
|
|
"state" is a 1-based state index for the object.
|
|
|
|
if state is -1, then current state is used.
|
|
|
|
if state is 0, then all states are saved.
|
|
|
|
'''
|
|
return _self.get_str('pdb', selection, state, ref, ref_state, -1, quiet)
|
|
|
|
def _get_dump_str(obj):
|
|
if is_list(obj):
|
|
list = map(_get_dump_str,obj)
|
|
result = "[ " + ",\n".join(list) + " ] "
|
|
elif is_dict(obj):
|
|
list = []
|
|
for key in obj.keys():
|
|
list.append( _get_dump_str(key)+" : "+_get_dump_str(obj[key]) )
|
|
result = "{ " + ",\n".join(list) + " } "
|
|
elif is_tuple(obj):
|
|
list = map(_get_dump_str,obj)
|
|
result = "( " + ",\n".join(list) + " ) "
|
|
else:
|
|
result = str(obj)
|
|
return result
|
|
|
|
def _session_convert_legacy(session, version, _self=cmd):
|
|
'''
|
|
Convert session contents to be compatible with previous PyMOL versions
|
|
'''
|
|
if version >= _self.get_version()[1]:
|
|
return
|
|
|
|
print(" Applying pse_export_version=%.3f compatibility" % (version))
|
|
|
|
def bitmaskToList(mask):
|
|
if not isinstance(mask, int):
|
|
return mask
|
|
r = []
|
|
while (mask >> len(r)):
|
|
r.append(1 if ((mask >> len(r)) & 1) else 0)
|
|
return r
|
|
|
|
def convert_settings(settings_list):
|
|
if not settings_list:
|
|
return
|
|
|
|
# get index -> setting dict
|
|
settings = dict((s[0], s) for s in settings_list if s is not None)
|
|
|
|
# changed type
|
|
cast_fn = {
|
|
3: float,
|
|
4: lambda v: list(_self.get_color_tuple(v)),
|
|
}
|
|
for (i, old, new) in [
|
|
(6, 4, 5), # bg_rgb
|
|
(254, 3, 1), # scenes_changed
|
|
]:
|
|
if i in settings and settings[i][1] == new:
|
|
settings[i][1] = old
|
|
settings[i][2] = cast_fn[old](settings[i][2])
|
|
|
|
# changed defaults
|
|
changed = []
|
|
if version < 1.76:
|
|
changed += [
|
|
(91, 7, -1), # cartoon_sampling
|
|
(93, 6., -1.), # cartoon_loop_quality
|
|
(102, 10., -1.), # cartoon_oval_quality
|
|
(104, 9., -1.), # cartoon_tube_quality
|
|
(378, 11., -1.), # cartoon_putty_quality
|
|
]
|
|
if version < 1.5:
|
|
changed += [
|
|
(421, -1, 9), # sphere_mode
|
|
]
|
|
for (i, old, new) in changed:
|
|
if i in settings and settings[i][2] == new:
|
|
settings[i][2] = old
|
|
|
|
if 'settings' in session:
|
|
convert_settings(session['settings'])
|
|
|
|
# objects
|
|
for name in session['names']:
|
|
if name is None:
|
|
continue
|
|
|
|
# spec repOn
|
|
if version < 1.76 and name[3] is None:
|
|
name[3] = [0] * 20
|
|
|
|
# only continue for objects
|
|
if name[1] != 0:
|
|
continue
|
|
|
|
if name[4] == 8: # gadget
|
|
cobject = name[5][0][0]
|
|
else:
|
|
cobject = name[5][0]
|
|
|
|
# object visRep
|
|
if version < 1.76:
|
|
cobject[3] = bitmaskToList(cobject[3])
|
|
|
|
# object settings
|
|
convert_settings(cobject[8])
|
|
|
|
# molecule
|
|
if name[4] == 1:
|
|
# atoms visRep
|
|
if version < 1.76:
|
|
for atom in name[5][7]:
|
|
atom[20] = bitmaskToList(atom[20])
|
|
# state settings
|
|
for cset in name[5][4]:
|
|
if cset:
|
|
convert_settings(cset[7])
|
|
|
|
# map
|
|
elif name[4] == 2:
|
|
if version < 1.5:
|
|
# crystal -> [crystal, spacegroup]
|
|
for state in name[5][2]:
|
|
if state and state[1]:
|
|
state[1] = state[1][0]
|
|
|
|
# cgo
|
|
elif name[4] == 6:
|
|
if version < 1.506:
|
|
for state in name[5][2]:
|
|
if len(state) == 1:
|
|
# std and ray CGO
|
|
state.append(state[0])
|
|
|
|
def get_session(names='', partial=0, quiet=1, compress=-1, cache=-1,
|
|
binary=-1, version=-1,
|
|
*, _self=cmd):
|
|
'''
|
|
:param names: Names of objects to export, or the empty string to export all objects.
|
|
:param partial: If true, do not store selections, settings, view, movie.
|
|
:param compress: DEPRECATED Pickle the dictionary to a string and
|
|
compress that with zlib {default: session_compression}
|
|
:param cache: ?
|
|
:param binary: Use efficient binary format {default: pse_binary_dump}
|
|
:param version: {default: pse_export_version}
|
|
'''
|
|
session = {}
|
|
cache = int(cache)
|
|
compress = int(compress)
|
|
partial = int(partial)
|
|
quiet = int(quiet)
|
|
|
|
if version < 0:
|
|
version = _self.get_setting_float('pse_export_version')
|
|
|
|
pse_export_version = round(float(version), 4)
|
|
legacyscenes = (0 < pse_export_version < 1.76) and _self.get_scene_list()
|
|
|
|
if True:
|
|
legacypickle = (0 < pse_export_version < 1.9)
|
|
if legacypickle:
|
|
print(' Using Python 2 compatible legacy pickler')
|
|
cPickle.configure_legacy_dump(legacypickle)
|
|
|
|
if legacyscenes:
|
|
_self.pymol._scene_dict = {}
|
|
|
|
scene_current_name = _self.get('scene_current_name')
|
|
tempname = '_scene_db96724c3cef00875c3bebb4f348f711'
|
|
_self.scene(tempname, 'store')
|
|
|
|
for name in legacyscenes:
|
|
_self.scene(name, 'recall', animate=0)
|
|
wizard = _self.get_wizard()
|
|
message = wizard.message if getattr(wizard, 'from_scene', 0) else None
|
|
pymol.viewing._legacy_scene(name, 'store', message, _self=_self)
|
|
|
|
_self.scene(tempname, 'recall', animate=0)
|
|
_self.scene(tempname, 'clear')
|
|
_self.set('scene_current_name', scene_current_name)
|
|
|
|
if cache:
|
|
cache_opt = _self.get_setting_int('session_cache_optimize')
|
|
if cache_opt == 1 or (cache_opt != 0
|
|
and _self.get_setting_int('cache_mode')):
|
|
_self.cache('optimize')
|
|
|
|
with _self.lockcm:
|
|
_cmd.get_session(_self._COb, session, str(names), int(partial),
|
|
int(quiet), binary, pse_export_version)
|
|
|
|
if True:
|
|
try:
|
|
session['session'] = copy.deepcopy(_self._pymol.session)
|
|
if cache and hasattr(_self._pymol,'_cache'):
|
|
session['cache'] = _self._pymol._cache
|
|
except:
|
|
colorprinting.print_exc()
|
|
|
|
for a in _self._pymol._session_save_tasks:
|
|
assert a is not None
|
|
error = False
|
|
try:
|
|
# TODO _session_restore_tasks use `not <return-value>`
|
|
# instead of `is_error(<return-value>)`
|
|
error = is_error(a(session, _self=_self))
|
|
except:
|
|
if not quiet:
|
|
colorprinting.print_exc([__file__])
|
|
error = True
|
|
if error:
|
|
msg = f'Warning: session-save-task "{a.__name__}" failed.'
|
|
if not quiet:
|
|
msg += '\n The resulting session file may be incomplete.'
|
|
colorprinting.warning(msg)
|
|
|
|
if legacyscenes:
|
|
del session['moviescenes']
|
|
session['scene_dict'] = _self.pymol._scene_dict
|
|
session['scene_order'] = legacyscenes
|
|
pymol.viewing._legacy_scene('*', 'clear', _self=_self)
|
|
del _self.pymol._scene_dict
|
|
|
|
if True:
|
|
if pse_export_version > 0.0:
|
|
try:
|
|
_session_convert_legacy(session, pse_export_version, _self)
|
|
except Exception as e:
|
|
print(' Warning: failed to backport session:', e)
|
|
|
|
if(compress<0):
|
|
compress = _self.get_setting_boolean('session_compression')
|
|
if(compress):
|
|
colorprinting.warning(
|
|
' Warning: `session_compression` is deprecated. Save '
|
|
'to ".pze" or ".pse.gz" files instead, and/or use '
|
|
'`pse_binary_dump`')
|
|
import zlib
|
|
session = zlib.compress(cPickle.dumps(session, 1))
|
|
return session
|
|
|
|
def _unit2px(value, dpi, unit=''):
|
|
'''API only. Returns pixel units given a string representation in other units'''
|
|
if cmd.is_string(value):
|
|
m = re.search(r'[a-z].*', value, re.I)
|
|
if m:
|
|
value, unit = value[:m.start()], m.group(0).lower()
|
|
|
|
if not unit or unit == 'px':
|
|
return int(value)
|
|
|
|
upi = {'in': 1.0, 'mm': 25.4, 'cm': 2.54}
|
|
if unit not in upi:
|
|
raise pymol.CmdException('unknown unit, supported units are: ' +
|
|
', '.join(upi))
|
|
|
|
if dpi < 1:
|
|
raise pymol.CmdException('dpi > 0 required with unit "%s" '
|
|
'(hint: set the "image_dots_per_inch" setting)' % unit)
|
|
|
|
return float(value) * dpi / upi[unit] + 0.5
|
|
|
|
def png(filename, width=0, height=0, dpi=-1.0, ray=0,
|
|
quiet=1, prior=0, format=0, *, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"png" saves a PNG format image file of the current display.
|
|
|
|
USAGE
|
|
|
|
png filename [, width [, height [, dpi [, ray]]]]
|
|
|
|
ARGUMENTS
|
|
|
|
filename = string: file path to be written
|
|
|
|
width = integer or string: width in pixels (without units), inches (in)
|
|
or centimeters (cm). If unit suffix is given, dpi argument is required
|
|
as well. If only one of width or height is given, the aspect ratio of
|
|
the viewport is preserved. {default: 0 (current)}
|
|
|
|
height = integer or string: height (see width) {default: 0 (current)}
|
|
|
|
dpi = float: dots-per-inch {default -1.0 (unspecified)}
|
|
|
|
ray = 0 or 1: should ray be run first {default: 0 (no)}
|
|
|
|
EXAMPLES
|
|
|
|
png image.png
|
|
png image.png, dpi=300
|
|
png image.png, 10cm, dpi=300, ray=1
|
|
|
|
NOTES
|
|
|
|
PNG is the only image format supported by PyMOL.
|
|
|
|
SEE ALSO
|
|
|
|
mpng, save
|
|
|
|
PYMOL API
|
|
|
|
cmd.png(string filename, int width, int height, float dpi,
|
|
int ray, int quiet)
|
|
'''
|
|
ray = int(ray)
|
|
|
|
PRIOR_TRY = -1
|
|
PRIOR_NO = 0
|
|
PRIOR_YES = 1
|
|
|
|
prior = int(prior)
|
|
assert prior in (PRIOR_TRY, PRIOR_YES, PRIOR_NO)
|
|
|
|
FORMAT_GUESS = -1
|
|
FORMAT_PNG = 0
|
|
FORMAT_PPM = 1
|
|
|
|
if format == 'png':
|
|
format = FORMAT_PNG
|
|
|
|
assert format in (FORMAT_PNG, FORMAT_PPM, FORMAT_GUESS)
|
|
|
|
if format == FORMAT_GUESS:
|
|
if filename and filename.endswith(".ppm"):
|
|
format = FORMAT_PPM
|
|
else:
|
|
format = FORMAT_PNG
|
|
|
|
if filename and not filename.startswith('\x01'):
|
|
if format == FORMAT_PNG and not filename.endswith(".png"):
|
|
filename += ".png"
|
|
|
|
filename = cmd.exp_path(filename)
|
|
|
|
dpi = float(dpi)
|
|
if dpi < 0:
|
|
dpi = _self.get_setting_float('image_dots_per_inch')
|
|
|
|
width = _unit2px(width, dpi)
|
|
height = _unit2px(height, dpi)
|
|
filename = "" if filename is None else filename
|
|
|
|
def func():
|
|
with _self.lockcm:
|
|
return _cmd.png(_self._COb, filename, int(width), int(height),
|
|
dpi, ray, int(quiet), prior, format)
|
|
|
|
if prior:
|
|
# fetch the prior image, without doing any work (fast-path / non-GLUT thread-safe)
|
|
r = func()
|
|
if r:
|
|
return r
|
|
|
|
if prior != PRIOR_TRY:
|
|
raise pymol.CmdException("no prior image available")
|
|
|
|
print("no prior image available, fall back to rendering")
|
|
prior = PRIOR_NO
|
|
|
|
if ray:
|
|
return func()
|
|
|
|
return _self._call_with_opengl_context(func)
|
|
|
|
def multisave(filename, pattern="all", state=-1,
|
|
append=0, format='', quiet=1, *, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"multisave" will save a multi-entry PDB file.
|
|
|
|
Every object in the given selection (pattern) will have a HEADER and a
|
|
CRYST (if symmetry is defined) record, and is terminated with END.
|
|
Loading such a multi-entry PDB file into PyMOL will load each entry
|
|
as a separate object.
|
|
|
|
This behavior is different to the "save" command, where a multi-object
|
|
selection is written "flat" to a PDB file, without HEADER or CRYST
|
|
records.
|
|
|
|
ARGUMENTS
|
|
|
|
filename = string: file path to be written
|
|
|
|
pattern = str: atom selection (before 1.8.4: object name pattern)
|
|
|
|
state = int: object state (-1=current, 0=all) {default: -1}
|
|
|
|
append = 0/1: append to existing file {default: 0}
|
|
|
|
format = str: file format {default: guess from extension, or 'pdb'}
|
|
'''
|
|
from pymol.importing import filename_to_format
|
|
_, _, format_guessed, zipped = filename_to_format(filename)
|
|
|
|
if zipped:
|
|
raise pymol.CmdException(zipped + ' not supported with multisave')
|
|
|
|
if not format:
|
|
format = format_guessed or 'pdb'
|
|
|
|
if format == 'pmo':
|
|
raise pymol.CmdException('pmo format not supported anymore')
|
|
|
|
if format not in ('pdb', 'cif'):
|
|
raise pymol.CmdException(format + ' format not supported with multisave')
|
|
|
|
s = _self.get_str(format, pattern, state, '', -1, 1, quiet)
|
|
|
|
if s is None:
|
|
raise QuietException
|
|
|
|
filename = _self.exp_path(filename)
|
|
|
|
with open(filename, 'a' if int(append) else 'w') as handle:
|
|
handle.write(s)
|
|
|
|
return DEFAULT_SUCCESS
|
|
|
|
def assign_atom_types( selection, format = "mol2", state=1, quiet=1, *, _self=cmd):
|
|
fmt = {'mol2': 1, 'mmd': 2}[format]
|
|
with _self.lockcm:
|
|
# format : mol2/sybyl = 1, macromodel/mmd = 2, global setting atom_type_format = 0
|
|
_cmd.assign_atom_types(_self._COb, selection, fmt, int(state - 1),
|
|
quiet)
|
|
|
|
def get_str(format, selection='(all)', state=-1, ref='',
|
|
ref_state=-1, multi=-1, quiet=1, *, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
Like "get_bytes" but return a unicode string.
|
|
'''
|
|
assert format not in ('mmtf',), 'binary format, use get_bytes'
|
|
b = _self.get_bytes(format, selection, state, ref, ref_state, multi, quiet)
|
|
if b is None:
|
|
return None
|
|
return b.decode('utf-8')
|
|
|
|
def get_bytes(format, selection='(all)', state=-1, ref='',
|
|
ref_state=-1, multi=-1, quiet=1, *, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
API-only function which exports the selection to a molecular file
|
|
format and returns it as a binary ("bytes") string.
|
|
|
|
ARGUMENTS
|
|
|
|
format = str: pdb|cif|sdf|mol|mol2|mae|pqr|xyz
|
|
|
|
selection = str: atom selection {default: all}
|
|
|
|
state = int: object state (-1=current, 0=all) {default: -1}
|
|
|
|
ref = str: object name which defines reference frame {default: }
|
|
|
|
ref_state = int: state of ref object {default: -1}
|
|
|
|
multi = int: for multi-entry file formats, 0 = single entry,
|
|
1 = by object, 2 = by object-state, -1 = format default {default: -1}
|
|
'''
|
|
with _self.lockcm:
|
|
return _cmd.get_str(_self._COb, str(format), str(selection),
|
|
int(state) - 1, str(ref), int(ref_state),
|
|
int(multi), int(quiet))
|
|
|
|
def multifilesave(filename, selection='*', state=-1, format='', ref='',
|
|
ref_state=-1, quiet=1, *, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
For a selection that spans multiple molecular objects and/or states,
|
|
save each object and/or state to a separate file. Takes a filename
|
|
argument with placeholders:
|
|
|
|
{name} : object name
|
|
{state} : state number
|
|
{title} : state title
|
|
{num} : file number
|
|
{} : object name (first) or state (second)
|
|
|
|
EXAMPLES
|
|
|
|
multifilesave /tmp/{name}.pdb
|
|
multifilesave /tmp/{name}-{state}.cif, state=0
|
|
multifilesave /tmp/{}-{}.cif, state=0
|
|
multifilesave /tmp/{}-{title}.sdf, state=0
|
|
'''
|
|
for (fname, osele, ostate) in multifilenamegen(
|
|
filename, selection, int(state), _self):
|
|
r = _self.save(fname, osele, ostate, format, ref, ref_state, quiet)
|
|
return r
|
|
|
|
|
|
def multifilenamegen(filename, selection, state, _self=cmd):
|
|
'''Given a filename pattern, atom selection and state argument,
|
|
Generate object-state specific filenames and selections.
|
|
'''
|
|
import string
|
|
|
|
filename = _self.exp_path(filename)
|
|
|
|
fmt_keys = [v[1]
|
|
for v in string.Formatter().parse(filename)
|
|
if v[1] is not None]
|
|
|
|
nindexed = fmt_keys.count('')
|
|
multiobject = nindexed > 0 or 'name' in fmt_keys or 'num' in fmt_keys
|
|
multistate = nindexed > 1 or 'state' in fmt_keys or 'title' in fmt_keys
|
|
|
|
if not (multiobject or multistate):
|
|
raise ValueError('need one or more of {name}, {num}, {state}, {title}')
|
|
|
|
odata = []
|
|
for oname in _self.get_object_list(selection):
|
|
osele = '(%s) & ?%s' % (selection, oname)
|
|
first = last = state
|
|
|
|
if multistate:
|
|
if state < 0:
|
|
first = last = _self.get_object_state(oname)
|
|
|
|
if first == 0:
|
|
first = 1
|
|
last = _self.count_states('%' + oname)
|
|
|
|
for ostate in range(first, last + 1):
|
|
odata.append((oname, osele, ostate))
|
|
|
|
# pad {state} and {num} with zeros
|
|
swidth = len(str(max(v[2] for v in odata)))
|
|
nwidth = len(str(len(odata)))
|
|
filename = filename.replace('{state}', '{state:0%d}' % swidth)
|
|
filename = filename.replace('{num}', '{num:0%d}' % nwidth)
|
|
|
|
for num, (oname, osele, ostate) in enumerate(odata, 1):
|
|
title = _self.get_title(oname, ostate) if multistate else ''
|
|
fname = filename.format(oname, ostate,
|
|
name=oname, state=ostate, num=num,
|
|
title=title)
|
|
yield fname, osele, ostate
|
|
|
|
|
|
def save(filename, selection='(all)', state=-1, format='', ref='',
|
|
ref_state=-1, quiet=1, partial=0, *, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"save" writes content to a file.
|
|
|
|
USAGE
|
|
|
|
save filename [, selection [, state [, format ]]]
|
|
|
|
ARGUMENTS
|
|
|
|
filename = string: file path to be written
|
|
|
|
selection = string: atoms to save {default: (all)}
|
|
|
|
state = integer: state to save {default: -1 (current state)}
|
|
|
|
PYMOL API
|
|
|
|
cmd.save(string file, string selection, int state, string format)
|
|
|
|
NOTES
|
|
|
|
The file format is automatically chosen if the extesion is one of
|
|
the supported output formats: pdb, pqr, mol, sdf, pkl, pkla, mmd, out,
|
|
dat, mmod, cif, pov, png, pse, psw, aln, fasta, obj, mtl, wrl, dae, idtf,
|
|
or mol2.
|
|
|
|
If the file format is not recognized, then a PDB file is written
|
|
by default.
|
|
|
|
For molecular files and where applicable and supported:
|
|
|
|
* if state = -1 (default), then only the current state is written.
|
|
|
|
* if state = 0, then a multi-state output file is written.
|
|
|
|
SEE ALSO
|
|
|
|
load, get_model
|
|
'''
|
|
quiet = int(quiet)
|
|
|
|
# preprocess selection
|
|
selection = selector.process(selection)
|
|
#
|
|
r = DEFAULT_ERROR
|
|
|
|
# analyze filename
|
|
from pymol.importing import filename_to_format, _eval_func
|
|
_, _, format_guessed, zipped = filename_to_format(filename)
|
|
filename = _self.exp_path(filename)
|
|
|
|
# file format
|
|
if not format:
|
|
if not format_guessed:
|
|
raise pymol.CmdException('Unrecognized file format')
|
|
format = format_guessed
|
|
|
|
# PyMOL session
|
|
if format in ('pse', 'psw',):
|
|
_self.set("session_file",
|
|
# always use unix-like path separators
|
|
filename.replace("\\", "/"), quiet=1)
|
|
if not quiet:
|
|
print(" Save: Please wait -- writing session file...")
|
|
|
|
func_type4 = {
|
|
'mmod': io.mmd.toFile,
|
|
'pkl': io.pkl.toFile, # binary pickle
|
|
'pkla': lambda model, filename: io.pkl.toFile(model, filename, bin=0), # ascii pickle
|
|
}
|
|
|
|
contents = None
|
|
|
|
if format in savefunctions:
|
|
# generic forwarding to format specific save functions
|
|
func = savefunctions[format]
|
|
func = _eval_func(func)
|
|
kw_all = {
|
|
'filename': filename,
|
|
'selection': selection,
|
|
'name': selection, # alt (get_ccp4str)
|
|
'state': state,
|
|
'format': format,
|
|
'ref': ref,
|
|
'ref_state': ref_state,
|
|
'quiet': quiet,
|
|
'partial': partial,
|
|
'_self': _self,
|
|
}
|
|
|
|
import inspect
|
|
sig = inspect.signature(func, follow_wrapped=False)
|
|
kw = {}
|
|
|
|
for n, param in sig.parameters.items():
|
|
if param.kind == inspect.Parameter.VAR_KEYWORD:
|
|
kw = kw_all
|
|
break
|
|
|
|
if param.kind == inspect.Parameter.VAR_POSITIONAL:
|
|
print('FIXME: savefunctions[%s]: *args' % (format))
|
|
elif param.kind == inspect.Parameter.POSITIONAL_ONLY:
|
|
raise Exception('positional-only arguments not supported')
|
|
elif n in kw_all:
|
|
kw[n] = kw_all[n]
|
|
|
|
contents = func(**kw)
|
|
|
|
if 'filename' in sig.parameters:
|
|
# assume function wrote directly to file and returned a status
|
|
return contents
|
|
|
|
elif format in func_type4:
|
|
func_type4[format](_self.get_model(selection, state, ref, ref_state), filename)
|
|
r = DEFAULT_SUCCESS
|
|
else:
|
|
raise pymol.CmdException('File format not supported for export')
|
|
|
|
# function returned sequence of strings or bytes
|
|
if isinstance(contents, (tuple, list)) and contents:
|
|
contents = contents[0][:0].join(contents)
|
|
|
|
if cmd.is_string(contents):
|
|
if not isinstance(contents, bytes):
|
|
contents = contents.encode()
|
|
|
|
if zipped == 'gz':
|
|
import gzip
|
|
fopen = gzip.open
|
|
else:
|
|
fopen = open
|
|
if zipped == 'bz2':
|
|
import bz2
|
|
contents = bz2.compress(contents)
|
|
|
|
with fopen(filename, 'wb') as handle:
|
|
handle.write(contents)
|
|
r = DEFAULT_SUCCESS
|
|
|
|
if _self._raising(r): raise QuietException
|
|
|
|
if not quiet:
|
|
if r == DEFAULT_SUCCESS:
|
|
print(' Save: wrote "' + filename + '".')
|
|
else:
|
|
print(' Save-Error: no file written')
|
|
|
|
return r
|
|
|
|
def get_cifstr(selection="all", state=-1, quiet=1, *, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
API-only function which returns a mmCIF string.
|
|
|
|
SEE ALSO
|
|
|
|
get_pdbstr
|
|
'''
|
|
return _self.get_str('cif', selection, state, '', -1, -1, quiet)
|
|
|
|
def get_xyzstr(selection, state=-1, quiet=1, *, _self=cmd):
|
|
return _self.get_str('xyz', selection, state, '', -1, -1, quiet)
|
|
|
|
def get_sdfstr(selection, state=-1, ref='', ref_state=-1, quiet=1, *, _self=cmd):
|
|
return _self.get_str('sdf', selection, state, ref, ref_state, -1, quiet)
|
|
|
|
def get_mol2str(selection, state=-1, ref='', ref_state=-1, quiet=1, *, _self=cmd):
|
|
return _self.get_str('mol2', selection, state, ref, ref_state, -1, quiet)
|
|
|
|
def get_alnstr(selection, state=-1, quiet=1, *, _self=cmd):
|
|
with _self.lockcm:
|
|
return _cmd.get_seq_align_str(_self._COb, str(selection),
|
|
int(state)-1, 0, int(quiet))
|
|
|
|
def get_pqrstr(selection, state=-1, ref='', ref_state=-1, quiet=1, *, _self=cmd):
|
|
return _self.get_str('pqr', selection, state, ref, ref_state, -1, quiet)
|
|
|
|
def get_maestr(selection, state=-1, ref='', ref_state=-1, quiet=1, *, _self=cmd):
|
|
return _self.get_str('mae', selection, state, ref, ref_state, -1, quiet)
|
|
|
|
def get_ccp4str(name, state=1, quiet=1, format='ccp4', *, _self=cmd):
|
|
ftype = getattr(loadable, format, -1)
|
|
with _self.lockcm:
|
|
return _cmd.get_ccp4str(_self._COb, str(name),
|
|
int(state) - 1, int(quiet), ftype)
|
|
|
|
def get_psestr(selection, partial, quiet, _self):
|
|
if '(' in selection: # ignore selections
|
|
selection = ''
|
|
session = _self.get_session(selection, partial, quiet)
|
|
return cPickle.dumps(session, 1)
|
|
|
|
def _get_mtl_obj(format, _self):
|
|
# TODO mtl not implemented, always returns empty string
|
|
if format == 'mtl':
|
|
raise pymol.CmdException('.MTL export not implemented')
|
|
i = {'mtl': 0, 'obj': 1}.get(format)
|
|
return _self.get_mtl_obj()[i]
|
|
|
|
savefunctions = {
|
|
'cif': get_str, # mmCIF
|
|
'xyz': get_str,
|
|
'pdb': get_str,
|
|
'pqr': get_str,
|
|
'sdf': get_str,
|
|
'mol2': get_str,
|
|
'mae': get_str,
|
|
'mol': get_str,
|
|
'mmtf': get_bytes,
|
|
|
|
'pse': get_psestr,
|
|
'psw': get_psestr,
|
|
|
|
'fasta': get_fastastr,
|
|
'aln': get_alnstr,
|
|
'ccp4': get_ccp4str,
|
|
'mrc': get_ccp4str,
|
|
'map': get_ccp4str,
|
|
|
|
'png': png,
|
|
|
|
# no arguments (some have a "version" argument)
|
|
'dae': 'pymol.querying:get_collada',
|
|
'gltf': 'pymol.querying:get_gltf',
|
|
'wrl': 'pymol.querying:get_vrml',
|
|
'pov': 'pymol.querying:get_povray',
|
|
'idtf': 'pymol.querying:get_idtf',
|
|
'mtl': _get_mtl_obj, # TODO not implemented
|
|
'obj': _get_mtl_obj,
|
|
'stl': 'pymol.lazyio:get_stlstr',
|
|
}
|