"""
This class implements a sequence area widget that performs actual display
and manipulation of the sequences and annotation data.
Copyright Schrodinger, LLC. All rights reserved.
"""
# Contributors: Piotr Rotkiewicz
import math
from past.utils import old_div
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtGui
from schrodinger.Qt import QtWidgets
from . import constants
from . import maestro as maestro_helpers
[docs]class SequenceArea(QtWidgets.QWidget):
    """
    The sequence area displays sequence rows and associated annotations.
    It is a scrollable widget. The presence of horizontal and vertical
    scrollbars depends on current sequence viewer mode (wrapped or not wrapped).
    """
[docs]    def __init__(self, parent=None):
        # Initialize base class.
        QtWidgets.QWidget.__init__(self, parent)
        self.viewer = None  #Parent sequence viewer widget.
        self.init_row = None  # Initial clicked row.
        self.init_pos = None  # Initial clicked row position.
        self.last_pos = None  # Last pointed row position.
        self.init_res = None
        self.last_found_position = None  # Where a residue was found.
        self.selecting_columns = False
        self.cursor_enabled = False  # text edit cursor
        self.cursor_row = None  # of text cursor
        self.cursor_pos = None  # of text cursor
        self.is_dragging_rect = False  # is a rectangle dragged?
        self.drag_coords = (None, None, None, None, None)  #of dragged rect.
        self.last_constraint_pos = None  # Previously clicked constraint pos.
        self.last_constraint_seq = None
        self.last_tooltip_pos = None  # of mouse
        self.last_tooltip_row = None  # of mouse
        self.last_key = None  # Last key pressed.
        self.unselect = False  # Ctrl-selection. (depends on init clicked res)
        #: Used for Shift-click selection
        self.min_row = None
        self.max_row = None
        self.min_pos = None
        self.max_pos = None
        self.position_moved = False
        self.horizontal_scroll_bar = QtWidgets.QScrollBar(self)
        self.vertical_scroll_bar = QtWidgets.QScrollBar(self)
        self.horizontal_scroll_bar.setOrientation(QtCore.Qt.Horizontal)
        self.horizontal_scroll_bar.valueChanged.connect(
            self.horizontalScrollBarChanged)
        self.vertical_scroll_bar.setOrientation(QtCore.Qt.Vertical)
        self.vertical_scroll_bar.valueChanged.connect(
            self.verticalScrollBarChanged)
        # Set mouse tracking True to receive mouse events even when the mouse
        # button is released (important for mouse hover tooltips).
        self.setMouseTracking(True)
        self.setFocusPolicy(QtCore.Qt.WheelFocus)  # of widget
        self.ignore_click = False
        self.emphasized = False
        self.selection_changed = False
        self.alignment_changed = False
        self.auto_scroll = False 
[docs]    def focusInEvent(self, event):
        if event.reason() == QtCore.Qt.ActiveWindowFocusReason:
            self.ignore_click = True 
[docs]    def mouseToCoords(self, x, y):
        """
        Translates mouse coordinates in global space to sequence area
        discrete coordinates (row, position).
        :type x: int
        :param x: x coordinate
        :type y: int
        :param y: y coordinate
        :rtype: (int, int)
        :return: (row, position) coordinates
        """
        xoffset = 0
        if self.viewer.display_boundaries:
            xoffset += 6 * self.viewer.font_width
        yoffset = 0
        if self.viewer.has_header_row:
            yoffset = 20
        return (old_div((x - self.viewer.margin - xoffset),
                        self.viewer.cell_width),
                old_div((y - self.viewer.margin - yoffset),
                        self.viewer.font_height)) 
[docs]    def rowPosToCoords(self, row, pos, visible_only=True):
        """
        Translates (row, position) coordinates to widget coordinates.
        This method is used for drawing auxiliary objects such as text edit
        cursor.
        :type row: (`Sequence`, int, int, int)
        :param row: row
        :type pos: int
        :param pos: row position
        :rtype: (int, int)
        :return: pair of widget coordinates, or (None, None) if the conversion
            fails.
        """
        rows = self.viewer.rows
        offset = 0
        if self.viewer.display_boundaries:
            offset += 6 * self.viewer.font_width
        y_position = self.viewer.margin
        if self.viewer.has_header_row:
            y_position += 20
        height = self.height()
        reference = self.viewer.sequence_group.reference
        if not reference:
            reference = self.viewer.sequence_group.profile
        row_idx = 0
        num_persistent_rows = 0
        if not self.viewer.wrapped:
            for tmp_row in rows:
                num_persistent_rows += 1
                seq, start, end, row_height = tmp_row
                if not (seq.type == constants.SEQ_RULER or
                        seq.global_sequence or seq == reference):
                    break
        top_row = self.viewer.top_row + num_persistent_rows
        row_idx = 0
        for tmp_row in rows:
            row_idx = row_idx + 1
            seq, start, end, row_height = tmp_row
            if self.viewer.wrapped or not (seq.type == constants.SEQ_RULER or
                                           seq.global_sequence or
                                           seq == reference):
                if row_idx < top_row:
                    continue
            if visible_only and y_position > height:
                break
            seq, start, end, row_height = tmp_row
            if tmp_row == row:
                return ((pos - start) * self.viewer.cell_width +
                        self.viewer.margin + offset, y_position)
            # Display the sequence if it is visible.
            if seq and seq.visible:
                y_position += row_height * self.viewer.font_height
        return (None, None) 
[docs]    def mouseToRowPosition(self, x, y):
        """
        Converts mouse coordinates to (row, position, residue) tuple.
        :type x: int
        :param x: x coordinate
        :type y: int
        :param y: y coordinate
        :rtype: (row, int, `Residue`)
        :return: row, position pair or (None, None, None) if the conversion
            fails.
        """
        rows = self.viewer.rows
        if rows is None or len(rows) == 0:
            return (None, None, None)
        row_idx = 0
        current_row = 0
        coord_x, coord_y = self.mouseToCoords(x, y)
        found = False
        reference = self.viewer.sequence_group.reference
        if not reference:
            reference = self.viewer.sequence_group.profile
        row_idx = 0
        num_persistent_rows = 0
        if not self.viewer.wrapped:
            for row in rows:
                num_persistent_rows += 1
                seq, start, end, row_height = row
                if not (seq.type == constants.SEQ_RULER or
                        seq.global_sequence or seq == reference):
                    break
        top_row = self.viewer.top_row + num_persistent_rows
        row_idx = 0
        for row in rows:
            row_idx = row_idx + 1
            seq, start, end, row_height = row
            if self.viewer.wrapped or not (seq.type == constants.SEQ_RULER or
                                           seq.global_sequence or
                                           seq == reference):
                if row_idx < top_row:
                    continue
            for h in range(row_height):
                if coord_y == current_row:
                    found = True
                    break
                current_row = current_row + 1
            if found:
                break
        residue = None
        position = None
        if found:
            position = coord_x + start
            self.last_found_position = position
            if position >= 0 and position < seq.length() and position < end:
                residue = seq.residues[position]
        else:
            return (None, None, None)
        return (row, self.last_found_position, residue) 
[docs]    def mouseDoubleClickEvent(self, event):
        """
        Handles Qt double click event.
        """
        # Compute sequence area coordinates.
        row, pos, res = self.mouseToRowPosition(event.x(), event.y())
        if row and pos and res:
            sequence, start, end, length = row
            if sequence and sequence.parent_sequence and \
                    
sequence.type == constants.SEQ_SECONDARY:
                code = res.code
                parent_residues = sequence.parent_sequence.residues
                if not (event.modifiers() & QtCore.Qt.ControlModifier):
                    sequence.parent_sequence.unselectResidues()
                pos = init_pos = sequence.residues.index(res)
                while pos >= 0 and (sequence.residues[pos].code == code or
                                    sequence.residues[pos].is_gap):
                    parent_residues[pos].selected = True
                    pos -= 1
                pos = init_pos
                while pos < len(parent_residues) and \
                    
(sequence.residues[pos].code == code or \
                    
sequence.residues[pos].is_gap):
                    parent_residues[pos].selected = True
                    pos += 1
                self.viewer.selection_changed = True
                self.viewer.updateView() 
[docs]    def selectExtent(self, row, pos, reset=False):
        """
        Select minimum residue extent between previous selection
        and current position.
        """
        if not row:
            return
        row_idx = self.viewer.rows.index(row)
        if reset or self.min_row is None:
            self.min_row = self.max_row = row_idx
            self.min_pos = self.max_pos = pos
            return
        if row_idx < self.min_row:
            self.min_row = row_idx
        if row_idx > self.max_row:
            self.max_row = row_idx
        if pos < self.min_pos:
            self.min_pos = pos
        if pos > self.max_pos:
            self.max_pos = pos
        self.viewer.sequence_group.selectResidues(
            self.viewer.rows[self.min_row], self.min_pos,
            self.viewer.rows[self.max_row], self.max_pos, True)
        self.selection_changed = True 
[docs]    def mousePressEvent(self, event):
        """
        Handles Qt mouse press event.
        :type event: QMouseEvent
        :param event: Qt mouse event.
        """
        if self.ignore_click:
            return
        self.last_found_position = None
        self.position_moved = False
        self.selection_changed = False
        self.alignment_changed = False
        # Compute sequence area coordinates.
        row, pos, res = self.mouseToRowPosition(event.x(), event.y())
        self.last_tooltip_pos = pos
        self.last_tooltip_row = row
        # Middle button centers on a clicked residue in Maestro workspace.
        # Otherwise, it doesn't have any effect.
        if event.button() == QtCore.Qt.MidButton:
            if res and res.sequence and res.sequence.from_maestro and maestro_helpers.hasMaestro(
            ):
                maestro_helpers.maestroCenterOnResidue(self.viewer, self, res)
            return
        # Right button popups a context menu and returns, unless in
        # Insert/Remove Gaps mode.
        if event.button() == QtCore.Qt.RightButton and \
           
self.viewer.mode != constants.MODE_INSERT_GAPS:
            if self.viewer.sequence_popup_menu:
                self.viewer.sequence_popup_menu(event.globalPos(), res)
            return
        # When clicked on a reference ligand,
        # add a ligand-query constraint and return
        if row and self.viewer.build_mode:
            seq, start, end, height = row
            if seq.parent_sequence == self.viewer.sequence_group.reference and \
                    
seq.annotation_type == constants.ANNOTATION_LIGAND:
                if res and not res.is_gap:
                    # Toggle color of the ligand residue
                    # to indicate a constrained position
                    if hasattr(res, 'query_constraint') and \
                       
res.query_constraint:
                        res.color = (255, 255, 255)
                        res.query_constraint = False
                    else:
                        res.color = (255, 0, 0)
                        res.query_constraint = True
                return
        # In case Shift is pressed, select residues between this and
        # previously clicked residue.
        if event.modifiers() & QtCore.Qt.ShiftModifier and \
                
self.viewer.mode != constants.MODE_GRAB_AND_DRAG:
            if row:
                seq, start, end, height = row
                if seq.type == constants.SEQ_RULER:
                    self.viewer.sequence_group.selectColumns(self.prev_pos, pos)
                    self.updateColumns(self.prev_pos, pos)
                elif self.viewer.mode == constants.MODE_SELECT_AND_SLIDE or \
                    
self.viewer.mode == constants.MODE_SELECT_ONLY or \
                    
(self.viewer.build_mode and
                     self.viewer.sequence_group.build_mode == \
                     
constants.PRIME_MODE_COMPOSITE):
                    self.selectExtent(row, pos)
                self.selection_changed = True
                self.update()
                return
        else:
            self.selectExtent(row, pos, reset=True)
        # Store initial row and position.
        # @note: If the rows are going to be regenerated between press/release
        # events, these variables will be not valid.
        if (not ((event.modifiers() & QtCore.Qt.ShiftModifier) and
                 self.init_row and self.init_pos)) or \
                
self.viewer.mode == constants.MODE_GRAB_AND_DRAG:
            self.init_row = row
            self.init_pos = pos
            self.init_res = res
        self.last_pos = pos
        self.prev_pos = pos
        if res and res.selected:
            self.unselect = True
        else:
            self.unselect = False
        # Will be set to True if selecting columns.
        self.selecting_columns = False
        # Set cursor shape according to the current mode.
        if self.viewer.mode == constants.MODE_GRAB_AND_DRAG:
            self.setCursor(QtCore.Qt.ArrowCursor)
            # Store current state for undo.
            if row:
                seq, start, end, height = row
                # In case we are editing a child sequence, use parent
                # sequence for undo.
                if seq:
                    if seq.parent_sequence:
                        seq = seq.parent_sequence
                    if seq in self.viewer.sequence_group.sequences:
                        index = self.viewer.sequence_group.sequences.index(seq)
                        self.viewer.undo_stack.storeStateSequence(
                            self.viewer.sequence_group,
                            index,
                            label="Edit Sequence")
        elif self.viewer.mode == constants.MODE_SELECT_AND_SLIDE or \
                
self.viewer.mode == constants.MODE_SELECT_ONLY:
            self.setCursor(QtCore.Qt.CrossCursor)
            self.selection_changed = True
        # If clicked on the ruler, start selecting columns.
        if row and self.viewer.mode != constants.MODE_VIEW_ONLY:
            seq, start, end, height = row
            if seq.type == constants.SEQ_RULER:
                self.selecting_columns = True
                self.setCursor(QtCore.Qt.ArrowCursor)
                is_selected = self.viewer.sequence_group.isColumnSelected(pos)
                if not (event.modifiers() & QtCore.Qt.ControlModifier or
                        event.modifiers() & QtCore.Qt.ShiftModifier):
                    self.viewer.sequence_group.unselectAll()
                if is_selected:
                    self.viewer.sequence_group.selectColumns(pos,
                                                             pos,
                                                             select=False)
                    self.updateColumns(pos, pos)
                else:
                    self.viewer.sequence_group.selectColumns(pos, pos)
                    self.updateColumns(pos, pos)
                self.selection_changed = True
                self.update()
                return
        # Initially, disable the mouse cursor.
        self.cursor_enabled = False
        if self.viewer.mode == constants.MODE_VIEW_ONLY:
            pass
        elif self.viewer.mode == constants.MODE_INSERT_GAPS:
            self.setCursor(QtCore.Qt.PointingHandCursor)
            if res and res.sequence and \
               
(res.sequence.type == constants.SEQ_AMINO_ACIDS or
                    res.sequence.type == constants.SEQ_SECONDARY):
                if seq.parent_sequence:
                    seq = seq.parent_sequence
                index = self.viewer.sequence_group.sequences.index(seq)
                if not seq.selected:
                    # Insert / remove gaps in only one sequence.
                    if event.buttons() == QtCore.Qt.LeftButton:
                        self.viewer.undo_stack.storeStateSequence(
                            self.viewer.sequence_group,
                            index,
                            label="Insert Gap")
                        self.viewer.sequence_group.insertGap(
                            self.init_row, self.init_pos)
                        self.viewer.sequence_group.updateHistory(
                            self.init_pos, self.init_pos)
                        self.viewer.updateView()
                    elif event.buttons() == QtCore.Qt.RightButton:
                        if res.is_gap:
                            self.viewer.undo_stack.storeStateSequence(
                                self.viewer.sequence_group,
                                index,
                                label="Remove Gap")
                            self.viewer.sequence_group.removeGap(
                                self.init_row, self.init_pos)
                            self.viewer.sequence_group.updateHistory(
                                self.init_pos, self.init_pos)
                            self.viewer.updateView()
                else:
                    # Insert / remove gaps in all selected sequences.
                    if event.buttons() == QtCore.Qt.LeftButton:
                        self.viewer.undo_stack.storeStateGroupDeep(
                            self.viewer.sequence_group, label="Insert Gap")
                        for row in self.viewer.rows:
                            seq, start, end, height = row
                            if seq.selected and not seq.parent_sequence:
                                self.viewer.sequence_group.insertGap(
                                    row, self.init_pos)
                            if seq.type == constants.SEQ_SEPARATOR:
                                break
                        self.viewer.updateView()
                    elif event.buttons() == QtCore.Qt.RightButton:
                        self.viewer.undo_stack.storeStateGroupDeep(
                            self.viewer.sequence_group, label="Remove Gap")
                        for row in self.viewer.rows:
                            seq, start, end, height = row
                            if seq.selected and not seq.parent_sequence:
                                self.viewer.sequence_group.removeGap(
                                    row, self.init_pos)
                            if seq.type == constants.SEQ_SEPARATOR:
                                break
                        self.viewer.updateView()
        elif ((self.viewer.mode == constants.MODE_SELECT_AND_SLIDE or
               self.viewer.mode == constants.MODE_EDIT) and res and
              res.selected and
              not (event.modifiers() & QtCore.Qt.ControlModifier)):
            self.startDraggingRect(row, pos)
        elif (not res) and \
                
not (event.modifiers() & QtCore.Qt.ControlModifier or
                     event.modifiers() & QtCore.Qt.ShiftModifier) and \
                
not (self.viewer.set_constraints or self.viewer.set_query_constraints):
            self.viewer.sequence_group.unselectAll(make_active=bool(not res))
            self.selection_changed = True
            self.update()
            return
        if not res:
            return
        if self.viewer.build_mode and \
                
self.viewer.sequence_group.build_mode == constants.PRIME_MODE_COMPOSITE:
            res.selected = True
            self.update()
            return
        if (self.viewer.mode == constants.MODE_SELECT_AND_SLIDE or
                self.viewer.mode == constants.MODE_SELECT_ONLY):
            if not (event.modifiers() & QtCore.Qt.ControlModifier or
                    event.modifiers() & QtCore.Qt.ShiftModifier):
                new_state = True
                if self.viewer.mode != constants.MODE_SELECT_AND_SLIDE:
                    new_state = not res.selected
                    self.viewer.sequence_group.unselectAll()
                res.selected = new_state
                if res.sequence.parent_sequence:
                    res.sequence.parent_sequence.residues[
                        res.sequence.residues.index(res)].selected = new_state
                self.update()
                return
        if (event.modifiers() & QtCore.Qt.ControlModifier):
            res.selected = not res.selected
            self.update() 
[docs]    def startDraggingRect(self, row, pos):
        """
        Begins rectangle dragging. Determines boundaries of the area that
        needs to be dragged and calculates coordinates of the auxiliary
        rectangle. The initial coordinates (row and position) should be inside
        the area that needs to be dragged. This method assumes the area is
        rectangular. Otherwise, results are non-predictable - usually a
        rectangular superset of irregular area will be dragged.
        :type row: (`Sequence`, int, int ,int)
        :param row: sequence viewer row in the dragged area
        :type pos: int
        :param pos: row position
        """
        seq, start, end, height = row
        if seq and seq.residues:
            # Find horizontal extends
            min_pos = pos
            while min_pos >= 0 and \
                    
seq.residues[min_pos].selected:
                min_pos -= 1
            max_pos = pos
            while max_pos < len(seq.residues) and \
              
seq.residues[max_pos].selected:
                max_pos += 1
            min_pos += 1
            max_pos -= 1
            min_row = max_row = row
            row_index = last_row_index = self.viewer.rows.index(row)
            # Find top row
            while row_index > 0:
                row_index -= 1
                seq, start, end, height = self.viewer.rows[row_index]
                if seq.parent_sequence:
                    seq = seq.parent_sequence
                if len(seq.residues) <= pos or not seq.residues[pos].selected:
                    break
                last_row_index = row_index
            min_row = self.viewer.rows[last_row_index]
            # Find bottom row
            row_index = last_row_index = self.viewer.rows.index(row)
            while row_index < len(self.viewer.rows) - 1:
                row_index += 1
                seq, start, end, height = self.viewer.rows[row_index]
                if seq.parent_sequence:
                    seq = seq.parent_sequence
                if len(seq.residues) <= pos or not seq.residues[pos].selected:
                    break
                last_row_index = row_index
            max_row = self.viewer.rows[last_row_index]
            self.is_dragging_rect = True
            self.drag_coords = (min_row, max_row, min_pos, max_pos, min_pos)
            x0, y0 = self.rowPosToCoords(min_row, min_pos)
            x1, y1 = self.rowPosToCoords(max_row, max_pos)
            self.setCursor(QtCore.Qt.ClosedHandCursor)
            self.update() 
[docs]    def mouseReleaseEvent(self, event):
        """
        Handles Qt mouse release event.
        :type event: QMouseEvent
        :param event: Qt mouse event.
        """
        self.auto_scroll = False
        if self.ignore_click:
            self.ignore_click = False
            return
        seq = None
        if self.is_dragging_rect:
            self.is_dragging_rect = False
            min_row, max_row, min_pos, max_pos, init_pos = self.drag_coords
            if min_pos != init_pos:
                self.viewer.undo_stack.storeStateGroupDeep(
                    self.viewer.sequence_group, label="Slide Selected Block")
                self.viewer.sequence_group.grabAndDragBlock(
                    self.viewer.rows, min_row, init_pos, max_row, min_pos,
                    abs(max_pos - min_pos), int(self.viewer.lock_downstream))
                self.drag_coords = (None, None, None, None, None)
                self.viewer.sequence_group.updateHistory(init_pos, min_pos)
                self.alignment_changed = True
                self.viewer.updateView()
            else:
                self.drag_coords = (None, None, None, None, None)
                self.update()
        # Translate mouse cursor coordinates to row and position.
        row, pos, res = self.mouseToRowPosition(event.x(), event.y())
        if self.viewer.set_constraints or self.viewer.set_query_constraints:
            if row and not self.position_moved:
                seq, start, end, height = row
                if self.last_constraint_seq and not (
                        self.viewer.set_query_constraints and \
                        
pos == self.last_constraint_pos):
                    self.viewer.sequence_group.selectResidues(
                        row, pos, row, pos, True)
                    self.viewer.sequence_group.expandSelectionRef()
                    self.viewer.sequence_group.addConstraint(
                        self.last_constraint_seq,
                        self.last_constraint_pos,
                        seq,
                        pos,
                        for_prime=self.viewer.set_query_constraints)
                    self.viewer.sequence_group.unselectAll()
                    self.viewer.updateView()
                    maestro_helpers.synchronizePropertiesWithMaestro(
                        self.viewer, selection=True)
                    self.last_constraint_pos = None
                    self.last_constraint_seq = None
                    self.viewer.displayMessage("Pick first query residue "
                                               "to add or remove a constraint.")
                else:
                    # self.viewer.sequence_group.unselectAll()
                    self.viewer.sequence_group.selectResidues(
                        row, pos, row, pos, True)
                    self.viewer.sequence_group.expandSelectionRef()
                    self.last_constraint_seq = seq
                    self.last_constraint_pos = pos
                    self.viewer.displayMessage("Pick second query residue "
                                               "to add or remove a constraint.")
                    self.viewer.update()
        if res and res.selected and \
           
(self.viewer.mode == constants.MODE_SELECT_AND_SLIDE or
                self.viewer.mode == constants.MODE_SELECT_ONLY):
            self.setCursor(QtCore.Qt.OpenHandCursor)
            self.last_pos = pos
        else:
            if res and self.viewer.mode == constants.MODE_EDIT and \
               
(res.sequence.type == constants.SEQ_AMINO_ACIDS or
                (res.sequence.type == constants.SEQ_ANNOTATION and
                 (res.sequence.annotation_type == constants.ANNOTATION_SSP or
                  res.sequence.annotation_type == constants.ANNOTATION_CUSTOM))):
                if self.is_dragging_rect:
                    self.setCursor(QtCore.Qt.ClosedHandCursor)
                else:
                    self.setCursor(QtCore.Qt.IBeamCursor)
            elif res and self.viewer.mode == constants.MODE_INSERT_GAPS:
                self.setCursor(QtCore.Qt.PointingHandCursor)
            else:
                self.setCursor(QtCore.Qt.ArrowCursor)
        # Track changes.
        if self.viewer.mode == constants.MODE_GRAB_AND_DRAG:
            self.viewer.sequence_group.updateHistory(self.init_pos, pos)
        # When mouse button is pushed and released in the same position,
        # move the edit cursor there.
        if self.viewer.mode == constants.MODE_EDIT:
            if res and row == self.init_row and pos == self.init_pos and \
               
(res.sequence.type == constants.SEQ_AMINO_ACIDS or
                res.sequence.type == constants.SEQ_CUSTOM or
                (res.sequence.type == constants.SEQ_ANNOTATION and
                 res.sequence.annotation_type == constants.ANNOTATION_SSP)):
                self.cursor_row = row
                if not self.viewer.wrapped:
                    self.cursor_pos = pos - self.viewer.left_column
                else:
                    self.cursor_pos = pos
                cursor_x, cursor_y = \
                    
self.rowPosToCoords(row, pos)
                if cursor_x and cursor_y:
                    self.cursor_enabled = True
            else:
                self.cursor_row = None
                self.cursor_pos = None
            self.update()
        if self.viewer.build_mode and \
           
self.viewer.sequence_group.build_mode == constants.PRIME_MODE_COMPOSITE:
            self.viewer.sequence_group.markTemplateRegion()
            self.update()
        self.viewer.selection_changed = True
        self.viewer.updateView(generate_rows=False)
        # Update profile in case identity is calculated in selected columns.
        if self.viewer.sequence_group.identity_in_columns:
            self.viewer.updateView()
        # Show mouse hover tooltip.
        self.showToolTip(event.globalPos(), pos, res, seq)
        if self.selection_changed:
            self.viewer.selection_changed = True
            self.viewer.updateView(generate_rows=False, update_colors=False)
        if self.alignment_changed:
            self.viewer.alignment_changed = True
            self.alignment_changed = False
            self.viewer.updateView() 
[docs]    def keyReleaseEvent(self, event):
        """
        Handles Qt key release events.
        :type event: QKeyEvent
        :param event: Qt key release event
        """
        if event.type() == QtCore.QEvent.KeyRelease:
            key = event.key()
            if key == QtCore.Qt.Key_Shift:
                self.init_row = None
                self.init_pos = None
                self.init_res = None 
[docs]    def keyPressEvent(self, event):
        """
        Handles Qt key press events.
        :type event: QKeyEvent
        :param event: Qt key press event
        """
        if self.viewer.mode != constants.MODE_EDIT:
            return
        if self.cursor_row is not None and self.cursor_pos is not None:
            seq, start, end, row_height = self.cursor_row
            if self.cursor_row not in self.viewer.rows:
                self.cursor_row = None
                self.cursor_pos = None
                self.update()
                return
            cursor_row_index = self.viewer.rows.index(self.cursor_row)
            if event.type() == QtCore.QEvent.KeyPress:
                key = event.key()
                if seq.parent_sequence:
                    seq = seq.parent_sequence
                if seq not in self.viewer.sequence_group.sequences:
                    return
                seq_index = self.viewer.sequence_group.sequences.index(seq)
                if key == QtCore.Qt.Key_Space:
                    if seq.type == constants.SEQ_CUSTOM:
                        seq.residues[self.cursor_pos +
                                     self.viewer.left_column].code = ' '
                        self.cursor_pos += 1
                        self.viewer.update()
                        return
                    # Shift key is used to invert current
                    # grab-and-drag behavior
                    invert = bool(event.modifiers() & QtCore.Qt.ShiftModifier)
                    if self.last_key != key:
                        self.viewer.undo_stack.storeStateSequence(
                            self.viewer.sequence_group,
                            seq_index,
                            label="Edit Sequence")
                    if not self.viewer.wrapped:
                        self.viewer.sequence_group.grabAndDrag(
                            self.cursor_row,
                            self.cursor_pos + start,
                            self.cursor_row,
                            self.cursor_pos + start + 1,
                            int(self.viewer.lock_downstream) ^ int(invert),
                            gap_insert_mode=True)
                        self.alignment_changed = True
                        self.cursor_pos += 1
                        if self.cursor_pos >= self.viewer.max_columns - 1:
                            self.cursor_pos = self.viewer.max_columns - 1
                            if event.modifiers() & QtCore.Qt.ControlModifier:
                                self.viewer.left_column += 10
                            else:
                                self.viewer.left_column += 1
                    else:
                        self.viewer.sequence_group.grabAndDrag(
                            self.cursor_row,
                            self.cursor_pos,
                            self.cursor_row,
                            self.cursor_pos + 1,
                            int(self.viewer.lock_downstream) ^ int(invert),
                            gap_insert_mode=True)
                        self.alignment_changed = True
                        self.cursor_pos += 1
                        if self.cursor_pos >= end:
                            self.cursor_pos = end - 1
                    self.viewer.updateView(repaint=False)
                    self.cursor_row = self.viewer.rows[cursor_row_index]
                    self.viewer.update()
                elif key == QtCore.Qt.Key_Backspace:
                    # Do not delete first position
                    if len(seq.residues) <= 1 or \
                            
self.cursor_pos == start:
                        return
                    # Shift key is used to invert current grab-and-drag
                    # behavior.
                    invert = bool(event.modifiers() & QtCore.Qt.ShiftModifier)
                    if self.last_key != key:
                        self.viewer.undo_stack.storeStateSequence(
                            self.viewer.sequence_group,
                            seq_index,
                            label="Edit Sequence")
                    if not self.viewer.wrapped:
                        if seq.residues[self.cursor_pos + start - 1].is_gap:
                            self.viewer.sequence_group.grabAndDrag(
                                self.cursor_row, self.cursor_pos + start,
                                self.cursor_row, self.cursor_pos + start - 1,
                                True)
                            self.alignment_changed = True
                            self.cursor_pos -= 1
                            if self.cursor_pos < 0:
                                self.cursor_pos = 0
                            self.viewer.left_column -= 1
                            if self.viewer.left_column < 0:
                                self.viewer.left_column = 0
                        elif (not seq.from_maestro) or self.viewer.mutate:
                            seq.residues.remove(seq.residues[self.cursor_pos +
                                                             start - 1])
                            for child in seq.children:
                                child.residues.remove(
                                    child.residues[self.cursor_pos + start - 1])
                            self.cursor_pos -= 1
                            if self.cursor_pos < 0:
                                self.cursor_pos = 0
                            self.viewer.left_column -= 1
                            if self.viewer.left_column < 0:
                                self.viewer.left_column = 0
                    else:
                        if seq.residues[self.cursor_pos - 1].is_gap:
                            self.viewer.sequence_group.grabAndDrag(
                                self.cursor_row, self.cursor_pos,
                                self.cursor_row, self.cursor_pos - 1, True)
                            self.alignment_changed = True
                            self.cursor_pos -= 1
                            seq, start, end, height = self.cursor_row
                            if self.cursor_pos < start:
                                self.cursor_pos = start
                        elif (not seq.from_maestro) or self.viewer.mutate:
                            seq.residues.remove(seq.residues[self.cursor_pos -
                                                             1])
                            for child in seq.children:
                                child.residues.remove(
                                    child.residues[self.cursor_pos - 1])
                            self.cursor_pos -= 1
                            seq, start, end, height = self.cursor_row
                            if self.cursor_pos < start:
                                self.cursor_pos = start
                    self.viewer.updateView(repaint=False)
                    self.cursor_row = self.viewer.rows[cursor_row_index]
                    self.viewer.update()
                elif key == QtCore.Qt.Key_Delete:
                    # Do not delete last residue
                    if len(seq.residues) <= 1 or \
                            
self.cursor_pos >= end - 1:
                        return
                    invert = bool(event.modifiers() & QtCore.Qt.ShiftModifier)
                    if self.last_key != key:
                        self.viewer.undo_stack.storeStateSequence(
                            self.viewer.sequence_group,
                            seq_index,
                            label="Edit Sequence")
                    if not self.viewer.wrapped:
                        if seq.residues[self.cursor_pos + start].is_gap:
                            self.viewer.sequence_group.grabAndDrag(
                                self.cursor_row, self.cursor_pos + start + 1,
                                self.cursor_row, self.cursor_pos + start, True)
                            self.alignment_changed = True
                        elif (not seq.from_maestro) or self.viewer.mutate:
                            seq.residues.remove(seq.residues[self.cursor_pos +
                                                             start])
                            for child in seq.children:
                                child.residues.remove(
                                    child.residues[self.cursor_pos + start])
                    else:
                        if seq.residues[self.cursor_pos].is_gap:
                            self.viewer.sequence_group.grabAndDrag(
                                self.cursor_row, self.cursor_pos + 1,
                                self.cursor_row, self.cursor_pos, True)
                            self.alignment_changed = True
                        elif (not seq.from_maestro) or self.viewer.mutate:
                            seq.residues.remove(seq.residues[self.cursor_pos])
                            for child in seq.children:
                                child.residues.remove(
                                    child.residues[self.cursor_pos])
                    self.viewer.updateView(repaint=False)
                    self.cursor_row = self.viewer.rows[cursor_row_index]
                    self.viewer.update()
                elif key == QtCore.Qt.Key_Left:
                    if event.modifiers() & QtCore.Qt.ControlModifier:
                        self.cursor_pos -= 10
                    else:
                        self.cursor_pos -= 1
                    if not self.viewer.wrapped:
                        if self.cursor_pos < 0:
                            self.cursor_pos = 0
                            if event.modifiers() & QtCore.Qt.ControlModifier:
                                self.viewer.left_column -= 10
                            else:
                                self.viewer.left_column -= 1
                            if self.viewer.left_column < 0:
                                self.viewer.left_column = 0
                            index = self.viewer.rows.index(self.cursor_row)
                            self.viewer.generateRows()
                            self.cursor_row = self.viewer.rows[index]
                    else:
                        seq, start, end, height = self.cursor_row
                        if self.cursor_pos < start:
                            self.cursor_pos = start
                    if event.modifiers() & QtCore.Qt.ShiftModifier:
                        if self.shift_press_pos != self.cursor_pos:
                            self.viewer.sequence_group.unselectAll()
                            self.viewer.sequence_group.selectResidues(
                                self.shift_press_row, self.shift_press_pos,
                                self.cursor_row, self.cursor_pos, True)
                            self.selection_changed = True
                            self.update()
                    self.update()
                elif key == QtCore.Qt.Key_Right:
                    if event.modifiers() & QtCore.Qt.ControlModifier:
                        self.cursor_pos += 10
                    else:
                        self.cursor_pos += 1
                    if not self.viewer.wrapped:
                        if self.cursor_pos >= self.viewer.max_columns - 1:
                            self.cursor_pos = self.viewer.max_columns - 1
                            if event.modifiers() & QtCore.Qt.ControlModifier:
                                self.viewer.left_column += 10
                            else:
                                self.viewer.left_column += 1
                            index = self.viewer.rows.index(self.cursor_row)
                            self.viewer.generateRows()
                            self.cursor_row = self.viewer.rows[index]
                    else:
                        seq, start, end, height = self.cursor_row
                        if self.cursor_pos >= end:
                            self.cursor_pos = end - 1
                    if event.modifiers() & QtCore.Qt.ShiftModifier:
                        if self.shift_press_pos != self.cursor_pos:
                            self.viewer.sequence_group.unselectAll()
                            self.viewer.sequence_group.selectResidues(
                                self.shift_press_row, self.shift_press_pos,
                                self.cursor_row, self.cursor_pos, True)
                            self.selection_changed = True
                            self.update()
                    self.update()
                elif key == QtCore.Qt.Key_Down:
                    row = self.cursor_row
                    seq, prev_start, prev_end, height = row
                    index = self.viewer.rows.index(row)
                    while index < len(self.viewer.rows):
                        index += 1
                        if index < len(self.viewer.rows):
                            row = self.viewer.rows[index]
                            seq, start, end, height = row
                            if seq.type == constants.SEQ_AMINO_ACIDS or \
                                
seq.type == constants.SEQ_CUSTOM or \
                               
(seq.type == constants.SEQ_ANNOTATION and
                                    seq.annotation_type == constants.ANNOTATION_SSP):
                                self.cursor_row = row
                                offset = self.cursor_pos - prev_start
                                self.cursor_pos = start + offset
                                if self.cursor_pos >= end:
                                    self.cursor_pos = end - 1
                                break
                    if event.modifiers() & QtCore.Qt.ShiftModifier:
                        if self.shift_press_row != self.cursor_row:
                            self.viewer.sequence_group.unselectAll()
                            self.viewer.sequence_group.selectResidues(
                                self.shift_press_row, self.shift_press_pos,
                                self.cursor_row, self.cursor_pos, True)
                            self.selection_changed = True
                    self.update()
                elif key == QtCore.Qt.Key_Up:
                    row = self.cursor_row
                    seq, prev_start, prev_end, height = row
                    index = self.viewer.rows.index(row)
                    while index >= 0:
                        index -= 1
                        if index >= 0:
                            row = self.viewer.rows[index]
                            seq, start, end, height = row
                            if seq.type == constants.SEQ_AMINO_ACIDS or \
                                
seq.type == constants.SEQ_CUSTOM or \
                               
(seq.type == constants.SEQ_ANNOTATION and
                                    seq.annotation_type == constants.ANNOTATION_SSP):
                                self.cursor_row = row
                                offset = self.cursor_pos - prev_start
                                self.cursor_pos = start + offset
                                if self.cursor_pos >= end:
                                    self.cursor_pos = end - 1
                                break
                    if event.modifiers() & QtCore.Qt.ShiftModifier:
                        if self.shift_press_row != self.cursor_row:
                            self.viewer.sequence_group.unselectAll()
                            self.viewer.sequence_group.selectResidues(
                                self.shift_press_row, self.shift_press_pos,
                                self.cursor_row, self.cursor_pos, True)
                            self.selection_changed = True
                    self.update()
                elif key == QtCore.Qt.Key_Home:
                    if self.viewer.wrapped:
                        seq, start, end, height = self.cursor_row
                        self.cursor_pos = start
                    else:
                        if self.cursor_pos == 0 and self.last_key == key:
                            index = self.viewer.rows.index(self.cursor_row)
                            self.horizontal_scroll_bar.setValue(0)
                            # self.viewer.generateRows()
                            self.cursor_row = self.viewer.rows[index]
                        self.cursor_pos = 0
                    self.update()
                elif key == QtCore.Qt.Key_End:
                    if self.viewer.wrapped:
                        seq, start, end, height = self.cursor_row
                        self.cursor_pos = end - 1
                    else:
                        if self.cursor_pos == self.viewer.max_columns - 1 and \
                           
self.last_key == key:
                            index = self.viewer.rows.index(self.cursor_row)
                            self.horizontal_scroll_bar.setValue(seq.length())
                            self.cursor_pos = seq.length() - 5
                            # self.viewer.generateRows()
                            self.cursor_row = self.viewer.rows[index]
                        self.cursor_pos = self.viewer.max_columns - 1
                    self.update()
                elif key == QtCore.Qt.Key_Shift:
                    self.shift_press_row = self.cursor_row
                    self.shift_press_pos = self.cursor_pos
                elif seq.type == constants.SEQ_CUSTOM:
                    modified = False
                    seq, start, end, height = self.cursor_row
                    if event.text():
                        code = event.text()[0]
                        if code:
                            offset = 0
                            if not self.viewer.wrapped:
                                offset = self.viewer.left_column
                            seq.residues[self.cursor_pos + offset].code = code
                            modified = True
                    if modified:
                        # Sequence modified, update cursor position.
                        if not self.viewer.wrapped:
                            self.cursor_pos += 1
                            if self.cursor_pos >= self.viewer.max_columns:
                                self.cursor_pos -= 1
                                self.viewer.left_column += 1
                        else:
                            self.cursor_pos += 1
                            if self.cursor_pos >= end:
                                self.cursor_pos = end - 1
                        self.viewer.generateRows()
                        self.viewer.updateView()
                elif (key >= QtCore.Qt.Key_A and key <= QtCore.Qt.Key_Z) or \
                    
key == QtCore.Qt.Key_Minus or \
                     
(key >= QtCore.Qt.Key_0 and key <= QtCore.Qt.Key_9):
                    # Do not type ouside of the sequence boundary
                    if self.cursor_pos >= end:
                        return
                    modified = False
                    if seq.type == constants.SEQ_AMINO_ACIDS:
                        if (not seq.from_maestro) or self.viewer.mutate:
                            new_code = chr(key).upper()
                            if new_code in list(constants.AMINO_ACIDS):
                                seq, start, end, height = self.cursor_row
                                self.viewer.undo_stack.storeStateSequence(
                                    self.viewer.sequence_group,
                                    seq_index,
                                    label="Mutate Residue")
                                res = seq.residues[self.cursor_pos +
                                                   self.viewer.left_column]
                                maestro_helpers.maestroMutateResidue(
                                    self.viewer, res, new_code)
                                modified = True
                                self.viewer.updateView()
                    elif seq.type == constants.SEQ_ANNOTATION and \
                            
seq.annotation_type == constants.ANNOTATION_SSP:
                        if key == QtCore.Qt.Key_H or key == QtCore.Qt.Key_E or\
                           
key == QtCore.Qt.Key_Minus:
                            self.viewer.undo_stack.storeStateSequence(
                                self.viewer.sequence_group,
                                seq_index,
                                label="Edit Secondary Structure")
                            res = seq.residues[self.cursor_pos +
                                               self.viewer.left_column]
                            if key == QtCore.Qt.Key_H:
                                res.code = 'H'
                                res.color = (240, 96, 64)
                            elif key == QtCore.Qt.Key_E:
                                res.code = 'E'
                                res.color = (128, 240, 240)
                            else:
                                res.code = '-'
                                res.color = (32, 32, 32)
                                res.inverted = False
                            modified = True
                    if modified:
                        # Sequence modified, update cursor position.
                        if not self.viewer.wrapped:
                            self.cursor_pos += 1
                            if self.cursor_pos >= self.viewer.max_columns:
                                self.cursor_pos -= 1
                                self.viewer.left_column += 1
                        else:
                            self.cursor_pos += 1
                            if self.cursor_pos >= end:
                                self.cursor_pos = end - 1
                        self.viewer.generateRows()
                        self.viewer.updateView()
                if self.selection_changed:
                    self.viewer.selection_changed = True
                    self.viewer.updateView(generate_rows=False,
                                           update_colors=False)
                # Remember previous key.
                self.last_key = key 
[docs]    def wheelEvent(self, event):
        """
        Handles Qt mouse wheel event.
        :type event: QWheelEvent
        :param event: Qt mouse wheel event.
         """
        if self.viewer.wrapped:
            self.vertical_scroll_bar.wheelEvent(event)
        else:
            self.horizontal_scroll_bar.wheelEvent(event)
        event.accept() 
[docs]    def mouseMoveEvent(self, event):
        """
        Handles mouse move event for the sequence viewer widget.
        :type event: QMouseEvent
        :param event: Qt mouse move event.
        """
        row, pos, res = self.mouseToRowPosition(event.x(), event.y())
        if (row is None) or (pos < 0) or (pos is None):
            QtWidgets.QToolTip.hideText()
        if pos is None:
            return
        self.auto_scroll = False
        update_tooltip = False
        if row != self.last_tooltip_row or pos != self.last_tooltip_pos:
            update_tooltip = True
        self.last_tooltip_row = row
        self.last_tooltip_pos = pos
        if pos != self.init_pos or row != self.init_row:
            self.position_moved = True
        if self.viewer.feedback_label:
            self.viewer.feedback_label.setText("")
        if res and res.sequence:
            if self.viewer.feedback_label:
                text = ""
                if res.is_gap:
                    text = "  Mouse over gap"
                elif res.sequence.isValidProtein():
                    text = "  Mouse over residue "
                    text += str(res.num) + res.icode
                    text += "(" + res.code + "/" + res.name + "/"
                    if res.code in list(constants.AMINO_ACIDS):
                        text += constants.AMINO_ACIDS[res.code][1]
                        if constants.AMINO_ACIDS[res.code][0] != res.name:
                            text += " variant"
                    text += ")"
                    text += " in sequence " + res.sequence.short_name
                    if res.sequence.name != res.sequence.short_name:
                        text += " (" + \
                            
res.sequence.name.replace("\n", " ") + ")"
                    text += ", chain '" + res.sequence.chain_id + "'."
                self.viewer.feedback_label.setText(text)
            if self.viewer.build_mode and \
            
self.viewer.sequence_group.build_mode == \
            
constants.PRIME_MODE_COMPOSITE:
                self.setCursor(QtCore.Qt.IBeamCursor)
            elif self.viewer.mode == constants.MODE_EDIT and \
                
(res.sequence.type == constants.SEQ_AMINO_ACIDS or
                 res.sequence.type == constants.SEQ_CUSTOM or
                 (res.sequence.type == constants.SEQ_ANNOTATION and
                  res.sequence.annotation_type == constants.ANNOTATION_SSP)):
                if self.is_dragging_rect:
                    self.setCursor(QtCore.Qt.ClosedHandCursor)
                else:
                    self.setCursor(QtCore.Qt.IBeamCursor)
            else:
                self.setCursor(QtCore.Qt.PointingHandCursor)
        else:
            self.setCursor(QtCore.Qt.ArrowCursor)
        if event.buttons() == QtCore.Qt.NoButton or \
           
self.viewer.mode == constants.MODE_SELECT_AND_SLIDE or \
           
(self.viewer.mode == constants.MODE_EDIT and self.is_dragging_rect):
            if not self.is_dragging_rect:
                if row:
                    seq, start, end, height = row
                    if seq and update_tooltip:
                        self.showToolTip(event.globalPos(), pos, res, seq)
                else:
                    self.showToolTip(None, None, None, None)
            else:
                min_row, max_row, min_pos, max_pos, init_pos = self.drag_coords
                d_pos = pos - self.last_pos
                if d_pos != 0 and \
                   
min_pos + d_pos >= 0:
                    seq0, start, end, height = min_row
                    seq1, start, end, height = max_row
                    min_pos += d_pos
                    max_pos += d_pos
                    self.drag_coords = (min_row, max_row, min_pos, max_pos,
                                        init_pos)
                    self.update()
                    if self.viewer.has_tooltips:
                        tip = "Sliding block:\n[%s: %d, %s: %d] to " \
                            
"\n[%s: %d, %s: %d]" % \
                            
(seq0.short_name, init_pos + 1,
                             seq1.short_name,
                             init_pos + (max_pos - min_pos) + 1,
                             seq0.short_name, min_pos + 1,
                             seq1.short_name, max_pos + 1)
                        if update_tooltip:
                            QtWidgets.QToolTip.showText(event.globalPos(), tip)
                self.last_pos = pos
        if event.buttons() & QtCore.Qt.LeftButton:
            if self.selecting_columns:
                if not event.modifiers() & QtCore.Qt.ControlModifier:
                    self.viewer.sequence_group.unselectAll()
                self.viewer.sequence_group.selectColumns(self.init_pos, pos)
                result = self.updateColumns(self.init_pos, pos)
                self.update()
                if result and not self.viewer.wrapped:
                    self.last_pos = pos
                    self.auto_scroll = True
                    new_pos = event.pos()
                    self.last_mouse_move_event = QtGui.QMouseEvent(
                        event.type(), new_pos, event.button(), event.buttons(),
                        event.modifiers())
                    QtCore.QTimer.singleShot(100, self.autoScroll)
                return
            if self.init_res:
                if self.viewer.mode == constants.MODE_SELECT_AND_SLIDE or \
                   
self.viewer.mode == constants.MODE_SELECT_ONLY or \
                   
(self.viewer.build_mode and
                    self.viewer.sequence_group.build_mode == constants.PRIME_MODE_COMPOSITE) or \
                   
(self.viewer.mode == constants.MODE_GRAB_AND_DRAG and
                        event.modifiers() & QtCore.Qt.ControlModifier):
                    if self.is_dragging_rect:
                        self.setCursor(QtCore.Qt.ClosedHandCursor)
                    else:
                        if row != self.init_row or pos != self.init_pos:
                            self.setCursor(QtCore.Qt.CrossCursor)
                            select = True
                            if not event.modifiers(
                            ) & QtCore.Qt.ControlModifier:
                                self.viewer.sequence_group.unselectAll()
                            else:
                                select = not self.unselect
                            if self.viewer.build_mode and \
                               
self.viewer.sequence_group.build_mode == constants.PRIME_MODE_COMPOSITE:
                                row = self.init_row
                            self.viewer.sequence_group.selectResidues(
                                self.init_row, self.init_pos, row, pos, select)
                            start = self.init_pos
                            end = pos
                            if start > end:
                                tmp = end
                                end = start
                                start = tmp
                            max_columns = self.viewer.maxColumns()
                            if end > self.viewer.left_column + max_columns:
                                self.viewer.sequence_area.horizontal_scroll_bar.setValue(
                                    self.viewer.sequence_area.
                                    horizontal_scroll_bar.value() + 1)
                                self.auto_scroll = True
                            elif start < self.viewer.left_column:
                                self.viewer.sequence_area.horizontal_scroll_bar.setValue(
                                    self.viewer.sequence_area.
                                    horizontal_scroll_bar.value() - 1)
                                self.auto_scroll = True
                            if self.auto_scroll:
                                self.last_pos = pos
                                new_pos = event.pos()
                                self.last_mouse_move_event = QtGui.QMouseEvent(
                                    event.type(), new_pos, event.button(),
                                    event.buttons(), event.modifiers())
                                QtCore.QTimer.singleShot(100, self.autoScroll)
                            self.last_pos = pos
                            self.update()
                            return
                            diff = pos - self.last_pos
                            if diff > 0:
                                diff = 1
                            elif diff < 0:
                                diff = -1
                            self.last_pos = pos
                            self.update()
                            if diff and not self.viewer.wrapped:
                                self.auto_scroll = True
                                new_pos = event.pos()
                                new_pos.setX(new_pos.x() + diff)
                                self.last_mouse_move_event = QtGui.QMouseEvent(
                                    event.type(), new_pos, event.button(),
                                    event.buttons(), event.modifiers())
                                QtCore.QTimer.singleShot(100, self.autoScroll)
                elif self.viewer.mode == constants.MODE_GRAB_AND_DRAG:
                    # Shift key is used to invert current
                    # grab-and-drag behavior
                    invert = bool(event.modifiers() & QtCore.Qt.ShiftModifier)
                    alt = bool(event.modifiers() & QtCore.Qt.AltModifier)
                    # Grab-and-drag the alignment.
                    self.viewer.sequence_group.grabAndDrag(
                        self.init_row,
                        self.last_pos,
                        row,
                        pos,
                        int(self.viewer.lock_downstream) ^ int(invert),
                        slide_sequence=alt)
                    self.last_pos = pos
                    self.setCursor(QtCore.Qt.ArrowCursor)
                    self.alignment_changed = True
                    self.viewer.updateView()
                elif self.viewer.mode == constants.MODE_EDIT and \
                        
not self.is_dragging_rect:
                    if self.init_row != row or \
                       
self.init_pos != pos:
                        self.setCursor(QtCore.Qt.CrossCursor)
                        select = True
                        if event.modifiers() & QtCore.Qt.ControlModifier:
                            select = not self.unselect
                        else:
                            self.viewer.sequence_group.unselectAll()
                        self.viewer.sequence_group.selectResidues(
                            self.init_row, self.init_pos, row, pos, select)
                        self.update() 
[docs]    def setViewer(self, viewer):
        """
        Sets a parent sequence viewer widget for this sequence area.
        """
        self.viewer = viewer 
[docs]    def resizeEvent(self, event):
        """
        Qt resize event handler. When the widget is resized, the sequence
        viewer has to re-generate rows and update itself.
        :type event: QResizeEvent
        :param event: Qt resize event
        """
        if self.viewer.wrapped:
            self.horizontal_scroll_bar.hide()
        else:
            self.horizontal_scroll_bar.show()
        self.horizontal_scroll_bar.setGeometry(0,
                                               self.height() - 15,
                                               self.width() - 15, 15)
        self.vertical_scroll_bar.setGeometry(self.width() - 15, 0, 15,
                                             self.height())
        self.viewer.max_columns = self.viewer.maxColumns()
        self.viewer.generateRows()
        self.viewer.update()
        if self.viewer.feedback_label:
            max_width = self.viewer.width() - 150
            if max_width < 1:
                max_width = 1
            self.viewer.feedback_label.setMaximumWidth(max_width) 
[docs]    def paintAuxRectangle(self, painter):
        """
        Paints the auxiliary rectangle on a specified painter.
        The aux rectangle is used to mark a sequence area in "select and
        slide" sequence viewer mode.
        :type painter: QPainter
        :param painter: target Qt painter
        """
        min_row, max_row, min_pos, max_pos, init_pos = self.drag_coords
        if not min_row or not max_row:
            return
        x0, y0 = self.rowPosToCoords(min_row, min_pos)
        x1, y1 = self.rowPosToCoords(max_row, max_pos)
        if not (x0 and y0 and x1 and y1):
            return
        pen = QtGui.QPen(QtCore.Qt.white)
        pen.setWidth(5)
        painter.setPen(pen)
        painter.setBrush(QtCore.Qt.NoBrush)
        w = x1 - x0 + self.viewer.cell_width + 2
        h = y1 - y0 + self.viewer.font_height + 2
        x0 -= 1
        y0 -= 1
        painter.drawRect(x0, y0, w, h)
        pen = QtGui.QPen(QtGui.QColor(64, 64, 192))
        pen.setWidth(2)
        painter.setPen(pen)
        painter.drawRect(x0, y0, w + 1, h + 1) 
[docs]    def paintUserAnnotations(self, painter):
        """
        Renders user-defined annotations.
        """
        if not self.viewer.sequence_group.user_annotations:
            return
        x_position = self.viewer.margin
        if self.viewer.display_boundaries:
            x_position += 6 * self.viewer.font_width
        painter.setClipRect(x_position, 0,
                            self.viewer.maxColumns() * self.viewer.cell_width,
                            self.height())
        painter.setClipping(True)
        for ann in self.viewer.sequence_group.user_annotations:
            name, start, end, text, color = ann
            if name == "region":
                first_row = last_row = None
                region_start = 0
                if not self.viewer.wrapped:
                    region_start = self.viewer.left_column
                region_end = region_start + self.viewer.maxColumns()
                for row in self.viewer.rows:
                    seq, d, d, row_height = row
                    if seq.isValidProtein():
                        if not first_row:
                            first_row = row
                    if (seq.type == constants.SEQ_SEPARATOR or
                            seq.type == constants.SEQ_CUSTOM) or \
                            
row == self.viewer.rows[-1]:
                        if seq.type != constants.SEQ_SEPARATOR:
                            seq, d, d, row_height = row
                            if seq.type not in (constants.SEQ_SEPARATOR,
                                                constants.SEQ_EMPTY,
                                                constants.SEQ_CUSTOM):
                                last_row = row
                        if first_row and last_row and \
                            
not ((region_start < start and region_end < start) or
                                 (region_start > end and region_end > end)):
                            # Draw here
                            x0, y0 = self.rowPosToCoords(
                                first_row, start, False)  # - region_start)
                            x1, y1 = self.rowPosToCoords(
                                last_row, end, False)  # - region_start)
                            if x0 is not None and y0 is not None and \
                                    
x1 is not None and y1 is not None:
                                r, g, b = color
                                seq, d, d, row_height = last_row
                                pen = QtGui.QPen(QtGui.QColor(r, g, b))
                                pen.setWidth(3)
                                painter.setPen(pen)
                                painter.setBrush(QtCore.Qt.NoBrush)
                                w = x1 - x0 + self.viewer.cell_width + 2
                                h = y1 - y0 + row_height * \
                                    
self.viewer.font_height + 2
                                x0 -= 1
                                y0 -= 1
                                painter.drawRect(x0, y0, w, h)
                        first_row = last_row = None
                        region_start += self.viewer.maxColumns()
                        region_end += self.viewer.maxColumns()
                    last_row = row
            elif name == "rectangle":
                first_row = last_row = None
                region_start = 0
                if not self.viewer.wrapped:
                    region_start = self.viewer.left_column
                region_end = region_start + self.viewer.maxColumns()
                first, start = start
                last, end = end
                final_row = self.viewer.rows[-1]
                for row in self.viewer.rows:
                    seq, d, d, row_height = row
                    draw = False
                    if seq.type == constants.SEQ_SEPARATOR or row == final_row:
                        draw = True
                    if seq == first:
                        first_row = row
                    if seq == last:
                        last_row = row
                    if draw and first_row and last_row:
                        s1, d, d, d = first_row
                        s2, d, d, d = last_row
                        # Draw here
                        x0, y0 = self.rowPosToCoords(first_row, start,
                                                     False)  # - region_start)
                        x1, y1 = self.rowPosToCoords(last_row, end,
                                                     False)  # - region_start)
                        if x0 is not None and y0 is not None and \
                                
x1 is not None and y1 is not None:
                            r, g, b = color
                            seq, d, d, row_height = last_row
                            pen = QtGui.QPen(QtGui.QColor(r, g, b))
                            pen.setWidth(3)
                            painter.setPen(pen)
                            painter.setBrush(QtCore.Qt.NoBrush)
                            w = x1 - x0 + self.viewer.cell_width + 2
                            h = y1 - y0 + row_height * \
                                
self.viewer.font_height + 2
                            x0 -= 1
                            y0 -= 1
                            painter.drawRect(x0, y0, w, h)
                        first_row = last_row = None
                        region_start += self.viewer.maxColumns()
                        region_end += self.viewer.maxColumns()
        painter.setClipping(False) 
[docs]    def paintCursor(self, painter):
        """
        Draw edit mode cursor at present cursor position.
        :param painter: painter used to draw the cursor
        :type painter: QPainter
        """
        if self.cursor_enabled:
            if self.cursor_row is not None and \
               
self.cursor_pos is not None:
                cursor_x, cursor_y = \
                    
self.rowPosToCoords(self.cursor_row,
                                        self.viewer.left_column +
                                        self.cursor_pos)
                if cursor_x and cursor_y:
                    pen = QtGui.QPen(QtCore.Qt.white)
                    pen.setWidth(3)
                    painter.setPen(pen)
                    painter.setBrush(QtCore.Qt.NoBrush)
                    painter.drawRect(cursor_x, cursor_y,
                                     self.viewer.cell_width + 1,
                                     self.viewer.font_height + 1)
                    pen = QtGui.QPen(QtCore.Qt.red)
                    pen.setWidth(1)
                    painter.setPen(pen)
                    painter.drawRect(cursor_x, cursor_y,
                                     self.viewer.cell_width + 1,
                                     self.viewer.font_height + 1) 
[docs]    def paintEvent(self, event):
        """
        Qt paint event handler. Calls self.paintSequenceArea to paint
        the widget contents.
        :type event: QPaintEvent
        :param event: Qt paint event
        """
        painter = QtGui.QPainter(self)
        if constants.MSV_DEBUG:
            self.paintSequenceArea(painter, event)
        else:
            try:
                self.paintSequenceArea(painter, event)
            except:
                # This should never happen, however, just to be safe
                # we ignore the painting exception.
                pass 
[docs]    def paintRowBackground(self,
                           painter,
                           row,
                           x_pos,
                           y_pos,
                           draw_text,
                           selection_only=False,
                           use_parent=False):
        """
        Paints a row background.
        """
        seq, start, end, row_height = row
        if end - start <= 0:
            return
        if use_parent and seq.parent_sequence:
            seq = seq.parent_sequence
        residues = seq.residues
        use_colors = self.viewer.use_colors
        length = seq.length()
        seq_position = start
        font_height = self.viewer.font_height
        cell_width = self.viewer.cell_width
        # The row image is scaled and used to display row background colors
        row_image = QtGui.QImage(end - start, 1, QtGui.QImage.Format_RGB32)
        if self.viewer.build_mode and \
                
self.viewer.sequence_group.build_mode == constants.PRIME_MODE_COMPOSITE:
            structures = self.viewer.sequence_group.getStructureList(
                omit_reference=True)
        br = self.viewer.background_color.red()
        bg = self.viewer.background_color.green()
        bb = self.viewer.background_color.blue()
        while seq_position < end and seq_position < length:
            residue = residues[seq_position]
            # Initially, set to background color.
            r = br
            g = bg
            b = bb
            if residue.selected:
                r = constants.SELECTION_COLOR[0]
                g = constants.SELECTION_COLOR[1]
                b = constants.SELECTION_COLOR[2]
            elif self.viewer.build_mode and \
                    
self.viewer.sequence_group.build_mode == \
                    
constants.PRIME_MODE_COMPOSITE:
                # Display template coverage here.
                if residue.model:
                    r, g, b = constants.TEMPLATE_COLORS[
                        structures.index(seq) % constants.N_TEMPLATE_COLORS]
            elif use_colors and not residue.is_gap and residue.active:
                if not selection_only and not residue.selected:
                    r, g, b = residue.color
                if residue.marked_color:
                    if residue.marked_color == 'bold':
                        pass
                    elif residue.marked_color == 'italic':
                        pass
                    else:
                        r, g, b = residue.marked_color
            if residue.structureless and \
                    
seq.has_structure:
                # Desaturate color of structureless residues
                r = old_div(r, 4) + 3 * br / 4
                g = old_div(g, 4) + 3 * bg / 4
                b = old_div(b, 4) + 3 * bb / 4
            rgb_color = QtGui.qRgb(r, g, b)
            row_image.setPixel(seq_position - start, 0, rgb_color)
            seq_position += 1
        img_height = font_height * row_height
        img_offset = 0
        if seq.annotation_type == constants.ANNOTATION_CUSTOM:
            img_height = 2
            img_offset = 1
        scaled_image = row_image.scaled((end - start) * cell_width, img_height)
        painter.drawImage(x_pos, y_pos + img_offset, scaled_image) 
[docs]    def paintRowText(self, painter, row, row_position, x_pos, y_pos, x_offset):
        """
        Paints the row text contents.
        """
        seq, start, end, row_height = row
        if end - start <= 0:
            return
        residues = seq.residues
        use_colors = self.viewer.use_colors
        seq_position = start
        font_width = self.viewer.font_width
        cell_width = self.viewer.cell_width
        display_dots = self.viewer.display_dots
        auto_color = self.viewer.auto_color
        profile = self.viewer.sequence_group.profile
        reference = self.viewer.sequence_group.reference
        if not reference:
            reference = profile
        reference_length = reference.length()
        x_position = self.viewer.margin
        if self.viewer.display_boundaries:
            x_position += 6 * font_width
        y_offset = -2
        seq_position = start
        last_color = None
        text = ""
        draw_3letter_codes = False
        if self.viewer.zoom_factor > 3 and \
           
seq.type == constants.SEQ_AMINO_ACIDS:
            draw_3letter_codes = True
            painter.setFont(self.viewer.original_font)
            x_pos = x_position - 2
            #y_offset = 0
        else:
            x_pos = x_position + x_offset + 1
        y_pos = row_position + y_offset
        painter.setPen(self.viewer.inv_background_color)
        if draw_3letter_codes:
            while seq_position < end:
                residue = residues[seq_position]
                is_active = residue.active
                code = residue.code
                if residue.structureless and \
                   
seq.has_structure:
                    is_active = False
                if display_dots and \
                        
seq.type == constants.SEQ_AMINO_ACIDS and \
                        
seq_position < reference_length and \
                        
not residue.is_gap and \
                        
seq != reference and \
                        
code == reference.residues[seq_position].code:
                    code = '  .  '
                elif code in list(constants.AMINO_ACIDS):
                    code = constants.AMINO_ACIDS[code][0]
                    code = " " + code + " "
                if residue.is_gap:
                    code = " " + code * 3 + " "
                if residue.is_gap or not use_colors:
                    color = self.viewer.inv_background_color
                elif residue.selected:
                    color = QtCore.Qt.white
                elif not is_active:
                    color = QtCore.Qt.gray
                elif residue.inverted:
                    color = QtCore.Qt.black
                    if auto_color:
                        if residue.marked_color and \
                                
residue.marked_color not in ("bold", "italic"):
                            r, g, b = residue.marked_color
                        else:
                            r, g, b = residue.color
                        if QtGui.qGray(r, g, b) < 127:
                            color = QtCore.Qt.white
                else:
                    r, g, b = residue.color
                    color = QtGui.QColor(r, g, b)
                if seq.type == constants.SEQ_CUSTOM or \
                        
seq.annotation_type == constants.ANNOTATION_CUSTOM:
                    color = self.viewer.inv_background_color
                painter.setPen(color)
                painter.drawText(x_pos, y_pos, code)
                x_pos += cell_width
                seq_position += 1
        else:
            while seq_position < end:
                residue = residues[seq_position]
                is_active = residue.active
                code = residue.code
                if residue.structureless and \
                   
seq.has_structure:
                    is_active = False
                if draw_3letter_codes:
                    if code in list(constants.AMINO_ACIDS):
                        code = constants.AMINO_ACIDS[code][0]
                        code = "." + code + "."
                    if residue.is_gap:
                        code = ".." + code + ".."
                if display_dots and \
                        
seq.type == constants.SEQ_AMINO_ACIDS and \
                        
seq_position < reference_length and \
                        
not residue.is_gap and \
                        
seq != reference and \
                        
code == reference.residues[seq_position].code:
                    code = '.'
                if residue.is_gap or not use_colors:
                    color = self.viewer.inv_background_color
                elif residue.selected:
                    color = QtCore.Qt.white
                elif not is_active:
                    color = QtCore.Qt.gray
                elif residue.inverted:
                    color = QtCore.Qt.black
                    if auto_color:
                        if residue.marked_color and \
                                
residue.marked_color not in ("bold", "italic"):
                            r, g, b = residue.marked_color
                        else:
                            r, g, b = residue.color
                        if QtGui.qGray(r, g, b) < 127:
                            color = QtCore.Qt.white
                else:
                    r, g, b = residue.color
                    color = QtGui.QColor(r, g, b)
                if seq.type == constants.SEQ_CUSTOM or \
                        
seq.annotation_type == constants.ANNOTATION_CUSTOM:
                    color = self.viewer.inv_background_color
                if color != last_color:
                    if text:
                        painter.drawText(x_pos, y_pos, text)
                        x_pos += cell_width * len(text)
                        text = ""
                    painter.setPen(color)
                    last_color = color
                text += code
                seq_position += 1
        if text:
            painter.drawText(x_pos, y_pos, text)
        if draw_3letter_codes:
            painter.setFont(self.viewer.font()) 
[docs]    def paintRowLogo(self, painter, row, row_position):
        """
        Paints the row text contents.
        """
        seq, start, end, row_height = row
        residues = seq.residues
        length = seq.length()
        seq_position = start
        font_height = self.viewer.font_height
        font_width = self.viewer.font_width
        cell_width = self.viewer.cell_width
        hscale = old_div(float(cell_width), float(font_width))
        profile = self.viewer.sequence_group.profile
        reference = self.viewer.sequence_group.reference
        if not reference:
            reference = profile
        x_position = self.viewer.margin
        if self.viewer.display_boundaries:
            x_position += 6 * font_width
        y_offset = -2
        seq_position = start
        text = ""
        draw_3letter_codes = False
        if self.viewer.zoom_factor > 4 and \
           
seq.type == constants.SEQ_AMINO_ACIDS:
            draw_3letter_codes = True
            painter.setFont(self.viewer.original_font)
            x_pos = x_position
            y_offset = 0
        else:
            x_pos = x_position + 1
        y_pos = row_position + y_offset + font_height * (row_height - 1)
        painter.setPen(QtCore.Qt.gray)
        residues = self.viewer.sequence_group.profile.residues
        length = len(residues)
        while seq_position < end and seq_position < length:
            residue = residues[seq_position]
            profile_res = profile.residues[seq_position]
            r, g, b = profile.residues[seq_position].colorType()
            color = QtGui.QColor(r, g, b)
            painter.setPen(color)
            pos = 0.0
            rlist = []
            for pair in residue.frequencies[0:5]:
                c, f = pair
                if f == 0.0:
                    break
                tmpcode = profile_res.code
                profile_res.code = c
                r, g, b = profile_res.colorType()
                profile_res.code = tmpcode
                color = QtGui.QColor(r, g, b)
                f *= residue.bits * 0.9
                rlist.append((c, pos, f, color))
                pos += f
                if f < 0.5 or pos > 3.0:
                    break
            painter.save()
            painter.translate(x_pos, y_pos)
            painter.scale(hscale, 1.0)
            for index, letter in enumerate(rlist):
                c, pos, scale, color = letter
                if index and scale <= 0.5:
                    break
                painter.setPen(color)
                painter.save()
                if hscale < 1.0:
                    painter.setBrush(color)
                    painter.drawRect(0.0, 0.0, cell_width,
                                     -scale * 0.7 * font_height)
                else:
                    painter.scale(1.0, scale)
                    painter.drawText(0.0, 0.0, c)
                painter.restore()
                painter.translate(0.0, -0.66 * (font_height + 2) * scale)
            painter.restore()
            seq_position = seq_position + 1
            x_pos += cell_width
        if text:
            painter.drawText(x_pos, y_pos, text)
        if draw_3letter_codes:
            painter.setFont(self.viewer.font()) 
[docs]    def paintRowSSA(self, painter, row, row_position):
        seq, start, end, row_height = row
        if end - start <= 0:
            return
        residues = seq.residues
        seq_position = start
        font_height = self.viewer.font_height
        font_width = self.viewer.font_width
        cell_width = self.viewer.cell_width
        profile = self.viewer.sequence_group.profile
        reference = self.viewer.sequence_group.reference
        if not reference:
            reference = profile
        x_position = self.viewer.margin
        if self.viewer.display_boundaries:
            x_position += 6 * font_width
        seq_position = start
        color_c = self.viewer.inv_background_color
        y_position = row_position - font_height
        painter.setRenderHint(QtGui.QPainter.Antialiasing, False)
        mid_pos = int(0.5 * font_height)
        half_height = int(0.25 * font_height)
        if half_height % 2 == 0:
            half_height += 1
        while seq_position < end:
            if seq.parent_sequence and \
               
len(seq.parent_sequence.residues) > seq_position:
                parent_residue = seq.parent_sequence.residues[seq_position]
            else:
                parent_residue = None
            residue = residues[seq_position]
            if not residue.is_gap:
                if residue.code == 'H':
                    next_res = seq.nextUngappedResidue(seq_position)
                    previous_res = seq.previousUngappedResidue(seq_position)
                    if (previous_res and previous_res.code != 'H') \
                            
or not previous_res:
                        painter.drawImage(x_position, y_position,
                                          self.ssa_image_helix_start)
                    elif next_res and next_res.code != 'H':
                        painter.drawImage(x_position, y_position,
                                          self.ssa_image_helix_end)
                    else:
                        painter.drawImage(x_position, y_position,
                                          self.ssa_image_helix_mid)
                elif residue.code == 'E':
                    next_res = seq.nextUngappedResidue(seq_position)
                    previous_res = seq.previousUngappedResidue(seq_position)
                    if (previous_res and previous_res.code != 'E') \
                            
or not previous_res:
                        painter.drawImage(x_position, y_position,
                                          self.ssa_image_sheet_start)
                    elif next_res and next_res.code != 'E':
                        painter.drawImage(x_position, y_position,
                                          self.ssa_image_sheet_end)
                    else:
                        painter.drawImage(x_position, y_position,
                                          self.ssa_image_sheet_mid)
                else:
                    if parent_residue and \
                       
parent_residue.structureless:
                        # Light-gray for structureless residues
                        painter.setPen(QtGui.QColor(192, 192, 192))
                    else:
                        painter.setPen(color_c)
                    painter.drawLine(x_position, y_position + mid_pos,
                                     x_position + cell_width - 1,
                                     y_position + mid_pos)
            x_position += cell_width
            seq_position += 1 
[docs]    def paintRowSSBond(self, painter, row, row_position):
        """
        Paints disulfide bond row (both assignment and prediction).
        """
        seq, start, end, row_height = row
        if end - start <= 0:
            return
        residues = seq.residues
        seq_position = start
        font_height = self.viewer.font_height
        font_width = self.viewer.font_width
        cell_width = self.viewer.cell_width
        profile = self.viewer.sequence_group.profile
        reference = self.viewer.sequence_group.reference
        if not reference:
            reference = profile
        x_position = self.viewer.margin
        if self.viewer.display_boundaries:
            x_position += 6 * font_width
        seq_position = start
        y_position = row_position - font_height
        painter.setRenderHint(QtGui.QPainter.Antialiasing, False)
        pen = QtGui.QPen(self.viewer.inv_background_color)
        pen.setStyle(QtCore.Qt.SolidLine)
        pen.setWidthF(1 + (font_width * 0.1))
        painter.setPen(pen)
        painter.setBrush(QtCore.Qt.NoBrush)
        y0 = y_position + 0.5 * font_height
        painter.setClipRect(x_position, 0,
                            self.viewer.maxColumns() * cell_width,
                            self.height())
        painter.setClipping(True)
        seq_position = start
        bonds = []
        if seq.annotation_type == constants.ANNOTATION_SSBOND:
            bond_list = seq.parent_sequence.ssb_bond_list
        else:
            bond_list = seq.bond_list
        for bi, bond in enumerate(bond_list):
            first, second = bond
            first_res = seq.parent_sequence.getResidue(first, ungapped=True)
            second_res = seq.parent_sequence.getResidue(second, ungapped=True)
            if first_res and second_res and \
                    
first_res in seq.parent_sequence.residues and \
                    
second_res in seq.parent_sequence.residues:
                r1 = seq.parent_sequence.residues.index(first_res)
                r2 = seq.parent_sequence.residues.index(second_res)
                bonds.append((r1, r2))
        while seq_position < end and seq_position < len(residues):
            residue = residues[seq_position]
            if residue and \
               
not residue.is_gap:
                for bi, bond in enumerate(bonds):
                    if seq.annotation_type == constants.ANNOTATION_CCB:
                        g = int(255.0 * float(bi) / len(bonds))
                        color = QtGui.QColor(g, g, g)
                        pen.setColor(color)
                        painter.setPen(pen)
                    r1, r2 = bond
                    y0 = y_position + 0.4 * font_height * (bi + 1)
                    if seq_position > r1 and seq_position < r2:
                        painter.drawLine(x_position, y0,
                                         x_position + cell_width - 1, y0)
                    if r1 == seq_position:
                        painter.drawLine(x_position + 0.5 * cell_width, y0,
                                         x_position + cell_width - 1, y0)
                        painter.drawLine(x_position + 0.5 * cell_width,
                                         y_position,
                                         x_position + 0.5 * cell_width, y0)
                    if r2 == seq_position:
                        painter.drawLine(x_position, y0,
                                         x_position + 0.5 * cell_width, y0)
                        painter.drawLine(x_position + 0.5 * cell_width,
                                         y_position,
                                         x_position + 0.5 * cell_width, y0)
            x_position += cell_width
            seq_position += 1
        painter.setRenderHint(QtGui.QPainter.Antialiasing, False)
        painter.setClipping(False) 
[docs]    def paintRowConstraints(self, painter, row, row_position):
        """
        Paints constraints row (both alignment and Prime).
        """
        seq, start, end, row_height = row
        font_height = self.viewer.font_height
        font_width = self.viewer.font_width
        cell_width = self.viewer.cell_width
        profile = self.viewer.sequence_group.profile
        reference = self.viewer.sequence_group.reference
        if not reference:
            reference = profile
        x_position = self.viewer.margin
        if self.viewer.display_boundaries:
            x_position += 6 * font_width
        y_position = row_position - font_height
        painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
        pen = painter.pen()
        if self.viewer.set_query_constraints:
            pencolor = QtGui.QColor(255, 127, 127)
            penlightcolor = QtGui.QColor(255, 255, 127)
        else:
            pencolor = QtGui.QColor(127, 127, 255)
            penlightcolor = QtGui.QColor(127, 255, 255)
        pen.setStyle(QtCore.Qt.SolidLine)
        pen.setWidthF(1.0)
        painter.setPen(pen)
        painter.setBrush(QtCore.Qt.NoBrush)
        painter.setClipRect(x_position, 0,
                            self.viewer.maxColumns() * cell_width,
                            self.height())
        painter.setClipping(True)
        for constraint in seq.constraint_list:
            cstart, cend, target, prime = constraint
            if self.viewer.wrapped:
                if (cstart < start and cend < start) or \
                   
(cstart > end and cend > end):
                    continue
            if prime != self.viewer.set_query_constraints:
                continue
            if prime:
                cstart_res = reference.getResidue(cstart, ungapped=True)
                cend_res = reference.getResidue(cend, ungapped=True)
            else:
                cstart_res = reference.getResidue(cstart, ungapped=True)
                cend_res = target.getResidue(cend, ungapped=True)
            if cstart_res and cend_res:
                cstart = reference.residues.index(cstart_res)
                if prime:
                    cend = reference.residues.index(cend_res)
                else:
                    cend = target.residues.index(cend_res)
                if not self.viewer.wrapped:
                    cstart -= self.viewer.left_column
                    cend -= self.viewer.left_column
                else:
                    cstart -= start
                    cend -= start
                for it in [3, 1]:
                    pen.setWidthF(float(it))
                    if it == 3:
                        color = pencolor
                    else:
                        color = penlightcolor
                    pen.setColor(color)
                    painter.setPen(pen)
                    y0 = y_position + 3
                    y1 = y_position + int(1.0 * row_height * font_height - 3)
                    painter.drawLine(x_position + cstart * cell_width + 1, y0,
                                     x_position + (cstart + 1) * cell_width - 2,
                                     y0)
                    if prime:
                        painter.drawLine(
                            x_position + cend * cell_width + 1, y0,
                            x_position + (cend + 1) * cell_width - 2, y0)
                    else:
                        painter.drawLine(
                            x_position + cend * cell_width + 1, y1,
                            x_position + (cend + 1) * cell_width - 2, y1)
                    if math.fabs(cstart - cend) < 2:
                        painter.drawLine(
                            x_position + (cstart + 0.5) * cell_width, y0,
                            x_position + (cend + 0.5) * cell_width, y1)
                    else:
                        path = QtGui.QPainterPath()
                        if prime:
                            path.moveTo(x_position + (cend + 0.5) * cell_width,
                                        y0)
                            path.arcTo(x_position + (cstart + 0.5) * cell_width,
                                       y0 + (y1 - y0) * 0.5,
                                       (cend - cstart) * cell_width, y0 - y1, 0,
                                       180)
                        else:
                            path.moveTo(
                                x_position + (cstart + 0.5) * cell_width, y0)
                            path.cubicTo(x_position + cstart * cell_width, y1,
                                         x_position + cend * cell_width, y0,
                                         x_position + (cend + 0.5) * cell_width,
                                         y1)
                        painter.drawPath(path)
        painter.setRenderHint(QtGui.QPainter.Antialiasing, False)
        painter.setClipping(False) 
[docs]    def paintRowAnnotationPlot(self, painter, row, row_position):
        """
        Paints annotation plot.
        """
        seq, start, end, row_height = row
        if end - start <= 0:
            return
        residues = seq.residues
        seq_position = start
        font_height = self.viewer.font_height
        font_width = self.viewer.font_width
        cell_width = self.viewer.cell_width
        profile = self.viewer.sequence_group.profile
        reference = self.viewer.sequence_group.reference
        if not reference:
            reference = profile
        x_position = self.viewer.margin
        if self.viewer.display_boundaries:
            x_position += 6 * font_width
        seq_position = start
        y_position = row_position - font_height + 1
        min_y_position = y_position + 2
        max_y_position = y_position - 2 + seq.height * font_height
        mid_y_position = 0.5 * (max_y_position - min_y_position) + y_position
        if seq.plot_style == constants.PLOT_LINE:
            painter.setPen(QtGui.QColor(QtCore.Qt.gray))
            painter.pen().setStyle(QtCore.Qt.DashLine)
            painter.drawLine(x_position, mid_y_position,
                             x_position + cell_width - 2 * self.viewer.margin,
                             mid_y_position)
            painter.pen().setStyle(QtCore.Qt.SolidLine)
        range = seq.max_avg_value - seq.min_avg_value
        plot_height = (max_y_position - min_y_position)
        if range > 0.0:
            if seq.plot_style == constants.PLOT_HISTOGRAM:
                r, g, b = seq.plot_color
                color = QtGui.QColor(r, g, b)
                painter.setPen(color)
                painter.setBrush(color)
                if seq.max_avg_value > 0.0 and seq.min_avg_value < 0.0:
                    max_val = max(math.fabs(seq.max_avg_value),
                                  math.fabs(seq.min_avg_value))
                    range = 2.0 * max_val
                    if range == 0.0:
                        inv_range = 1.0
                    else:
                        inv_range = old_div(1.0, max_val)
                    half_height = 0.5 * plot_height
                    while seq_position < end:
                        residue = residues[seq_position]
                        if residue and not residue.is_gap:
                            y_pos = int(half_height * (inv_range *
                                                       (residue.avg_value)))
                            if y_pos < 0:
                                painter.drawRect(x_position + 1, mid_y_position,
                                                 cell_width - 2, -y_pos)
                            else:
                                painter.drawRect(x_position + 1,
                                                 mid_y_position - y_pos,
                                                 cell_width - 2, y_pos)
                        x_position += cell_width
                        seq_position += 1
                else:
                    inv_range = old_div(1.0, range)
                    while seq_position < end:
                        residue = residues[seq_position]
                        if residue and not residue.is_gap:
                            y_pos = math.ceil(
                                plot_height *
                                (inv_range *
                                 (residue.avg_value - seq.min_avg_value))) + 1
                            painter.drawRect(
                                x_position + 1,
                                y_position + plot_height - y_pos + 2,
                                cell_width - 2, y_pos)
                        x_position += cell_width
                        seq_position += 1
            elif seq.plot_style == constants.PLOT_LINE:
                r, g, b = seq.plot_color
                painter.setPen(QtGui.QColor(QtGui.QColor(r, g, b)))
                painter.pen().setStyle(QtCore.Qt.SolidLine)
                if seq.max_avg_value > 0.0 and seq.min_avg_value < 0.0:
                    max_val = max(math.fabs(seq.max_avg_value),
                                  math.fabs(seq.min_avg_value))
                    range = 2.0 * max_val
                    if range == 0.0:
                        inv_range = 1.0
                    else:
                        inv_range = old_div(1.0, max_val)
                    half_height = 0.5 * plot_height
                    while seq_position < end:
                        residue = residues[seq_position]
                        if residue and not residue.is_gap:
                            prev_y_pos = y_position + plot_height * \
                                
(1.0 - inv_range * (residue.previous_avg_value
                                                    - seq.min_avg_value)) + 1
                            y_pos = y_position + plot_height * \
                                
(1.0 - inv_range * (residue.avg_value -
                                  seq.min_avg_value)) + 1
                            next_y_pos = y_position + plot_height * \
                                
(1.0 - inv_range * (residue.next_avg_value -
                                  seq.min_avg_value)) + 1
                            prev_y_pos = y_pos + 0.5 * (prev_y_pos - y_pos)
                            next_y_pos = y_pos + 0.5 * (next_y_pos - y_pos)
                            painter.drawLine(x_position, prev_y_pos,
                                             x_position + 0.5 * cell_width,
                                             y_pos)
                            painter.drawLine(x_position + 0.5 * cell_width,
                                             y_pos, x_position + cell_width,
                                             next_y_pos)
                        x_position += cell_width
                        seq_position += 1 
[docs]    def cacheSSAImages(self):
        """
        Paints a secondary structure annotation row.
        """
        font_height = self.viewer.font_height
        cell_width = self.viewer.cell_width
        mid_pos = int(0.5 * font_height)
        half_height = int(0.25 * font_height)
        hh = half_height
        color_h = QtGui.QColor(constants.SS_COLOR_HELIX[0],
                               constants.SS_COLOR_HELIX[1],
                               constants.SS_COLOR_HELIX[2])
        color_e = QtGui.QColor(constants.SS_COLOR_EXTENDED[0],
                               constants.SS_COLOR_EXTENDED[1],
                               constants.SS_COLOR_EXTENDED[2])
        color_c = self.viewer.inv_background_color
        # QtGui.QColor(
        #    constants.SS_COLOR_COIL[0],
        #    constants.SS_COLOR_COIL[1],
        #    constants.SS_COLOR_COIL[2])
        self.ssa_image_helix_start = QtGui.QImage(cell_width, font_height,
                                                  QtGui.QImage.Format_ARGB32)
        self.ssa_image_helix_start.fill(QtGui.qRgba(0, 0, 0, 0))
        painter = QtGui.QPainter(self.ssa_image_helix_start)
        painter.setPen(color_h)
        painter.setBrush(QtGui.QBrush(color_h))
        painter.drawRect(half_height, mid_pos - half_height,
                         cell_width - half_height, 2 * half_height)
        painter.setPen(color_h.darker())
        painter.drawLine(half_height, mid_pos - half_height, cell_width,
                         mid_pos - half_height)
        painter.drawLine(half_height, mid_pos + half_height, cell_width,
                         mid_pos + half_height)
        painter.setPen(color_h.darker())
        painter.setBrush(QtGui.QBrush(QtCore.Qt.white))
        painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
        painter.drawEllipse(0, mid_pos - half_height + 1.0,
                            2.0 * half_height - 1.0, 2.0 * half_height - 1.0)
        painter.setRenderHint(QtGui.QPainter.Antialiasing, False)
        painter.setPen(color_c)
        painter.drawLine(0, mid_pos, half_height, mid_pos)
        painter.end()
        self.ssa_image_helix_mid = QtGui.QImage(cell_width, font_height,
                                                QtGui.QImage.Format_ARGB32)
        self.ssa_image_helix_mid.fill(QtGui.qRgba(0, 0, 0, 0))
        painter = QtGui.QPainter(self.ssa_image_helix_mid)
        painter.setPen(color_h)
        painter.setBrush(QtGui.QBrush(color_h))
        painter.drawRect(0, mid_pos - half_height, cell_width, 2 * half_height)
        painter.setPen(color_h.darker())
        painter.drawLine(0, mid_pos - half_height, cell_width,
                         mid_pos - half_height)
        painter.drawLine(0, mid_pos + half_height, cell_width,
                         mid_pos + half_height)
        painter.end()
        self.ssa_image_helix_end = QtGui.QImage(cell_width, font_height,
                                                QtGui.QImage.Format_ARGB32)
        self.ssa_image_helix_end.fill(QtGui.qRgba(0, 0, 0, 0))
        painter = QtGui.QPainter(self.ssa_image_helix_end)
        painter.setPen(color_h.darker())
        painter.setBrush(QtGui.QBrush(color_h))
        painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
        painter.drawEllipse(cell_width - 2 * half_height,
                            mid_pos - half_height + 1.0,
                            2.0 * half_height - 1.0, 2.0 * half_height - 1.0)
        painter.setRenderHint(QtGui.QPainter.Antialiasing, False)
        painter.setPen(color_h)
        painter.drawRect(0, mid_pos - half_height + 1, cell_width - half_height,
                         2 * half_height - 2)
        painter.setPen(color_h.darker())
        painter.drawLine(0, mid_pos - half_height, cell_width - half_height,
                         mid_pos - half_height)
        painter.drawLine(0, mid_pos + half_height, cell_width - half_height,
                         mid_pos + half_height)
        painter.end()
        self.ssa_image_sheet_start = QtGui.QImage(cell_width, font_height,
                                                  QtGui.QImage.Format_ARGB32)
        self.ssa_image_sheet_start.fill(QtGui.qRgba(0, 0, 0, 0))
        painter = QtGui.QPainter(self.ssa_image_sheet_start)
        painter.setPen(color_e)
        painter.setBrush(QtGui.QBrush(color_e))
        painter.drawRect(0, mid_pos - hh, cell_width, 2 * hh)
        painter.setPen(color_e.darker())
        painter.drawLine(0, mid_pos - hh, cell_width, mid_pos - hh)
        painter.drawLine(0, mid_pos + hh, cell_width, mid_pos + hh)
        painter.drawLine(0, mid_pos - hh, 0, mid_pos + hh)
        painter.end()
        self.ssa_image_sheet_mid = QtGui.QImage(cell_width, font_height,
                                                QtGui.QImage.Format_ARGB32)
        self.ssa_image_sheet_mid.fill(QtGui.qRgba(0, 0, 0, 0))
        painter = QtGui.QPainter(self.ssa_image_sheet_mid)
        painter.setPen(color_e)
        painter.setBrush(QtGui.QBrush(color_e))
        painter.drawRect(0, mid_pos - hh, cell_width, 2 * hh)
        painter.setPen(color_e.darker())
        painter.drawLine(0, mid_pos - hh, cell_width - 1, mid_pos - hh)
        painter.drawLine(0, mid_pos + hh, cell_width - 1, mid_pos + hh)
        painter.end()
        self.ssa_image_sheet_end = QtGui.QImage(cell_width, font_height,
                                                QtGui.QImage.Format_ARGB32)
        self.ssa_image_sheet_end.fill(QtGui.qRgba(0, 0, 0, 0))
        painter = QtGui.QPainter(self.ssa_image_sheet_end)
        painter.setPen(color_e.darker())
        painter.setBrush(QtGui.QBrush(color_e))
        painter.drawLine(0, mid_pos, 0, mid_pos - half_height - 2)
        painter.drawLine(0, mid_pos, 0, mid_pos + half_height + 2)
        painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
        painter.drawLine(cell_width, mid_pos, 0, mid_pos - half_height - 2)
        painter.drawLine(cell_width, mid_pos, 0, mid_pos + half_height + 2)
        painter.drawPolygon(QtCore.QPoint(0, mid_pos - half_height - 2),
                            QtCore.QPoint(cell_width, mid_pos),
                            QtCore.QPoint(0, mid_pos + half_height + 2))
        painter.setRenderHint(QtGui.QPainter.Antialiasing, False)
        painter.setPen(color_e)
        painter.drawLine(0, mid_pos - half_height + 1, 0,
                         mid_pos + half_height - 1)
        painter.end() 
[docs]    def paintSequenceArea(self,
                          painter,
                          event,
                          clip=True,
                          custom_width=None,
                          custom_height=None):
        """
        Actually paints the sequence area widget.
        :note: This code should be re-factored. Each component of the sequence
            area should have its own painting method.
        :type painter: QPainter
        :param painter: target Qt painter
        :type event: QPaintEvent
        :param event: Qt paint event
        """
        my_font = self.viewer.font()
        test_str = 'X' * 100
        font_metrics = QtGui.QFontMetrics(my_font)
        ratio = 100.0 * float(self.viewer.cell_width) / \
            
float((font_metrics.width(test_str)) * 0.01)
        my_font.setLetterSpacing(QtGui.QFont.PercentageSpacing, ratio)
        painter.setFont(my_font)
        rows = self.viewer.rows
        font_width = self.viewer.font_width
        font_height = self.viewer.font_height
        font_xheight = self.viewer.font_xheight
        cell_width = self.viewer.cell_width
        width = self.width()
        height = self.height()
        if custom_width is not None:
            width = custom_width
        if custom_height is not None:
            height = custom_height
        if self.emphasized:
            painter.setPen(
                QtGui.QPen(QtGui.QColor(QtGui.qRgb(127, 127, 255)), 3))
        else:
            painter.setPen(
                QtGui.QPen(QtGui.QBrush(self.viewer.background_color), 1))
        painter.setBrush(QtGui.QBrush(self.viewer.background_color))
        if clip:
            if self.viewer.wrapped:
                painter.drawRect(1, 1, width - 19, height - 4)
                painter.setClipRect(3, 3, width - 19, height - 6)
            else:
                painter.drawRect(1, 1, width - 19, height - 19)
                painter.setClipRect(3, 3, width - 19, height - 19)
        painter.setPen(self.viewer.inv_background_color)
        # This flag is True if current zoom level allows for text drawing.
        draw_text = True
        # Calculate a horizontal text offset.
        x_offset = int(old_div((cell_width - font_width), 2))
        if x_offset < 0:
            x_offset = 0
            draw_text = False
        y_offset = 0
        self.viewer.updateStatusBar()
        sep_scale = old_div(float(self.viewer.separator_scale), 100.0)
        profile = self.viewer.sequence_group.profile
        if not profile:
            return
        reference = self.viewer.sequence_group.reference
        if not reference:
            reference = profile
        y_position = self.viewer.margin
        if self.viewer.has_header_row:
            header_height = 2.0 * self.viewer.margin + font_height
            painter.setPen(QtGui.QPen(QtGui.QBrush(QtCore.Qt.black), 1))
            painter.setBrush(QtGui.QBrush(QtGui.QColor(224, 224, 224)))
            painter.drawRect(0, 0, width, header_height)
            x_position = self.viewer.margin
            if self.viewer.display_boundaries:
                x_position += 6 * font_width
                painter.drawLine(x_position, 0, x_position, header_height)
            painter.setFont(self.viewer.header_font)
            painter.drawText(x_position + 3, font_height + self.viewer.margin,
                             "Sequence")
            if rows:
                seq, start, end, row_height = rows[0]
                x_position += (end - start) * font_width
                painter.drawLine(x_position, 0, x_position, header_height)
                x_position = width - self.viewer.margin - 6 * font_width
                if self.viewer.display_identity or \
                   
self.viewer.display_similarity or \
                   
self.viewer.display_homology or self.viewer.display_score:
                    txt = "ID%"
                    if self.viewer.display_similarity:
                        txt = "SIM%"
                    elif self.viewer.display_homology:
                        txt = "HOM%"
                    elif self.viewer.display_score:
                        txt = "SCORE"
                    painter.drawText(x_position + 3,
                                     font_height + self.viewer.margin, txt)
                    painter.drawLine(x_position, 0, x_position, header_height)
            y_position += header_height
            painter.setFont(my_font)
        row_idx = 0
        num_persistent_rows = 0
        if not self.viewer.wrapped:
            for row in rows:
                num_persistent_rows += 1
                # Draw a single row.
                seq, start, end, row_height = row
                if not (seq.type == constants.SEQ_RULER or \
                        
seq.global_sequence or seq == reference):
                    break
        top_row = self.viewer.top_row + num_persistent_rows
        for row in rows:
            row_idx += 1
            # Draw a single row.
            seq, start, end, row_height = row
            if self.viewer.wrapped or \
                
not (seq.type == constants.SEQ_RULER or
                     seq.global_sequence or
                     seq == reference):
                if row_idx < top_row and clip:
                    continue
            if y_position > height and clip:
                break
            # Draw the sequence only if it is visible.
            if seq and seq.visible:
                try:
                    row_position = y_position + font_height
                    x_position = self.viewer.margin
                    if self.viewer.display_boundaries:
                        x_position += 6 * font_width
                    seq_position = start
                    residues = seq.residues
                    if residues and len(residues) > 0:
                        if seq.type == constants.SEQ_CUSTOM:
                            self.paintRowText(painter, row, row_position,
                                              x_position, y_position, x_offset)
                        elif seq.type == constants.SEQ_SECONDARY:
                            self.paintRowBackground(painter,
                                                    row,
                                                    x_position,
                                                    y_position,
                                                    draw_text,
                                                    selection_only=True,
                                                    use_parent=True)
                            self.paintRowSSA(painter, row, row_position)
                        elif seq.type == constants.SEQ_ANNOTATION and \
                                
seq.annotation_type != constants.ANNOTATION_SSBOND and \
                                
seq.annotation_type != constants.ANNOTATION_LIGAND and \
                                
seq.annotation_type != constants.ANNOTATION_SSP and \
                                
seq.annotation_type != constants.ANNOTATION_ACC and \
                                
seq.annotation_type != constants.ANNOTATION_DIS and \
                                
seq.annotation_type != constants.ANNOTATION_DOM and \
                                
seq.annotation_type != constants.ANNOTATION_CCB and \
                                
seq.annotation_type != constants.ANNOTATION_RESNUM and \
                                
seq.annotation_type != constants.ANNOTATION_CUSTOM and \
                                
seq.annotation_type != constants.ANNOTATION_PFAM and \
                                
seq.plot_style != constants.PLOT_COLOR_BLOCKS:
                            self.paintRowBackground(painter,
                                                    row,
                                                    x_position,
                                                    y_position,
                                                    draw_text,
                                                    selection_only=True,
                                                    use_parent=True)
                            self.paintRowAnnotationPlot(painter, row,
                                                        row_position)
                        elif seq.annotation_type != constants.ANNOTATION_SSBOND and \
                                
seq.annotation_type != constants.ANNOTATION_CCB and \
                                
seq.annotation_type != constants.ANNOTATION_RESNUM:
                            # Draw row's background.
                            self.paintRowBackground(painter, row, x_position,
                                                    y_position, draw_text)
                            if draw_text:
                                self.paintRowText(painter, row, row_position,
                                                  x_position, y_position,
                                                  x_offset)
                    if seq.type == constants.SEQ_ANNOTATION and \
                       
(seq.annotation_type == constants.ANNOTATION_CCB or
                            seq.annotation_type == constants.ANNOTATION_SSBOND):
                        self.paintRowBackground(painter,
                                                row,
                                                x_position,
                                                y_position,
                                                draw_text,
                                                selection_only=True,
                                                use_parent=True)
                        self.paintRowSSBond(painter, row, row_position)
                    elif seq.type == constants.SEQ_CONSTRAINTS:
                        self.paintRowConstraints(painter, row, row_position)
                    elif seq.type == constants.SEQ_SEPARATOR and sep_scale > 0.0:
                        painter.setBrush(QtGui.QColor(QtCore.Qt.gray))
                        pen = QtGui.QPen(QtCore.Qt.gray)
                        pen.setStyle(QtCore.Qt.DotLine)
                        painter.setPen(pen)
                        ypos = y_position + int(
                            sep_scale * (font_height - font_xheight)) - 2
                        painter.drawLine(0, ypos, width, ypos)
                        painter.pen().setStyle(QtCore.Qt.SolidLine)
                    elif seq.type == constants.SEQ_LOGO:
                        self.paintRowLogo(painter, row, row_position)
                    elif seq.annotation_type == constants.ANNOTATION_RESNUM:
                        self.paintRowBackground(painter,
                                                row,
                                                x_position,
                                                y_position,
                                                draw_text,
                                                selection_only=True,
                                                use_parent=True)
                        painter.setPen(self.viewer.inv_background_color)
                        # Use a slightly smaller font size.
                        self.viewer.ruler_font.setLetterSpacing(
                            QtGui.QFont.AbsoluteSpacing, 0)
                        painter.setFont(self.viewer.ruler_font)
                        while seq_position < end and \
                                
seq_position < seq.parent_sequence.length():
                            position = seq_position + 1
                            pres = seq.parent_sequence.residues[seq_position]
                            resn = pres.num
                            if (not pres.is_gap) and (resn % 5 == 0):
                                res_s = str(resn) + pres.icode
                                res_s = res_s.rstrip()
                                if len(res_s) + seq_position < end:
                                    x_pos = x_position + x_offset
                                    if not draw_text:
                                        x_pos -= self.viewer.margin
                                    painter.drawText(x_pos, row_position - 2,
                                                     res_s)
                                    l = len(res_s)
                                    seq_position = seq_position + l
                                    x_position = x_position + l * cell_width
                            seq_position = seq_position + 1
                            x_position = x_position + cell_width
                        # Restore a previous font size.
                        painter.setFont(my_font)  # self.viewer.font())
                    elif seq.type == constants.SEQ_RULER:
                        # Draw a ruler row.
                        painter.setPen(self.viewer.inv_background_color)
                        # Use a slightly smaller font size.
                        self.viewer.ruler_font.setLetterSpacing(
                            QtGui.QFont.AbsoluteSpacing, 0)
                        painter.setFont(self.viewer.ruler_font)
                        while seq_position < end:
                            x_pos = x_position + x_offset
                            if not draw_text:
                                x_pos -= self.viewer.margin
                            position = seq_position + 1
                            if position == 1:
                                painter.drawText(x_pos, row_position, '1')
                                painter.drawLine(x_pos + 0.5 * font_width,
                                                 row_position + 3,
                                                 x_pos + 0.5 * font_width,
                                                 row_position + font_height)
                            elif position % 5:
                                if self.viewer.zoom_factor >= 1.0:
                                    painter.drawText(x_pos,
                                                     row_position + font_height,
                                                     '.')
                            elif position % 10:
                                if self.viewer.zoom_factor >= 0.5:
                                    painter.drawLine(
                                        x_pos + 0.5 * font_width,
                                        row_position + 0.5 * font_height,
                                        x_pos + 0.5 * font_width,
                                        row_position + font_height)
                                else:
                                    painter.drawText(x_pos,
                                                     row_position + font_height,
                                                     '.')
                            else:
                                if self.viewer.zoom_factor >= 0.5 or \
                                   
(self.viewer.zoom_factor >= 0.33
                                    and not (position % 20)) or \
                                   
(self.viewer.zoom_factor < 0.33
                                        and not (position % 50)):
                                    painter.drawText(x_pos, row_position,
                                                     str(position))
                                painter.drawLine(x_pos + 0.5 * font_width,
                                                 row_position + 3,
                                                 x_pos + 0.5 * font_width,
                                                 row_position + font_height)
                            seq_position = seq_position + 1
                            x_position = x_position + cell_width
                        # Restore a previous font size.
                        painter.setFont(my_font)  # self.viewer.font())
                    # Display right column.
                    if (seq.type == constants.SEQ_AMINO_ACIDS or
                            seq.type == constants.SEQ_CONSENSUS or
                        (seq.parent_sequence and
                         self.viewer.group_annotations)):
                        offset = 0
                        y_offset = -1
                        if seq.type != constants.SEQ_AMINO_ACIDS and \
                           
seq.type != constants.SEQ_CONSENSUS:
                            seq = seq.parent_sequence
                        self.viewer.original_font.setLetterSpacing(
                            QtGui.QFont.AbsoluteSpacing, 0)
                        painter.setFont(self.viewer.original_font)
                        if (seq.type == constants.SEQ_AMINO_ACIDS or
                            seq.type == constants.SEQ_CONSENSUS) and \
                           
not seq.parent_sequence and \
                           
(self.viewer.display_identity or
                                self.viewer.display_similarity or
                                self.viewer.display_homology or
                                self.viewer.display_score):
                            id_text = "  0%"
                            if self.viewer.display_identity:
                                if seq.identity:
                                    id_text = "%3.0f%%" % (100.0 * seq.identity)
                            elif self.viewer.display_similarity:
                                if seq.similarity:
                                    id_text = "%3.0f%%" % (100.0 *
                                                           seq.similarity)
                            elif self.viewer.display_homology:
                                if seq.homology:
                                    id_text = "%3.0f%%" % (100.0 * seq.homology)
                            else:
                                if seq.score:
                                    id_text = "%4.0f" % (seq.score)
                            painter.setPen(self.viewer.inv_background_color)
                            offset = 6 * font_width
                            painter.drawText(
                                width - offset,
                                y_position + font_height + y_offset, id_text)
                        if self.viewer.display_boundaries and \
                                
seq.isValidProtein():
                            start_id = "%5s" % seq.ungappedId(start, start, end)
                            end_id = " %s" % seq.ungappedId(
                                end - 1, start, end, backwards=True)
                            painter.setPen(self.viewer.inv_background_color)
                            painter.drawText(
                                self.viewer.margin + font_width - 2,
                                y_position + font_height + y_offset, start_id)
                            offset = 6 * font_width + (end - start) * cell_width
                            painter.drawText(
                                self.viewer.margin + offset,
                                y_position + font_height + y_offset, end_id)
                        painter.setFont(my_font)  # self.viewer.font())
                except:
                    print("Painter exception at row", row_idx, row, seq,
                          seq.name)
                    raise
                # Move the the next row position.
                if seq.type != constants.SEQ_SEPARATOR:
                    y_position = y_position + font_height * row_height
                else:
                    y_position = y_position + int(
                        sep_scale * font_height * row_height)
        self.paintCursor(painter)
        self.paintUserAnnotations(painter)
        self.paintAuxRectangle(painter)
        if self.emphasized:
            painter.setClipping(False)
            painter.setPen(QtGui.QPen(QtGui.QColor(QtGui.qRgb(0, 255, 255)), 1))
            painter.setBrush(QtGui.QBrush(QtCore.Qt.NoBrush))
            if self.viewer.wrapped:
                painter.drawRect(1, 1, width - 19, height - 4)
            else:
                painter.drawRect(1, 1, width - 19, height - 19) 
[docs]    def updateColumns(self, start, end):
        if start is None or end is None:
            return False
        if start > end:
            tmp = end
            end = start
            start = tmp
        if start < 0:
            start = 0
        if self.viewer:
            max_columns = self.viewer.maxColumns()
            if end > self.viewer.left_column + max_columns:
                self.viewer.sequence_area.horizontal_scroll_bar.setValue(
                    self.viewer.sequence_area.horizontal_scroll_bar.value() + 1)
                return True
            if start < self.viewer.left_column:
                self.viewer.sequence_area.horizontal_scroll_bar.setValue(
                    self.viewer.sequence_area.horizontal_scroll_bar.value() - 1)
                return True
        return False