"""
A module containing a class for labeling atoms in the workspace.
Copyright Schrodinger, LLC. All rights reserved.
"""
from schrodinger.structutils import analyze
try:
from schrodinger.maestro import maestro
except ImportError:
maestro = None
SAVE_FMT_PROP = 's_bioluminate_saved_label_format'
DEFAULT_OPTIONS = {
'1charge': 'FALSE',
'1chargeformatstring': '%C1.3',
'2charge': 'FALSE',
'2chargeformatstring': '%C2.3',
'acolor': 'TRUE',
'anum': 'FALSE',
'anumformatstring': '%NU',
'atomicnumber': 'FALSE',
'atomname': 'FALSE',
'atomnameformatstring': '%AT',
'atype': 'FALSE',
'atypeformatstring': '%TY',
'chain': 'FALSE',
'chainformatstring': '%CH',
'chirality': 'FALSE',
'chiralityformatstring': '%CY',
'cindex': '2',
'compositionfields': '',
'dmsopka': 'FALSE',
'dmsopkaformatstring': '%DP',
'element': 'FALSE',
'elementformatstring': '%EL',
'entryname': 'FALSE',
'entrynameformatstring': '%EN',
'entrypropertynames': '',
'font': '',
'font_size': '14',
'font_style': 'bold',
'formalcharge': 'FALSE',
'formalchargeformatstring': '%FC',
'growname': 'FALSE',
'grownameformatstring': '%GN',
'h2opka': 'FALSE',
'h2opkaformatstring': '%HP',
'headings': 'FALSE',
'isotopenumber': 'FALSE',
'isotopenumformatstring': '%IN',
'keeplabels': 'on_top',
'mode': 'replace',
'molnum': 'FALSE',
'molnumentry': 'FALSE',
'molnumentryformatstring': '%ME',
'numentry': 'FALSE',
'numentryformatstring': '%NE',
'nummol': 'FALSE',
'nummolformatstring': '%NM',
'occupancy': 'FALSE',
'occupancyformatstring': '%OC',
'oneletter': 'FALSE',
'pdbname': 'FALSE',
'pdbnameformatstring': '%PA',
'reapply': 'FALSE',
'reapplylabels': 'FALSE',
'resname': 'FALSE',
'resnameformatstring': '%RT',
'resnum': 'FALSE',
'resnumformatstring': '%RN',
'separator': '',
'showhisotopes': 'TRUE',
'showlabel': 'FALSE',
'stereochemistry': 'FALSE',
'stereochemistryformatstring': '%ST',
'title': 'FALSE',
'titleformatstring': '%ET',
'user': 'FALSE',
'useratompropertyformatstring': '%UA',
'utext': '',
'xoffset': '0.05',
'xyz': 'FALSE',
'xyzformatstring': '%XY',
'yoffset': '-0.15'
}
[docs]def removeBadOptions():
"""
Test each option in DEFAULT_OPTIONS and remove any bad or deprecated ones.
"""
badoptions = set()
for option in DEFAULT_OPTIONS:
try:
maestro.get_command_option('labelatom', option)
except KeyError: #If an option has been deprecated or removed
badoptions.add(option)
for option in badoptions:
del DEFAULT_OPTIONS[option]
if maestro:
removeBadOptions()
[docs]class WorkspaceLabeler(object):
"""
Class with methods for manipulating workspace atom labels.
"""
[docs] def __init__(self):
self.saved_options = {}
[docs] def resetLabelOptions(self):
"""
Resets Maestro's internally persistent label options to ensure
consistent behavior of the labeler regardless of intervening calls to
the Maestro command 'labelatom' that might change label options.
It is generally not necessary to use this command; however it could be
useful when invoking custom 'labelatom' commands.
"""
maestro.command('labelatomfieldsnone')
[docs] def saveLabelOptions(self, options=None):
"""
Saves the state of all of Maestro's 'labelatom' options, generally for
the purpose of changing the label options by the WorkspaceLabeler class
or by other Python scripts and then changing them back to their original
state.
:type options: dict
:param options: dictionary containing a set of options and values
Default: saved_options
"""
if not options:
options = self.saved_options
for option in DEFAULT_OPTIONS:
options[option] = maestro.get_command_option('labelatom', option)
[docs] def restoreLabelOptions(self, options=None):
"""
Restores the state of all of Maestro's 'labelatom' options from a
dictionary, generally for the purpose of restoring them to their
original state.
:type options: dict
:param options: dictionary containing a set of options and values
Default: saved_options
"""
if not options:
options = self.saved_options
option_string = ''
for option, value in options.items():
option_string += option + '="' + value + '" '
maestro.command('labelatom ' + option_string)
[docs] def labelAtoms(self, asl, options='atomname=TRUE'):
"""
Labels the specified atoms. By default labelAtoms will label with
atomname, but the label content can be customized with the options
parameter.
:type asl: str
:param asl: ASL string specifying atoms to label
:type options: str
:param options: An options string that will be passed to the Maestro
command 'labelatom' to specify the labeling behavior. See the
Maestro Command Reference for full documentation.
Default: 'atomname=TRUE'
"""
self.saveLabelOptions() #Save persistent option state
self.resetLabelOptions()
asl = '(' + asl + ')' #Doesn't hurt and simplifies testing
commandstring = ' '.join(('labelatom', options, asl))
maestro.command(commandstring)
self.restoreLabelOptions() #Restore persistent option state
[docs] def clearLabels(self, asl):
"""
Deletes all labels from the specified atoms.
:type asl: str
:param asl: ASL string specifying atoms to remove labels from
"""
asl = '(' + asl + ')' #Doesn't hurt and simplifies testing
maestro.command('labelclear ' + asl)
[docs] def labelResidues(self, asl, options='resname=TRUE resnum=TRUE'):
"""
Labels the alpha carbon of every residue that has at least one atom in
the specified ASL. This means that labels may be added to atoms that
are outside the ASL.
By default labelAtoms will label each CA with the 3-letter residue code
and residue number, but the label content can be customized with the
options parameter.
:type asl: str
:param asl: ASL string specifying atoms that are in residues to label
:type options: str
:param options: An options string that will be passed to the Maestro
command 'labelatom' to specify the labeling behavior. See the
Maestro Command Reference for full documentation.
Default: 'resname=TRUE resnum=TRUE'
"""
ca_asl = 'fillres(' + asl + ') AND atom. CA'
self.labelAtoms(ca_asl, options)
[docs] def labelChains(self, asl, options='chain=TRUE'):
"""
Labels the first and last alpha carbon within the specified ASL of each
chain that has at least one atom specified by the ASL. First and last
CA are determined by residue numbering.
By default, labelChains will label each of the two CA's with the chain
name, but the label content can be customized with the options paramter.
:type asl: str
:param asl: ASL string specifying atoms that are in residues to label
:type options: str
:param options: An options string that will be passed to the Maestro
command 'labelatom' to specify the labeling behavior. See the
Maestro Command Reference for full documentation.
Default: 'chain=TRUE'
"""
workspace = maestro.workspace_get()
asl = '(' + asl + ') AND not water' # Don't want to label water
atoms = analyze.get_atoms_from_asl(workspace, asl)
firstatoms = {
} # Dictionaries keyed by (entryid, chain.name) containing
lastatoms = {} # (res, index), where res is (resnum, inscode)
# Store the index of the highest- and lowest-index atom from each chain
for atom in atoms:
chainkey = (atom.entry_id, atom.chain)
res = (atom.resnum, atom.inscode)
try:
if res < firstatoms[chainkey][0]: # First element is res-tuple
firstatoms[chainkey] = (res, atom.index)
except KeyError:
firstatoms[chainkey] = (res, atom.index)
try:
if res > lastatoms[chainkey][0]: # First element is res-tuple
lastatoms[chainkey] = (res, atom.index)
except KeyError:
lastatoms[chainkey] = (res, atom.index)
# Two loops to collect all the atom indices for labeling
labelatoms = set()
for val in firstatoms.values():
labelatoms.add(str(val[1])) # Second element is atom index
for val in lastatoms.values():
labelatoms.add(str(val[1])) # Second element is atom index
self.labelResidues('atom.n ' + ' '.join(labelatoms), 'chain=TRUE')
[docs] def hideLabels(self, asl):
"""
Hides the labels on the specified atoms. The difference between
hideLabels() and clearLabels() is that labels that have been hidden can
be unhidden. Any time a hidden label is replaced or updated, the new
label is visible, as in PyMol. Hidden labels can also be cleared;
they do not then reappear on showLabels().
:type asl: str
:param asl: ASL string specifying atoms to hide labels on
"""
workspace = maestro.workspace_get()
atoms = analyze.evaluate_asl(workspace, asl)
for atom in atoms:
fmt = workspace.atom[atom].label_format
# Save label_format for each atom
if not fmt == '%HIDDEN': # Don't double-hide
workspace.atom[atom].property[SAVE_FMT_PROP] = fmt
# Maestro ignores meaningless label_format '%HIDDEN'. This is non-
# standard behavior which is not guaranteed to work
workspace.atom[atom].label_format = '%HIDDEN'
maestro.command('labelclear ' + asl)
maestro.workspace_set(workspace) # Must come after labelclear
[docs] def showLabels(self, asl):
"""
Unhides previously hidden labels on the specified atoms.
:type asl: str
:param asl: ASL string specifying atoms to unhide labels on
"""
workspace = maestro.workspace_get()
atoms = analyze.evaluate_asl(workspace, asl)
for atom in atoms:
try:
# Recall saved label_format
fmt = workspace.atom[atom].property[SAVE_FMT_PROP]
except KeyError: # Unhiding a label that has never been hidden
pass
# Only untouched hidden labels will retain the '%HIDDEN' format
if workspace.atom[atom].label_format == '%HIDDEN':
workspace.atom[atom].label_format = fmt
maestro.workspace_set(workspace)