Source code for schrodinger.application.bioluminate.ssv.toolbars
"""
Toolbars for the `SimplifiedSequenceViewer`
"""
# Contributors: Joshua Williams, Matvey Adzhigirey
#- Imports -------------------------------------------------------------------
from schrodinger.Qt import QtWidgets
from schrodinger.Qt.QtCore import Qt
from ..actions import configs as action_config
from ..actions import factory
from ..actions import icons
from ..protein import Consensus
try:
from schrodinger.maestro import maestro
except ImportError:
maestro = None # For testing outside of Maestro
#- Classes -------------------------------------------------------------------
[docs]class ImportToolBar(QtWidgets.QToolBar):
"""
Get the toolbar that has the import dialogs. This toolbar has a single
action, that when clicked opens a menu of different import options.
"""
[docs] def __init__(self, parent, title=None, action_order=None, text=True):
"""
:param title: The title of the toolbar. Used in right-click menu in
a main window's toolbar area.
:type title: str
:param action_order: Order of the actions displayed in the popup menu
:see: schrodinger.application.bioluminate.actions.config.IMPORT_STRUCTURES
"""
self._parent = parent
super(ImportToolBar, self).__init__(parent)
self.setMovable(False)
# Keep this to offer same constructors as QToolBar
if title:
self.setWindowTitle(self.tr(title))
# Set up the actions
action_factory = factory.Factory(parent)
import_params = {
'default_order': ['import'],
'import': {
'icon': icons.MSV_OPEN_FILE,
'tooltip': 'Import homologs from structure or sequence',
'slot': self.openMenu,
}
}
action_factory.setActions(import_params)
import_action = action_factory.getAction('import')
import_action.setIconText('Import homologs')
action_factory.setActions(action_config.IMPORT_STRUCTURES,
action_order=action_order)
self.import_menu = action_factory.getMenu()
self.addAction(import_action)
self.addSeparator()
self.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
[docs] def openMenu(self):
"""
Opens the import pull-down menu
"""
self.import_menu.exec(self._parent.mapToGlobal(self.pos()))
[docs]class FindToolBar(QtWidgets.QToolBar):
"""
Create an instance of the "Find Pattern" toolbar from the MSV.
"""
[docs] def __init__(self, parent, title=None):
"""
Construct toolbar. Here the `parent` should be an instance of a
`schrodinger.ui.sequencealignment.sequence_viewer.SequenceViewer`.
If it is not the parent will need a `findPattern` method.
"""
if not hasattr(parent, 'findPattern'):
raise RuntimeError('Invalid parent, needs findPattern method.')
self.parent = parent
super(FindToolBar, self).__init__(parent)
self.setMovable(False)
# Keep this to offer same constructors as QToolBar
if title:
self.setWindowTitle(self.tr(title))
# Create the label and line edit
label = QtWidgets.QLabel(self.tr("Find Pattern: "))
self.line_edit = QtWidgets.QLineEdit()
self.line_edit.setToolTip(
"Type PROSITE pattern to search the sequences:\n\n"
"[AIL] - find any occurence of A, I, or L\n"
"{ED} - exclude all occurences of E and D\n"
"a - acidic residue [DE]\n"
"b - basic residue [KR]\n"
"o - hydrophobic residue [WILYFVPCA]\n"
"p - aromatic residue [WYF]\n"
"x or . or ? - any amino acid")
self.line_edit.textChanged.connect(self.findTextChanged)
# Get the "Previous" and "Next" actions
action_factory = factory.Factory(parent)
action_factory.setActions(action_config.FIND_PATTERN_ACTIONS)
# Add the widgets and actions
self.addWidget(label)
self.addWidget(self.line_edit)
self.addActions(action_factory.actions)
[docs] def findTextChanged(self, string):
"""
Called whenever text in "Find pattern" widget has changed.
:type string: str
:param string: pattern string
"""
self.parent.findPattern(str(string))
[docs]class ConsensusToolBar(QtWidgets.QToolBar):
"""
A toolbar associated with the viewer that will handle display options.
These actions will be available through the toolbar:
- Display the first protein in the sequence viewer
- Display all proteins in the sequence viewer, superimposed
- Toggle waters between visible/hidden
- Toggle counterions between visible/hidden
- Toggle ligands between visible/hidden
- Display consensus waters
- Display consensus counterions
- Display consensus ligands
"""
ASL_WATER = '(water AND (NOT atom.ele H))'
ASL_IONS = 'ions'
ASL_LIGAND = '(((m.atoms 5-130)) and not ((ions) or '
ASL_LIGAND += '(res.pt ACE ACT ACY BCT BME BOG CAC CIT CO3 DMS EDO EGL EPE '
ASL_LIGAND += 'FES FMT FS3 FS4 GOL HEC HED HEM IOD IPA MES MO6 MPD MYR NAG '
ASL_LIGAND += 'NCO NH2 NH3 NO3 PG4 PO4 POP SEO SO4 SPD SPM SUC SUL TRS )))'
[docs] def __init__(self, parent):
"""
The parent needs to be an instance of the `SimplifiedSequenceViewer`.
"""
self.parent = parent
super(ConsensusToolBar, self).__init__(parent)
self.setWindowTitle('Consensus Display Toolbar')
self.factory = factory.Factory(self)
self.factory.setActions(action_config.CONSENSUS_DISPLAY)
self.addActions(self.factory.actions)
[docs] def initActionStates(self):
"""
Called when a set of structures are imported. This will:
- Include all structures in the WS
- Hide waters
- Hide ions
- Show all ligands
"""
self._undisplayInWorkspace('water')
self._undisplayInWorkspace(self.ASL_IONS)
self.ligand_action.setChecked(True)
@property
def water_action(self):
"""
Get the toggle_water action for the corresponding toolbar button
:rtype: `action<schrodinger.application.bioluminate.action.SAction>`
"""
return self.factory.getAction('toggle_waters')
@property
def ion_action(self):
"""
Get the toggle_counterions action for the corresponding toolbar button
:rtype: `action<schrodinger.application.bioluminate.action.SAction>`
"""
return self.factory.getAction('toggle_counterions')
@property
def ligand_action(self):
"""
Get the toggle_ligands action for the corresponding toolbar button
:rtype: `action<schrodinger.application.bioluminate.action.SAction>`
"""
return self.factory.getAction('toggle_ligands')
def _displayInWorkspace(self, asl):
""" Displays all atoms with `asl` in the workspace """
if maestro:
maestro.command('displayatom %s' % asl)
def _undisplayInWorkspace(self, asl):
""" Hides all atoms with `asl` in the workspace """
if maestro:
maestro.command('undisplayatom %s' % asl)
def _toggleView(self, asl):
"""
Private method to display/hide atoms based on ASL. This method
must be called by a toggle slot method.
"""
toggle = self.sender()
if toggle.isChecked():
self._displayInWorkspace(asl)
else:
self._undisplayInWorkspace(asl)
def _viewConsensus(self, asl):
"""
Private method to display/hide consensus atoms based on ASL.
"""
# If we dont have a reference don't do anything
ref_entry_id = self.getReferenceEntryId()
if not ref_entry_id:
return None
# Show all proteins since we need to display the consensus atoms
self.viewAllProteins()
ref_st = self.parent.getReferenceStructure()
mol_dict = {ref_entry_id: []}
# Loop over all PT rows associated with the SV rows, except the
# reference row. Then get the consensus atoms for each reference and
# query st.
for row in self.parent.generateSeqProjectRows(include_reference=False):
mol_dict[row.entry_id] = []
mobile_st = row.getStructure()
consensus = Consensus(ref_st, mobile_st, asl)
mol_index_map = consensus.molecule_indices
mol_dict[ref_entry_id].extend(list(mol_index_map))
mol_dict[row.entry_id].extend(list(mol_index_map.values()))
# Use the stored entry ids and their consensus molnums to display
# all atoms
for entry_id, mols in mol_dict.items():
if not mols:
continue
molnums = set(str(m) for m in mols)
asl = '((entry.id "%s")) AND (mol.entry %s)' % (entry_id,
','.join(molnums))
self._displayInWorkspace(asl)
if maestro:
maestro.command('fit all')
[docs] def getReferenceEntryId(self):
"""
Gets the PT entry ID for the reference structure.
:returns: Reference entry id
:rtype: int
"""
reference = self.parent.sequence_group.reference
return reference.maestro_entry_id
[docs] def viewFirstProtein(self):
""" Displays only the reference st in the workspace """
entry_id = self.getReferenceEntryId()
maestro.command('entrywsincludeonly entry "%s"' % entry_id)
[docs] def viewAllProteins(self):
""" Includes all structures in the sequence viewer in the Workspace """
maestro.command('delete atom all')
for seq in self.parent.structure_sequences:
if seq.maestro_entry_id is not None:
# If this sequence is in the PT
maestro.command('entrywsinclude entry "%s"' %
seq.maestro_entry_id)
[docs] def consensusWaters(self):
""" Slot to show all consensus waters """
# Turn off all waters
self.water_action.setChecked(False)
self._viewConsensus(self.ASL_WATER)
[docs] def toggleCounterions(self):
""" Slot to show/hide all ions """
self._toggleView(self.ASL_IONS)
[docs] def consensusCounterions(self):
""" Slot to show all consensus ions """
# Turn off all ions
self.ion_action.setChecked(False)
self._viewConsensus(self.ASL_IONS)
[docs] def toggleLigands(self):
""" Slot to show/hide all ligands """
self._toggleView(self.ASL_LIGAND)
[docs] def consensusLigands(self):
""" Slot to show all consensus ligands """
# Turn off all ligands
self.ligand_action.setChecked(False)
self._viewConsensus(self.ASL_LIGAND)
[docs]class AntibodyNumberingToolBar(QtWidgets.QToolBar):
"""
Get the toolbar that has the combobox with the antibody numering
schemes. This toolbar has a single action. When the combobox's
index is changed the numbering scheme chosen is applied to the
`SimplifiedSequenceViewer`
"""
SCHEMES = [('Chothia', 'Chothia'), ('EnhancedChothia', 'Enhanced Chothia'),
('Kabat', 'Kabat'), ('IMGT', 'IMGT'), ('AHo', 'AHo')]
"""
Antibody numbering schemes to choose from. Each item in the
list is a tuple of: (<backend name>, <display name>).
"""
[docs] def __init__(self, parent, title='Antibody numbering scheme'):
"""
:param title: The title of the toolbar. Used in right-click menu in
a main window's toolbar area.
:type title: str
"""
self._parent = parent
super(AntibodyNumberingToolBar, self).__init__(parent)
#self.setMovable(False)
# Keep this to offer same constructors as QToolBar
if title:
self.setWindowTitle(self.tr(title))
self._setup()
def _setup(self):
"""
Private method to set up the combobox.
"""
# Create the label and combobox
label = QtWidgets.QLabel(self.tr("Antibody numbering scheme: "))
self.combo = QtWidgets.QComboBox(self._parent)
self.combo.addItems([d_name for b_name, d_name in self.SCHEMES])
self.combo.currentIndexChanged.connect(self.indexChanged)
# Add the widgets
self.addWidget(label)
self.addWidget(self.combo)
[docs] def currentScheme(self, idx=None):
"""
Returns the backend name of the scheme matching the specified index
or the current index on the combobox, if no index is supplied.
:param idx: The index of the scheme
:type idx: int
:rtype: str
:return: The name of the requested backend scheme
"""
if not idx:
idx = self.combo.currentIndex()
backend_name, display_name = self.SCHEMES[idx]
return backend_name
[docs] def showScheme(self, scheme=None):
"""
Shows the specified scheme in the viewer, or, if no scheme is specified,
shows the scheme currently selected in the combobox.
:param scheme: The scheme to show
:type scheme: str
"""
if not scheme:
scheme = self.currentScheme()
self._parent.assignAntibodyScheme(scheme=scheme)
self._parent.antibody_scheme = scheme
[docs] def hideScheme(self):
"""
Removes the antibody scheme from the viewer altogether
"""
scheme = self.currentScheme()
self._parent.assignAntibodyScheme(scheme=scheme, remove=True)
[docs] def indexChanged(self, idx):
"""
Slot called when the combobox's index is changed. This will
change the `SimplifiedSequenceViewer` antibody numbering
scheme.
"""
scheme = self.currentScheme(idx)
self.showScheme(scheme)