Source code for schrodinger.ui.sequencealignment.tree_node
"""
Implementation of multiple sequence viewer TreeNode class.
Copyright Schrodinger, LLC. All rights reserved.
"""
# Contributors: Piotr Rotkiewicz
[docs]class TreeNode:
"""
The TreeNode class is a simple implementation of tree structure
used to represent and display phylogenetic trees in the sequence viewer.
"""
[docs] def __init__(self, level):
self.branches = []
self.lengths = [] # of branches
self.sequences = [] # Associated sequences
self.name = None # of the node
self.level = level # node depth level
self.coordinates = None #Physical screen coords corresponding to node.
# When drawing the tree, we use a copy (visible subset) of the original
# tree. The node coordinates are stored in the original tree. Therefore
# we need to remember a pointer to the original tree node in order
# to retrieve the coordinates for mouse interactions.
#: Corresponding node in the original tree.
self.original_node = self
[docs] def maxLevel(self, level=0):
"""
Returns a maximum tree depth.
:type level: int (optional)
:param level: optional initial level
:rtype: int
:return: Maximum tree depth level.
"""
if self.level > level:
level = self.level
for branch in self.branches:
level = branch.maxLevel(level=level)
return level
[docs] def findNode(self, x, y, radius):
"""
Finds a tree node at a specified position within a specified radius.
:type x: int
:param x: x coordinate
:type y: int
:param y: y coordinate
:type radius: int
:param radius: node search radius (used as a Manhattan distance)
:rtype: `TreeNode`
:return: Tree node at (x, y) or None if node was not found there.
"""
if self.coordinates:
bx, by = self.coordinates
if x >= bx - radius and x <= bx + radius and \
y >= by - radius and y <= by + radius:
return self
found = None
for branch in self.branches:
if branch.coordinates:
bx, by = branch.coordinates
if x >= bx - radius and x <= bx + radius and \
y >= by - radius and y <= by + radius:
found = branch
break
if not found:
found = branch.findNode(x, y, radius)
return found
[docs] def selectSequences(self):
"""
Recursively select all sequences within self.
"""
for branch in self.branches:
for seq in branch.sequences:
seq.selected = True
branch.selectSequences()
[docs] def hideSequences(self):
"""
Recursively hide all sequences within self.
"""
for branch in self.branches:
for seq in branch.sequences:
seq.selected = False
seq.visible = False
branch.hideSequences()
[docs] def sequencesVisible(self):
"""
Return True if any sequence within self is visible.
:return: True if any sequence is visible
:rtype: boolean
"""
for seq in self.sequences:
if seq.visible:
return True
for branch in self.branches:
if branch.sequencesVisible():
return True
return False
[docs] def swap(self):
"""
Swaps tree branches.
"""
if self.branches:
for branch in self.branches:
branch.swap()
self.branches.reverse()
self.lengths.reverse()
self.sequences.reverse()
[docs] def getSequenceList(self, sequence_list):
"""
Builds a list of sequences by traversing the tree starting from self
and appending the sequences to the sequence list.
:type sequence_list: list of `Sequence`
:param sequence_list: list of sequences to be extended
:rtype: list of `Sequence`
:return: built list of sequences
"""
for seq in self.sequences:
if seq.visible:
sequence_list.append(seq)
for branch in self.branches:
branch.getSequenceList(sequence_list)
return sequence_list
[docs] def getVisibleTree(self):
"""
Return a subset of the tree that includes only the visible sequences.
This method is used to actually draw the tree with branches that are
partially hidden.
:rtype: `TreeNode`
:return: visible tree
"""
new_node = TreeNode(self.level)
for index, branch in enumerate(self.branches):
if branch.sequencesVisible():
new_node.branches.append(branch.getVisibleTree())
new_node.name = self.name
new_node.original_node = self
for seq in self.sequences:
if seq.visible:
new_node.sequences.append(seq)
return new_node
[docs] def buildSequenceList(self, sequence_group):
"""
Build a list of sequences using a given sequence group as a source.
Initially, the tree has no sequences associated with it. Every node
has a name corresposing to a particular sequence index in the sequence
group. This method finds these correspoding sequences in the sequence
group and adds them to the node's sequence list.
This method expects the sequence names to be in format used by
fileio.load_clustal_file function, i.e. in format SEQxxxx where xxxx
corresponds to a sequence index in the sequence group.
:type sequence_group: `SequenceGroup`
:param sequence_group: source sequence group
"""
self.sequences = []
if len(self.branches) > 0:
for branch in self.branches:
branch.buildSequenceList(sequence_group)
elif self.name:
index = int(self.name[3:])
if index >= 0 and index < len(sequence_group.sequences):
self.sequences.append(sequence_group.sequences[index])