"""
Implementation of Qt dialog boxes used by multiple sequence viewer.
Copyright Schrodinger, LLC. All rights reserved.
"""
# Contributors: Piotr Rotkiewicz
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtGui
from schrodinger.Qt import QtWidgets
from schrodinger.ui.sequencealignment import constants
from schrodinger.ui.sequencealignment import dialogs
from schrodinger.ui.sequencealignment import sequence
from schrodinger.ui.sequencealignment.maestro import getMaestroLigandList
try:
    from schrodinger.maestro import maestro
except:
    maestro = None
_PRIME_SETTINGS_DIALOG = None
[docs]class PrimeSettingsDialog(QtWidgets.QDockWidget):
    """
    This class implements the "Prime Settings" dialog.
    """
[docs]    def __init__(self, sequence_group, viewer=None):
        # Initialize base class.
        QtWidgets.QDockWidget.__init__(self)
        self.progress_dialog = None
        self.build_model_func = None
        self.setAllowedAreas(QtCore.Qt.RightDockWidgetArea)
        self.viewer = viewer
        if self.viewer and hasattr(self.viewer, "main_window"):
            self.viewer.main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea,
                                                  self)
        self.setWindowTitle("Build Homology Model")
        self.setWindowModality(QtCore.Qt.WindowModal)
        self.sequence_group = sequence_group
        #: "Build model" push button.
        self.button_ok = QtWidgets.QPushButton(self)
        self.button_ok.setText("Build Model")
        self.button_ok.setDefault(True)
        self.button_ok.clicked.connect(self.buildModel)
        #: "Help" push button.
        self.button_help = QtWidgets.QPushButton(self)
        self.button_help.setText("Help")
        self.button_help.clicked.connect(self.helpCB)
        self.visibilityChanged.connect(self._visibilityChanged)
        self.button_clear_constraints = QtWidgets.QPushButton(self)
        self.button_clear_constraints.setText("Clear All Constraints")
        self.button_clear_constraints.setToolTip(
            "Remove all proximity constraints")
        self.button_clear_constraints.clicked.connect(self.clearConstraints)
        self.template_regions_rb = QtWidgets.QRadioButton(self)
        self.template_regions_rb.setText("Select sequence regions " +
                                         "to build a composite/chimera model")
        self.template_regions_rb.setToolTip(
            "Select sequence regions "
            "to build a composite model.\n"
            "Click on a template name to select "
            "entire sequence.")
        self.template_regions_rb.clicked.connect(self.buildModeChanged)
        self.homomultimers_rb = QtWidgets.QRadioButton(self)
        self.homomultimers_rb.setChecked(True)
        self.homomultimers_rb.setText("Select multiple templates " +
                                      "to build a homo-multimer model")
        self.homomultimers_rb.setToolTip("Select multiple templates.\n"
                                         "Each homo-multimer chain will"
                                         " be built\nusing individual template"
                                         " chains.")
        self.homomultimers_rb.clicked.connect(self.buildModeChanged)
        self.heteromultimers_cb = QtWidgets.QCheckBox(self)
        self.heteromultimers_cb.setChecked(False)
        self.heteromultimers_cb.setText("Build a hetero-multimer model")
        self.heteromultimers_cb.setToolTip("Create and select new query tab "
                                           "for each hetero-multimer chain.\n"
                                           "Then select them from a list of "
                                           "queries.")
        self.heteromultimers_cb.clicked.connect(self.buildHeteroMultimer)
        self.single_template_rb = QtWidgets.QRadioButton(self)
        self.single_template_rb.setChecked(True)
        self.single_template_rb.setText("Select single template " +
                                        "to build a single chain model")
        self.single_template_rb.setToolTip("Select single template sequence.\n"
                                           "Otherwise the model will be built "
                                           "using a first available template.")
        self.single_template_rb.clicked.connect(self.buildModeChanged)
        self.consensus_rb = QtWidgets.QRadioButton(self)
        self.consensus_rb.setChecked(False)
        self.consensus_rb.setText("Select multiple templates " +
                                  "to build a consensus homology model")
        self.consensus_rb.clicked.connect(self.buildModeChanged)
        #: Knowledge-based mode radio button.
        self.knowledge_based_rb = QtWidgets.QRadioButton()
        self.knowledge_based_rb.setText("Knowledge-based (faster)")
        self.knowledge_based_rb.setToolTip(
            "Construct insertions and close gaps using segments from "
            "known structures")
        self.knowledge_based_rb.setChecked(True)
        self.knowledge_based_rb.clicked.connect(self.buildModeChanged)
        #: Energy-based mode radio button.
        self.energy_based_rb = QtWidgets.QRadioButton()
        self.energy_based_rb.setText("Energy-based")
        self.energy_based_rb.setToolTip(
            "Energy-based construction and refinement of residues "
            "not derived from the template")
        self.energy_based_rb.clicked.connect(self.buildModeChanged)
        #: Knowledge-based mode line edit.
        self.kb_models_le = QtWidgets.QLineEdit()
        self.kb_models_le.setText("1")
        self.kb_models_le.setValidator(
            QtGui.QIntValidator(1, 100, self.kb_models_le))
        self.kb_models_le.setMaximumWidth(40)
        self.kb_models_le.setAlignment(QtCore.Qt.AlignRight)
        #: Number of models layout
        self.kb_models_layout = QtWidgets.QHBoxLayout()
        self.kb_models_layout.addSpacing(20)
        self.kb_models_layout.addWidget(QtWidgets.QLabel("Build"))
        self.kb_models_layout.addWidget(self.kb_models_le)
        self.kb_models_layout.addWidget(QtWidgets.QLabel("model(s)"))
        self.kb_models_layout.addStretch()
        #: Method layout
        self.method_layout = QtWidgets.QVBoxLayout()
        self.method_layout.addWidget(self.single_template_rb)
        self.method_layout.addWidget(self.template_regions_rb)
        self.method_layout.addWidget(self.homomultimers_rb)
        self.method_layout.addWidget(self.consensus_rb)
        self.method_layout.addStretch()
        #: "Close" push button.
        self.button_close = QtWidgets.QPushButton(self)
        self.button_close.setText("Close")
        self.button_close.clicked.connect(self.close)
        spacer = QtWidgets.QWidget()
        spacer.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
                             QtWidgets.QSizePolicy.Preferred)
        self.constraints_layout = QtWidgets.QHBoxLayout()
        self.constraints_layout.addWidget(self.button_clear_constraints)
        self.button_import = QtWidgets.QPushButton(self)
        self.button_import.setText("1) Import Sequence")
        self.button_import.setToolTip(
            "Import the sequence of the model to be built.")
        self.button_import.clicked.connect(self.buttonImportCB)
        self.button_find = QtWidgets.QPushButton(self)
        self.button_find.setText("2) Find Templates")
        self.button_find.setToolTip(
            "Run a BLAST search to identify potential templates.")
        self.button_find.clicked.connect(self.buttonFindCB)
        self.button_get = QtWidgets.QPushButton(self)
        self.button_get.setText("3) Get Structures")
        self.button_get.setToolTip(
            "Retrieve structures from the PDB for the current templates.")
        self.button_get.clicked.connect(self.buttonGetCB)
        #: Top button layout.
        self.top_button_layout = QtWidgets.QHBoxLayout()
        self.top_button_layout.addWidget(self.button_import)
        self.top_button_layout.addWidget(self.button_find)
        self.top_button_layout.addWidget(self.button_get)
        #: Button layout.
        self.button_layout = QtWidgets.QHBoxLayout()
        self.button_layout.addWidget(self.button_help)
        self.button_layout.addStretch()
        self.button_layout.addWidget(self.button_ok)
        self.button_layout.addWidget(self.button_close)
        #: Main widget.
        self.contents = QtWidgets.QWidget(self)
        self.contents.setLayout(QtWidgets.QVBoxLayout())
        #: Settings tabs.
        self.tabs = QtWidgets.QTabWidget()
        self.setWidget(self.contents)
        #: Retain residue numbers checkbox.
        self.retain_numbers_cb = QtWidgets.QCheckBox()
        self.retain_numbers_cb.setText(
            "Preserve the residue numbers of the template")
        self.retain_numbers_cb.setChecked(False)
        self.retain_numbers_cb.clicked.connect(self.buildModeChanged)
        #: Retain rotamers checkbox.
        self.retain_rotamers_cb = QtWidgets.QCheckBox()
        self.retain_rotamers_cb.setText(
            "Retain rotamers for conserved residues")
        self.retain_rotamers_cb.setChecked(True)
        #: Optimize side chains checkbox.
        self.optimize_side_chains_cb = QtWidgets.QCheckBox()
        self.optimize_side_chains_cb.setText("Optimize side chains")
        self.optimize_side_chains_cb.setChecked(True)
        self.optimize_side_chains_cb.clicked.connect(
            self.optimizeSideChainsChanged)
        #: Minimize residues not obtained from templates.
        self.minimize_residues_cb = QtWidgets.QCheckBox()
        self.minimize_residues_cb.setText(
            "Minimize residues not obtained from templates")
        self.minimize_residues_cb.setChecked(True)
        self.insertions_cb = QtWidgets.QCheckBox()
        self.insertions_cb.setText("Build insertions of up to")
        self.insertions_cb.setChecked(True)
        self.insertions_cb.setToolTip(
            "Build untemplated loops of less than the given number of residues;\n"
            "otherwise delete the loops and cap ends with NMA and ACE")
        self.insertions_le = QtWidgets.QLineEdit()
        self.insertions_le.setText("20")
        self.insertions_le.setValidator(
            QtGui.QDoubleValidator(1, 100, 1, self.insertions_le))
        self.insertions_le.setMaximumWidth(40)
        self.insertions_le.setAlignment(QtCore.Qt.AlignRight)
        self.junctions_cb = QtWidgets.QCheckBox()
        self.junctions_cb.setText("Close template junctions")
        self.junctions_cb.setChecked(True)
        self.junctions_cb.setToolTip(
            "Build structure through template junctions so that\n"
            "there are no gaps; otherwise cap ends with NMA and ACE")
        self.insertions_layout = QtWidgets.QHBoxLayout()
        self.insertions_layout.setContentsMargins(0, 0, 0, 0)
        self.insertions_layout.addWidget(self.insertions_cb)
        self.insertions_layout.addWidget(self.insertions_le)
        self.insertions_label = QtWidgets.QLabel("residues")
        self.insertions_layout.addWidget(self.insertions_label)
        self.insertions_layout.addStretch()
        self.deletions_cb = QtWidgets.QCheckBox()
        self.deletions_cb.setText("Close deletions")
        self.deletions_cb.setChecked(True)
        self.deletions_cb.setToolTip(
            "Build structure through deletions in the template so that\n"
            "there are no gaps; otherwise cap ends with NMA and ACE")
        self.insertions_cb.setEnabled(False)
        self.settings_page = QtWidgets.QWidget()
        self.settings_page_layout = QtWidgets.QVBoxLayout()
        self.settings_page_layout.addWidget(self.retain_numbers_cb)
        self.settings_page_layout.addWidget(self.retain_rotamers_cb)
        self.settings_page_layout.addWidget(self.optimize_side_chains_cb)
        self.settings_page_layout.addWidget(self.minimize_residues_cb)
        self.settings_page_layout.addLayout(self.insertions_layout)
        self.settings_page_layout.addWidget(self.junctions_cb)
        self.settings_page_layout.addWidget(self.deletions_cb)
        self.settings_page_layout.addStretch(1000)
        self.settings_page.setLayout(self.settings_page_layout)
        self.multimer_page = QtWidgets.QWidget()
        self.template_list = QtWidgets.QListWidget()
        self.template_list.setSelectionMode(
            QtWidgets.QAbstractItemView.MultiSelection)
        self.template_list.itemSelectionChanged.connect(
            self.templateListSelectionChanged)
        self.multimer_page_layout = QtWidgets.QVBoxLayout()
        self.multimer_page.setLayout(self.multimer_page_layout)
        self.multimer_page_layout.addWidget(
            QtWidgets.QLabel(
                "Create a new query for each template chain "
                "(click '+'\nabove sequence window), then select them from list."
            ))
        self.multimer_page_layout.addWidget(self.template_list)
        # Constraints tab.
        self.constraints_page = QtWidgets.QWidget()
        self.constraints_page_layout = QtWidgets.QVBoxLayout()
        self.constraints_page.setLayout(self.constraints_page_layout)
        self.constraints_page_layout.addWidget(
            QtWidgets.QLabel(
                "You can create proximity constraints in order to define "
                "<br>pairs of residues to be close in final model."
                "<ul><li>Select pairs of query residues to add or remove a constraint.<br>"
                "<li>Click 'Clear All Constraints' to remove all constraints.</ul>"
            ))
        self.constraints_page_layout.addStretch()
        self.constraints_page_layout.addLayout(self.constraints_layout)
        self.tabs.currentChanged.connect(self.tabChanged)
        self.ligands_page = QtWidgets.QWidget()
        self.ligands_list = QtWidgets.QListWidget()
        self.ligands_list.setSelectionMode(
            QtWidgets.QAbstractItemView.MultiSelection)
        self.ligands_list.itemSelectionChanged.connect(
            self.ligandListSelectionChanged)
        self.all_ligands_cb = QtWidgets.QCheckBox(self)
        self.all_ligands_cb.setText("Show All")
        self.all_ligands_cb.clicked.connect(self.refreshLigands)
        self.ligands_page_layout = QtWidgets.QVBoxLayout()
        self.ligands_page.setLayout(self.ligands_page_layout)
        self.ligands_page_layout.addWidget(
            QtWidgets.QLabel(
                "Select ligands from list "
                "to include them in a final model.\n"
                "The selected ligands will be highlighted in the workspace.\n\n"
                "Click on a position in the ligand annotation row of the sequence viewer \n"
                "to set a protein-ligand proximity constraint.  The query residue at that \n"
                "position will be in close proximity to the ligand in the final model."
            ))
        self.ligands_page_layout.addWidget(self.all_ligands_cb)
        self.ligands_page_layout.addWidget(self.ligands_list)
        self.method_gb = QtWidgets.QGroupBox()
        self.method_gb.setLayout(self.method_layout)
        self.mode_page = QtWidgets.QWidget()
        self.mode_page_layout = QtWidgets.QVBoxLayout()
        self.mode_page.setLayout(self.mode_page_layout)
        self.mode_page_layout.addWidget(self.knowledge_based_rb)
        self.mode_page_layout.addLayout(self.kb_models_layout)
        self.mode_page_layout.addWidget(self.energy_based_rb)
        self.mode_page_layout.addStretch()
        self.tabs.addTab(self.mode_page, "Method")
        self.tabs.addTab(self.settings_page, "Settings")
        self.tabs.addTab(self.ligands_page, "Ligands")
        self.tabs.addTab(self.constraints_page, "Constraints")
        self.tabs.addTab(self.multimer_page, "Hetero-multimer")
        self.tabs.setCurrentIndex(0)
        self.tabs.setTabToolTip(3, "Energy-based mode only")
        self.hetero_layout = QtWidgets.QVBoxLayout()
        self.hetero_layout.addWidget(self.heteromultimers_cb)
        self.hetero_gb = QtWidgets.QGroupBox(self)
        self.hetero_gb.setLayout(self.hetero_layout)
        self.hetero_gb.setTitle("")
        self.contents.layout().addLayout(self.top_button_layout)
        self.contents.layout().addWidget(self.method_gb)
        self.contents.layout().addWidget(self.hetero_gb)
        self.contents.layout().addWidget(self.tabs)
        self.contents.layout().addLayout(self.button_layout)
        self.updateWidgets()
        self.query_list = []
        self.ignore_ligand_selection_change = False 
[docs]    def optimizeSideChainsChanged(self):
        if self.optimize_side_chains_cb.isChecked():
            self.minimize_residues_cb.setEnabled(True)
        else:
            self.minimize_residues_cb.setEnabled(False) 
[docs]    def toggleConstraints(self, visible):
        """
        Toggles visibility of the query constraints.
        """
        if visible:
            if hasattr(self.sequence_group, 'query_constraints'):
                for index, seq in enumerate(self.sequence_group.sequences):
                    if seq == self.sequence_group.reference:
                        self.sequence_group.sequences.insert(
                            index + 1, self.sequence_group.query_constraints)
                        return
        else:
            for seq in self.sequence_group.sequences:
                if seq.type == constants.SEQ_CONSTRAINTS:
                    self.sequence_group.query_constraints = seq
                    self.sequence_group.sequences.remove(seq)
                    return 
    def _visibilityChanged(self, visible):
        """
        Panel visibility change.
        """
        if not visible:
            self.viewer.emphasizeArea(0)
            self.viewer.build_mode = False
            self.viewer.disableQueryConstraints()
            self.toggleConstraints(False)
            self.removeQueryLigands()
        else:
            self.viewer.build_mode = True
            self.buildModeChanged()
            self.tabChanged(0)
        self.validateBuildMode()
[docs]    def tabChanged(self, index):
        """
        Tab changed callback.
        """
        page = self.tabs.currentWidget()
        if page == self.multimer_page:
            self.refreshTemplates()
        if page == self.constraints_page:
            self.toggleConstraints(True)
            self.addConstraints()
        else:
            self.toggleConstraints(False)
            self.viewer.disableQueryConstraints()
        if page == self.ligands_page:
            self.refreshLigands()
        else:
            self.removeQueryLigands() 
[docs]    def removeQueryLigands(self):
        """
        Clears the list of query ligand annotations.
        Called when ligand tab is closed or the user exits the build mode.
        """
        reference = self.sequence_group.reference
        if not reference:
            return
        # Remove all ligand annotations
        reference.children = [
            child for child in reference.children
            if child.annotation_type != constants.ANNOTATION_LIGAND
        ]
        # Repaint viewer
        if self.viewer:
            self.viewer.alignment_changed = True
            self.viewer.updateView() 
[docs]    def addQueryLigands(self, ligand_name_list):
        """
        Creates ligand annotations for the query sequence.
        The annotations are used to set ligand-query constraints.
        :type ligand_name_list: list(str)
        :param ligand_name_list: List of ligand names.
        """
        reference = self.sequence_group.reference
        if not reference:
            return
        current_ligand_list = []
        for child in reference.children:
            if child.annotation_type == constants.ANNOTATION_LIGAND:
                current_ligand_list.append(child.name)
        if ligand_name_list == current_ligand_list:
            # The list did not change; return early.
            return
        self.removeQueryLigands()
        has_ligand_sequences = False
        # Check if there are stored ligand annotations
        # and use them if they are valid
        if hasattr(self.sequence_group, 'ligand_list') and \
                
self.sequence_group.ligand_list:
            group_ligand_names, group_ligand_sequences = list(
                zip(*self.sequence_group.ligand_list))
            if list(group_ligand_names) == ligand_name_list:
                # The ligand name list matches exisiting annotations; reuse.
                reference.children += group_ligand_sequences
                has_ligand_sequences = True
        if not has_ligand_sequences:
            group_ligand_list = []
            # Create new ligand annotations
            for ligand in ligand_name_list:
                lig_sequence = sequence.Sequence()
                lig_sequence.type = constants.SEQ_ANNOTATION
                lig_sequence.annotation_type = constants.ANNOTATION_LIGAND
                lig_sequence.name = ligand
                lig_sequence.short_name = lig_sequence.name
                lig_sequence.appendResidues(reference.gaplessText())
                lig_sequence.parent_sequence = reference
                for res in lig_sequence.residues:
                    res.code = ' '
                reference.children.append(lig_sequence)
                group_ligand_list.append((ligand, lig_sequence))
            self.sequence_group.ligand_list = group_ligand_list
        # Update gaps in the ligand sequences
        reference.propagateGapsToChildren()
        # Repaint the viewer
        if self.viewer:
            self.viewer.alignment_changed = True
            self.viewer.updateView() 
[docs]    def ligandListSelectionChanged(self):
        """
        Called when ligand list selection changes.
        """
        if self.ignore_ligand_selection_change:
            return
        selected_items = self.ligands_list.selectedItems()
        ligand_name_list = []
        if selected_items:
            for item in selected_items:
                ligand_name_list.append(str(item.text())[5:])
        self.addQueryLigands(ligand_name_list) 
[docs]    def templateListSelectionChanged(self):
        """
        Called when template list selection changes.
        """
        self.validateBuildMode() 
[docs]    def refreshLigands(self):
        """
        Refreshes contents of the ligand table
        """
        if not maestro or not self.isVisible():
            return
        self.ignore_ligand_selection_change = True
        self.ligands_list.clear()
        self.ligands_list.setSelectionRectVisible(True)
        template_list = self.getTemplateList(selected_only=True)
        self.ligand_name_list = []
        self.ligand_entry_list = []
        if template_list:
            # Retrieve the ligands only if there are selected templates in MSV
            self.ligand_name_list, self.ligand_entry_list = \
                
getMaestroLigandList(template_list,
                                     list_all=self.all_ligands_cb.isChecked())
        group_ligand_names = []
        if hasattr(self.sequence_group, 'ligand_list') and \
                
self.sequence_group.ligand_list:
            group_ligand_names, group_ligand_sequences = list(
                zip(*self.sequence_group.ligand_list))
        # Restore selection state of individual ligands
        for ligand_name in self.ligand_name_list:
            item = QtWidgets.QListWidgetItem(ligand_name)
            self.ligands_list.addItem(item)
            if ligand_name[5:] in group_ligand_names:
                item.setSelected(True)
        self.ignore_ligand_selection_change = False
        self.ligandListSelectionChanged() 
[docs]    def refreshTemplates(self):
        """
        Rebuilds the list of templates.
        """
        if not self.isVisible():
            return
        selected_list = []
        # Get list selection status.
        for index in range(self.template_list.count()):
            if self.template_list.item(index).isSelected():
                selected_list.append(True)
            else:
                selected_list.append(False)
        self.template_list.clear()
        self.template_list.setSelectionRectVisible(True)
        self.query_list = []
        for group in self.viewer.sequence_group_list:
            group.updateReference()
            if len(group.getStructureList(omit_reference=True)) > 0 and \
               
group.reference:
                item = QtWidgets.QListWidgetItem(group.name)
                item.setData(1, group)
                self.template_list.addItem(item)
                self.query_list.append((item, group))
        if self.template_list.count() == len(selected_list):
            for index in range(self.template_list.count()):
                self.template_list.item(index).setSelected(selected_list[index])
        if len(self.sequence_group.getStructureList(omit_reference=True)) > 1:
            self.consensus_rb.setEnabled(True)
            self.homomultimers_rb.setEnabled(True)
            self.template_regions_rb.setEnabled(True)
        else:
            self.consensus_rb.setEnabled(False)
            self.homomultimers_rb.setEnabled(False)
            self.template_regions_rb.setEnabled(False) 
[docs]    def getQueryList(self):
        """
        Returns list of selected queries for heteromultimer modeling.
        """
        selected_items = self.template_list.selectedItems()
        if len(selected_items) == 0:
            return []
        selected_query_list = []
        for item, group in self.query_list:
            if item in selected_items:
                selected_query_list.append(group)
        return selected_query_list 
[docs]    def getTemplateList(self, selected_only=False):
        """
        Returns a full list of templates for all queries.
        If selected_only is True, returns only selected templates.
        """
        template_list = []
        query_list = []
        if self.heteromultimers_cb.isChecked():
            query_list = self.getQueryList()
        if query_list == []:
            query_list.append(self.sequence_group)
        if self.all_ligands_cb.isChecked():
            selected_only = False
        if self.template_regions_rb.isChecked():
            selected_only = False
        for group in query_list:
            template_list.extend(
                group.getTemplates(selected_only=selected_only))
        return template_list 
[docs]    def addConstraints(self):
        """
        Enables query-query constraint mode.
        """
        self.viewer.enableQueryConstraints() 
[docs]    def clearConstraints(self):
        """
        Clears all constraints.
        """
        self.viewer.clearQueryConstraints()
        # Clear ligand constraints
        self.sequence_group.ligand_list = []
        self.ligandListSelectionChanged()
        self.removeQueryLigands() 
[docs]    def buildHeteroMultimer(self):
        """
        Enters heteromultimer building mode.
        """
        if self.heteromultimers_cb.isChecked():
            self.tabs.setTabEnabled(4, True)
            self.tabs.setCurrentIndex(4)
        else:
            self.tabs.setTabEnabled(4, False)
        self.buildModeChanged()
        self.validateBuildMode() 
[docs]    def buildModeChanged(self):
        """
        Called when build mode changes. Validates and updates GUI settings.
        """
        self.heteromultimers_cb.setEnabled(True)
        self.kb_models_le.setEnabled(False)
        self.tabs.setEnabled(True)
        if not self.heteromultimers_cb.isChecked():
            self.tabs.setTabEnabled(4, False)
        else:
            self.tabs.setTabEnabled(4, True)
            self.sequence_group.build_mode = constants.PRIME_MODE_HETEROMULTIMER
        if self.template_regions_rb.isChecked():
            self.sequence_group.build_mode = constants.PRIME_MODE_COMPOSITE
            self.viewer.emphasizeArea(2)
            self.sequence_group.unselectAllSequences()
            self.sequence_group.premarkTemplateRegion()
        elif self.homomultimers_rb.isChecked():
            self.sequence_group.build_mode = constants.PRIME_MODE_HOMOMULTIMER
            if not self.sequence_group.hasSelectedSequences(
                    exclude_reference=True):
                self.sequence_group.selectFirstTemplate(n_templates=2)
            self.viewer.emphasizeArea(1)
        elif self.consensus_rb.isChecked():
            self.sequence_group.build_mode = constants.PRIME_MODE_CONSENSUS
            if not self.sequence_group.hasSelectedSequences(
                    exclude_reference=True):
                self.sequence_group.selectFirstTemplate(n_templates=2)
            self.viewer.emphasizeArea(1)
            self.heteromultimers_cb.setEnabled(False)
            self.heteromultimers_cb.setChecked(False)
            self.tabs.setEnabled(False)
        else:
            self.sequence_group.build_mode = constants.PRIME_MODE_SINGLE
            if not self.sequence_group.hasSelectedSequences(
                    exclude_reference=True):
                self.sequence_group.selectFirstTemplate()
            self.viewer.emphasizeArea(1)
        self.updateWidgets()
        self.validateBuildMode() 
[docs]    def getParameters(self):
        """
        Returns a dictionary of parameters for the Prime backend.
        """
        parameters = {}
        parameters["preview_model"] = False
        parameters["keep_rotamers"] = self.retain_rotamers_cb.isChecked()
        parameters[
            "optimize_side_chains"] = self.optimize_side_chains_cb.isChecked()
        parameters["minimize_residues"] = self.minimize_residues_cb.isChecked()
        parameters["build_insertions"] = self.insertions_cb.isChecked()
        parameters["max_insertion_length"] = int(self.insertions_le.text())
        parameters["build_transitions"] = self.junctions_cb.isChecked()
        parameters["build_deletions"] = self.deletions_cb.isChecked()
        parameters["knowledge_based"] = self.knowledge_based_rb.isChecked()
        parameters["num_output_struct"] = int(self.kb_models_le.text())
        parameters[
            "template_residue_numbers"] = self.retain_numbers_cb.isChecked()
        return parameters 
[docs]    def validateQuerySequences(self, group_list):
        """
        Tests if all query sequences in the provided list of groups
        include only valid characters.
        :type group_list: list of `SequenceGroup`
        :param group_list: List of groups used to build the model.
        :rtype: bool
        :return: True if all sequences are valid, False otherwise.
        """
        invalid_characters = 'BJOUXZ'
        # The assumption here is that the sequence already consists of
        # A-Z characters and gap symbols, so we only need to check
        # a few disallowed characters.
        for group in group_list:
            for ch in invalid_characters:
                if ch in group.reference.text():
                    dialogs.error_dialog(
                        'Invalid Query Sequence',
                        'Query sequence includes invalid character: \'' + ch +
                        '\'\n'
                        'Please modify the query seqeunce before '
                        'building homology model.')
                    return False
        return True 
[docs]    def buildModel(self):
        """
        Actually launch the model building job.
        """
        if self.viewer:
            self.viewer.build_mode = False
        self.close()
        if not self.build_model_func:
            return
        parameters = self.getParameters()
        groups = []
        if self.heteromultimers_cb.isChecked():
            modeling_mode = constants.PRIME_MODE_HETEROMULTIMER
            groups += self.getQueryList()
        else:
            modeling_mode = self.sequence_group.build_mode
        if not groups:
            groups = [self.sequence_group]
        templates = self.getTemplateList()
        if self.viewer:
            job_settings = self.viewer.job_settings
        else:
            job_settings = None
        if not self.validateQuerySequences(groups):
            return
        return self.build_model_func(settings=parameters,
                                     mode=modeling_mode,
                                     group_list=groups,
                                     valid_template_list=templates,
                                     progress_dialog=self.progress_dialog,
                                     viewer=self.viewer,
                                     job_settings=job_settings) 
[docs]    def previewModel(self):
        """
        Launch the model building job in "preview" mode.
        """
        if self.viewer:
            self.viewer.build_mode = False
        self.sequence_group.repaintTemplateStructures()
        self.close()
        if not self.build_model_func:
            return
        parameters = self.getParameters()
        parameters["preview_model"] = True
        groups = []
        if self.heteromultimers_cb.isChecked():
            modeling_mode = constants.PRIME_MODE_HETEROMULTIMER
            groups += self.getQueryList()
        else:
            modeling_mode = self.sequence_group.build_mode
        if not groups:
            groups = [self.sequence_group]
        templates = self.getTemplateList()
        if self.viewer:
            job_settings = self.viewer.job_settings
        else:
            job_settings = None
        return self.build_model_func(settings=parameters,
                                     mode=modeling_mode,
                                     group_list=groups,
                                     valid_template_list=templates,
                                     progress_dialog=self.progress_dialog,
                                     job_settings=job_settings) 
[docs]    def validateBuildMode(self):
        """
        Validates current build mode and Sets 'Build model' button status.
        """
        valid = True
        n_templates = 0
        n_selected_templates = 0
        for seq in self.sequence_group.sequences:
            if seq.isValidTemplate(reference=self.sequence_group.reference):
                n_templates += 1
                if seq.selected:
                    n_selected_templates += 1
        if self.sequence_group.reference is None:
            valid = False
        elif self.sequence_group.build_mode == constants.PRIME_MODE_SINGLE and \
                
n_selected_templates == 0:
            valid = False
        elif self.sequence_group.build_mode == constants.PRIME_MODE_HOMOMULTIMER and \
                
n_selected_templates < 2:
            valid = False
        elif self.sequence_group.build_mode == constants.PRIME_MODE_CONSENSUS \
                
and n_selected_templates < 2:
            valid = False
        elif n_templates == 0:
            valid = False
        if self.heteromultimers_cb.isChecked():
            selected_count = 0
            # Get list selection status.
            for index in range(self.template_list.count()):
                if self.template_list.item(index).isSelected():
                    selected_count += 1
            if selected_count < 2:
                valid = False
        self.button_ok.setEnabled(valid) 
[docs]    def helpCB(self):
        """
        Invokes a Homology Modeling help page.
        """
        if maestro:
            maestro.command("helptopic TOOLS_MENU_MSV_BUILD_MODEL") 
 
[docs]def showPrimeSettingsDialog(viewer, sequence_group, build_model_func,
                            progress_dialog):
    """
    Opens Prime settings dialog and returns a parameter tuple.
    """
    global _PRIME_SETTINGS_DIALOG
    if not _PRIME_SETTINGS_DIALOG:
        _PRIME_SETTINGS_DIALOG = PrimeSettingsDialog(sequence_group,
                                                     viewer=viewer)
    setPrimeSequenceGroup(sequence_group)
    _PRIME_SETTINGS_DIALOG.build_model_func = build_model_func
    _PRIME_SETTINGS_DIALOG.progress_dialog = progress_dialog
    _PRIME_SETTINGS_DIALOG.viewer = viewer
    _PRIME_SETTINGS_DIALOG.show()
    return _PRIME_SETTINGS_DIALOG.getParameters() 
[docs]def setPrimeSequenceGroup(sequence_group):
    global _PRIME_SETTINGS_DIALOG
    if sequence_group.build_mode < 1:
        sequence_group.build_mode = constants.PRIME_MODE_SINGLE
    if _PRIME_SETTINGS_DIALOG:
        _PRIME_SETTINGS_DIALOG.sequence_group = sequence_group
        _PRIME_SETTINGS_DIALOG.single_template_rb.setChecked(
            sequence_group.build_mode == constants.PRIME_MODE_SINGLE)
        _PRIME_SETTINGS_DIALOG.homomultimers_rb.setChecked(
            sequence_group.build_mode == constants.PRIME_MODE_HOMOMULTIMER)
        _PRIME_SETTINGS_DIALOG.template_regions_rb.setChecked(
            sequence_group.build_mode == constants.PRIME_MODE_COMPOSITE)
        _PRIME_SETTINGS_DIALOG.consensus_rb.setChecked(
            sequence_group.build_mode == constants.PRIME_MODE_CONSENSUS)
        _PRIME_SETTINGS_DIALOG.buildModeChanged()
        _PRIME_SETTINGS_DIALOG.refreshLigands()
        _PRIME_SETTINGS_DIALOG.validateBuildMode() 
[docs]def getPrimeLigandsForEntryID(entry_id):
    global _PRIME_SETTINGS_DIALOG
    dialog = _PRIME_SETTINGS_DIALOG
    if not dialog or not maestro:
        return []
    ligand_list = []
    # ligand_name_list, ligand_entry_list = getMaestroLigandList()
    for index, ligand_name in enumerate(dialog.ligand_name_list):
        item = dialog.ligands_list.item(index)
        if item and item.isSelected():
            id, pdbres, asl, st = dialog.ligand_entry_list[index]
            if entry_id == id:
                ligand_list.append(st)
    return ligand_list 
[docs]def updatePrimeQueryList(refresh_templates=True, refresh_ligands=False):
    global _PRIME_SETTINGS_DIALOG
    if _PRIME_SETTINGS_DIALOG:
        if _PRIME_SETTINGS_DIALOG.isVisible():
            _PRIME_SETTINGS_DIALOG.buildModeChanged()
        if refresh_ligands and \
                
_PRIME_SETTINGS_DIALOG.tabs.currentWidget() == \
                
_PRIME_SETTINGS_DIALOG.ligands_page:
            _PRIME_SETTINGS_DIALOG.refreshLigands()
        if refresh_templates:
            _PRIME_SETTINGS_DIALOG.refreshTemplates() 
[docs]def primeValidateBuildMode():
    global _PRIME_SETTINGS_DIALOG
    if _PRIME_SETTINGS_DIALOG and \
       
_PRIME_SETTINGS_DIALOG.isVisible():
        _PRIME_SETTINGS_DIALOG.validateBuildMode()