"""
This class implements a sequence viewer main window, all menus, toolbars and
associated actions.
Copyright Schrodinger, LLC. All rights reserved.
"""
# Contributors: Deacon Sweeney, Piotr Rotkiewicz
import collections
import itertools
import os
import sys
from schrodinger import get_maestro
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtGui
from schrodinger.Qt import QtWidgets
# Schrodinger Qt Widgets
from schrodinger.ui.qt.appframework import AppFramework
from schrodinger.ui.qt.filedialog import FileDialog
from schrodinger.ui.sequencealignment.msv_menu import MAESTRO_MENU_LIST
from schrodinger.ui.sequencealignment.msv_menu import MENU_LIST
from schrodinger.ui.sequencealignment.msv_menu import SEPARATOR
from schrodinger.utils import fileutils
# This import is required for icons
from . import constants
from . import dialogs
from . import maestro as maestro_helpers
from . import predictors
from . import sequencealignment_rc  # noqa: F401, pylint: disable=unused-import
from .find_gui import createFindToolbar
from .prime_gui import updatePrimeQueryList
from .sequence_viewer import SequenceViewer
maestro = get_maestro()
try:
    from schrodinger.infra.mm import mmfile_schrodinger_appdata_dir
except:
    mmfile_schrodinger_appdata_dir = None
#Paths to setChecked to False at start:
START_UNCHECKED_PATHS = [
    "&Sequences/Sort by &Tree Order",
    "Sett&ings/&Display Percentage Similarity", "&Alignment/Use Constraints",
    "Color/&Weight Colors by Alignment Quality", "&Alignment/Track Changes",
    "Color/&Average Colors In Columns", "Color/Color Matching Residues Only",
    "Sett&ings/&Display Percentage Identity", "Color/Adjust Text Contrast",
    "Sett&ings/&Display Sequence Boundaries",
    "Color/Color Different Residues Only",
    "Sett&ings/Group Annotations by Type", "Sett&ings/&Display Header Row",
    "Sett&ings/&Replace Identities With Dots",
    "Sett&ings/&Display Percentage Homology", "Sett&ings/&Display Score",
    "Sett&ings/Calculate Sequence Identity Only in Selected Columns"
]
#Paths to setChecked to True at start:
START_CHECKED_PATHS = [
    "Color/Color Sequences", "Sett&ings/&Display Ruler",
    "Sett&ings/&Font Size/12", "Sett&ings/&Display Alignment Tooltips",
    "Sett&ings/Include Gaps in Sequence Identity Calculations",
    "Sett&ings/Automatically Update Sequence Profile",
    "Sett&ings/Ask Before Accessing a Remote Server"
]
START_UNCHECKED_PATHS_MAESTRO = [
    "&Annotations/Ligand Contacts", "&Maestro/Allow Structural Changes"
]
START_CHECKED_PATHS_MAESTRO = [
    "&Maestro/&Update Automatically from Maestro",
    "&Maestro/Include Incorporated Entries in Workspace",
    "&Annotations/Antibody Numbering Scheme/Chothia"
]
[docs]class AlignmentWindow(AppFramework):
    """
    AlignmentWindow class implements a main window of the sequence viewer.
    """
[docs]    def __init__(self, filename=None):
        # Initialize base class.
        AppFramework.__init__(self)
        self.sequence_viewer = SequenceViewer(self)  #widget
        self.sequence_viewer.main_window = self
        # These toggles are specific to standalone MSV (not SSV)
        self.sequence_viewer.save_state = True
        self.sequence_viewer.incorporate_scratch_entry = False
        self.sequence_viewer.update_annotations_menu = True
        #: Current project name.
        self.project_name = None
        self.query_tabs = QtWidgets.QTabBar(self)
        self.query_tabs.setExpanding(False)
        self.query_tabs.setTabsClosable(True)
        self.query_tabs.setMovable(False)
        self.query_tabs.setAutoFillBackground(False)
        self.query_tabs.setDrawBase(False)
        self.query_tabs.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.query_tabs.currentChanged.connect(self.queryTabChanged)
        self.query_tabs.tabCloseRequested.connect(self.queryTabClose)
        self.query_tabs.customContextMenuRequested.connect(
            self.queryContextMenu)
        self.ignore_tab_change = False
        self.central_frame = QtWidgets.QWidget()
        self.central_frame.setContentsMargins(0, 0, 0, 0)
        vlayout = QtWidgets.QVBoxLayout()
        vlayout.setSpacing(0)
        self.central_frame.setLayout(vlayout)
        self.new_tab_button = QtWidgets.QToolButton()
        self.new_tab_button.setText("+")
        self.new_tab_button.setToolTip("Add a New Tab")
        self.new_tab_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextOnly)
        self.new_tab_button.clicked.connect(self.addNewQueryTab)
        self.query_tabs_layout = QtWidgets.QHBoxLayout()
        self.query_tabs_layout.setSpacing(0)
        self.query_tabs_layout.addWidget(self.query_tabs)
        self.query_tabs_layout.addSpacing(5)
        self.query_tabs_layout.addWidget(self.new_tab_button)
        self.query_tabs_layout.addStretch(100)
        vlayout.addLayout(self.query_tabs_layout)
        vlayout.addWidget(self.sequence_viewer)
        # Set the sequence viewer as a central widget.
        self.setCentralWidget(self.central_frame)
        self.createActions()
        self.createMenus()
        self.createToolBars()  #window toolbars
        self.createStatusBar()  #at the bottom
        self.viewerNewSet()
        self.resize(QtCore.QSize(900, 250))  # Resize window to a default size.
        # File dialogs.
        self.file_import_dialog = None
        self.file_export_dialog = None
        self.project_open_dialog = None
        self.project_save_dialog = None
        self.image_export_dialog = None
        self.knime_output_file = None
        # File dialogs custom widgets.
        self.file_import_opt_merge = QtWidgets.QCheckBox(
            "Align to query sequence")
        self.file_import_opt_merge.setChecked(False)
        self.file_import_opt_replace = QtWidgets.QCheckBox(
            "Replace matching sequences")
        self.file_import_opt_replace.setChecked(False)
        self.file_import_opt_incorporate = QtWidgets.QCheckBox(
            "Incorporate imported structures into Maestro project")
        self.file_import_opt_incorporate.setChecked(True)
        self.file_import_opt_translate = QtWidgets.QCheckBox(
            "Translate DNA / RNA sequences")
        self.file_import_opt_translate.setChecked(False)
        self.image_export_opt_save_visible = QtWidgets.QCheckBox(
            "Export image of the entire alignment")
        self.image_export_opt_save_visible.setChecked(True)
        self.file_export_opt_save_annotations = QtWidgets.QCheckBox(
            "Save annotations")
        self.file_export_opt_save_annotations.setChecked(False)
        self.file_export_opt_save_similarity = QtWidgets.QCheckBox(
            "Save similarity values")
        self.file_export_opt_save_similarity.setChecked(False)
        self.file_export_opt_selected_only = QtWidgets.QCheckBox(
            "Export only selected part of the alignment")
        self.file_export_opt_selected_only.setChecked(False)
        # Create settings
        if mmfile_schrodinger_appdata_dir:
            settings_path = mmfile_schrodinger_appdata_dir()
            QtCore.QSettings.setPath(QtCore.QSettings.IniFormat,
                                     QtCore.QSettings.UserScope, settings_path)
        self.settings = QtCore.QSettings(QtCore.QSettings.IniFormat,
                                         QtCore.QSettings.UserScope,
                                         "multiple_sequence_viewer")
        # Read and apply settings. The settings are stored using Qt mechanism.
        # On Unix they are stored in ~/.config/Schrodinger/
        # On Windows they are stored in the registry.
        self.readSettings(self.settings)
        self.setWindowTitle("Multiple Sequence Viewer (Deprecated)")
        # Hide tree viewer pane.
        sizes_list = self.sequence_viewer.sizes()
        sizes_list[0] = 0
        self.sequence_viewer.setSizes(sizes_list)
        self.sequence_viewer.setUndoRedoActions(self.actions["&Edit/&Undo"],
                                                self.actions["&Edit/&Redo"])
        # Make sure the font size is up-to-date.
        self.sequence_viewer.updateFontSize(self.sequence_viewer.font_size)
        # If running from Maestro, synchronize with workspace contents.
        if maestro:
            self.viewerSynchronizeWithMaestro()
        if filename:
            self.sequence_viewer.loadProject(filename)
            self.queryTabChanged(0)
        self.sequence_viewer.always_ask_action = \
            
self.actions["Sett&ings/Ask Before Accessing a Remote Server"]
        self.sequence_viewer.query_tabs = self.query_tabs
        self.sequence_viewer.setPopupMenus(self.nameMenuCB, self.sequenceMenuCB,
                                           self.treeMenuCB)
        self.actions["&File/Send Back to &Knime"].setVisible(False)
        self.picked_query = -1
        # Do not replace main menu bar on Darwin
        if sys.platform == "darwin":
            self.menuBar().setNativeMenuBar(False)
        self.build_model_mode = False 
[docs]    def closeEvent(self, event):
        """
        Called when the main window is about to be closed.
        """
        self.writeSettings(self.settings)
        # Remove Maestro callbacks, they will be restored when the
        # window is re-opened.
        self.sequence_viewer.removeMaestroCallbacks()
        # This preserves MSV state in current Maestro project
        self.sequence_viewer.closeEvent(event)
        event.accept() 
[docs]    def showEvent(self, event):
        """
        Synchronize with Maestro whenever the MSV window is opened
        and the sequence group is empty.
        """
        if self.sequence_viewer and maestro:
            if not self.sequence_viewer.sequence_group.sequences:
                self.sequence_viewer.incorporateIncludedEntries(
                    incorporate_scratch_entry=True)
                self.sequence_viewer.maestroProjectChanged()
                if self.build_model_mode:
                    self.build_model_mode = False
                    self.sequence_viewer.buildModel()
                self.sequence_viewer.contents_changed = True
                self.sequence_viewer.updateView()
            # Initialize Maestro callbacks.
            if self.sequence_viewer:
                self.sequence_viewer.initMaestro() 
[docs]    def setBuildModel(self):
        """
        Called in multiseqviewer.py
        """
        self.build_model_mode = True 
[docs]    def newFile(self):
        """
        Creates a new project. Deletes all contents and sets a default
        window title.
        """
        self.project_name = None
        self.fetchLineEdit.setText("")
        self.findLineEdit.setText("")
        self.sequence_viewer.clearSet()
        self.setWindowTitle("Multiple Sequence Viewer") 
[docs]    def open(self):
        """
        Opens a sequence file. Currently supported formats are FASTA, PDB
        and SwissProt.
        :rtype: bool
        :return: True if operation succeeded, False otherwise
        """
        result = False
        if not self.file_import_dialog:
            self.file_import_dialog = FileDialog()
            self.file_import_dialog.setFileMode(
                QtWidgets.QFileDialog.ExistingFiles)
            self.file_import_dialog.setOption(
                QtWidgets.QFileDialog.DontUseNativeDialog)
            self.file_import_dialog.setViewMode(QtWidgets.QFileDialog.Detail)
            self.file_import_dialog.setWindowTitle("Import Sequences")
            file_formats = [
                ("FASTA", [fileutils.SeqFormat.fasta]),
                ("PDB", [fileutils.PDB]),
                ("SWISSPROT", [fileutils.SeqFormat.swissprot]),
                ("GCG", [fileutils.SeqFormat.gcg]),
                ("EMBL", [fileutils.SeqFormat.embl]),
                ("PIR", [fileutils.SeqFormat.pir]),
            ]
            if maestro:
                file_formats.insert(1, ("Maestro", [fileutils.MAESTRO]))
            all_formats = ("All supported formats",
                           list(
                               itertools.chain(*(v for __, v in file_formats))))
            file_formats.insert(0, all_formats)
            file_formats.append(("Any file", ['ALL']))
            self.file_import_dialog.setNameFilters(
                fileutils.get_name_filter(
                    collections.OrderedDict(file_formats)))
            self.options_layout = QtWidgets.QVBoxLayout()
            self.options_layout.addWidget(self.file_import_opt_merge)
            self.options_layout.addWidget(self.file_import_opt_replace)
            self.options_layout.addWidget(self.file_import_opt_translate)
            if maestro:
                self.options_layout.addWidget(self.file_import_opt_incorporate)
            self.file_import_dialog.layout().addLayout(
                self.options_layout,
                self.file_import_dialog.layout().rowCount(), 1)
        self.file_import_dialog.exec_()
        if self.file_import_dialog.result():
            file_names = self.file_import_dialog.selectedFiles()
            for name in file_names:
                to_maestro = False
                include = False
                if maestro:
                    to_maestro = self.file_import_opt_incorporate.isChecked()
                    include = self.actions[
                        "&Maestro/Include Incorporated Entries in Workspace"].isChecked(
                        )
                result = self.sequence_viewer.loadFile(
                    str(name),
                    merge=self.file_import_opt_merge.isChecked(),
                    replace=self.file_import_opt_replace.isChecked(),
                    translate=self.file_import_opt_translate.isChecked(),
                    to_maestro=to_maestro,
                    maestro_include=include) or result
        if result:
            self.setWindowTitle("Multiple Sequence Viewer -" + name)
        return result 
[docs]    def processCommandLine(self, cmdline):
        """
        This processes the command line.
        """
        input_file = None
        command_file = None
        knime_file = None
        if len(cmdline) > 1:
            if cmdline[1][0] != '-':
                self.loadFiles([cmdline[1]])
                return
            this_cmd = None
            for cmd in cmdline:
                if cmd[0] == '-':
                    this_cmd = cmd
                else:
                    if this_cmd:
                        if this_cmd[1] == 'c':
                            command_file = cmd
                        elif this_cmd[1] == 'i':
                            input_file = cmd
                        elif this_cmd[1] == 'k':
                            knime_file = cmd
                        this_cmd = None
        if input_file:
            self.loadFiles([input_file])
        if knime_file:
            self.knime_output_file = knime_file
            self.actions["&File/Send Back to &Knime"].setVisible(True)
        if command_file:
            self.sequence_viewer.executeCommandFile(command_file) 
[docs]    def loadFiles(self, file_list):
        for name in file_list:
            self.sequence_viewer.loadFile(
                str(name),
                merge=self.file_import_opt_merge.isChecked(),
                replace=self.file_import_opt_replace.isChecked(),
                translate=self.file_import_opt_translate.isChecked(),
                to_maestro=True) 
[docs]    def loadProject(self):
        """
        Loads a MSV project.
        """
        result = False
        if not self.project_open_dialog:
            self.project_open_dialog = FileDialog()
            self.project_open_dialog.setOption(
                QtWidgets.QFileDialog.DontUseNativeDialog)
            self.project_open_dialog.setViewMode(QtWidgets.QFileDialog.Detail)
            self.project_open_dialog.setWindowTitle("Open Project")
            self.project_open_dialog.setNameFilters(
                ["Multiple Sequence Viewer (*.msv)"])
        self.project_open_dialog.exec_()
        if self.project_open_dialog.result():
            file_names = self.project_open_dialog.selectedFiles()
            for name in file_names:
                result = result or self.sequence_viewer.loadProject(str(name))
        if result:
            self.setWindowTitle("Multiple Sequence Viewer -" + name)
        return result 
[docs]    def saveAs(self):
        """
        Opens "Export Sequences" file dialog.
        """
        result = False
        if not self.file_export_dialog:
            self.file_export_dialog = FileDialog()
            self.file_export_dialog.setAcceptMode(
                QtWidgets.QFileDialog.AcceptSave)
            self.file_export_dialog.setOption(
                QtWidgets.QFileDialog.DontUseNativeDialog)
            self.file_export_dialog.setWindowTitle("Export Sequences")
            self.file_export_dialog.setViewMode(QtWidgets.QFileDialog.Detail)
            self.file_export_dialog.setNameFilters(
                ["FASTA (*.fasta *.fst *.fas *.seq)", "Text (*.*)"])
            self.file_export_dialog.layout().addWidget(
                self.file_export_opt_save_annotations)
            self.file_export_dialog.layout().addWidget(
                self.file_export_opt_save_similarity)
            self.file_export_dialog.layout().addWidget(
                self.file_export_opt_selected_only)
        self.file_export_dialog.exec_()
        if self.file_export_dialog.result():
            file_names = self.file_export_dialog.selectedFiles()
            if len(file_names) < 1:
                return False
            format = str(self.file_export_dialog.selectedNameFilter())[:3]
            result = self.sequence_viewer.saveFile(
                file_names[0],
                save_annotations=self.file_export_opt_save_annotations.
                isChecked(),
                selected_only=self.file_export_opt_selected_only.isChecked(),
                save_similarity=self.file_export_opt_save_similarity.isChecked(
                ),
                format=format)
        if result:
            self.setWindowTitle("Multiple Sequence Viewer -" + file_names[0])
        return result 
[docs]    def saveProject(self):
        """
        Saves the project.
        """
        if self.project_name:
            self.sequence_viewer.saveProject(self.project_name)
        else:
            self.saveProjectAs() 
[docs]    def saveProjectAs(self):
        """
        Saves MSV project.
        """
        result = False
        if not self.project_save_dialog:
            self.project_save_dialog = FileDialog()
            self.project_save_dialog.setAcceptMode(
                QtWidgets.QFileDialog.AcceptSave)
            self.project_save_dialog.setOption(
                QtWidgets.QFileDialog.DontUseNativeDialog)
            self.project_save_dialog.setWindowTitle("Save Project")
            self.project_save_dialog.setViewMode(QtWidgets.QFileDialog.Detail)
            self.project_save_dialog.setNameFilters(
                ["Sequence viewer project (*.msv)"])
        self.project_save_dialog.exec_()
        if self.project_save_dialog.result():
            file_names = self.project_save_dialog.selectedFiles()
            if len(file_names) < 1:
                return False
            file_name = file_names[0]
            if len(file_name) == 0:
                return False
            name, ext = os.path.splitext(str(file_name))
            if (not ext) or (ext != ".msv"):
                file_name += ".msv"
            result = self.sequence_viewer.saveProject(file_name)
        if result:
            self.setWindowTitle("Multiple Sequence Viewer -" + file_name)
            self.project_name = file_name
        return result 
[docs]    def backToKnime(self):
        """
        Sends the alignment back to a specified output file for KNIME.
        """
        if self.knime_output_file:
            self.sequence_viewer.saveFile(self.knime_output_file)
            self.close() 
[docs]    def saveImageAs(self):
        """
        Export image file dialog.
        """
        result = False
        if not self.image_export_dialog:
            self.image_export_dialog = FileDialog()
            self.image_export_dialog.setAcceptMode(
                QtWidgets.QFileDialog.AcceptSave)
            self.image_export_dialog.setOption(
                QtWidgets.QFileDialog.DontUseNativeDialog)
            self.image_export_dialog.setWindowTitle("Export Image")
            self.image_export_dialog.setViewMode(QtWidgets.QFileDialog.Detail)
            self.image_export_dialog.setNameFilters(
                ["PNG Image (*.png)", "PDF Image (*.pdf)"])
            self.image_export_dialog.layout().addWidget(
                self.image_export_opt_save_visible)
        self.image_export_dialog.exec_()
        if self.image_export_dialog.result():
            file_names = self.image_export_dialog.selectedFiles()
            if len(file_names) < 1:
                return False
            file_name = file_names[0]
            if len(file_name) == 0:
                return False
            name, ext = os.path.splitext(str(file_name))
            format = str(self.image_export_dialog.selectedNameFilter())[:3]
            if (not ext) or (ext != "." + format.lower()):
                file_name += "." + format.lower()
            result = self.sequence_viewer.saveImage(
                file_name,
                save_all=self.image_export_opt_save_visible.isChecked(),
                format=format)
        return result 
[docs]    def createActionsAndMenu(self, list):
        """
        Creates all menus and their actions. Sets their slots, short cuts,
        tool tips, and checkable. Also creates self.actions = {labels: QActions}
        All base components of MENU_LIST are strings.
        :param list: MENU_LIST of all the menus, actions, and slots.
        :type list: list of never ending lists with strings inside
        """
        self.actions = {}  #holds all created actions
        self.menus = {}  #holds all created menus
        for menu_list in list:
            menu_name = menu_list[0]
            menu = self.menuBar().addMenu(self.tr(menu_name))
            self.menus[menu_name] = menu
            for action_list in menu_list[1:]:
                if action_list[0] == SEPARATOR:
                    menu.addSeparator()
                else:
                    if hasattr(action_list[1], "__iter__") and not isinstance(
                            action_list[1], str):
                        sub_menu_name = action_list[0]
                        sub_menu = menu.addMenu(self.tr(sub_menu_name))
                        sub_menu_key = "/".join([menu_name, sub_menu_name])
                        self.menus[sub_menu_key] = sub_menu
                        for sub_action_list in action_list[1:]:
                            if sub_action_list[0] == SEPARATOR:
                                sub_menu.addSeparator()
                            else:
                                (key_value, action) = self.createAction(
                                    sub_action_list, sub_menu_name, sub_menu)
                                key_value = "/".join([menu_name, key_value])
                                self.actions[key_value] = action
                    else:
                        (key_value,
                         action) = self.createAction(action_list, menu_name,
                                                     menu)
                        self.actions[key_value] = action 
[docs]    def createAction(self, action_list, menu_name, menu):
        """
        Creates actions from sublists of msv_menu
        :param action_list: ['action label', 'slot method name', 'tool tip',
                            'shortcut', 'set checkable', 'icon'] or [SEPARATOR]
        :param menu_name: string name of Menu label
        :param menu: menu item to add action to
        :return: (key_value, action) for self.actions
        """
        action_name = action_list[0]
        icon = action_list[5]
        if icon:
            action = QtWidgets.QAction(QtGui.QIcon(icon), self.tr(action_name),
                                       self)
        else:
            action = QtWidgets.QAction(action_name, self)
        slot = getattr(self, action_list[1])
        tooltip = action_list[2]
        shortcut = action_list[3]
        checkable = action_list[4]
        action.triggered.connect(slot)
        if tooltip:
            action.setStatusTip(self.tr(tooltip))
        if shortcut:
            action.setShortcut(self.tr(shortcut))
        if checkable:
            action.setCheckable(True)
        menu.addAction(action)
        key_value = "/".join([menu_name, action_name])
        return (key_value, action) 
[docs]    def setUpActionsDisabled(self):
        """ Disables desired actions for setup. """
        self.actions["&Edit/&Undo"].setEnabled(False)
        self.actions["&Edit/&Redo"].setEnabled(False)
        self.actions["&Alignment/Clear Constraints"].setEnabled(False) 
[docs]    def fixShortCuts(self):
        """ Sets shortcuts that were not set by createActionsAndMenu """
        self.actions["&Sequences/&Collapse All"].setShortcut(QtCore.Qt.CTRL +
                                                             QtCore.Qt.Key_Up)
        self.actions["&Sequences/&Expand All"].setShortcut(QtCore.Qt.CTRL +
                                                           QtCore.Qt.Key_Down)
        self.actions["&Alignment/Delete"].setShortcut(QtCore.Qt.SHIFT +
                                                      QtCore.Qt.Key_Backspace) 
[docs]    def createActions(self):
        """
        Create Qt actions.
        """
        if maestro:
            self.createActionsAndMenu(MAESTRO_MENU_LIST)
        else:
            self.createActionsAndMenu(MENU_LIST)
        self.setUpActionsDisabled()
        self.setUpMenuActionsChecked()
        self.fixShortCuts()
        self.addColorIconsToMenu()
        self.cutAct = QtWidgets.QAction(self.tr("Cu&t"), self)
        self.cutAct.setShortcut(self.tr("Ctrl+X"))
        self.cutAct.setStatusTip(
            self.tr("Cut the current selection's contents to the clipboard"))
        self.cutAct.setEnabled(False)
        self.copyAct = QtWidgets.QAction(self.tr("&Copy"), self)
        self.copyAct.setShortcut(self.tr("Ctrl+C"))
        self.copyAct.setStatusTip(
            self.tr("Copy the current selection's contents to the clipboard"))
        self.copyAct.setEnabled(False)
        self.pasteAct = QtWidgets.QAction(self.tr("&Paste"), self)
        self.pasteAct.setShortcut(self.tr("Ctrl+V"))
        self.pasteAct.setStatusTip(
            self.tr("Paste the clipboard's contents into the current "
                    " selection"))
        self.anchorAct = QtWidgets.QAction(
            self.tr("Anchor Residues Outside Selection"), self)
        self.anchorAct.setStatusTip(
            self.tr("Preserve alignment outside of the selection"))
        self.anchorAct.triggered.connect(self.viewerAnchorSelection)
        self.clearAnchorsAct = QtWidgets.QAction(
            QtGui.QIcon(), self.tr("Clear Restricted Region"), self)
        self.clearAnchorsAct.setStatusTip(
            self.tr("Clear the restricted region"))
        self.clearAnchorsAct.triggered.connect(self.viewerClearAnchors)
        self.lockDownstreamAct = QtWidgets.QAction(
            QtGui.QIcon(":/icons/icon_lock_downstream.png"),
            self.tr("&Lock Sequence Downstream"), self)
        self.lockDownstreamAct.setCheckable(True)
        self.lockDownstreamAct.setEnabled(True)
        self.lockDownstreamAct.setStatusTip(
            self.tr("Toggle Downstream Sequence Locking"))
        self.lockDownstreamAct.triggered.connect(
            self.viewerToggleLockDownstream)
        self.colorGrayscaleAct = QtWidgets.QAction(QtGui.QIcon(""),
                                                   self.tr("Grayscale"), self)
        self.colorGrayscaleAct.triggered.connect(self.viewerColorGrayscale)
        self.colorWhiteAct = QtWidgets.QAction(QtGui.QIcon(""),
                                               self.tr("White"), self)
        self.colorWhiteAct.triggered.connect(self.viewerColorWhite)
        self.findPreviousAct = QtWidgets.QAction(
            QtGui.QIcon(":/icons/icon_left_arrow.png"),
            self.tr("&Previous Pattern"), self)
        self.findPreviousAct.setStatusTip(
            self.tr("Find Previous Pattern Occurence"))
        self.findPreviousAct.triggered.connect(self.viewerFindPrevious)
        self.findNextAct = QtWidgets.QAction(
            QtGui.QIcon(":/icons/icon_right_arrow.png"),
            self.tr("&Next Pattern"), self)
        self.findNextAct.setStatusTip(self.tr("Find Next Pattern Occurence"))
        self.findNextAct.triggered.connect(self.viewerFindNext)
        self.fetchAct = QtWidgets.QAction(self.tr("&Fetch A Sequence"), self)
        self.fetchAct.setStatusTip(
            self.tr("Fetch a sequence from a local or online database"))
        self.fetchAct.triggered.connect(self.viewerFetchSequence)
        self.addIdentityAct = QtWidgets.QAction(self.tr("Identity"), self)
        self.addIdentityAct.triggered.connect(self.viewerAddIdentityAnnotation)
        self.addSimilarityAct = QtWidgets.QAction(self.tr("Similarity"), self)
        self.addSimilarityAct.triggered.connect(
            self.viewerAddSimilarityAnnotation)
        self.colorHelixTerminatorsAct = QtWidgets.QAction(
            self.tr("Helix Termination"), self)
        self.colorHelixTerminatorsAct.triggered.connect(
            self.viewerColorHelixTerminators)
        self.selectLigandContactsAct = QtWidgets.QAction(
            self.tr("&Select Ligand Contacts"), self)
        self.selectLigandContactsAct.triggered.connect(
            self.viewerSelectLigandContacts)
        self.antibodyActionGroup = QtWidgets.QActionGroup(self)
        self.antibodyActionGroup.setExclusive(True)
        self.antibodyActionGroup.addAction(
            self.actions["&Annotations/Antibody Numbering Scheme/Kabat"])
        self.antibodyActionGroup.addAction(
            self.actions["&Annotations/Antibody Numbering Scheme/Chothia"])
        self.antibodyActionGroup.addAction(
            self.
            actions["&Annotations/Antibody Numbering Scheme/Enhanced Chothia"])
        self.antibodyActionGroup.addAction(
            self.actions["&Annotations/Antibody Numbering Scheme/IMGT"])
        self.antibodyActionGroup.addAction(
            self.actions["&Annotations/Antibody Numbering Scheme/AHo"])
        self.mouseAcrossAct = QtWidgets.QAction(
            self.tr("Allow Selection Across Rows"), self)
        self.mouseAcrossAct.setCheckable(True)
        self.mouseAcrossAct.triggered.connect(self.viewerToggleMouseAcross)
        self.zoomInAct = QtWidgets.QAction(
            QtGui.QIcon(":/icons/icon_zoom_in.png"), self.tr("Zoom &In"), self)
        self.zoomInAct.triggered.connect(self.viewerZoomIn)
        self.zoomOutAct = QtWidgets.QAction(
            QtGui.QIcon(":/icons/icon_zoom_out.png"), self.tr("Zoom &Out"),
            self)
        self.zoomOutAct.triggered.connect(self.viewerZoomOut)
        self.treeSwapBranchesAct = QtWidgets.QAction(self.tr("&Swap Branches"),
                                                     self)
        self.treeSwapBranchesAct.triggered.connect(self.treeSwapBranches)
        self.treeSelectSequencesAct = QtWidgets.QAction(
            self.tr("&Select Sequences"), self)
        self.treeSelectSequencesAct.triggered.connect(self.treeSelectSequences)
        self.treeHideBranchesAct = QtWidgets.QAction(self.tr("&Hide Branch"),
                                                     self)
        self.treeHideBranchesAct.triggered.connect(self.treeHideBranches)
        self.maestroAlignStructuresAct = QtWidgets.QAction(
            QtGui.QIcon(""), self.tr("Structure Alignment"), self)
        self.maestroAlignStructuresAct.triggered.connect(
            self.viewerSynchronizeWithMaestro)
        self.propagateColorsToMaestroAct = QtWidgets.QAction(
            QtGui.QIcon(""), self.tr("Apply to Maestro Workspace"), self)
        self.propagateColorsToMaestroAct.triggered.connect(
            self.viewerPropagateColors)
        self.setAsReferenceAct = QtWidgets.QAction(
            QtGui.QIcon(""), self.tr("Set as Query Sequence"), self)
        self.setAsReferenceAct.triggered.connect(self.viewerSetAsReference)
        self.alwaysAskAnswer = QtWidgets.QAction(self)
        self.alwaysAskAnswer.setCheckable(True)
        self.alwaysAskAnswer.setChecked(True)
        self.helpAct = QtWidgets.QAction(
            QtGui.QIcon(""), self.tr("Multiple Sequence Viewer Help"), self)
        self.helpAct.triggered.connect(self.viewerHelp)
        self.renameSequenceAct = QtWidgets.QAction(QtGui.QIcon(""),
                                                   self.tr("Rename Sequence"),
                                                   self)
        self.renameSequenceAct.triggered.connect(self.viewerRenameSequence)
        # Font size actions.
        self.fontActionGroup = QtWidgets.QActionGroup(self)
        self.fontActionGroup.setExclusive(True)
        self.fontActionGroup.triggered.connect(self.viewerSetFontSize)
        self.connectFontActions()
        # Connect splitter moved event.
        self.sequence_viewer.splitterMoved.connect(self.viewerSplitterMoved)
        self.translateAct = QtWidgets.QAction(
            QtGui.QIcon(""), self.tr("Translate DNA / RNA Sequence"), self)
        self.translateAct.triggered.connect(self.viewerTranslate) 
[docs]    def connectFontActions(self):
        font_sizes = [4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24]
        for size in font_sizes:
            path = "/".join(["Sett&ings/&Font Size", str(size)])
            self.actions[path].setActionGroup(self.fontActionGroup) 
[docs]    def viewerSetFontSize(self, action):
        """
        Set font size action callback.
        :type action: QAction
        :param action: Qt action
        """
        font_size = int(action.text())
        self.sequence_viewer.updateFontSize(font_size)
        path = "/".join(["Sett&ings/&Font Size", str(font_size)])
        self.actions[path].setChecked(True)
        self.sequence_viewer.generateRows()
        self.sequence_viewer.repaint() 
[docs]    def createStatusBar(self):
        """
        Creates a status bar.
        """
        self.status_bar_message = QtWidgets.QLabel("Ready")
        self.statusBar().addPermanentWidget(self.status_bar_message, 2)
        self.sequence_viewer.message_status_bar = self.status_bar_message
        self.sequence_viewer.statistics_status_bar = QtWidgets.QLabel(
            "0 sequences total, 0 selected, 0 hidden.")
        self.sequence_viewer.statistics_status_bar.setAlignment(
            QtCore.Qt.AlignRight)
        self.statusBar().addPermanentWidget(
            self.sequence_viewer.statistics_status_bar, 2)
        self.statusBar().messageChanged[str].connect(
            self.statusBarMessageChanged) 
[docs]    def statusBarMessageChanged(self, message):
        if message == "":
            message = "Ready"
        self.status_bar_message.setText(message) 
[docs]    def readSettings(self, settings):
        """
        Reads sequence viewer user settings.
        """
        pos = settings.value("pos", QtCore.QPoint(100, 100), type=QtCore.QPoint)
        size = settings.value("size", QtCore.QSize(800, 350), type=QtCore.QSize)
        # Apply size and position settings
        self.resize(size)
        self.move(pos)
        tree_width = settings.value("viewer_tree_width", 50, type=int)
        ann_width = settings.value("viewer_ann_width", 100, type=int)
        seq_width = settings.value("viewer_seq_width", 500, type=int)
        self.sequence_viewer.setSizes([tree_width, ann_width, seq_width])
        try:
            size = settings.value("viewer_font_size", 12, type=int)
            path = "/".join(["Sett&ings/&Font Size", str(size)])
            self.actions[path].setChecked(True)
            self.sequence_viewer.updateFontSize(size)
        except ValueError:
            pass
        mode = settings.value("viewer_mode", 0, type=int)
        mode_to_index = {
            constants.MODE_SELECT_ONLY: 0,
            constants.MODE_SELECT_AND_SLIDE: 1,
            constants.MODE_GRAB_AND_DRAG: 2,
            constants.MODE_EDIT: 3,
            constants.MODE_INSERT_GAPS: 4
        }
        self.modeComboBox.setCurrentIndex(mode_to_index[mode])
        self.sequence_viewer.mode = mode
        color = settings.value("viewer_custom_color",
                               (255 << 16) + (255 << 8) + 255,
                               type=int)
        self.sequence_viewer.sequence_group.custom_color = \
            
((color >> 16) & 255, (color >> 8) & 255, color & 255)
        color = settings.value("viewer_background_color",
                               (255 << 16) + (255 << 8) + 255,
                               type=int)
        self.sequence_viewer.setBackgroundColor(
            ((color >> 16) & 255, (color >> 8) & 255, color & 255))
        index = settings.value("viewer_color_mode",
                               constants.COLOR_SIDECHAIN_CHEMISTRY,
                               type=int)
        if index == constants.COLOR_MAESTRO and not maestro:
            index = constants.COLOR_SIDECHAIN_CHEMISTRY
        self.sequence_viewer.sequence_group.color_mode = index
        wrapped = settings.value("viewer_wrapped",
                                 self.sequence_viewer.wrapped,
                                 type=bool)
        self.sequence_viewer.setWrapped(wrapped)
        self.actions["Sett&ings/&Wrap Sequences"].setChecked(wrapped)
        self.mouseAcrossAct.setEnabled(wrapped)
        auto_color = settings.value("viewer_auto_color",
                                    self.sequence_viewer.auto_color,
                                    type=bool)
        self.sequence_viewer.auto_color = auto_color
        self.actions["Color/Adjust Text Contrast"].setChecked(auto_color)
        padded = settings.value("viewer_padded",
                                self.sequence_viewer.padded,
                                type=bool)
        self.sequence_viewer.setPadded(padded)
        self.actions["Sett&ings/&Pad Alignment with Gaps"].setChecked(padded)
        tooltips = settings.value("viewer_tooltips",
                                  self.sequence_viewer.has_tooltips,
                                  type=bool)
        self.actions["Sett&ings/&Display Alignment Tooltips"].setChecked(
            tooltips)
        self.sequence_viewer.setHasTooltips(tooltips)
        ruler = settings.value("viewer_ruler",
                               self.sequence_viewer.has_ruler,
                               type=bool)
        self.actions["Sett&ings/&Display Ruler"].setChecked(ruler)
        self.sequence_viewer.setHasRuler(ruler)
        lock = settings.value("viewer_lock",
                              self.sequence_viewer.lock_downstream,
                              type=bool)
        self.lockDownstreamAct.setChecked(lock)
        self.sequence_viewer.lock_downstream = lock
        boundaries = settings.value("viewer_boundaries",
                                    self.sequence_viewer.display_boundaries,
                                    type=bool)
        self.actions["Sett&ings/&Display Sequence Boundaries"].setChecked(
            boundaries)
        self.sequence_viewer.setBoundaries(boundaries)
        perc_id = settings.value("viewer_percentage_identity",
                                 self.sequence_viewer.display_identity,
                                 type=bool)
        self.actions["Sett&ings/&Display Percentage Identity"].setChecked(
            perc_id)
        self.sequence_viewer.setDisplayIdentity(perc_id)
        perc_hom = settings.value("viewer_percentage_homology",
                                  self.sequence_viewer.display_homology,
                                  type=bool)
        self.actions["Sett&ings/&Display Percentage Homology"].setChecked(
            perc_hom)
        self.sequence_viewer.setDisplayHomology(perc_hom)
        perc_sim = settings.value("viewer_percentage_similarity",
                                  self.sequence_viewer.display_similarity,
                                  type=bool)
        self.actions["Sett&ings/&Display Percentage Similarity"].setChecked(
            perc_sim)
        self.sequence_viewer.setDisplaySimilarity(perc_sim)
        score = settings.value("viewer_score",
                               self.sequence_viewer.display_score,
                               type=bool)
        self.actions["Sett&ings/&Display Score"].setChecked(score)
        self.sequence_viewer.setDisplayScore(score)
        mouse_across = settings.value("viewer_mouse_across",
                                      self.sequence_viewer.use_mouse_across,
                                      type=bool)
        self.mouseAcrossAct.setChecked(mouse_across)
        self.sequence_viewer.setMouseAcross(mouse_across)
        use_colors = settings.value("viewer_use_colors",
                                    self.sequence_viewer.use_colors,
                                    type=bool)
        self.actions["Color/Color Sequences"].setChecked(use_colors)
        self.sequence_viewer.setUseColors(use_colors)
        lock_down = settings.value("viewer_lock_downstream",
                                   self.sequence_viewer.lock_downstream,
                                   type=bool)
        self.sequence_viewer.lock_downstream = lock_down
        self.lockDownstreamAct.setChecked(lock_down)
        mutate = settings.value("viewer_mutate_residues",
                                self.sequence_viewer.mutate,
                                type=bool)
        self.sequence_viewer.mutate = mutate
        self.actions["&Maestro/Allow Structural Changes"].setChecked(mutate)
        average_columns = settings.value("viewer_average_columns",
                                         self.sequence_viewer.average_columns,
                                         type=bool)
        self.sequence_viewer.average_columns = average_columns
        self.actions["Color/&Average Colors In Columns"].setChecked(
            average_columns)
        weight_colors = settings.value("viewer_weight_colors",
                                       self.sequence_viewer.weight_colors,
                                       type=bool)
        self.sequence_viewer.weight_colors = weight_colors
        self.actions["Color/&Weight Colors by Alignment Quality"].setChecked(
            weight_colors)
        group_annotations = settings.value(
            "viewer_group_annotations",
            self.sequence_viewer.group_annotations,
            type=bool)
        self.sequence_viewer.group_annotations = group_annotations
        self.actions["Sett&ings/Group Annotations by Type"].setChecked(
            group_annotations)
        weight_colors_by_identity = settings.value(
            "viewer_weight_colors_identity",
            self.sequence_viewer.weight_colors_by_identity,
            type=bool)
        self.sequence_viewer.weight_colors_by_identity = \
            
weight_colors_by_identity
        self.actions["Color/Color Matching Residues Only"].setChecked(
            weight_colors_by_identity)
        weight_colors_by_difference = settings.value(
            "viewer_weight_colors_difference",
            self.sequence_viewer.weight_colors_by_difference,
            type=bool)
        self.sequence_viewer.weight_colors_by_difference = \
            
weight_colors_by_difference
        self.actions["Color/Color Different Residues Only"].setChecked(
            weight_colors_by_difference)
        consider_gaps = settings.value("viewer_consider_gaps",
                                       self.sequence_viewer.getConsiderGaps(),
                                       type=bool)
        self.sequence_viewer.setConsiderGaps(consider_gaps)
        self.actions[
            "Sett&ings/Include Gaps in Sequence Identity Calculations"].setChecked(
                consider_gaps)
        auto_synchronize = settings.value("viewer_auto_synchronize",
                                          self.sequence_viewer.auto_synchronize,
                                          type=bool)
        self.sequence_viewer.auto_synchronize = auto_synchronize
        self.actions["&Maestro/&Update Automatically from Maestro"].setChecked(
            auto_synchronize)
        auto_profile = settings.value("viewer_auto_profile",
                                      self.sequence_viewer.auto_profile,
                                      type=bool)
        self.sequence_viewer.auto_profile = auto_profile
        self.actions[
            "Sett&ings/Automatically Update Sequence Profile"].setChecked(
                auto_profile)
        self.actions["Sett&ings/Update Sequence Profile"].setEnabled(
            not auto_profile)
        value = settings.value("viewer_file_import_opt_merge",
                               self.file_import_opt_merge.isChecked(),
                               type=bool)
        self.file_import_opt_merge.setChecked(value)
        value = settings.value("viewer_file_import_opt_translate",
                               self.file_import_opt_translate.isChecked(),
                               type=bool)
        self.file_import_opt_translate.setChecked(value)
        value = settings.value("viewer_file_import_opt_replace",
                               self.file_import_opt_replace.isChecked(),
                               type=bool)
        self.file_import_opt_replace.setChecked(value)
        value = settings.value("viewer_file_import_opt_incorporate",
                               self.file_import_opt_incorporate.isChecked(),
                               type=bool)
        self.file_import_opt_incorporate.setChecked(value)
        incorporate_action = "&Maestro/Include Incorporated Entries in Workspace"
        do_incorporate = self.actions[incorporate_action].isChecked()
        value = settings.value("viewer_file_import_opt_include",
                               do_incorporate,
                               type=bool)
        self.actions[incorporate_action].setChecked(value)
        value = settings.value(
            "viewer_file_export_opt_save_annotations",
            self.file_export_opt_save_annotations.isChecked(),
            type=bool)
        self.file_export_opt_save_annotations.setChecked(value)
        value = settings.value("viewer_file_export_opt_selected_only",
                               self.file_export_opt_selected_only.isChecked(),
                               type=bool)
        self.file_export_opt_selected_only.setChecked(value)
        value = settings.value("viewer_image_export_opt_save_visible",
                               self.image_export_opt_save_visible.isChecked(),
                               type=bool)
        self.image_export_opt_save_visible.setChecked(value)
        ask_before_remote = "Sett&ings/Ask Before Accessing a Remote Server"
        value = settings.value("viewer_ask_before_remote_access",
                               self.actions[ask_before_remote].isChecked(),
                               type=bool)
        self.actions[ask_before_remote].setChecked(value) 
[docs]    def writeSettings(self, settings):
        """
        Writes sequence viewer user settings.
        """
        settings.setValue("pos", self.pos())
        settings.setValue("size", self.size())
        tree_width, ann_width, seq_width = self.sequence_viewer.sizes()
        settings.setValue("viewer_tree_width", tree_width)
        settings.setValue("viewer_ann_width", ann_width)
        settings.setValue("viewer_seq_width", seq_width)
        settings.setValue("viewer_mode", self.sequence_viewer.mode)
        if self.sequence_viewer.sequence_group.color_mode == \
                                                    
constants.COLOR_MAESTRO:
            settings.setValue("viewer_color_mode",
                              constants.COLOR_SIDECHAIN_CHEMISTRY)
        else:
            settings.setValue("viewer_color_mode",
                              self.sequence_viewer.sequence_group.color_mode)
        r, g, b = self.sequence_viewer.sequence_group.custom_color
        settings.setValue("viewer_custom_color", (r << 16) + (g << 8) + b)
        r = self.sequence_viewer.background_color.red()
        g = self.sequence_viewer.background_color.green()
        b = self.sequence_viewer.background_color.blue()
        settings.setValue("viewer_background_color", (r << 16) + (g << 8) + b)
        settings.setValue("viewer_wrapped", self.sequence_viewer.wrapped)
        settings.setValue("viewer_auto_color", self.sequence_viewer.auto_color)
        settings.setValue("viewer_padded", self.sequence_viewer.padded)
        settings.setValue("viewer_tooltips", self.sequence_viewer.has_tooltips)
        settings.setValue("viewer_ruler", self.sequence_viewer.has_ruler)
        settings.setValue("viewer_lock_downstream",
                          self.sequence_viewer.lock_downstream)
        settings.setValue("viewer_average_columns",
                          self.sequence_viewer.average_columns)
        settings.setValue("viewer_weight_colors",
                          self.sequence_viewer.weight_colors)
        settings.setValue("viewer_weight_colors_identity",
                          self.sequence_viewer.weight_colors_by_identity)
        settings.setValue("viewer_mouse_across",
                          self.sequence_viewer.use_mouse_across)
        settings.setValue("viewer_use_colors", self.sequence_viewer.use_colors)
        settings.setValue("viewer_font_size", self.sequence_viewer.font_size)
        settings.setValue("viewer_mutate_residues", self.sequence_viewer.mutate)
        settings.setValue("viewer_boundaries",
                          self.sequence_viewer.display_boundaries)
        settings.setValue("viewer_percentage_identity",
                          self.sequence_viewer.display_identity)
        settings.setValue("viewer_percentage_similarity",
                          self.sequence_viewer.display_similarity)
        settings.setValue("viewer_score", self.sequence_viewer.display_score)
        settings.setValue("viewer_group_annotations",
                          self.sequence_viewer.group_annotations)
        settings.setValue("viewer_lock", self.sequence_viewer.lock_downstream)
        settings.setValue("viewer_consider_gaps",
                          self.sequence_viewer.getConsiderGaps())
        settings.setValue("viewer_auto_synchronize",
                          self.sequence_viewer.auto_synchronize)
        settings.setValue("viewer_auto_profile",
                          self.sequence_viewer.auto_profile)
        settings.setValue("viewer_file_import_opt_merge",
                          self.file_import_opt_merge.isChecked())
        settings.setValue("viewer_file_import_opt_translate",
                          self.file_import_opt_translate.isChecked())
        settings.setValue("viewer_file_import_opt_replace",
                          self.file_import_opt_replace.isChecked())
        settings.setValue("viewer_file_import_opt_incorporate",
                          self.file_import_opt_incorporate.isChecked())
        settings.setValue(
            "viewer_file_import_opt_include",
            self.actions["&Maestro/Include Incorporated Entries in Workspace"].
            isChecked())
        settings.setValue("viewer_file_export_opt_save_annotations",
                          self.file_export_opt_save_annotations.isChecked())
        settings.setValue("viewer_file_export_opt_selected_only",
                          self.file_export_opt_selected_only.isChecked())
        settings.setValue("viewer_image_export_opt_save_visible",
                          self.image_export_opt_save_visible.isChecked())
        settings.setValue(
            "viewer_ask_before_remote_access",
            self.actions["Sett&ings/Ask Before Accessing a Remote Server"].
            isChecked())
        settings.sync() 
[docs]    def viewerModeChanged(self, value):
        """
        Mode changed action callback.
        :type value: int
        :param value: Sequence viewer mode, same as Mode combo box item index.
        """
        index_to_mode = {
            0: constants.MODE_SELECT_ONLY,
            1: constants.MODE_SELECT_AND_SLIDE,
            2: constants.MODE_GRAB_AND_DRAG,
            3: constants.MODE_EDIT,
            4: constants.MODE_INSERT_GAPS
        }
        self.sequence_viewer.setMode(index_to_mode[value]) 
[docs]    def viewerTranslate(self):
        """ Translates DNA / RNA sequence to amino acids. """
        self.sequence_viewer.translate() 
[docs]    def viewerWrapSequences(self):
        """ Toggle wrap sequences action callback. """
        self.sequence_viewer.setWrapped(not self.sequence_viewer.wrapped)
        if self.sequence_viewer.wrapped:
            self.mouseAcrossAct.setEnabled(True)
        else:
            self.mouseAcrossAct.setEnabled(False) 
[docs]    def viewerToggleMouseAcross(self):
        """ Toggle mouse across rows action callback. """
        self.sequence_viewer.setMouseAcross(
            not self.sequence_viewer.use_mouse_across) 
[docs]    def viewerToggleIdentityInColumns(self):
        """ Toggle mouse across rows action callback. """
        self.sequence_viewer.setIdentityInColumns(self.actions[
            "Sett&ings/Calculate Sequence Identity Only in Selected Columns"].
                                                  isChecked()) 
[docs]    def viewerToggleColors(self):
        """ Toggle mouse across rows action callback. """
        self.sequence_viewer.setUseColors(not self.sequence_viewer.use_colors) 
[docs]    def viewerPadAlignment(self):
        """ Pad alignment action callback. """
        self.sequence_viewer.setPadded(not self.sequence_viewer.padded) 
[docs]    def viewerHideEmptyLines(self):
        """ Hide empty lines action callback. """
        self.sequence_viewer.hide_empty_lines = \
                        
self.actions["Sett&ings/Hide Empty Lines"].isChecked()
        self.sequence_viewer.updateView() 
[docs]    def viewerHideSelected(self):
        """ Hide selected sequences action callback. """
        self.sequence_viewer.hideSelectedSequences() 
[docs]    def viewerDeleteSelected(self):
        """ Delete selected sequences action callback. """
        all = self.sequence_viewer.deleteSelectedSequences()
        if all:
            self.setWindowTitle("Multiple Sequence Viewer") 
[docs]    def viewerShowAll(self):
        """ Show all sequences action callback. """
        self.sequence_viewer.showAllSequences() 
[docs]    def viewerFillGaps(self):
        """ Fill gaps action callback. """
        self.sequence_viewer.fillGaps() 
[docs]    def viewerRemoveGaps(self):
        """ Remove gaps action callback. """
        self.sequence_viewer.removeGaps() 
[docs]    def viewerDeleteSelectedResidues(self):
        """ Delete selected residues action callback. """
        self.sequence_viewer.deleteSelectedResidues() 
[docs]    def viewerMinimizeAlignment(self):
        """ Minimize alignment action callback. """
        self.sequence_viewer.minimizeAlignment() 
[docs]    def viewerToggleRuler(self):
        """ Toggle ruler action callback. """
        self.sequence_viewer.setHasRuler(not self.sequence_viewer.has_ruler) 
[docs]    def viewerToggleDisplayIdentity(self):
        """
        Toggle display percentage identity action callback.
        """
        self.actions["Sett&ings/&Display Percentage Similarity"].setChecked(
            False)
        self.actions["Sett&ings/&Display Score"].setChecked(False)
        self.actions["Sett&ings/&Display Percentage Homology"].setChecked(False)
        self.sequence_viewer.setDisplayIdentity(
            not self.sequence_viewer.display_identity)
        self.sequence_viewer.setDisplaySimilarity(False)
        self.sequence_viewer.setDisplayHomology(False)
        self.sequence_viewer.setDisplayScore(False) 
[docs]    def viewerToggleDisplaySimilarity(self):
        """
        Toggle display percentage similarity action callback.
        """
        self.actions["Sett&ings/&Display Percentage Identity"].setChecked(False)
        self.actions["Sett&ings/&Display Score"].setChecked(False)
        self.actions["Sett&ings/&Display Percentage Homology"].setChecked(False)
        self.sequence_viewer.setDisplaySimilarity(
            not self.sequence_viewer.display_similarity)
        self.sequence_viewer.setDisplayIdentity(False)
        self.sequence_viewer.setDisplayHomology(False)
        self.sequence_viewer.setDisplayScore(False) 
[docs]    def viewerToggleDisplayScore(self):
        """ Toggle display score action callback. """
        self.actions["Sett&ings/&Display Percentage Identity"].setChecked(False)
        self.actions["Sett&ings/&Display Percentage Similarity"].setChecked(
            False)
        self.actions["Sett&ings/&Display Percentage Homology"].setChecked(False)
        self.sequence_viewer.setDisplayScore(
            not self.sequence_viewer.display_score)
        self.sequence_viewer.setDisplayIdentity(False)
        self.sequence_viewer.setDisplayHomology(False)
        self.sequence_viewer.setDisplaySimilarity(False) 
[docs]    def viewerToggleDisplayHomology(self):
        """ Toggle display homology action callback. """
        self.actions["Sett&ings/&Display Percentage Identity"].setChecked(False)
        self.actions["Sett&ings/&Display Percentage Similarity"].setChecked(
            False)
        self.sequence_viewer.setDisplayHomology(
            not self.sequence_viewer.display_homology)
        self.sequence_viewer.setDisplayIdentity(False)
        self.sequence_viewer.setDisplayScore(False)
        self.sequence_viewer.setDisplaySimilarity(False) 
[docs]    def viewerToggleDisplayBoundaries(self):
        """ Toggle display score action callback. """
        self.sequence_viewer.setBoundaries(
            not self.sequence_viewer.display_boundaries) 
[docs]    def viewerLockGaps(self):
        """ Lock gaps action callback. """
        self.sequence_viewer.lockGaps() 
[docs]    def viewerExpandAll(self):
        """ Expand all action callback. """
        self.sequence_viewer.expandAllSequences() 
[docs]    def viewerCollapseAll(self):
        """ Collapse all action callback. """
        self.sequence_viewer.collapseAllSequences() 
[docs]    def viewerAddAllColorBlocks(self):
        """ Adds all color block annotations. """
        self.sequence_viewer.addAllColorBlocks() 
[docs]    def viewerRemoveAllColorBlocks(self):
        """ Adds all color block annotations. """
        self.sequence_viewer.removeAllColorBlocks() 
[docs]    def viewerUnlockGaps(self):
        """ Unlock gaps action callback. """
        self.sequence_viewer.unlockGaps() 
[docs]    def viewerSelectBlocks(self):
        """ Select blocks action callback. """
        self.sequence_viewer.selectAlignedBlocks() 
[docs]    def viewerSelectStructure(self):
        """ Select structure action callback. """
        self.sequence_viewer.selectStructureBlocks() 
[docs]    def viewerSelectIdentities(self):
        """ Select identities action callback. """
        self.sequence_viewer.selectIdentities() 
[docs]    def viewerToggleLockDownstream(self):
        """ Toggle lock downstream action callback. """
        self.sequence_viewer.lock_downstream = \
            
not self.sequence_viewer.lock_downstream 
[docs]    def viewerRunClustal(self):
        """ Run Clustal action callback. """
        self.sequence_viewer.runClustal() 
[docs]    def viewerRunPfam(self):
        """ Run Pfam action callback. """
        self.sequence_viewer.runPfam() 
[docs]    def viewerRunSSP(self):
        """ Run SSP action callback. """
        if predictors.has_predictor("sspro"):
            self.sequence_viewer.runPredictors(['sspro'])
        else:
            self.sequence_viewer.runSSP() 
[docs]    def viewerRunBlast(self):
        """ Run Blast action callback. """
        self.sequence_viewer.runBlast() 
[docs]    def viewerShowBlast(self):
        """ Show Blast action callback. """
        self.sequence_viewer.showBlastResults() 
[docs]    def viewerZoomIn(self):
        """ Zoom in action callback. """
        self.sequence_viewer.zoomIn() 
[docs]    def viewerZoomOut(self):
        """ Zoom out action callback. """
        self.sequence_viewer.zoomOut() 
[docs]    def viewerHelp(self):
        """ Invokes MSV help. """
        if maestro:
            maestro.command("helptopic TOOLS_MENU_MULTIPLE_SEQUENCE_VIEWER") 
[docs]    def viewerAddConsensus(self):
        """ Add conesensus action callback. """
        self.sequence_viewer.addConsensus(toggle=True) 
[docs]    def viewerAddSymbols(self):
        """ Add symbols action callback. """
        self.sequence_viewer.addSymbols(toggle=True) 
[docs]    def viewerAddGlobal(self):
        """ Add global annotations action callback. """
        self.actions["&Annotations/Consensus Sequence"].setChecked(True)
        self.actions["&Annotations/Consensus Symbols"].setChecked(True)
        self.actions["&Annotations/Mean Hydrophobicity"].setChecked(True)
        self.actions["&Annotations/Mean Isoelectric Point"].setChecked(True)
        self.actions["&Annotations/Sequence Logo"].setChecked(True)
        self.sequence_viewer.addGlobalAnnotations() 
[docs]    def viewerRemoveGlobal(self):
        """ Add global annotations action callback. """
        self.actions["&Annotations/Consensus Sequence"].setChecked(False)
        self.actions["&Annotations/Consensus Symbols"].setChecked(False)
        self.actions["&Annotations/Mean Hydrophobicity"].setChecked(False)
        self.actions["&Annotations/Mean Isoelectric Point"].setChecked(False)
        self.actions["&Annotations/Sequence Logo"].setChecked(False)
        self.sequence_viewer.removeGlobalAnnotations() 
[docs]    def viewerAddHistory(self):
        """ Add history action callback. """
        self.sequence_viewer.toggleHistory() 
[docs]    def viewerSetAlwaysAsk(self):
        self.settings.setValue(
            "viewer_ask_before_remote_access",
            self.actions["Sett&ings/Ask Before Accessing a Remote Server"].
            isChecked()) 
[docs]    def viewerResetHistory(self):
        """ Reset history action callback. """
        self.sequence_viewer.resetHistory() 
[docs]    def viewerAddMeanHydrophobicity(self):
        """ Add mean hydrophobicity action callback. """
        self.sequence_viewer.addMeanHydrophobicity(toggle=True) 
[docs]    def viewerAddMeanPI(self):
        """ Add mean isoelectric point action callback. """
        self.sequence_viewer.addMeanPI(toggle=True) 
[docs]    def viewerAddSequenceLogo(self):
        """ Add sequence logo action callback. """
        self.sequence_viewer.addSequenceLogo(toggle=True) 
[docs]    def viewerDeleteAnnotations(self):
        """ Delete annotations action callback. """
        self.actions["&Annotations/Consensus Sequence"].setChecked(False)
        self.actions["&Annotations/Consensus Symbols"].setChecked(False)
        self.actions["&Annotations/Sequence Logo"].setChecked(False)
        self.actions["&Annotations/Mean Hydrophobicity"].setChecked(False)
        self.actions["&Annotations/Mean Isoelectric Point"].setChecked(False)
        self.sequence_viewer.deleteAnnotations() 
[docs]    def viewerDeletePredictions(self):
        """ Delete predictions action callback. """
        self.sequence_viewer.deletePredictions() 
[docs]    def viewerGroupAnnotations(self):
        """ Group annotations action callback. """
        self.sequence_viewer.group_annotations = \
            
not self.sequence_viewer.group_annotations
        self.sequence_viewer.updateView() 
[docs]    def viewerAverageColumns(self):
        """ Average columns action callback. """
        self.sequence_viewer.average_columns = \
            
not self.sequence_viewer.average_columns
        self.sequence_viewer.updateView(update_colors=True) 
[docs]    def viewerWeightColors(self):
        """ Weight colors action callback. """
        self.sequence_viewer.weight_colors = \
            
not self.sequence_viewer.weight_colors
        self.sequence_viewer.updateView(update_colors=True) 
[docs]    def viewerWeightColorsIdentity(self):
        """ Weight colors by identity action callback. """
        self.sequence_viewer.weight_colors_by_identity = \
            
bool(self.actions["Color/Color Matching Residues Only"].isChecked())
        if self.sequence_viewer.weight_colors_by_identity:
            self.actions["Color/Color Different Residues Only"].setChecked(
                False)
            self.sequence_viewer.weight_colors_by_difference = False
        self.sequence_viewer.updateView(update_colors=True) 
[docs]    def viewerWeightColorsDifference(self):
        """ Weight colors by difference action callback. """
        self.sequence_viewer.weight_colors_by_difference = \
            
bool(self.actions["Color/Color Different Residues Only"].isChecked(
                                                                            ))
        if self.sequence_viewer.weight_colors_by_difference:
            self.actions["Color/Color Matching Residues Only"].setChecked(False)
            self.sequence_viewer.weight_colors_by_identity = False
        self.sequence_viewer.updateView(update_colors=True) 
[docs]    def findTextChanged(self):
        """
        Called whenever text in "Find pattern" widget has changed.
        :type string: str
        :param string: pattern string
        """
        text = self.findLineEdit.text()
        self.sequence_viewer.findPattern(str(text)) 
[docs]    def clearPattern(self):
        """
        Called whenever user clicks on 'Clear Pattern' button.
        :type string: str
        :param string: pattern string
        """
        self.findLineEdit.setText("")
        self.sequence_viewer.findPattern("") 
[docs]    def viewerFindPrevious(self):
        """ Find previous action callback. """
        self.sequence_viewer.findPrevious() 
[docs]    def viewerFindNext(self):
        """ Find next action callback. """
        self.sequence_viewer.findNext() 
[docs]    def viewerRenumberResidues(self):
        """ Renumber residues action callback. """
        self.sequence_viewer.renumberResidues() 
[docs]    def viewerUndo(self):
        """ Undo action callback. """
        undo, redo = self.sequence_viewer.undo() 
[docs]    def viewerRedo(self):
        """ Redo action callback. """
        undo, redo = self.sequence_viewer.redo() 
[docs]    def viewerAddSimilarityAnnotation(self):
        """ Add similarity action callback. """
        self.sequence_viewer.addAnnotation(constants.ANNOTATION_SIMILARITY) 
[docs]    def viewerAddResnumAnnotation(self):
        """ Add residue number annotation. """
        self.sequence_viewer.addAnnotation(
            constants.ANNOTATION_RESNUM,
            remove=not self.actions["&Annotations/Residue Numbers"].isChecked()) 
[docs]    def viewerAddIdentityAnnotation(self):
        """ Add identity annotation action callback. """
        self.sequence_viewer.addAnnotation(constants.ANNOTATION_IDENTITY) 
[docs]    def viewerAddHydrophobicityAnnotation(self):
        """ Add helix propensity action callback. """
        self.sequence_viewer.addAnnotation(
            constants.ANNOTATION_HYDROPHOBICITY,
            remove=not self.actions["&Annotations/Hydrophobicity"].isChecked()) 
[docs]    def viewerAddPIAnnotation(self):
        """ Add helix propensity action callback. """
        self.sequence_viewer.addAnnotation(
            constants.ANNOTATION_PI,
            remove=not self.actions["&Annotations/Isoelectric Point"].isChecked(
            )) 
[docs]    def viewerAddBFactorAnnotation(self):
        """ Add helix propensity action callback. """
        self.sequence_viewer.addAnnotation(
            constants.ANNOTATION_BFACTOR,
            remove=not self.actions["&Annotations/B factor"].isChecked()) 
[docs]    def viewerAddSSBondAnnotation(self):
        """ Add helix propensity action callback. """
        self.sequence_viewer.addAnnotation(
            constants.ANNOTATION_SSBOND,
            remove=not self.actions["&Annotations/Disulfide Bonds"].isChecked()) 
[docs]    def viewerAddHelixPropensityAnnotation(self):
        """ Add helix propensity action callback. """
        self.sequence_viewer.addAnnotation(
            constants.ANNOTATION_HELIX_PROPENSITY,
            remove=not self.
            actions["&Annotations/Color Blocks/&Helix Propensity"].isChecked()) 
[docs]    def viewerAddStrandPropensityAnnotation(self):
        """ Add strand propensity action annotation callback. """
        self.sequence_viewer.addAnnotation(
            constants.ANNOTATION_STRAND_PROPENSITY,
            remove=not self.
            actions["&Annotations/Color Blocks/&Strand Propensity"].isChecked()) 
[docs]    def viewerAddTurnPropensityAnnotation(self):
        """ Add strand propensity annotation action callback. """
        self.sequence_viewer.addAnnotation(
            constants.ANNOTATION_TURN_PROPENSITY,
            remove=not self.
            actions["&Annotations/Color Blocks/&Turn Propensity"].isChecked()) 
[docs]    def viewerAddStericGroupAnnotation(self):
        """ Add steric group action annotation callback. """
        self.sequence_viewer.addAnnotation(
            constants.ANNOTATION_STERIC_GROUP,
            remove=not self.
            actions["&Annotations/Color Blocks/&Steric Propensity"].isChecked()) 
[docs]    def viewerAddExposureTendencyAnnotation(self):
        """ Add steric group action annotation callback. """
        self.sequence_viewer.addAnnotation(
            constants.ANNOTATION_EXPOSURE_TENDENCY,
            remove=not self.
            actions["&Annotations/Color Blocks/Exposure Tendency"].isChecked()) 
[docs]    def viewerAddHelixTerminatorsAnnotation(self):
        """ Add helix terminators annotation action callback. """
        self.sequence_viewer.addAnnotation(
            constants.ANNOTATION_HELIX_TERMINATORS,
            remove=not self.
            actions["&Annotations/Color Blocks/Helix Termination"].isChecked()) 
[docs]    def viewerAddSidechainChemistryAnnotation(self):
        """ Add sidechain chemistry annotation action callback. """
        self.sequence_viewer.addAnnotation(
            constants.ANNOTATION_SIDECHAIN_CHEMISTRY,
            remove=not self.actions[
                "&Annotations/Color Blocks/Side-Chain Chemistry"].isChecked()) 
[docs]    def viewerColorHelixPropensity(self):
        """ Add helix propensity action callback. """
        self.sequence_viewer.setColorMode(constants.COLOR_HELIX_PROPENSITY) 
[docs]    def viewerColorStrandPropensity(self):
        """ Add strand propensity action annotation callback. """
        self.sequence_viewer.setColorMode(constants.COLOR_STRAND_PROPENSITY) 
[docs]    def viewerColorTurnPropensity(self):
        """ Add strand propensity annotation action callback. """
        self.sequence_viewer.setColorMode(constants.COLOR_TURN_PROPENSITY) 
[docs]    def viewerColorStericGroup(self):
        """ Add steric group action annotation callback. """
        self.sequence_viewer.setColorMode(constants.COLOR_STERIC_GROUP) 
[docs]    def viewerColorExposureTendency(self):
        """ Add steric group action annotation callback. """
        self.sequence_viewer.setColorMode(constants.COLOR_EXPOSURE_TENDENCY) 
[docs]    def viewerColorHelixTerminators(self):
        """ Add helix terminators annotation action callback. """
        self.sequence_viewer.setColorMode(constants.COLOR_HELIX_TERMINATORS) 
[docs]    def viewerColorSidechainChemistry(self):
        """ Add sidechain chemistry annotation action callback. """
        self.sequence_viewer.setColorMode(constants.COLOR_SIDECHAIN_CHEMISTRY) 
[docs]    def viewerAnchorSelection(self):
        self.sequence_viewer.anchorSelection() 
[docs]    def viewerClearAnchors(self):
        self.sequence_viewer.clearAnchors() 
[docs]    def sortByName(self):
        """ Sort by name action callback. """
        self.sequence_viewer.sequence_group.sort(constants.SORT_NAME)
        self.sequence_viewer.updateView() 
[docs]    def sortByChain(self):
        """ Sort by chain ID action callback. """
        self.sequence_viewer.sequence_group.sort(constants.SORT_CHAIN)
        self.sequence_viewer.updateView() 
[docs]    def sortByLength(self):
        """ Sort by length action callback. """
        self.sequence_viewer.sequence_group.sort(constants.SORT_LENGTH)
        self.sequence_viewer.updateView() 
[docs]    def sortByGaps(self):
        """ Sort by gaps action callback. """
        self.sequence_viewer.sequence_group.sort(constants.SORT_GAPS)
        self.sequence_viewer.updateView() 
[docs]    def sortByIdentity(self):
        """ Sort by identity action callback. """
        self.sequence_viewer.sequence_group.sort(constants.SORT_IDENTITY)
        self.sequence_viewer.updateView() 
[docs]    def sortBySimilarity(self):
        """ Sort by similarity action callback. """
        self.sequence_viewer.sequence_group.sort(constants.SORT_SIMILARITY)
        self.sequence_viewer.updateView() 
[docs]    def sortByHomology(self):
        """ Sort by similarity action callback. """
        self.sequence_viewer.sequence_group.sort(constants.SORT_HOMOLOGY)
        self.sequence_viewer.updateView() 
[docs]    def sortByScore(self):
        """ Sort by score action callback.  """
        self.sequence_viewer.sequence_group.sort(constants.SORT_SCORE)
        self.sequence_viewer.updateView() 
[docs]    def sortByNameReverse(self):
        """ Reverse sort by name action callback. """
        self.sequence_viewer.sequence_group.sort(constants.SORT_NAME,
                                                 reverse_order=True)
        self.sequence_viewer.updateView() 
[docs]    def sortByChainReverse(self):
        """ Reverse sort by name action callback. """
        self.sequence_viewer.sequence_group.sort(constants.SORT_CHAIN,
                                                 reverse_order=True)
        self.sequence_viewer.updateView() 
[docs]    def sortByLengthReverse(self):
        """ Reverse sort by length action callback. """
        self.sequence_viewer.sequence_group.sort(constants.SORT_LENGTH,
                                                 reverse_order=True)
        self.sequence_viewer.updateView() 
[docs]    def sortByGapsReverse(self):
        """ Reverse sort by gaps action callback. """
        self.sequence_viewer.sequence_group.sort(constants.SORT_GAPS,
                                                 reverse_order=True)
        self.sequence_viewer.updateView() 
[docs]    def sortByIdentityReverse(self):
        """ Reverse sort by identity action callback. """
        self.sequence_viewer.sequence_group.sort(constants.SORT_IDENTITY,
                                                 reverse_order=True)
        self.sequence_viewer.updateView() 
[docs]    def sortBySimilarityReverse(self):
        """ Reverse sort by identity action callback. """
        self.sequence_viewer.sequence_group.sort(constants.SORT_SIMILARITY,
                                                 reverse_order=True)
        self.sequence_viewer.updateView() 
[docs]    def sortByHomologyReverse(self):
        """ Reverse sort by identity action callback. """
        self.sequence_viewer.sequence_group.sort(constants.SORT_HOMOLOGY,
                                                 reverse_order=True)
        self.sequence_viewer.updateView() 
[docs]    def sortByScoreReverse(self):
        """ Reverse sort by identity action callback. """
        self.sequence_viewer.sequence_group.sort(constants.SORT_SCORE,
                                                 reverse_order=True)
        self.sequence_viewer.updateView() 
[docs]    def treeSwapBranches(self):
        """ Swap tree branches action callback. """
        self.sequence_viewer.tree_area.swapBranches() 
[docs]    def treeHideBranches(self):
        """ Hide tree branches action callback. """
        self.sequence_viewer.tree_area.hideSequences() 
[docs]    def treeSelectSequences(self):
        """ Select tree branches action callback. """
        self.sequence_viewer.tree_area.selectSequences() 
[docs]    def sortByTreeOrder(self):
        """ Sort by tree order action callback. """
        if self.actions["&Sequences/Sort by &Tree Order"].isChecked():
            sizes_list = self.sequence_viewer.sizes()
            sizes_list[0] = 200
            self.sequence_viewer.setSizes(sizes_list)
            self.menus["&Sequences/Sort &Ascending"].setEnabled(False)
            self.menus["&Sequences/Sort &Descending"].setEnabled(False)
            self.actions["&Sequences/&Move to Top"].setEnabled(False)
            self.actions["&Sequences/&Move Down"].setEnabled(False)
            self.actions["&Sequences/&Move to Top"].setEnabled(False)
            self.actions["&Sequences/&Move to Bottom"].setEnabled(False)
            self.sequence_viewer.sequence_group.sortByTreeOrder()
            self.sequence_viewer.updateView()
        else:
            sizes_list = self.sequence_viewer.sizes()
            sizes_list[0] = 0
            self.sequence_viewer.setSizes(sizes_list)
            self.menus["&Sequences/Sort &Ascending"].setEnabled(True)
            self.menus["&Sequences/Sort &Descending"].setEnabled(True)
            self.actions["&Sequences/&Move to Top"].setEnabled(True)
            self.actions["&Sequences/&Move Down"].setEnabled(True)
            self.actions["&Sequences/&Move to Top"].setEnabled(True)
            self.actions["&Sequences/&Move to Bottom"].setEnabled(True) 
[docs]    def viewerSplitterMoved(self, pos, index):
        """
        Splitter moved action callback.
        :type pos: int
        :param pos: new splitter position
        :type index: int
        :param index: splitter index
        """
        if index == 1:
            if pos == 1:
                self.menus["&Sequences/Sort &Ascending"].setEnabled(True)
                self.menus["&Sequences/Sort &Descending"].setEnabled(True)
                self.actions["&Sequences/&Move to Top"].setEnabled(True)
                self.actions["&Sequences/&Move Down"].setEnabled(True)
                self.actions["&Sequences/&Move to Top"].setEnabled(True)
                self.actions["&Sequences/&Move to Bottom"].setEnabled(True)
                self.actions["&Sequences/Sort by &Tree Order"].setChecked(False)
            else:
                self.menus["&Sequences/Sort &Ascending"].setEnabled(False)
                self.menus["&Sequences/Sort &Descending"].setEnabled(False)
                self.actions["&Sequences/&Move to Top"].setEnabled(False)
                self.actions["&Sequences/&Move Down"].setEnabled(False)
                self.actions["&Sequences/&Move to Top"].setEnabled(False)
                self.actions["&Sequences/&Move to Bottom"].setEnabled(False)
                self.actions["&Sequences/Sort by &Tree Order"].setChecked(True) 
[docs]    def viewerFetchSequence(self):
        """ Fetch sequence action callback. """
        entry_id = self.fetchLineEdit.text()
        self.sequence_viewer.fetchSequence(
            str(entry_id),
            maestro_include=self.
            actions["&Maestro/Include Incorporated Entries in Workspace"].
            isChecked(),
            maestro_incorporate=self.file_import_opt_incorporate.isChecked()) 
[docs]    def viewerEditSequence(self):
        """ Edits a sequence as a text. """
        self.sequence_viewer.editSequence() 
[docs]    def viewerCreateSequence(self):
        """ Creates a new sequence. """
        self.sequence_viewer.createSequence() 
[docs]    def viewerMoveUp(self):
        """ Move sequences up action callback. """
        self.sequence_viewer.moveUp() 
[docs]    def viewerMoveDown(self):
        """ Move sequences down action callback. """
        self.sequence_viewer.moveDown() 
[docs]    def viewerMoveTop(self):
        """ Move sequences up action callback. """
        self.sequence_viewer.moveTop() 
[docs]    def viewerMoveBottom(self):
        """ Move sequences down action callback. """
        self.sequence_viewer.moveBottom() 
[docs]    def viewerInvertSelection(self):
        """ Invert residue selection range action callback. """
        self.sequence_viewer.invertSelection() 
[docs]    def viewerSelectAll(self):
        """ Select all residues action callback. """
        self.sequence_viewer.selectAll() 
[docs]    def viewerDeselectAll(self):
        """ Deselect all residues action callback. """
        self.sequence_viewer.deselectAll() 
[docs]    def viewerSynchronizeWithMaestro(self):
        """ Synchronize with Maestro action callback. """
        self.sequence_viewer.synchronizeWithMaestro() 
[docs]    def viewerPassSelectionToMaestro(self):
        self.sequence_viewer.passSelectionToMaestro() 
[docs]    def viewerAutoSynchronizeWithMaestro(self):
        """ Synchronize with Maestro action callback. """
        self.sequence_viewer.auto_synchronize = \
            
not self.sequence_viewer.auto_synchronize
        if self.sequence_viewer.auto_synchronize:
            self.sequence_viewer.synchronizeWithMaestro() 
[docs]    def viewerComputeSequenceProfile(self):
        """ Synchronize with Maestro action callback. """
        self.sequence_viewer.computeSequenceProfile() 
[docs]    def viewerAutoComputeSequenceProfile(self):
        """ Synchronize with Maestro action callback. """
        self.sequence_viewer.auto_profile = \
            
not self.sequence_viewer.auto_profile
        if self.sequence_viewer.auto_profile:
            self.sequence_viewer.computeSequenceProfile()
            self.actions["Sett&ings/Update Sequence Profile"].setEnabled(False)
        else:
            self.actions["Sett&ings/Update Sequence Profile"].setEnabled(True) 
[docs]    def viewerMutateWhileTyping(self):
        """ Mutate sequence while typing action callback. """
        self.sequence_viewer.mutate = not self.sequence_viewer.mutate 
[docs]    def viewerRemoveRedundancy(self):
        self.sequence_viewer.removeRedundancy() 
[docs]    def viewerDuplicate(self):
        """ Duplicates currently selected sequences. """
        self.sequence_viewer.duplicateSequences() 
[docs]    def viewerPairwiseAlignment(self):
        self.sequence_viewer.pairwiseAlignment() 
[docs]    def viewerAlignMerge(self):
        self.sequence_viewer.alignMerge() 
[docs]    def viewerAlignByResidueNumbers(self):
        self.sequence_viewer.alignByResidueNumbers() 
[docs]    def viewerRenumberToAntibody(self):
        scheme = 'Chothia'
        if self.actions[
                "&Annotations/Antibody Numbering Scheme/Enhanced Chothia"].isChecked(
                ):
            scheme = 'EnhancedChothia'
        elif self.actions[
                "&Annotations/Antibody Numbering Scheme/Kabat"].isChecked():
            scheme = 'Kabat'
        elif self.actions[
                "&Annotations/Antibody Numbering Scheme/IMGT"].isChecked():
            scheme = 'IMGT'
        elif self.actions[
                "&Annotations/Antibody Numbering Scheme/AHo"].isChecked():
            scheme = 'AHo'
        self.sequence_viewer.assignAntibodyScheme(
            scheme=scheme,
            remove=not self.actions["&Annotations/&Antibody CDRs"].isChecked()) 
[docs]    def viewerSelectAntibodyRegions(self):
        scheme = 'Chothia'
        if self.actions[
                "&Annotations/Antibody Numbering Scheme/Enhanced Chothia"].isChecked(
                ):
            scheme = 'EnhancedChothia'
        elif self.actions[
                "&Annotations/Antibody Numbering Scheme/Kabat"].isChecked():
            scheme = 'Kabat'
        elif self.actions[
                "&Annotations/Antibody Numbering Scheme/IMGT"].isChecked():
            scheme = 'IMGT'
        elif self.actions[
                "&Annotations/Antibody Numbering Scheme/AHo"].isChecked():
            scheme = 'AHo'
        self.sequence_viewer.assignAntibodyScheme(remove=False,
                                                  renumber=False,
                                                  renumber_entry=False,
                                                  annotate=False,
                                                  select=True,
                                                  scheme=scheme) 
[docs]    def viewerSetAsReference(self):
        self.sequence_viewer.setAsReference() 
[docs]    def viewerSelectAllSequences(self):
        self.sequence_viewer.selectAllSequences() 
[docs]    def viewerUnselectAllSequences(self):
        self.sequence_viewer.unselectAllSequences() 
[docs]    def viewerInvertSequenceSelection(self):
        self.sequence_viewer.invertSequenceSelection() 
[docs]    def viewerPasteFasta(self):
        self.sequence_viewer.pasteFasta() 
[docs]    def viewerColorResidueType(self):
        self.sequence_viewer.setColorMode(constants.COLOR_RESIDUE_TYPE) 
[docs]    def viewerColorSecondary(self):
        self.sequence_viewer.setColorMode(constants.COLOR_SECONDARY) 
[docs]    def viewerColorBfactor(self):
        self.sequence_viewer.setColorMode(constants.COLOR_BFACTOR) 
[docs]    def viewerColorPosition(self):
        self.sequence_viewer.setColorMode(constants.COLOR_POSITION) 
[docs]    def viewerToggleUseDots(self):
        self.sequence_viewer.display_dots = not self.sequence_viewer.display_dots
        self.sequence_viewer.updateView() 
[docs]    def viewerToggleAutoColor(self):
        self.sequence_viewer.auto_color = not self.sequence_viewer.auto_color
        self.sequence_viewer.updateView() 
[docs]    def viewerColorSimilarity(self):
        self.sequence_viewer.setColorMode(constants.COLOR_SIMILARITY) 
[docs]    def viewerColorKyteDoolittle(self):
        self.sequence_viewer.setColorMode(constants.COLOR_KYTE_DOOLITTLE) 
[docs]    def viewerColorHoppWoods(self):
        self.sequence_viewer.setColorMode(constants.COLOR_HOPP_WOODS) 
[docs]    def viewerColorGrayscale(self):
        self.sequence_viewer.setColorMode(constants.COLOR_BLACK) 
[docs]    def viewerColorWhite(self):
        self.sequence_viewer.setColorMode(constants.COLOR_WHITE) 
[docs]    def viewerColorTaylor(self):
        self.sequence_viewer.setColorMode(constants.COLOR_TAYLOR) 
[docs]    def viewerColorMaestro(self):
        maestro_helpers.synchronizePropertiesWithMaestro(self.sequence_viewer,
                                                         colors=True,
                                                         selection=False)
        self.sequence_viewer.setColorMode(constants.COLOR_MAESTRO) 
[docs]    def viewerPropagateColors(self):
        """ Propagates colors to Maestro workspace. """
        self.sequence_viewer.propagateColors() 
[docs]    def viewerConsiderGaps(self):
        self.sequence_viewer.setConsiderGaps(
            not self.sequence_viewer.getConsiderGaps()) 
[docs]    def viewerClearConstraints(self):
        self.sequence_viewer.clearConstraints() 
[docs]    def viewerSetConstraints(self):
        result = self.sequence_viewer.toggleConstraints()
        self.actions["&Alignment/Clear Constraints"].setEnabled(result)
        self.actions["&Alignment/Use Constraints"].setChecked(result) 
[docs]    def viewerAddSSA(self):
        if self.actions[
                "&Annotations/Secondary Structure Assignment"].isChecked():
            self.sequence_viewer.sequence_group.updateSSA()
        else:
            self.sequence_viewer.sequence_group.updateSSA(remove=True) 
[docs]    def viewerRunACCpro(self):
        """ Run solvent accessibility prediction. """
        self.sequence_viewer.runPredictors(['accpro']) 
[docs]    def viewerRunDOMpro(self):
        """ Run domain arrangement prediction. """
        self.sequence_viewer.runPredictors(['dompro']) 
[docs]    def viewerRunDISpro(self):
        """ Run disordered region prediction. """
        self.sequence_viewer.runPredictors(['dispro']) 
[docs]    def viewerRunDIpro(self):
        """  Run disulfide bridges prediction. """
        self.sequence_viewer.runPredictors(['dipro']) 
[docs]    def viewerRunBETApro(self):
        """ Run beta sheet prediction. """
        self.sequence_viewer.runPredictors(['betapro']) 
[docs]    def viewerRunAllPredictions(self):
        self.sequence_viewer.runPredictors(
            ["sspro", "accpro", "dispro", "dompro", "dipro"]) 
[docs]    def viewerExpandSelection(self):
        self.sequence_viewer.expandSelection() 
[docs]    def viewerHideColumns(self):
        self.sequence_viewer.hideColumns() 
[docs]    def viewerHideUnselectedColumns(self):
        self.sequence_viewer.hideColumns(unselected=True) 
[docs]    def viewerShowColumns(self):
        self.sequence_viewer.showColumns() 
[docs]    def viewerExpandSelectionRef(self):
        self.sequence_viewer.expandSelectionRef() 
[docs]    def viewerMarkColorCustom(self):
        color = QtWidgets.QColorDialog.getColor()
        if color:
            color = (color.red(), color.green(), color.blue())
            self.sequence_viewer.markResidues(color) 
[docs]    def viewerBackgroundColorCustom(self):
        color = QtWidgets.QColorDialog.getColor()
        if color:
            color = (color.red(), color.green(), color.blue())
            self.sequence_viewer.setBackgroundColor(color) 
[docs]    def viewerConstantColorCustom(self):
        color = QtWidgets.QColorDialog.getColor()
        if color:
            color = (color.red(), color.green(), color.blue())
            self.sequence_viewer.colorSequences(color) 
[docs]    def viewerMaestroSuperposition(self):
        """
        Superposes structures in Maestro workspace according to the
        current alignment.
        """
        if maestro:
            maestro_helpers.maestroSuperposition(self.sequence_viewer) 
[docs]    def viewerAlignmentSettings(self):
        """ Displays alignment setting dialog. """
        self.sequence_viewer.alignmentSettingsDialog() 
[docs]    def viewerDownloadPDB(self):
        """ Downloads a corresponding PDB structure. """
        self.sequence_viewer.downloadPDB(
            maestro_include=self.
            actions["&Maestro/Include Incorporated Entries in Workspace"].
            isChecked(),
            maestro_incorporate=self.file_import_opt_incorporate.isChecked()) 
[docs]    def viewerMarkResidues(self):
        color_actions = {
            "Red": (255, 32, 32),
            "Green": (32, 255, 32),
            "Blue": (32, 32, 255),
            "Cyan": (32, 255, 255),
            "Magenta": (255, 32, 255),
            "Yellow": (255, 255, 32),
            "Orange": (255, 127, 32),
            "Teal": (32, 127, 127),
            "Black": (0, 0, 0),
            "Dark Gray": (63, 63, 63),
            "Light Gray": (191, 191, 191),
            "White": (255, 255, 255)
        }
        action = QtCore.QObject.sender(self)
        if action:
            color = action.text()
            rgb = color_actions[str(color)]
            self.sequence_viewer.markResidues(rgb) 
[docs]    def viewerBackgroundColor(self):
        rgb_values = {
            "Black": (0, 0, 0),
            "Dark Gray": (63, 63, 63),
            "Light Gray": (191, 191, 191),
            "White": (255, 255, 255)
        }
        action = QtCore.QObject.sender(self)
        if action:
            color = action.text()
            rgb = rgb_values[str(color)]
            self.sequence_viewer.setBackgroundColor(rgb) 
[docs]    def viewerColorResidues(self):
        color_actions = {
            "Red": (255, 32, 32),
            "Green": (32, 255, 32),
            "Blue": (32, 32, 255),
            "Cyan": (32, 255, 255),
            "Magenta": (255, 32, 255),
            "Yellow": (255, 255, 32),
            "Orange": (255, 127, 32),
            "Teal": (32, 127, 127),
            "Black": (0, 0, 0),
            "Dark Gray": (63, 63, 63),
            "Light Gray": (191, 191, 191),
            "White": (255, 255, 255)
        }
        action = QtCore.QObject.sender(self)
        if action:
            color = action.text()
            rgb = color_actions[str(color)]
            self.sequence_viewer.colorSequences(rgb) 
[docs]    def viewerClearMarkedResidues(self):
        self.sequence_viewer.clearMarkedResidues() 
[docs]    def viewerShowJobLog(self):
        """ Shows job log window. """
        self.sequence_viewer.showJobLog() 
[docs]    def viewerShowJobSettings(self):
        """ Shows job settings window. """
        self.sequence_viewer.showJobSettings() 
[docs]    def viewerBuildModel(self):
        """ Builds a 3D model of the reference sequence. """
        self.sequence_viewer.buildModel() 
[docs]    def connectNoSlot(self):
        """ Hacker fix to not connecting
        "Include Incorporated Entries in Workspace" to anything""" 
[docs]    def viewerColorEntrySurface(self):
        self.sequence_viewer.colorEntrySurface() 
[docs]    def viewerAssociateMaestroEntries(self):
        self.sequence_viewer.associateMaestroEntries() 
[docs]    def viewerAlignStructures(self):
        """ Aligns 3D protein structures. """
        self.sequence_viewer.alignStructures() 
[docs]    def viewerGetStructureAlignment(self):
        """ Aligns 3D protein structures. """
        self.sequence_viewer.getStructureAlignment() 
[docs]    def viewerCropSelectedResidues(self):
        """ Crops the residues in the selected area. """
        self.sequence_viewer.cropSelectedResidues() 
[docs]    def viewerShowLigands(self):
        """ Displays Maestro ligand interaction fingerprints. """
        remove = not self.actions["&Annotations/Ligand Contacts"].isChecked()
        self.sequence_viewer.addAnnotation(constants.ANNOTATION_LIGAND,
                                           remove=remove)
        if not remove:
            self.sequence_viewer.displayLigands() 
[docs]    def viewerFindPattern(self):
        """ Switch focus to Find Pattern input box. """
        self.findToolBar.show()
        self.findLineEdit.setFocus() 
[docs]    def viewerNewSet(self):
        self.sequence_viewer.excludeQueryEntries()
        set_name = self.sequence_viewer.newSet()
        self.query_tabs.insertTab(self.query_tabs.count(), set_name)
        self.query_tabs.setCurrentIndex(self.query_tabs.count() - 1)
        return set_name 
[docs]    def viewerDuplicateSet(self):
        self.queryDuplicate() 
[docs]    def addNewQueryTab(self, open_file=True):
        """  Adds a new query tab. """
        self.viewerNewSet()
        if open_file:
            self.open() 
[docs]    def queryTabChanged(self, index):
        if self.ignore_tab_change:
            self.ignore_tab_change = False
            return
        self.sequence_viewer.excludeQueryEntries()
        self.sequence_viewer.changeQuery(index)
        self.sequence_viewer.includeQueryEntries()
        self.sequence_viewer.setBackgroundColor(
            (self.sequence_viewer.background_color.red(),
             self.sequence_viewer.background_color.green(),
             self.sequence_viewer.background_color.blue()))
        updatePrimeQueryList() 
[docs]    def queryDelete(self):
        if self.picked_query >= 0:
            index = self.picked_query
        else:
            index = self.query_tabs.currentIndex()
        self.picked_query = -1
        self.queryTabClose(index) 
[docs]    def queryTabClose(self, index):
        if self.query_tabs.count() == 1:
            return False
        answer = dialogs.question_dialog("Deleting Query Tab",
                                         "This operation cannot be undone.\n"
                                         "Do you wish to continue?",
                                         buttons=["yes", "no"])
        if answer != "yes":
            return False
        action = QtCore.QObject.sender(self)
        if action:
            self.ignore_tab_change = True
            self.sequence_viewer.excludeQueryEntries()
            self.query_tabs.removeTab(index)
            self.sequence_viewer.deleteSet(index)
            self.sequence_viewer.includeQueryEntries()
            self.ignore_tab_change = False
            return True
        return False 
[docs]    def queryDuplicate(self):
        """ Duplicates the picked query. """
        if self.picked_query >= 0:
            index = self.picked_query
        else:
            index = self.query_tabs.currentIndex()
        self.picked_query = -1
        # Get the source group (pointed by the user)
        group1 = self.sequence_viewer.sequence_group_list[index]
        # Create new empty group
        self.addNewQueryTab(open_file=False)
        # Copy the contents
        self.sequence_viewer.copySequences(group1) 
[docs]    def queryRename(self):
        """ Renames the picked query. """
        if self.picked_query >= 0:
            index = self.picked_query
        else:
            index = self.query_tabs.currentIndex()
        self.picked_query = -1
        name, ok = dialogs.string_input_dialog("Rename Query",
                                               "Enter new query name",
                                               self.query_tabs.tabText(index))
        if name and ok:
            self.query_tabs.setTabText(index, name)
            self.sequence_viewer.sequence_group.name = name 
[docs]    def viewerSetSequenceColor(self):
        color = QtWidgets.QColorDialog.getColor()
        if color:
            color = (color.red(), color.green(), color.blue())
            self.sequence_viewer.colorSequenceNames(color) 
[docs]    def viewerIncorporateSelected(self):
        self.sequence_viewer.incorporateSelectedEntries() 
[docs]    def viewerIncorporateIncluded(self):
        self.sequence_viewer.incorporateIncludedEntries() 
[docs]    def viewerRenameSequence(self):
        self.sequence_viewer.renameSequence() 
[docs]    def viewerAnalyzeBindingSite(self):
        self.sequence_viewer.analyzeBindingSite() 
[docs]    def viewerCompareSequences(self):
        """ Open 'Compare sequences' dialog. """
        self.sequence_viewer.showCompareSequencesDialog() 
[docs]    def viewerUserAddRectangle(self):
        self.sequence_viewer.addUserAnnotation(annotation_type="rectangle") 
[docs]    def viewerUserAddAlignmentRegion(self):
        self.sequence_viewer.addUserAnnotation(annotation_type="region") 
[docs]    def viewerUserAddCustomAnnotation(self):
        self.sequence_viewer.addUserAnnotation(
            annotation_type="custom_sequence") 
[docs]    def viewerUserRemoveAll(self):
        self.sequence_viewer.removeUserAnnotations() 
[docs]    def viewerResetSettings(self):
        """ Resets all user settings to default. """
        activate_if_checked = [
            "Sett&ings/&Display Percentage Similarity",
            "Sett&ings/&Wrap Sequences", "Sett&ings/Group Annotations by Type",
            "Sett&ings/&Display Ruler", "Sett&ings/&Display Score",
            "Sett&ings/&Display Percentage Identity",
            "Sett&ings/&Display Percentage Homology",
            "Sett&ings/&Display Sequence Boundaries",
            "Sett&ings/&Display Alignment Tooltips",
            "Sett&ings/&Pad Alignment with Gaps",
            "Sett&ings/&Display Header Row", "Sett&ings/&Display Header Row",
            "Sett&ings/&Replace Identities With Dots",
            "Sett&ings/Include Gaps in Sequence Identity Calculations",
            "Sett&ings/Calculate Sequence Identity Only in Selected Columns",
            "Color/&Average Colors In Columns",
            "Color/&Weight Colors by Alignment Quality",
            "Color/Color Matching Residues Only",
            "Color/Color Different Residues Only", "Color/Color Sequences",
            "Color/Adjust Text Contrast", "&Alignment/Use Constraints"
        ]
        for action_path in activate_if_checked:
            if self.actions[action_path].isChecked():
                self.actions[action_path].activate(QtWidgets.QAction.Trigger)
        if not self.lockDownstreamAct.isChecked():
            self.lockDownstreamAct.activate(QtWidgets.QAction.Trigger)
        if not self.mouseAcrossAct.isChecked():
            self.mouseAcrossAct.activate(QtWidgets.QAction.Trigger)
        self.actions["Sett&ings/&Font Size/10"].activate(
            QtWidgets.QAction.Trigger)  
# The application main loop.
# Used when the viewer is called as an standalone app and for debugging
# purposes. When invoked from Maestro, the main loop is included in
# multiseqviewer.panel function.
if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    app.setAttribute(QtCore.Qt.AA_DontShowIconsInMenus)
    mainWin = AlignmentWindow()
    mainWin.show()
    mainWin.raise_()
    sys.exit(app.exec_())