"""
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 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)