Commit 01a5e779 authored by YAO Hua-Ting's avatar YAO Hua-Ting
Browse files

Merge branch 'feature/annotation' into 'master'


See merge request !1
parents ecdb281e 0c76bfcd
An `Annotation` object represents a textual annotation added to a VARNA drawing.
The object stores the text and other informtation needed.
One can add `Annotation` to drawing using [VARNA.add_annotation](
Four annotation types allowed in VARNA are represented by four objects below.
::: varnaapi
members: ["BaseAnnotation", "LoopAnnotation", "HelixAnnotation", "StaticAnnotation"]
::: varnaapi
filters: ["!^VARNA", "!^_", "__init__"]
filters: ["!^VARNA", "!^_", "__init__", "!Annotation"]
......@@ -8,6 +8,7 @@ theme:
- Overview:
- API:
- Annotation:
......@@ -19,6 +20,8 @@ plugins:
- "!^_"
- "^__init__$"
show_root_toc_entry: False
import re
import os
import abc
from string import ascii_lowercase, ascii_uppercase
from typing import Union
......@@ -127,6 +128,97 @@ class BasesStyle:
return ",".join(lst)
class _Annotation:
"""Basic Annotation
def __init__(self, text, type, color="#000000", size=10):
self.text = text
self.type = type
self.color = color
self.size = size #: int: font size
def asdict(self):
return {'text': self.text, 'type': self.type, 'color': self.color,
'size': self.size}
def to_cmd(self):
class _ObjectAnnotation(_Annotation):
def __init__(self, text, type, anchor, color="#000000", size=10):
super().__init__(text, type, color, size)
self.anchor = anchor
def asdict(self):
d = super().asdict()
d['anchor'] = self.anchor
return d
def to_cmd(self):
return "{text}:type={type},anchor={anchor},color={color},size={size}"\
class BaseAnnotation(_ObjectAnnotation):
def __init__(self, text:str, anchor:int, color="#000000", size=10):
"""Annoation on a base.
text: Annotation caption
anchor: Index of base to annotate
color (Hex): Annotation color
size (int): Font size
super().__init__(text, 'B', color, size)
class LoopAnnotation(_ObjectAnnotation):
"""Same as [BaseAnnotation][varnaapi.BaseAnnotation] but on a loop.
Argument `anchor` can be index of any base in the loop of interest.
def __init__(self, text, anchor, color="#000000", size=10):
super().__init__(text, 'L', anchor, color, size)
class HelixAnnotation(_ObjectAnnotation):
"""Same as [BaseAnnotation][varnaapi.BaseAnnotation] but on an helix.
Argument `anchor` can be index of any base in the helix of interest.
def __init__(self, text, anchor, color="#000000", size=10):
super().__init__(text, 'H', anchor, color, size)
class StaticAnnotation(_Annotation):
def __init__(self, text, x, y, color="#000000", size=10):
"""Annotation on a specified position in VARNA drawing.
Unlike [BaseAnnotation][varnaapi.BaseAnnotation], argument `anchor` is omitted.
However, arguments `x` and `y` are needed to specify annotation position.
__Note:__ It is unrecommended to use static annotation unless you know what you're doing
x (int): x-coordinate of position
y (int): y-ccordinate of position
>>> sa = StaticAnnotation("Hello World", 100, 150, color="#FF0000")
super().__init__(text, 'P', color, size)
self.x = x
self.y = y
def asdict(self):
d = super().asdict()
d['x'] = self.x
d['y'] = self.y
return d
def to_cmd(self):
return "{text}:type={type},x={x},y={y},color={color},size={size}"\
def is_hex_color(color):
match =
if match:
......@@ -202,6 +294,7 @@ class VARNA:
self.options = {}
self.title = None
self.bases_styles = {}
self.annotations = []
if structure is not None:
if isinstance(structure, list):
......@@ -416,7 +509,7 @@ class VARNA:
>>> style2 = BasesStyle(fill="#FFFF00" outline="#00FF00")
>>> varna.add_bases_style(style1, [0,2,4])
>>> varna.add_bases_style(setye1, [10,11,12])
>>> varna.aff_bases_style(style2, [4,5,6,7])
>>> varna.add_bases_style(style2, [4,5,6,7])
if not isinstance(style, BasesStyle):
......@@ -424,6 +517,20 @@ class VARNA:
if len(bases) > 0:
self.bases_styles[style] = self.bases_styles.get(style, set()).union({i+1 for i in bases})
def add_annotation(self, annotation:_Annotation):
"""Add an annotation.
Argument should be a valid [Annotation](/annotation/) object
>>> a = LoopAnnotation("L1", 6, color="#FF00FF")
>>> varna.add_annotation(a)
# Assert is annotation
if not isinstance(annotation, _Annotation):
raise Exception("Should be a valid annotation object")
def _gen_command(self):
Return command to run VARNA
......@@ -465,6 +572,9 @@ class VARNA:
cmd += " -basesStyle{} {}".format(ind+1, str(style))
cmd += " -applyBasesStyle{}on {}".format(ind+1, ','.join(map(str, bases)))
# Annotations
cmd += " -annotations \"{}\"".format(';'.join([t.to_cmd() for t in self.annotations]))
return cmd
def savefig(self, output):
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment