Source code for schrodinger.application.msv.gui.alignment_pane

import enum

import schrodinger
from schrodinger.application.msv.gui import gui_models
from schrodinger.application.msv.gui import popups
from schrodinger.application.msv.gui import stylesheets
from schrodinger.application.msv.gui.viewconstants import \
    ALIGN_SELECTED_ONLY_CB_TT
from schrodinger.application.msv.gui.viewconstants import \
    MULTI_ALIGN_DISABLED_TT
from schrodinger.application.msv.gui.viewconstants import \
    PROF_ALIGN_DISABLED_TT
from schrodinger.application.msv.gui.viewconstants import AlignType
from schrodinger.application.msv.gui.viewconstants import MultAlnAlgorithm
from schrodinger.application.msv.gui.viewconstants import SeqAlnMode
from schrodinger.application.msv.gui.viewconstants import StructAlnMode
from schrodinger.application.msv.gui.viewconstants import StructAlnRefASLMode
from schrodinger.application.msv.gui.viewconstants import \
    StructAlnSequenceRepresents
from schrodinger.application.msv.gui.viewconstants import StructAlnTransform
from schrodinger.models import mappers
from schrodinger.protein import annotation
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtWidgets
from schrodinger.ui.qt import atomselector
from schrodinger.ui.qt import delegates
from schrodinger.ui.qt import mapperwidgets
from schrodinger.ui.qt import pop_up_widgets
from schrodinger.ui.qt import utils as qt_utils
from schrodinger.ui.qt import widgetmixins
from schrodinger.ui.qt.mapperwidgets import plptable

from . import alignment_pane_ui
from . import binding_site_aln_dialog_ui
from . import multiple_seq_aln_dialog_ui
from . import pairwise_seq_aln_dialog_ui
from . import protein_structure_aln_dialog_ui

maestro = schrodinger.get_maestro()
INFO_BTN_TOOLTIP = """This feature can only be used with the <i>Existing
entries</i> option, and only when the Reference sequence has structure.<br>The
Reference entry must have at least 2 chains that will be used in the
alignment. The entries to be aligned can have only 1 chain in the alignment.
"""

SELECTED_RES_TT = """
<p style='white-space:pre'>
This option pertains to sequence selection only.
Residue selection will automatically restrict the
sequence alignment to those residue blocks.
</p>"""

LOCK_GAPS_CB_TT = '''Preserve existing gaps in the Reference sequence (more gaps may still be
added).  When multiple pairwise alignments are requested simultaneously,
gaps will be locked automatically after the first pair is aligned.'''

PAIRWISE_SUBST_MATRICES = [
    "PAM250", "BLOSUM45", "BLOSUM62", "BLOSUM80", "GONNET", "Residue Identity"
]

SEQ_ANNO = annotation.ProteinSequenceAnnotations.ANNOTATION_TYPES

SEQ_MODE_TEXT = {
    SeqAlnMode.Multiple: "Multiple sequence alignment",
    SeqAlnMode.Pairwise: "Pairwise sequence alignment",
    SeqAlnMode.Structure: "Current structure superposition",
    SeqAlnMode.Residue: "Residue numbers",
    SeqAlnMode.Profile: "Profile alignment",
    SeqAlnMode.PairwiseSS: "Pairwise with secondary structure prediction",
}  # yapf: disable

STRUCT_MODE_TEXT = [(StructAlnMode.Superimpose, "Current sequence alignment"),
                    (StructAlnMode.Structure, "Protein structure alignment"),
                    (StructAlnMode.BindingSite, "Binding site alignment")]

OPTIONS_PAGE = enum.IntEnum('OptionsPage', [
    'multiple', 'pairwise', 'current_sequence', 'profile', 'prot_structure',
    'binding_site', 'sel_binding_site', 'current_structure', 'residue_numbers',
    'pairwise_ss'
],
                            start=0)
SETTINGS_PAGE = enum.IntEnum('SettingsPage', [
    'multiple', 'no', 'pairwise', 'prot_structure', 'binding_site',
    'pairwise_ss'
],
                             start=0)

COMBO_PAGE_MAPPING = {
    SeqAlnMode.Multiple: (OPTIONS_PAGE.multiple, SETTINGS_PAGE.multiple),
    SeqAlnMode.Pairwise: (OPTIONS_PAGE.pairwise, SETTINGS_PAGE.pairwise),
    SeqAlnMode.Structure: (OPTIONS_PAGE.current_structure, SETTINGS_PAGE.no),
    SeqAlnMode.Residue: (OPTIONS_PAGE.residue_numbers, SETTINGS_PAGE.no),
    SeqAlnMode.Profile: (OPTIONS_PAGE.profile, SETTINGS_PAGE.no),
    SeqAlnMode.PairwiseSS: (OPTIONS_PAGE.pairwise_ss, SETTINGS_PAGE.pairwise_ss),
    StructAlnMode.Superimpose: (OPTIONS_PAGE.current_sequence, SETTINGS_PAGE.no),
    StructAlnMode.Structure: (OPTIONS_PAGE.prot_structure, SETTINGS_PAGE.prot_structure),
    StructAlnMode.BindingSite: (OPTIONS_PAGE.binding_site, SETTINGS_PAGE.binding_site)
}  # yapf: disable


def _set_not_implemented(widget):
    """
    Mark a widget as not implemented
    """
    widget.setEnabled(False)
    font = widget.font()
    font.setStrikeOut(True)
    widget.setFont(font)
    widget.setToolTip("Not implemented")


[docs]class MsvComboBoxMixin:
[docs] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setStyleSheet(stylesheets.LIGHT_COMBOBOX) # MSV-1962: Workaround for a bug where the combobox background color # was being applied to the selected item background on Linux self.setItemDelegate(QtWidgets.QStyledItemDelegate(self))
[docs] def showPopup(self): # See parent class for documentation min_width = self._getWidth() self.view().setMinimumWidth(min_width) super().showPopup()
def _getWidth(self): """ Return the correct width for the text. This method is needed to prevent Macs from truncating text that is too long """ text_items = (self.itemText(i) for i in range(self.count())) font_metrics = self.fontMetrics() text_width = max( (font_metrics.horizontalAdvance(item) for item in text_items), default=0) padding = 20 return text_width + padding
[docs]class MsvMappableComboBox(MsvComboBoxMixin, mapperwidgets.MappableComboBox): pass
[docs]class MsvEnumComboBox(MsvComboBoxMixin, mapperwidgets.EnumComboBox): pass
[docs]class BasePopUp(pop_up_widgets.PopUp): """ PopUp for use with `widgetmixins.InitMixin` """
[docs] def setup(self): # No-op override of PopUp pass
[docs]class AlignmentPane(mappers.MapperMixin, widgetmixins.InitMixin, BasePopUp): """ Alignment pane for align options :ivar alignmentRequested: Signal emitted when "Align" is clicked. :vartype sequenceAlignmentRequested: QtCore.pyqtSignal """ model_class = gui_models.PageModel ui_module = alignment_pane_ui alignmentRequested = QtCore.pyqtSignal()
[docs] def initSetUp(self): super().initSetUp() self._previous_align_selected = True self.setObjectName('alignment_pane') self.setStyleSheet(stylesheets.ALIGNMENT_PANE) self._setUpComboBoxes() self._setUpProteinStructureAlnDialog() self._setUpBindingSiteAlnDialog() self._setUpMultipleSeqAlnDialog() self._setUpPairwiseSeqAlnDialog() ui = self.ui ui.dropdown_options_stacked_widget.setEnum(AlignType) self._mode_btn_grp = mapperwidgets.MappableButtonGroup({ ui.sequences_rb: AlignType.Sequence, ui.structures_rb: AlignType.Structure, }) ui.selected_res_info_btn.setToolTip(SELECTED_RES_TT) ui.clear_constraints_btn.pressed.connect( self._onClearConstraintsClicked) ui.ss_clear_constraints_btn.pressed.connect( self._onClearConstraintsClicked) ui.align_btn.clicked.connect(self._onAlignClicked) self.structure_dialog.alignBtnUpdateRequested.connect( self.updateAlignBtnVisibility) self.ui.lock_gaps_cb.setToolTip(LOCK_GAPS_CB_TT) self.ui.selected_only_cb.setToolTip(ALIGN_SELECTED_ONLY_CB_TT) self.ui.help_btn.clicked.connect(self._showHelp)
def _showHelp(self): qt_utils.help_dialog('MSV_ALIGN_PANE', parent=self)
[docs] def updateAlignBtnVisibility(self, valid): """ Enable/disable the Align button. Also, set the tooltip when disabled. :param valid: Whether to enable or disable the Align button. :type valid: bool """ self.ui.align_btn.setEnabled(valid) tooltip = "" if valid else ( "Chain mapping is invalid - correct values or toggle off option") self.ui.align_btn.setToolTip(tooltip)
[docs] def initLayOut(self): super().initLayOut() g = self.geometry() g.setWidth(0) # Force the pane to resize to the minimum width self.setGeometry(g)
[docs] def defineMappings(self): M = self.model_class settings = M.options.align_settings page_target = mappers.TargetSpec(slot=self._updatePanelState) align_type_lhs = (self._mode_btn_grp, self.ui.dropdown_options_stacked_widget, page_target) can_aln_tgt = mappers.TargetSpec(slot=self._updateCanAlign) can_aln_params = (M.menu_statuses.can_only_multiple_align, M.menu_statuses.can_only_profile_align, M.menu_statuses.can_aln_set_align) can_clear_constraints_tgt = mappers.TargetSpec( setter=self._updateClearConstraintsBtn) can_set_constraints_tgt = mappers.TargetSpec( setter=self._updateSetConstraintsBtn) return [ (align_type_lhs, settings.align_type), ((self.sequence_combo, page_target), settings.seq_align_mode), ((self.structure_combo, page_target), settings.struct_align_mode), (self.ui.selected_only_cb, settings.align_only_selected_seqs), (self.pairwise_dialog, settings.pairwise), (self.multiple_dialog, settings.multiple), (self.structure_dialog, settings.protein_structure), (self.binding_site_aln_dialog, settings.binding_site), (self.ui.globally_conserved_residues_cb, settings.multiple.find_globally_conserved), (self.ui.multiple_superimpose_structures_cb, settings.multiple.superimpose_after), (self.ui.set_constraints_cb, settings.pairwise.set_constraints), (self.ui.ss_set_constraints_cb, settings.pairwise_ss.set_constraints), (self.ui.gpcr_cb,settings.pairwise_ss.use_gpcr_aln), (can_set_constraints_tgt, M.menu_statuses.can_set_constraints), (can_clear_constraints_tgt, M.menu_statuses.can_clear_constraints), (self.ui.lock_gaps_cb, settings.pairwise.lock_gaps), (self.ui.pairwise_superimpose_structures_cb, settings.pairwise.superimpose_after), (self.ui.resnum_superimpose_structures_cb, settings.residue_number.superimpose_after), (self.ui.align_sequences_cb, settings.protein_structure.align_seqs), (self.ui.force_alignment_cb, settings.protein_structure.force), (self.ui.superimpose_selected_cb, settings.superimpose.align_sel_res_only), (self.ui.align_following_superposition_cb, settings.binding_site.align_seqs), (self.ui.align_sel_res_sequences_cb, settings.binding_site.align_seqs), ((self.binding_site_cutoff_combo, page_target), settings.binding_site.binding_site_cutoff), (can_aln_tgt, can_aln_params), ] # yapf: disable
[docs] def getSignalsAndSlots(self, model): # MSV-2583: Figure out why `res_selection_model` is getting # garbage collected self._res_sel_model_reference = model.aln.res_selection_model resSelectionChanged = model.aln.res_selection_model.selectionChanged ss = [ (resSelectionChanged, self._updateSelectedResInfoIconVisibility), (model.menu_statuses.can_align_binding_siteChanged, self._updateCanAlign), (model.menu_statuses.can_align_selected_seqsChanged, self._updateSelectedSeqCb), (model.menu_statuses.can_only_multiple_alignChanged, self._updateSelectedSeqCb), (model.menu_statuses.can_only_profile_alignChanged, self._updateSelectedSeqCb), (model.options.align_settings.binding_site.align_sel_res_onlyChanged, self._updatePanelState), ] # yapf: disable return ss
[docs] def setModel(self, model): super().setModel(model) if model is None: return self._updateSelectedResInfoIconVisibility()
def _setUpComboBoxes(self): self.sequence_combo = MsvMappableComboBox() for enum_member in SeqAlnMode: text = SEQ_MODE_TEXT[enum_member] self.sequence_combo.addItem(text, enum_member) self.ui.sequence_combo_layout.addWidget(self.sequence_combo) self.structure_combo = MsvEnumComboBox(enum=StructAlnMode) self.structure_combo.updateItemTexts(STRUCT_MODE_TEXT) self.ui.structure_combo_layout.addWidget(self.structure_combo) self.binding_site_cutoff_combo = popups.BindingSiteDistanceComboBox( parent=self) self.ui.binding_site_cutoff_layout.addWidget( self.binding_site_cutoff_combo) post_lbl = QtWidgets.QLabel(' of ligand') self.ui.binding_site_cutoff_layout.addWidget(post_lbl) def _setUpProteinStructureAlnDialog(self): """ Add settings button to create protein structure aln dialog """ protein_structure_aln_btn = popups.make_pop_up_tool_button( parent=self, pop_up_class=ProteinStructureAlnPopUp, obj_name='protein_structure_aln_btn', ) protein_structure_aln_btn.setPopupValign( protein_structure_aln_btn.ALIGN_BOTTOM) self.ui.protein_structure_alignment_settings_widget.layout().addWidget( protein_structure_aln_btn) self.structure_dialog = protein_structure_aln_btn.popup_dialog def _setUpBindingSiteAlnDialog(self): """ Add settings button that opens the binding site aln dialog """ binding_site_aln_btn = popups.make_pop_up_tool_button( parent=self, pop_up_class=BindingSiteAlnPopup, obj_name='binding_site_aln_btn', ) binding_site_aln_btn.setPopupValign(binding_site_aln_btn.ALIGN_BOTTOM) self.ui.binding_site_alignment_settings_widget.layout().addWidget( binding_site_aln_btn) self.binding_site_aln_dialog = binding_site_aln_btn.popup_dialog def _setUpMultipleSeqAlnDialog(self): """ Add settings button to create multiple seq aln dialog """ multiple_seq_aln_btn = popups.make_pop_up_tool_button( parent=self, pop_up_class=MultipleAlignSettingsPopUp, obj_name='multiple_seq_aln_btn') multiple_seq_aln_btn.setPopupValign(multiple_seq_aln_btn.ALIGN_BOTTOM) self.ui.Multiple_sequence_alignment_settings_widget.layout().addWidget( multiple_seq_aln_btn) self.multiple_dialog = multiple_seq_aln_btn.popup_dialog def _setUpPairwiseSeqAlnDialog(self): """ Add settings button to create pairwise seq aln dialog """ pairwise_seq_aln_btn = popups.make_pop_up_tool_button( parent=self, pop_up_class=PairwiseAlignSettingsPopUp, obj_name='pairwise_seq_aln_btn') pairwise_seq_aln_btn.setPopupValign(pairwise_seq_aln_btn.ALIGN_BOTTOM) self.ui.pairwise_sequence_alignment_settings_widget.layout().addWidget( pairwise_seq_aln_btn) self.pairwise_dialog = pairwise_seq_aln_btn.popup_dialog ############################################## # Mapper callbacks ############################################## @QtCore.pyqtSlot() def _updateSelectedSeqCb(self): """ Manage state of "Selected seqs" checkbox """ menu_statuses = self.model.menu_statuses align_restricted = (menu_statuses.can_only_multiple_align or menu_statuses.can_only_profile_align) enable = menu_statuses.can_align_selected_seqs and not align_restricted self.ui.selected_only_cb.setEnabled(enable) settings = self.model.options.align_settings if enable: # If we're enabling the checkbox, restore the value to what it was # when we disabled it. settings.align_only_selected_seqs = self._previous_align_selected else: # If we're disabling the checkbox, save the state first so # we can restore it later. self._previous_align_selected = settings.align_only_selected_seqs settings.align_only_selected_seqs = align_restricted @QtCore.pyqtSlot() def _updateSelectedResInfoIconVisibility(self): """ Manage the visibility of the info icon next to the "Selected Only" checkbox. The info icon will be shown for sequence alignment modes if any residues are selected. """ should_show = ( self.model.aln.res_selection_model.hasSelection() and self.model.options.align_settings.align_type is AlignType.Sequence) self.ui.selected_res_info_btn.setVisible(should_show) @QtCore.pyqtSlot() def _updatePanelState(self): """ Change options and settings pages based on current mode """ ui = self.ui settings = self.model.options.align_settings align_mode = settings.getAlignMode() options_page, settings_page = COMBO_PAGE_MAPPING[align_mode] if options_page == OPTIONS_PAGE.binding_site \ and settings.binding_site.align_sel_res_only: options_page = OPTIONS_PAGE.sel_binding_site ui.checkable_options_stacked_widget.setCurrentIndex(options_page) ui.settings_stacked_widget.setCurrentIndex(settings_page) self._updateCanAlign() self._updateSelectedResInfoIconVisibility() @QtCore.pyqtSlot() def _updateCanAlign(self): """ Update whether aligning is allowed """ enable_align = True settings = self.model.options.align_settings statuses = self.model.menu_statuses align_type = settings.align_type align_mode = settings.getAlignMode() align_btn_tooltip = '' if not statuses.can_aln_set_align: enable_align = False align_btn_tooltip = PROF_ALIGN_DISABLED_TT elif align_type is AlignType.Sequence: is_profile = align_mode is SeqAlnMode.Profile must_profile = statuses.can_only_profile_align if is_profile or must_profile: enable_align = is_profile and must_profile if not enable_align: align_btn_tooltip = (PROF_ALIGN_DISABLED_TT if is_profile else MULTI_ALIGN_DISABLED_TT) elif not self.model.split_chain_view: enable_align = False align_btn_tooltip = "Profile alignment not enabled in combined chain mode" elif statuses.can_only_multiple_align: enable_align = align_mode is SeqAlnMode.Multiple elif align_type is AlignType.Structure: if (statuses.can_only_multiple_align or statuses.can_only_profile_align): enable_align = False elif align_mode == StructAlnMode.Superimpose: enable_align = bool(maestro) elif align_mode == StructAlnMode.BindingSite and settings.binding_site.align_sel_res_only: enable_align = statuses.can_align_binding_site self.ui.align_btn.setEnabled(enable_align) self.ui.align_btn.setToolTip(align_btn_tooltip) @QtCore.pyqtSlot(bool) def _updateClearConstraintsBtn(self, can_clear_constraints): """ Update the enabled state of the clear constraints btn """ self.ui.clear_constraints_btn.setEnabled(can_clear_constraints) self.ui.ss_clear_constraints_btn.setEnabled(can_clear_constraints) @QtCore.pyqtSlot(bool) def _updateSetConstraintsBtn(self, can_set_constraints): """ Update the enabled state of the set constraints btn """ self.ui.set_constraints_cb.setEnabled(can_set_constraints) self.ui.ss_set_constraints_cb.setEnabled(can_set_constraints) ############################################## # Widget slots ############################################## def _onClearConstraintsClicked(self): self.model.aln.resetPairwiseConstraints() def _onAlignClicked(self): self.close() self.alignmentRequested.emit()
[docs]class PairwiseAlignSettingsPopUp(mappers.MapperMixin, widgetmixins.InitMixin, BasePopUp): model_class = gui_models.PairwiseAlignSettingsModel ui_module = pairwise_seq_aln_dialog_ui
[docs] def initSetUp(self): super().initSetUp() self.setObjectName('pairwise_seq_aln_dialog') self.setStyleSheet(stylesheets.PAIRWISE_SEQ_ALN_DIALOG) self._setUpMatrixCombobox() self.valid_map = True
def _setUpMatrixCombobox(self): """ Creates and adds drop down options for similarity matrix type """ self.subst_matrix_combobox = popups.MsvComboBox(self) for item in PAIRWISE_SUBST_MATRICES: self.subst_matrix_combobox.addItem(item, item) self.ui.matrix_cb_layout.layout().addWidget(self.subst_matrix_combobox)
[docs] def defineMappings(self): """ Sets mapping between objects and options model """ M = self.model_class return [ (self.subst_matrix_combobox, M.sub_matrix), (self.ui.creating_gap_sb, M.gap_open_penalty), (self.ui.extending_gap_sb, M.gap_extend_penalty), (self.ui.prevent_gaps_cb, M.prevent_ss_gaps), (self.ui.penalize_end_gaps_cb, M.penalize_end_gaps), ] # yapf: disable
[docs]class MultipleAlignSettingsPopUp(mappers.MapperMixin, widgetmixins.InitMixin, BasePopUp): model_class = gui_models.MultipleAlignSettingsModel ui_module = multiple_seq_aln_dialog_ui
[docs] def initSetUp(self): super().initSetUp() self.setObjectName('multiple_seq_aln_dialog') self.setStyleSheet(stylesheets.MULTIPLE_SEQ_ALN_DIALOG) self._algorithm_btn_grp = mapperwidgets.MappableButtonGroup({ self.ui.clustalW_rb: MultAlnAlgorithm.Clustal, self.ui.muscle_rb: MultAlnAlgorithm.Muscle })
[docs] def defineMappings(self): M = self.model_class return [ (self._algorithm_btn_grp, M.aln_algorithm), (self.ui.gap_open_sb, M.gap_open_penalty), (self.ui.gap_extend_sb, M.gap_extend_penalty), ] # yapf: disable
[docs]class MappableAtomSelector(mappers.TargetMixin, atomselector.AtomSelector):
[docs] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.aslTextModified.connect(self.targetValueChanged)
[docs] def targetGetValue(self): return self.getAsl()
[docs] def targetSetValue(self, value): self.setAsl(value)
[docs]class ProteinStructureAlnPopUp(mappers.MapperMixin, widgetmixins.InitMixin, BasePopUp): """ Popup for protein structure alignment settings :ivar alignBtnUpdateRequested: Signal emitted when the Structrue Alignment popup is closed, to disable/enable the 'Align' button. Emitted with a bool - True if the map is valid and False, otherwise. :vartype alignBtnUpdateRequested: QtCore.pyqtSignal """ alignBtnUpdateRequested = QtCore.pyqtSignal(bool) model_class = gui_models.ProteinStructureAlignSettingsModel ui_module = protein_structure_aln_dialog_ui
[docs] def initSetUp(self): super().initSetUp() self.setObjectName('protein_structure_aln_dialog') self.setStyleSheet(stylesheets.PROTEIN_STRUCTURE_ALN_DIALOG) self._seq_represents_btn_grp = mapperwidgets.MappableButtonGroup({ self.ui.single_chain_rb: StructAlnSequenceRepresents.SingleChain, self.ui.entire_entry_rb: StructAlnSequenceRepresents.EntireEntry }) self._align_transforms_btn_grp = mapperwidgets.MappableButtonGroup({ self.ui.existing_entries_rb: StructAlnTransform.Existing, self.ui.individual_chains_rb: StructAlnTransform.Individual, }) self._ref_atomselector_btn_grp = mapperwidgets.MappableButtonGroup({ self.ui.all_rb: StructAlnRefASLMode.All, self.ui.selected_rb: StructAlnRefASLMode.Selected, self.ui.matching_ASL_rb: StructAlnRefASLMode.ASL, }) self._other_atomselector_btn_grp = mapperwidgets.MappableButtonGroup({ self.ui.as_defined_above_rb: False, self.ui.matching_diff_ASL_rb: True, }) self.ref_atomselector = self._createAtomSelector() self.other_atomselector = self._createAtomSelector() self.ui.info_btn.setIconPath(':/msv/icons/info_icon.png') self.ui.info_btn.setToolTip(INFO_BTN_TOOLTIP) self._createChainMapTable() self.ui.warning_lbl.setVisible(False) self.ui.map_seqs_cb.setEnabled(False)
def _updateWarningLabel(self): """ Update the visibility and text of the warning message in the popup. """ valid_aln_map = self.model.validateAlignmentMap() warning_txt = self.model.getUnusedChainWarningText() self.ui.warning_lbl.setVisible(not valid_aln_map) self.ui.warning_lbl.setText(warning_txt)
[docs] def showEvent(self, event): self._updateWarningLabel() super().showEvent(event) if not self.model.map_seqs: self._adjustSizeInSingleShot()
[docs] def closeEvent(self, event): """ Request to enable/disable the 'Align' button in the 'Alignment pane', depending on whether the mapping is valid or not. """ self.alignBtnUpdateRequested.emit(self.model.validateAlignmentMap()) super().closeEvent(event)
[docs] def initLayOut(self): super().initLayOut() self.ui.ref_atomselector_layout.addWidget(self.ref_atomselector) self.ui.other_atomselector_layout.addWidget(self.other_atomselector)
[docs] def defineMappings(self): M = self.model_class seq_rep_tgt = mappers.TargetSpec(self._seq_represents_btn_grp, slot=self._updateSeqRepresents) ref_asl_tgt = mappers.TargetSpec( self._ref_atomselector_btn_grp, slot=self._updateRefAtomSelectorEnabled) other_asl_tgt = mappers.TargetSpec( self._other_atomselector_btn_grp, slot=self._updateOtherAtomSelectorEnabled) enable_seq_rep_chain = mappers.TargetSpec( getter=self.ui.single_chain_rb.isEnabled, setter=self.ui.single_chain_rb.setEnabled) return [ (seq_rep_tgt, M.seq_represents), (self._align_transforms_btn_grp, M.align_transforms), (self.ui.map_seqs_cb, M.map_seqs), (ref_asl_tgt, M.ref_asl_mode), (other_asl_tgt, M.other_define_asl), (self.ref_atomselector, M.ref_asl), (self.other_atomselector, M.other_asl), (enable_seq_rep_chain, M.seq_can_represent_chain), (self._chain_map_table, M.chain_name), (self._updateWarningLabel, M.chain_name), (self._disableChainMap, M.map_seqs_enable), (self._updateReferenceChainCombo, M.available_ref_chains), ] # yapf: disable
[docs] def getSignalsAndSlots(self, model): return [ (model.map_seqsChanged, self._setMappingOptionsVisibility), (model.align_transformsChanged, self._setMappingOptionsVisibility), ]
def _setMappingOptionsVisibility(self): visible = self.model.align_transforms == StructAlnTransform.Existing self.ui.map_seqs_cb.setVisible(visible) self.ui.info_btn.setVisible(visible) table_visible = visible and self.model.map_seqs self.ui.table_widget.setVisible(table_visible) warning_visible = table_visible and not self.model.isAlignmentMapValid() self.ui.warning_lbl.setVisible(warning_visible) self._adjustSizeInSingleShot() def _createChainMapTable(self): table = self.ui.table_widget table.setSpec(ChainMapTableSpec()) combo_delegate = delegates.ComboBoxDelegate(table) reference_col_idx = table.getColumnIndex(table.spec.reference) table.view.setItemDelegateForColumn(reference_col_idx, combo_delegate) table.setVisible(False) self._chain_map_table = table def _updateReferenceChainCombo(self): ref_chains = self.model.available_ref_chains self._chain_map_table.spec.setReferenceChains(ref_chains) def _disableChainMap(self): enable = self.model.map_seqs_enable self.ui.map_seqs_cb.setChecked(False) self.ui.map_seqs_cb.setEnabled(False) if enable: self.ui.map_seqs_cb.setEnabled(enable) def _updateStackedWidget(self, sw, new_index): """ Set a new index on the stacked widget and ignore the size of any other page, allowing the widget to expand and contract based on the size of the current page :param sw: Stacked widget :type sw: QtWidgets.QStackedWidget :param new_index: Index to set on the stacked widget :type new_index: int """ QSP = QtWidgets.QSizePolicy for idx in range(sw.count()): policy = QSP.Expanding if idx == new_index else QSP.Ignored widget = sw.widget(idx) widget.setSizePolicy(policy, policy) sw.setCurrentIndex(new_index) self.adjustSize() def _createAtomSelector(self): """ Create an atom selector customized for MSV2 protein structure alignment :return: Atom selector :rtype: AtomSelector """ atomselector = MappableAtomSelector( parent=self, show_select=False, show_selection=True, show_pick=False, show_plus=True, ) try: selection_button = atomselector.selection_button except AttributeError: pass else: # Workaround for MAE-42614 button_layout = QtWidgets.QHBoxLayout() button_layout.addStretch(1) button_layout.addWidget(selection_button) button_layout.addStretch() atomselector.main_layout.addLayout(button_layout) additional_stylesheet = """ QMenu QPushButton { color: black; } """ current_stylesheet = atomselector.styleSheet() atomselector.setStyleSheet(current_stylesheet + additional_stylesheet) return atomselector def _updateSeqRepresents(self): index = self.model.seq_represents.value self._updateStackedWidget(self.ui.entry_options_stacked_widget, index) def _updateRefAtomSelectorEnabled(self): asl_mode = self.model.ref_asl_mode enabled = asl_mode is StructAlnRefASLMode.ASL self.ref_atomselector.setEnabled(enabled) def _updateOtherAtomSelectorEnabled(self): enabled = self.model.other_define_asl self.other_atomselector.setEnabled(enabled) def _adjustSizeInSingleShot(self): # When the map table is hidden, the popup is not resized immediately and # leaves some empty space. To avoid this, adjusting size after 12 ms (12 # was out of trial and error). See MSV-3681 for details. QtCore.QTimer.singleShot(12, self.adjustSize)
[docs]class BindingSiteAlnPopup(mappers.MapperMixin, widgetmixins.InitMixin, BasePopUp): """ Popup for binding site alignment settings """ model_class = gui_models.BindingSiteAlignSettingsModel ui_module = binding_site_aln_dialog_ui
[docs] def initSetUp(self): super().initSetUp() self.setObjectName('binding_site_aln_dialog') self.setStyleSheet(stylesheets.BINDING_SITE_ALN_DIALOG) self._setUpComboBoxes()
def _setUpComboBoxes(self): self.mapping_dist_combo = popups.BindingSiteDistanceComboBox( parent=self) self.ui.mapping_dist_layout.addWidget(self.mapping_dist_combo)
[docs] def defineMappings(self): M = self.model_class return [ (self.ui.align_sel_res_only_cb, M.align_sel_res_only), (self.mapping_dist_combo, M.mapping_dist), (self.ui.previously_aligned_cb, M.previously_aligned) ] # yapf: disable
[docs]class ChainMapTableSpec(plptable.TableSpec): @plptable.FieldColumn(gui_models.ChainName.seq_chain, title="Sequence (1 per entry)") def sequence_chain(self, field): return f"{field[0]}_{field[1]}" reference = plptable.FieldColumn(gui_models.ChainName.reference, title="Reference Chain", editable=True)
[docs] @reference.data_method(role=delegates.ComboBoxDelegate.COMBOBOX_ROLE) def populateComboBox(self, field): return self._reference_chains
[docs] def setReferenceChains(self, ref_chains): """ Set all the available chains from the reference structure ready to be added to the combobox delegate. :param ref_chains: list of chains. :type ref_chains: list(str) """ self._reference_chains = ref_chains