Source code for schrodinger.ui.sequencealignment.name_area

"""
This class implements a name area widget that performs actual display
and manipulation of sequence names.

Copyright Schrodinger, LLC. All rights reserved.
"""

# Contributors: Piotr Rotkiewicz

from past.utils import old_div

from schrodinger.Qt import QtCore
from schrodinger.Qt import QtGui
from schrodinger.Qt import QtWidgets
from schrodinger.ui.sequencealignment.sequence import Sequence

from . import constants
from .maestro import maestroExcludeAllEntrySequences
from .maestro import maestroIncludeAllEntrySequences
from .prime_gui import primeValidateBuildMode

try:
    from schrodinger.maestro import maestro
except:
    maestro = None


[docs]class NameArea(QtWidgets.QWidget): """ The NameArea widget displays and manipulates the sequence names. """ inclusionChanged = QtCore.pyqtSignal(Sequence)
[docs] def __init__(self, parent=None): # Initalize parent class. QtWidgets.QWidget.__init__(self, parent) self.font_size = 11 # of widget self.viewer = None #Parent sequence viewer widget. self.last_selected = False #most recently selected sequence. self.last_seq = None #previously selected sequence. self.setMouseTracking(True) #for hover tooltips. self.emphasized = False self.selection_changed = False self.setFocusPolicy(QtCore.Qt.WheelFocus) self.compact_sizes = [] #Temp list of splitter sizes self.last_row_index = 1 #Previous Y position
[docs] def setViewer(self, viewer): """ Sets a parent sequence viewer widget for this sequence area. """ self.viewer = viewer
[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 """ yoffset = 0 if self.viewer.has_header_row: yoffset = 20 return (old_div(x, self.viewer.font_width), old_div((y - self.viewer.margin - yoffset), self.viewer.font_height))
[docs] def mouseToRow(self, x, y): """ Translates mouse coordinates to a sequence viewer row. :type x: int :param x: mouse x coordinate :type y: int :param y: mouse y coordinate :rtype: row (`Sequence`, int, int, int) :return: pointed row """ rows = self.viewer.rows if rows is None or \ len(rows) == 0: return None row_idx = 0 current_row = 0 found = False coord_x, coord_y = self.mouseToCoords(x, y) reference = self.viewer.sequence_group.reference if not reference: reference = self.viewer.sequence_group.profile 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 if not found: row = None return row
[docs] def mousePressEvent(self, event): """ Handles Qt mouse press event. :type event: QMouseEvent :param event: Qt mouse event. """ x = event.x() y = event.y() row = self.mouseToRow(x, y) self.selection_changed = False if event.buttons() & QtCore.Qt.RightButton: seq = None if row: seq, start, end, height = row if seq.type == constants.SEQ_SEPARATOR or \ seq.type == constants.SEQ_RULER: return if not self.viewer.sequence_group.hasSelectedSequences(): self.viewer.sequence_group.unselectAllSequences() self.selection_changed = True seq.selected = True self.repaint() if self.viewer.name_popup_menu: self.leaveEvent(None) self.viewer.name_popup_menu(event.globalPos(), seq) return seq = None if row: self.last_row_index = self.viewer.rows.index(row) seq, start, end, height = row was_selected = seq.selected if not (event.modifiers() & QtCore.Qt.ShiftModifier) and \ not (event.modifiers() & QtCore.Qt.ControlModifier) and \ not (event.modifiers() & QtCore.Qt.AltModifier): self.viewer.sequence_group.unselectAllSequences() self.selection_changed = True self.repaint() if seq and not (event.modifiers() & QtCore.Qt.AltModifier): if seq.type != constants.SEQ_SEPARATOR and \ seq.type != constants.SEQ_RULER: if x < 4 + self.viewer.font_width: if seq.from_maestro and maestro: if seq.maestro_included: maestro.command("entrywsexclude entry \"" + str(seq.maestro_entry_id) + "\"") seq.maestro_included = False maestroExcludeAllEntrySequences( self.viewer.sequence_group, seq.maestro_entry_id) # maestroProjectChanged() else: maestro.command("entrywsinclude entry \"" + str(seq.maestro_entry_id) + "\"") seq.maestro_included = True maestroIncludeAllEntrySequences( self.viewer.sequence_group, seq.maestro_entry_id) # maestroProjectChanged() self.inclusionChanged.emit(seq) elif x < 4 + 4 * self.viewer.font_width: if not seq.parent_sequence: seq.collapsed = not seq.collapsed if seq.collapsed: seq.hideChildren() else: seq.showChildren() self.viewer.generateRows() else: if self.viewer.build_mode \ and self.viewer.sequence_group.build_mode == \ constants.PRIME_MODE_COMPOSITE \ and seq.has_structure \ and seq != self.viewer.sequence_group.reference: self.viewer.sequence_group.unmarkTemplateRegions() seq.selectAllResidues() self.viewer.sequence_group.markTemplateRegion() self.viewer.sequence_group.unselectAllSequences() elif (event.modifiers() & QtCore.Qt.ShiftModifier) and \ self.last_seq: if self.last_seq in \ self.viewer.sequence_group.sequences and \ seq in self.viewer.sequence_group.sequences: index1 = self.viewer.sequence_group.sequences.index( seq) index2 = self.viewer.sequence_group.sequences.index( self.last_seq) for index in range(min(index1, index2), max(index1, index2) + 1): seq = self.viewer.sequence_group.sequences[ index] if seq.type != constants.SEQ_RULER and \ seq.type != constants.SEQ_SEPARATOR: seq.selected = True self.selection_changed = True elif self.viewer.build_mode: if not (event.modifiers() & QtCore.Qt.ControlModifier): self.viewer.sequence_group.unselectAllSequences() seq.selected = not was_selected self.last_selected = seq.selected else: seq.selected = not was_selected self.last_selected = seq.selected self.selection_changed = True self.viewer.repaint()
[docs] def mouseReleaseEvent(self, event): """ Handles Qt mouse release event. :type event: QMouseEvent :param event: Qt mouse event. """ self.last_seq = None self.last_row_index = -1 x = event.x() y = event.y() row = self.mouseToRow(x, y) if row: seq, start, end, height = row self.last_seq = seq primeValidateBuildMode() if self.selection_changed: primeValidateBuildMode() if self.viewer.cb_sequence_selection_changed: self.viewer.cb_sequence_selection_changed()
[docs] def keyPressEvent(self, event): """ Handles Qt key press events. :type event: QKeyEvent :param event: Qt key press event """ if event.type() == QtCore.QEvent.KeyPress: key = event.key() if key == QtCore.Qt.Key_Up: self.viewer.moveUp() elif key == QtCore.Qt.Key_Down: self.viewer.moveDown()
[docs] def showToolTip(self, mouse_pos, sequence): """ Displays a mouse hover tooltip for a given sequence. :type mouse_pos: (int, int) :param mouse_pos: global mouse position :type sequence: `Sequence` :param sequence: sequence that the mouse hovers over. """ if not self.viewer.has_tooltips: return tip = "" if sequence: if sequence.parent_sequence: tip += sequence.parent_sequence.name + "\n" tip += sequence.name elif sequence.residues: tip += sequence.name + "\n" if sequence.chain_id > ' ': tip += "Chain: " + sequence.chain_id + "\n" if sequence.isValidProtein(): real_length = sequence.gaplessLength() tip += str(real_length) + " residue" if real_length != 1: tip += "s" n_gaps = sequence.length() - real_length if n_gaps > 0: tip += ", " + str(n_gaps) + " gap" if n_gaps != 1: tip += "s" if sequence.from_maestro: tip += "\nMaestro entry ID: " + \ str(sequence.maestro_entry_id) QtWidgets.QToolTip.showText(mouse_pos, tip) else: QtWidgets.QToolTip.hideText()
[docs] def getMaxWidth(self): """ Returns maximum width of the widget so all names will fit without being clipped. """ max_width = 0 for seq in self.viewer.sequence_group.sequences: name = seq.short_name if self.viewer.chain_name_only: name = seq.chain_id max_width = max(max_width, (len(name) + 6) * self.viewer.font_width) for child in seq.children: max_width = max(max_width, (len(child.short_name) + 6) * self.viewer.font_width) return max_width
[docs] def enterEvent(self, event): if not self.viewer.auto_resize: return if self.compact_sizes: return self.compact_sizes = self.parent().sizes() max_width = self.getMaxWidth() width_inc = max_width - self.compact_sizes[1] if width_inc > 0: self.parent().setSizes([ self.compact_sizes[0], self.compact_sizes[1] + width_inc, self.compact_sizes[2] - width_inc ])
[docs] def leaveEvent(self, event): if not self.viewer.auto_resize: return if self.compact_sizes: self.parent().setSizes(self.compact_sizes) self.compact_sizes = []
[docs] def mouseMoveEvent(self, event): """ Handles Qt mouse move event. :type event: QMouseEvent :param event: Qt mouse move event. """ x = event.x() y = event.y() row = self.mouseToRow(x, y) if self.viewer.build_mode and self.viewer.sequence_group.build_mode \ == constants.PRIME_MODE_COMPOSITE: return if event.buttons() & QtCore.Qt.LeftButton: if not (event.modifiers() & QtCore.Qt.ShiftModifier) and \ not (event.modifiers() & QtCore.Qt.ControlModifier) and \ not (event.modifiers() & QtCore.Qt.AltModifier): self.selection_changed = True self.viewer.sequence_group.unselectAllSequences() if row: seq, start, end, height = row row_index = self.viewer.rows.index(row) self.showToolTip(event.globalPos(), seq) if event.buttons() & QtCore.Qt.LeftButton: if self.last_row_index >= 0 and \ event.modifiers() & QtCore.Qt.AltModifier: if row_index > self.last_row_index: for x in range(int(row_index - self.last_row_index)): self.viewer.moveDown() else: for x in range(int(self.last_row_index - row_index)): self.viewer.moveUp() elif seq.residues and seq.type != constants.SEQ_SEPARATOR and \ seq.type != constants.SEQ_RULER: seq.selected = self.last_selected self.repaint() self.last_row_index = row_index
[docs] def wheelEvent(self, event): """ Handles Qt mouse wheel event. :type event: QWheelEvent :param event: Qt mouse wheel event. """ self.viewer.sequence_area.vertical_scroll_bar.wheelEvent(event) event.accept()
[docs] def paintEvent(self, event): """ Qt paint event handler for the name area. This method calls paintNameArea method that does actual painting. :type event: QPaintEvent :param event: Qt paint event """ painter = QtGui.QPainter(self) font = self.font() font.setLetterSpacing(QtGui.QFont.AbsoluteSpacing, 0) self.setFont(font) if constants.MSV_DEBUG: self.paintNameArea(painter, event) else: try: self.paintNameArea(painter, event) except: # This should never happen, however, just to be safe # we are ignoring the painting exception. pass
[docs] def paintNameArea(self, painter, event, clip=True, custom_width=None, custom_height=None): """ Actually paints the name area. :type painter: QPainter :param painter: Qt painter :type event: QPaintEvent :param event: Qt paint event """ rows = self.viewer.rows font_height = self.viewer.font_height font_width = self.viewer.font_width font_xheight = self.viewer.font_xheight 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: painter.drawRect(1, 1, width - 3, height - 3) painter.setClipRect(3, 3, width - 6, height - 6) my_font = painter.font() 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) painter.setFont(self.viewer.header_font) painter.drawText(3, font_height + self.viewer.margin, "Name") y_position += header_height painter.setFont(my_font) row_idx = 0 sel_color = QtGui.QColor(constants.SELECTION_COLOR[0], constants.SELECTION_COLOR[1], constants.SELECTION_COLOR[2]) reference = self.viewer.sequence_group.reference self.viewer.updateStatusBar() sep_scale = int(old_div(self.viewer.separator_scale, 100.0)) lsize = int(old_div(font_width, 3)) br = self.viewer.background_color.red() bg = self.viewer.background_color.green() bb = self.viewer.background_color.blue() row_idx = 0 num_persistent_rows = 0 previous_seq = None 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 # Display the sequence if it is visible. row_position = y_position + font_height if seq and seq.visible: if seq.last_hidden: painter.setPen(QtCore.Qt.red) painter.setBrush(QtGui.QBrush(QtCore.Qt.red)) path = QtGui.QPainterPath() path.moveTo(width - 5, y_position) path.lineTo(width - 15, y_position - 5) path.lineTo(width - 15, y_position + 5) path.lineTo(width - 5, y_position) painter.drawPath(path) if seq == reference or \ seq.parent_sequence and seq.parent_sequence == reference: painter.setFont(self.viewer.bold_font) name = seq.short_name if (not seq.parent_sequence) and seq.chain_id != ' ': name += "_" + seq.chain_id if (not seq.parent_sequence) and self.viewer.chain_name_only: name = seq.chain_id if self.viewer.group_annotations and seq.parent_sequence and \ seq.parent_sequence.type != constants.SEQ_CONSENSUS: name = seq.parent_sequence.short_name + " " + name if seq.parent_sequence and (not self.viewer.group_annotations or seq.parent_sequence.type == constants.SEQ_CONSENSUS): h_pos = 4 * font_width else: h_pos = 3 * font_width h_pos += 4 + font_width if seq.from_maestro: if seq.selected: painter.setPen(QtCore.Qt.white) else: r, g, b = seq.color painter.setPen(QtGui.QColor(r, g, b)) painter.drawText(h_pos, row_position - 1, name) if seq.selected: painter.setBrush(QtGui.QBrush(sel_color)) painter.setPen(sel_color) painter.drawRect(0, y_position + 2, width, row_height * font_height) painter.setPen(QtCore.Qt.white) painter.drawText(h_pos, row_position - 1, name) elif name: r, g, b = seq.color if 0.3 * abs(r - br) + 0.6 * abs(g - bg) + \ 0.1 * abs(b - bb) < 127: bcolor = QtGui.QColor(255 - br, 255 - bg, 255 - br) painter.setBrush(QtGui.QBrush(bcolor)) painter.setPen(bcolor) painter.drawRect(h_pos, y_position + 2, font_width * len(name), row_height * font_height) painter.setPen(QtCore.Qt.white) painter.drawText(h_pos, row_position - 1, name) painter.setPen(QtGui.QColor(r, g, b)) painter.drawText(h_pos, row_position - 1, name) if seq.from_maestro: painter.setPen(QtGui.QColor(127, 127, 127)) if seq.maestro_included: painter.setBrush( QtGui.QBrush(self.viewer.inv_background_color)) else: painter.setBrush( QtGui.QBrush(self.viewer.background_color)) y_off = old_div((font_height - font_width), 2) painter.drawRect(3, y_position + y_off, font_width, font_width) painter.setPen(self.viewer.inv_background_color) painter.drawLine(4, y_position + y_off + 1, 4 + font_width - 2, y_position + y_off + 1) painter.drawLine(4, y_position + y_off + 1, 4, y_position + y_off + font_width - 1) if seq.length() > 0: h_pos = 4 + font_width v_pos = row_position - font_xheight painter.setPen(self.viewer.inv_background_color) painter.setBrush(QtGui.QBrush(self.viewer.background_color)) if not self.viewer.group_annotations or \ seq.type == constants.SEQ_CONSENSUS or \ (seq.parent_sequence and seq.parent_sequence.type == constants.SEQ_CONSENSUS): if seq.parent_sequence and \ seq.annotation_type != constants.ANNOTATION_RESNUM: pen = painter.pen() pen.setStyle(QtCore.Qt.DotLine) pen.setWidth(1.0) painter.setPen(pen) if previous_seq: if len(previous_seq.children) > 0: painter.drawLine( h_pos + font_width + lsize, v_pos, h_pos + font_width + lsize, v_pos - font_height + lsize + 1) else: painter.drawLine( h_pos + font_width + lsize, v_pos, h_pos + font_width + lsize, v_pos - previous_seq.height * font_height) painter.drawLine(h_pos + font_width + lsize + 1, v_pos, h_pos + 4 * font_width - lsize - 1, v_pos) else: if len(seq.children) > 0: pen = painter.pen() pen.setStyle(QtCore.Qt.SolidLine) pen.setWidth(2.0) painter.setPen(pen) painter.drawLine( h_pos + font_width + 1, v_pos, h_pos + font_width + 2 * lsize + 1, v_pos) if seq.collapsed: painter.drawLine( h_pos + font_width + lsize + 1, v_pos - lsize, h_pos + font_width + lsize + 1, v_pos + lsize) if seq.type == constants.SEQ_SEPARATOR and sep_scale > 0.0: painter.setBrush( QtGui.QColor(self.viewer.inv_background_color)) 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) if seq == reference or \ seq.parent_sequence and seq.parent_sequence == reference: painter.setFont(self.viewer.font()) previous_seq = seq 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) 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)) painter.drawRect(1, 1, width - 3, height - 3)