Skip to content

Drawing Classes

In VARNA API, all drawing classes are inherited from the class BasicDraw. We offer three Python classes Structure, Comparison, and Motif for different RNA secondary structure visualisation. The first two correspond to the classic and the comparison mode in VARNA. The last one is the special case for motif drawing.

  • Structure: The most common drawing class taking a secondary structure and/or an RNA sequence as input.
  • Comparison: Comparison between two structures and two sequences.
  • Motif: Customized class to draw motif, an union of loops.

BasicDraw

Bases: _VarnaConfig

Source code in src/varnaapi/models.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
class BasicDraw(_VarnaConfig):
    def __init__(self):
        super().__init__()

        self.structure = ""
        self.aux_BPs = []
        self.highlight_regions = []
        self._title = None
        self.bases_styles = {}
        self.annotations = []
        self.chem_prob = []
        self.length = 0
        self.colormap = None
        self.to_flip = []
        self.smart_flip = False

    def add_aux_BP(self, i:int, j:int, edge5:str='wc', edge3:str='wc', stericity:str='cis', color='blue', thickness:float=1, **kwargs):
        """Add an additional base pair `(i,j)`, possibly defining and using custom style

        Args:
            i: 5' position of base pair
            j: 3' position of base pair
            edge5: Edge 5' used for interaction in non-canonical base-pairs, as defined by the Leontis/Westhof classification of base-pairs. Admissible values are __wc__ (Watson/Crick edge), __h__ (Hoogsteen edge) and __s__ (Sugar edge).
            edge3: Edge 3' used for interaction in non-canonical base-pairs. Admissible values are __wc__, __h__ and __s__.
            stericity: Orientation of the strands. Admissible values are __cis__ and __trans__
            color (color): Base-pair color
            thickness: Base-pair thickness
        """
        assert_valid_interval(self.length, i, j)

        self.aux_BPs.append((i, j, _BPStyle(edge5=edge5, edge3=edge3, stericity=stericity, color=color, thickness=thickness, **kwargs)))

    def add_highlight_region(self, i:int, j:int, radius:float=16, fill="#BCFFDD", outline="#6ED86E", **kwargs):
        """Highlights a region by drawing a polygon of predefined radius,
        fill color and outline color around it.
        A region consists in an interval from base `i` to base `j`.

        Args:
            i: 5'-end of the highlight
            j: 3'-end of the highlight
            radius: Thickness of the highlight
            fill (color): The color used to fill the highlight
            outline (color): The color used to draw the line around the highlight
        """
        assert_valid_interval(self.length, i, j)

        self.highlight_regions.append((i, j, _Highlight(radius, fill, outline, **kwargs)))

    def set_title(self, title:str, color='#000000', size:int=19, **kwargs):
        """Set title displayed at the bottom of the panel with color and font size
        """
        self._title = _Title(title, color, size, **kwargs)

    def add_bases_style(self, style:BasesStyle, bases:list):
        """Apply a [BasesStyle][varnaapi.param.BasesStyle] to a list of positions.
        If a position is assigned to more than one styles,
        one of them will be randomly used.

        Args:
            style: Style to apply
            bases: List of 0-indexed positions

        Examples:
            >>> v = varnaapi.Structure()
            >>> style1 = varnaapi.param.BasesStyle(fill="#FF0000")
            >>> style2 = varnaapi.param.BasesStyle(fill="#FFFF00" outline="#00FF00")
            >>> v.add_bases_style(style1, [1,2,4])
            >>> v.add_bases_style(setye1, [10,11,12])
            >>> v.add_bases_style(style2, [4,5,6,7])

        """
        if not isinstance(style, BasesStyle):
            raise Exception("style should be BasesStyle object")
        if len(bases) > 0:
            self.bases_styles[style] = self.bases_styles.get(style, set()).union({i for i in bases})

    def add_annotation(self, annotation:_Annotation):
        """Add an annotation.
        Argument should be a valid [Annotation](/style#annotation) object

        Examples:
            >>> v = varnaapi.Structure()
            >>> a = varnaapi.param.LoopAnnotation("L1", 6, color="#FF00FF")
            >>> v.add_annotation(a)
        """
        # Assert is annotation
        if not isinstance(annotation, _Annotation):
            raise Exception("Should be a valid annotation object")
        self.annotations.append(annotation)

    def add_chem_prob(self, base:int, glyph:str='arrow', dir:str='in', intensity:float=1, color='#0000B2', **kwargs):
        """Add chemical probing annotation on two adjacent bases.

        Args:
            base: index of the first base of adjacent bases
            glyph: Shape of the annotation chosen from ['arrow', 'dot', 'pin', 'triangle']
            dir: Direction of the annotation chosen from ['in', 'out']
            intensity: Annotation intensity, _i.e._ thickness
            color (color): Color used to draw the annotation
        """
        assert_valid_interval(self.length, base)
        self.chem_prob.append((int(base), _ChemProb(glyph=glyph, dir=dir, intensity=intensity, color=color, **kwargs)))

    def add_colormap(self, values, vMin:float=None, vMax:float=None, caption:str="", style="energy", **kwargs):
        """Add color map on bases.

        Args:
            values (float list): list of values in float for each base. `0`s are added at the end of the list if the list length is shorter than the number of bases.
            vMin: Minium value for the color map
            vMax: Maximum value for the color map
            caption: Color map caption
            style: Color map style, which is one of the following

                - predefined style from

                    ['red', 'blue', 'green', 'heat', 'energy', 'bw']

                - customized style in dictionary {value: color}
        """
        self.colormap = _ColorMap(values, vMin, vMax, caption, style, **kwargs)

    def flip(self, *positions):
        """Flip one or more helices identfied by given positions.

        Note: Behind the flip
            For a given base or basepair, VARNA flips the helix the base or the basepair belongs to by identifying first the farest position at 5' and then redrawing the helix in the counter direction from that position.
            By default, VARNA positions bases in clockwise direction, therefore repositioning bases in counter clockwise direction gives the effect of flip.
            Such flipping rule gives the following results:

                1. No flip will happen if given position is unpaired.
                2. Giving even number of positions of the same helix cancels out the flip.
                3. Consider two helices separated by a loop. Giving the position of the first helix flips both helices as one. However, giving the position of the second helix will result the flipping of only the second one, which may cause two helices overlap in the drawing.
                4. In linear drawing mode, flipping will not draw basepair arcs in lower plane as if affects bases positioning.

        Args:
            positions: either a base in integer or a basepair in integer tuple of the helix to flip

        Examples:
            Consider secondary structure
            ```
                    ...(((...)))...((...))...(((...)))...
                    1234567890123456789012345678901234567
            ```
            One can flip the first and third branches by
            >>> v = varnaapi.Structure(structure=dbn)
            >>> v.flip(5, (27,33))

        __See Also:__ [BasicDraw.enable_smart_flip][varnaapi.BasicDraw.enable_smart_flip]
        """
        map(lambda x: assert_valid_interval(self.length, *(x if isinstance(x, tuple) else (x,))), positions)
        self.to_flip += positions


    def enable_smart_flip(self, enable:bool=True):
        """Enable to flip positions treating to address points 1-3 in flip().
        When enable, for each branch of exterior loop, VARNA API will send only the 5' most position to flip to VARNA if any position (unpaired included) of the branch is given by flip().

        Args:
            enable (bool): Enable or disable smart flip.

        __See Also:__ [BasicDraw.flip][varnaapi.BasicDraw.flip]
        """
        self.smart_flip = enable

    def _gen_command(self):
        """
        Return command to run VARNA
        """
        cmd = ['java', '-cp', varnaapi.settings.CONFIG['varnapath'], 'fr.orsay.lri.varna.applications.VARNAcmd']

        cmd += self._gen_input_cmd()

        cmd += ['-o', self.output]

        cmd += self._gen_param_cmd()

        # Title cmd
        if self._title is not None:
            cmd += self._title._to_cmd()

        # Aux Base pairs
        if len(self.aux_BPs) > 0:
            res = []
            for i, j, style in self.aux_BPs:
                s = "({},{})".format(i,j)
                setting = style._to_cmd(self.get_params(complete=True)['bp'])
                if not setting == "":
                    s += ":" + setting
                res.append(s)
            cmd += ["-auxBPs", ";".join(res)]

        # Highlight Region
        if len(self.highlight_regions) > 0:
            res = []
            for item in self.highlight_regions:
                s = "{}-{}".format(item[0], item[1])
                setting = item[2]._to_cmd()
                if not setting == "":
                    s += ":" + setting
                res.append(s)
            cmd += ['-highlightRegion', ';'.join(res)]

        # BasesStyles
        styles = {'fill': 'baseInner', 'outline': 'baseOutline', 'label': 'baseName', 'number': 'baseNum'}
        styles_dafault = {v: self.get_params().get(v) for v in styles.values() if v in self.get_params()}
        for ind, (style, bases) in enumerate(self.bases_styles.items()):
            s = style._to_cmd(**styles_dafault)
            if not s == "":
                cmd += ["-basesStyle{}".format(ind + 1), s]
                cmd += ["-applyBasesStyle{}on".format(ind + 1), ','.join(map(str, bases))]

        # Annotations
        if len(self.annotations) > 0:
            cmd += ["-annotations", ';'.join([t._to_cmd() for t in self.annotations])]

        # Chem Prob
        if len(self.chem_prob) > 0:
            res = []
            for i, style in self.chem_prob:
                s = "{}-{}".format(i, i+1)
                setting = style._to_cmd()
                if not setting == "":
                    s += ":" + setting
                res.append(s)
            cmd += ["-chemProb", ";".join(res)]

        # Color Map
        if self.colormap is not None:
            cmd += self.colormap._to_cmd()

        # flip
        if self.smart_flip:
            to_flip = _match_ext(self.format_structure(), self.to_flip)
        else:
            to_flip = self.to_flip
        if len(to_flip) > 0:
            cmd += ["-flip", ';'.join('-'.join(str(t) for t in (x if isinstance(x, tuple) else (x,))) for x in to_flip)]
        return cmd

    def format_structure(self):
        return self.structure

    def _gen_input_cmd(self):
        return []

    def savefig(self, output, show:bool=False):
        """Call VARNA to draw and store the paint in output

        Args:
            output: Output file name with extension is either png or svg
            show: Show the drawing

        Note:
            The argument `show` is used only in jupyter notebook
        """
        self.output = output
        cmd = self._gen_command()
        print(cmd)
        subprocess.run(cmd)

        if show:
            if output[-3:] == 'png':
                display(Image(filename=output))
            elif output[-3:] == 'svg':
                display(SVG(filename=output))
            else:
                raise Exception("File type should be either png or svg")

    def show(self, extension='png'):
        """Show the drawing
        Equivalent to `savefig(tmp, show=True)` where tmp is a temporary file
        """
        tmp = NamedTemporaryFile(suffix='.'+extension)
        self.savefig(tmp.name, show=True)

add_annotation(annotation)

Add an annotation. Argument should be a valid Annotation object

Examples:

>>> v = varnaapi.Structure()
>>> a = varnaapi.param.LoopAnnotation("L1", 6, color="#FF00FF")
>>> v.add_annotation(a)
Source code in src/varnaapi/models.py
159
160
161
162
163
164
165
166
167
168
169
170
171
def add_annotation(self, annotation:_Annotation):
    """Add an annotation.
    Argument should be a valid [Annotation](/style#annotation) object

    Examples:
        >>> v = varnaapi.Structure()
        >>> a = varnaapi.param.LoopAnnotation("L1", 6, color="#FF00FF")
        >>> v.add_annotation(a)
    """
    # Assert is annotation
    if not isinstance(annotation, _Annotation):
        raise Exception("Should be a valid annotation object")
    self.annotations.append(annotation)

add_aux_BP(i, j, edge5='wc', edge3='wc', stericity='cis', color='blue', thickness=1, **kwargs)

Add an additional base pair (i,j), possibly defining and using custom style

Parameters:

Name Type Description Default
i int

5' position of base pair

required
j int

3' position of base pair

required
edge5 str

Edge 5' used for interaction in non-canonical base-pairs, as defined by the Leontis/Westhof classification of base-pairs. Admissible values are wc (Watson/Crick edge), h (Hoogsteen edge) and s (Sugar edge).

'wc'
edge3 str

Edge 3' used for interaction in non-canonical base-pairs. Admissible values are wc, h and s.

'wc'
stericity str

Orientation of the strands. Admissible values are cis and trans

'cis'
color color

Base-pair color

'blue'
thickness float

Base-pair thickness

1
Source code in src/varnaapi/models.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
def add_aux_BP(self, i:int, j:int, edge5:str='wc', edge3:str='wc', stericity:str='cis', color='blue', thickness:float=1, **kwargs):
    """Add an additional base pair `(i,j)`, possibly defining and using custom style

    Args:
        i: 5' position of base pair
        j: 3' position of base pair
        edge5: Edge 5' used for interaction in non-canonical base-pairs, as defined by the Leontis/Westhof classification of base-pairs. Admissible values are __wc__ (Watson/Crick edge), __h__ (Hoogsteen edge) and __s__ (Sugar edge).
        edge3: Edge 3' used for interaction in non-canonical base-pairs. Admissible values are __wc__, __h__ and __s__.
        stericity: Orientation of the strands. Admissible values are __cis__ and __trans__
        color (color): Base-pair color
        thickness: Base-pair thickness
    """
    assert_valid_interval(self.length, i, j)

    self.aux_BPs.append((i, j, _BPStyle(edge5=edge5, edge3=edge3, stericity=stericity, color=color, thickness=thickness, **kwargs)))

add_bases_style(style, bases)

Apply a BasesStyle to a list of positions. If a position is assigned to more than one styles, one of them will be randomly used.

Parameters:

Name Type Description Default
style BasesStyle

Style to apply

required
bases list

List of 0-indexed positions

required

Examples:

>>> v = varnaapi.Structure()
>>> style1 = varnaapi.param.BasesStyle(fill="#FF0000")
>>> style2 = varnaapi.param.BasesStyle(fill="#FFFF00" outline="#00FF00")
>>> v.add_bases_style(style1, [1,2,4])
>>> v.add_bases_style(setye1, [10,11,12])
>>> v.add_bases_style(style2, [4,5,6,7])
Source code in src/varnaapi/models.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
def add_bases_style(self, style:BasesStyle, bases:list):
    """Apply a [BasesStyle][varnaapi.param.BasesStyle] to a list of positions.
    If a position is assigned to more than one styles,
    one of them will be randomly used.

    Args:
        style: Style to apply
        bases: List of 0-indexed positions

    Examples:
        >>> v = varnaapi.Structure()
        >>> style1 = varnaapi.param.BasesStyle(fill="#FF0000")
        >>> style2 = varnaapi.param.BasesStyle(fill="#FFFF00" outline="#00FF00")
        >>> v.add_bases_style(style1, [1,2,4])
        >>> v.add_bases_style(setye1, [10,11,12])
        >>> v.add_bases_style(style2, [4,5,6,7])

    """
    if not isinstance(style, BasesStyle):
        raise Exception("style should be BasesStyle object")
    if len(bases) > 0:
        self.bases_styles[style] = self.bases_styles.get(style, set()).union({i for i in bases})

add_chem_prob(base, glyph='arrow', dir='in', intensity=1, color='#0000B2', **kwargs)

Add chemical probing annotation on two adjacent bases.

Parameters:

Name Type Description Default
base int

index of the first base of adjacent bases

required
glyph str

Shape of the annotation chosen from ['arrow', 'dot', 'pin', 'triangle']

'arrow'
dir str

Direction of the annotation chosen from ['in', 'out']

'in'
intensity float

Annotation intensity, i.e. thickness

1
color color

Color used to draw the annotation

'#0000B2'
Source code in src/varnaapi/models.py
173
174
175
176
177
178
179
180
181
182
183
184
def add_chem_prob(self, base:int, glyph:str='arrow', dir:str='in', intensity:float=1, color='#0000B2', **kwargs):
    """Add chemical probing annotation on two adjacent bases.

    Args:
        base: index of the first base of adjacent bases
        glyph: Shape of the annotation chosen from ['arrow', 'dot', 'pin', 'triangle']
        dir: Direction of the annotation chosen from ['in', 'out']
        intensity: Annotation intensity, _i.e._ thickness
        color (color): Color used to draw the annotation
    """
    assert_valid_interval(self.length, base)
    self.chem_prob.append((int(base), _ChemProb(glyph=glyph, dir=dir, intensity=intensity, color=color, **kwargs)))

add_colormap(values, vMin=None, vMax=None, caption='', style='energy', **kwargs)

Add color map on bases.

Parameters:

Name Type Description Default
values float list

list of values in float for each base. 0s are added at the end of the list if the list length is shorter than the number of bases.

required
vMin float

Minium value for the color map

None
vMax float

Maximum value for the color map

None
caption str

Color map caption

''
style

Color map style, which is one of the following

  • predefined style from

    ['red', 'blue', 'green', 'heat', 'energy', 'bw']

  • customized style in dictionary {value: color}

'energy'
Source code in src/varnaapi/models.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
def add_colormap(self, values, vMin:float=None, vMax:float=None, caption:str="", style="energy", **kwargs):
    """Add color map on bases.

    Args:
        values (float list): list of values in float for each base. `0`s are added at the end of the list if the list length is shorter than the number of bases.
        vMin: Minium value for the color map
        vMax: Maximum value for the color map
        caption: Color map caption
        style: Color map style, which is one of the following

            - predefined style from

                ['red', 'blue', 'green', 'heat', 'energy', 'bw']

            - customized style in dictionary {value: color}
    """
    self.colormap = _ColorMap(values, vMin, vMax, caption, style, **kwargs)

add_highlight_region(i, j, radius=16, fill='#BCFFDD', outline='#6ED86E', **kwargs)

Highlights a region by drawing a polygon of predefined radius, fill color and outline color around it. A region consists in an interval from base i to base j.

Parameters:

Name Type Description Default
i int

5'-end of the highlight

required
j int

3'-end of the highlight

required
radius float

Thickness of the highlight

16
fill color

The color used to fill the highlight

'#BCFFDD'
outline color

The color used to draw the line around the highlight

'#6ED86E'
Source code in src/varnaapi/models.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
def add_highlight_region(self, i:int, j:int, radius:float=16, fill="#BCFFDD", outline="#6ED86E", **kwargs):
    """Highlights a region by drawing a polygon of predefined radius,
    fill color and outline color around it.
    A region consists in an interval from base `i` to base `j`.

    Args:
        i: 5'-end of the highlight
        j: 3'-end of the highlight
        radius: Thickness of the highlight
        fill (color): The color used to fill the highlight
        outline (color): The color used to draw the line around the highlight
    """
    assert_valid_interval(self.length, i, j)

    self.highlight_regions.append((i, j, _Highlight(radius, fill, outline, **kwargs)))

dump_param(filename)

Store style parameters into file

Parameters:

Name Type Description Default
filename

path to the file

required
Source code in src/varnaapi/param.py
627
628
629
630
631
632
633
634
def dump_param(self, filename):
    """Store style parameters into file

    Args:
        filename: path to the file
    """
    with open(filename, 'w') as f:
        yaml.dump(self.get_params(complete=True), f)

enable_smart_flip(enable=True)

Enable to flip positions treating to address points 1-3 in flip(). When enable, for each branch of exterior loop, VARNA API will send only the 5' most position to flip to VARNA if any position (unpaired included) of the branch is given by flip().

Parameters:

Name Type Description Default
enable bool

Enable or disable smart flip.

True

See Also: BasicDraw.flip

Source code in src/varnaapi/models.py
236
237
238
239
240
241
242
243
244
245
def enable_smart_flip(self, enable:bool=True):
    """Enable to flip positions treating to address points 1-3 in flip().
    When enable, for each branch of exterior loop, VARNA API will send only the 5' most position to flip to VARNA if any position (unpaired included) of the branch is given by flip().

    Args:
        enable (bool): Enable or disable smart flip.

    __See Also:__ [BasicDraw.flip][varnaapi.BasicDraw.flip]
    """
    self.smart_flip = enable

flip(*positions)

Flip one or more helices identfied by given positions.

Behind the flip

For a given base or basepair, VARNA flips the helix the base or the basepair belongs to by identifying first the farest position at 5' and then redrawing the helix in the counter direction from that position. By default, VARNA positions bases in clockwise direction, therefore repositioning bases in counter clockwise direction gives the effect of flip. Such flipping rule gives the following results:

1. No flip will happen if given position is unpaired.
2. Giving even number of positions of the same helix cancels out the flip.
3. Consider two helices separated by a loop. Giving the position of the first helix flips both helices as one. However, giving the position of the second helix will result the flipping of only the second one, which may cause two helices overlap in the drawing.
4. In linear drawing mode, flipping will not draw basepair arcs in lower plane as if affects bases positioning.

Parameters:

Name Type Description Default
positions

either a base in integer or a basepair in integer tuple of the helix to flip

()

Examples:

Consider secondary structure

        ...(((...)))...((...))...(((...)))...
        1234567890123456789012345678901234567
One can flip the first and third branches by

>>> v = varnaapi.Structure(structure=dbn)
>>> v.flip(5, (27,33))

See Also: BasicDraw.enable_smart_flip

Source code in src/varnaapi/models.py
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
def flip(self, *positions):
    """Flip one or more helices identfied by given positions.

    Note: Behind the flip
        For a given base or basepair, VARNA flips the helix the base or the basepair belongs to by identifying first the farest position at 5' and then redrawing the helix in the counter direction from that position.
        By default, VARNA positions bases in clockwise direction, therefore repositioning bases in counter clockwise direction gives the effect of flip.
        Such flipping rule gives the following results:

            1. No flip will happen if given position is unpaired.
            2. Giving even number of positions of the same helix cancels out the flip.
            3. Consider two helices separated by a loop. Giving the position of the first helix flips both helices as one. However, giving the position of the second helix will result the flipping of only the second one, which may cause two helices overlap in the drawing.
            4. In linear drawing mode, flipping will not draw basepair arcs in lower plane as if affects bases positioning.

    Args:
        positions: either a base in integer or a basepair in integer tuple of the helix to flip

    Examples:
        Consider secondary structure
        ```
                ...(((...)))...((...))...(((...)))...
                1234567890123456789012345678901234567
        ```
        One can flip the first and third branches by
        >>> v = varnaapi.Structure(structure=dbn)
        >>> v.flip(5, (27,33))

    __See Also:__ [BasicDraw.enable_smart_flip][varnaapi.BasicDraw.enable_smart_flip]
    """
    map(lambda x: assert_valid_interval(self.length, *(x if isinstance(x, tuple) else (x,))), positions)
    self.to_flip += positions

get_params(complete=False)

Get parameters with value in dictionary By default, only the parameters with value different than the default are returned. Set complete to True to get complete parameters.

Parameters:

Name Type Description Default
complete bool

Return complete parameters.

False
Source code in src/varnaapi/param.py
576
577
578
579
580
581
582
583
584
585
586
587
588
def get_params(self, complete:bool=False):
    """Get parameters with value in dictionary
    By default, only the parameters with value different than the default are returned.
    Set complete to True to get complete parameters.

    Args:
        complete: Return complete parameters.
    """
    if complete:
        param = self._params.copy()
    else:
        param = self._diff_param()
    return param

load_param(filename)

Load existing style parameters from file

Parameters:

Name Type Description Default
filename

path to the file

required
Source code in src/varnaapi/param.py
636
637
638
639
640
641
642
643
def load_param(self, filename):
    """Load existing style parameters from file

    Args:
        filename: path to the file
    """
    with open(filename, 'r') as f:
        self.update(loaded=True, **yaml.load(f, Loader=yaml.Loader))

savefig(output, show=False)

Call VARNA to draw and store the paint in output

Parameters:

Name Type Description Default
output

Output file name with extension is either png or svg

required
show bool

Show the drawing

False
Note

The argument show is used only in jupyter notebook

Source code in src/varnaapi/models.py
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
def savefig(self, output, show:bool=False):
    """Call VARNA to draw and store the paint in output

    Args:
        output: Output file name with extension is either png or svg
        show: Show the drawing

    Note:
        The argument `show` is used only in jupyter notebook
    """
    self.output = output
    cmd = self._gen_command()
    print(cmd)
    subprocess.run(cmd)

    if show:
        if output[-3:] == 'png':
            display(Image(filename=output))
        elif output[-3:] == 'svg':
            display(SVG(filename=output))
        else:
            raise Exception("File type should be either png or svg")

set_algorithm(algo)

Set algorithm other than radiate to draw secondary structure. Supported options are line, circular, radiate and naview.

Source code in src/varnaapi/param.py
592
593
594
595
596
def set_algorithm(self, algo):
    """Set algorithm other than __radiate__ to draw secondary structure.
    Supported options are __line__, __circular__, __radiate__ and __naview__.
    """
    self.update(algorithm=algo)

set_bp_style(style)

Set default style for base-pairs rendering, chosen among BP_STYLES

Note: lw is set by default

Examples:

>>> v = varnaapi.Structure()
>>> v.set_bp_style("simple")
Source code in src/varnaapi/param.py
598
599
600
601
602
603
604
605
606
607
def set_bp_style(self, style):
    """Set default style for base-pairs rendering, chosen among [BP_STYLES][varnaapi.param.BP_STYLES]

    Note: `lw` is set by default

    Examples:
        >>> v = varnaapi.Structure()
        >>> v.set_bp_style("simple")
    """
    self.update(bpStyle=style)

set_title(title, color='#000000', size=19, **kwargs)

Set title displayed at the bottom of the panel with color and font size

Source code in src/varnaapi/models.py
131
132
133
134
def set_title(self, title:str, color='#000000', size:int=19, **kwargs):
    """Set title displayed at the bottom of the panel with color and font size
    """
    self._title = _Title(title, color, size, **kwargs)

show(extension='png')

Show the drawing Equivalent to savefig(tmp, show=True) where tmp is a temporary file

Source code in src/varnaapi/models.py
351
352
353
354
355
356
def show(self, extension='png'):
    """Show the drawing
    Equivalent to `savefig(tmp, show=True)` where tmp is a temporary file
    """
    tmp = NamedTemporaryFile(suffix='.'+extension)
    self.savefig(tmp.name, show=True)

update(loaded=False, **kwargs)

Easy way to update params value. The list of keyward arguments can be found here

Source code in src/varnaapi/param.py
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
def update(self, loaded=False, **kwargs):
    """Easy way to update params value.
    The list of keyward arguments can be found [here](config.md)
    """
    # Assert argument is in the parameter list and type check
    if loaded:
        params = self._loaded_params
    else:
        params = self._params
    for key, value in kwargs.items():
        if value is not None:
            try:
                if not varnaapi.settings.CONFIG['hackmode']:
                    assert key in PARAM_LIST
                value = _params_type_check(key, value)
                params[key] = value
            except AssertionError:
                print('{} is not a valid parameter name'.format(key))
                print('A valid argument is one of', ', '.join(PARAM_LIST))
            except TypeError as e:
                print(e)

Structure

Bases: BasicDraw

Classic VARNA drawing mode. Constructor from given RNA sequence or/and secondary structure. If sequence and structure have different size, the larger one is used and s or .s will be added to sequence or structure to complement.

Parameters:

Name Type Description Default
sequence

Raw nucleotide sequence for the displayed RNA. Each base must be encoded in a single character. Letters others than A, C, G, U and space are tolerated.

None
structure str or list

RNA (pseudoknotted) secondary structure in dbn

None
Source code in src/varnaapi/models.py
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
class Structure(BasicDraw):
    """Classic VARNA drawing mode. Constructor from given RNA sequence or/and secondary structure.
    If sequence and structure have different size, the larger one is used
    and ` `s or `.`s will be added to sequence or structure to complement.

    Args:
        sequence: Raw nucleotide sequence for the displayed RNA.
             Each base must be encoded in a single character.
             Letters others than `A`, `C`, `G`, `U` and space are tolerated.
        structure (str or list): RNA (pseudoknotted) secondary structure in dbn
    """
    def __init__(self, structure=None, sequence=None):
        super().__init__()

        self.length = -1
        self.structure = []
        self.sequence = ""

        if structure is not None:
            self.structure = structure
            # if isinstance(structure, list):
            #     if len(structure) > 0:
            #         first = structure[0]
            #         if len(first)==1:
            #             self.structure = check_structure(structure)
            #         elif len(first)==2:
            #             self.structure = _bp_to_struct(structure)
            #         else:
            #             raise Exception("Unrecognized structure format for %s"%(structure))
            # Dot-Bracket Notation
            self.length = len(self.structure)
        if sequence is not None:
            self.length = max(self.length,len(sequence))
            self.sequence = sequence
        # Now we know the length, let's extend the sequence and structure if necessary
        # self.sequence += " "*(self.length-len(self.sequence))
        # self.structure += [-1]*(self.length-len(self.structure))

    # def format_structure(self):
    #     """Return secondary structure in dot-brackaet notation
    #     """
    #     def greedy_fill(c1, c2, res, ss, i, j):
    #         if i <= j:
    #             k = ss[i]
    #             if k == -1:
    #                 greedy_fill(c1, c2, res, ss, i+1, j)
    #             elif k > i:
    #                 if k <= j:
    #                     res[i], res[k] = c1, c2
    #                     ss[i], ss[k] = -1, -1
    #                     greedy_fill(c1, c2, res, ss, i+1, k-1)
    #                     greedy_fill(c1, c2, res, ss, k+1, j)

    #     res = ["." for _ in range(self.length)]
    #     ss = self.structure[:]
    #     for c1, c2 in PARENTHESES_SYSTEMS:
    #         greedy_fill(c1, c2, res, ss, i=0, j=self.length-1)
    #         finished = True
    #         for i in ss:
    #             if i != -1:
    #                 finished = False
    #         if finished:
    #             break
    #     return "".join(res)

    def _gen_input_cmd(self):
        return ['-sequenceDBN', self.sequence, '-structureDBN', self.format_structure()]

    def __repr__(self):
        return repr((self.format_structure(), self.sequence))

Comparison

Bases: BasicDraw

Drawing of two aligned RNAs. Unlike classic Structure mode, both sequences and structures MUST be specified and have the same size. Additionally, the merged secondary structures must currently be without any crossing interactions (e.g. pseudoknots), and merging them should give a secondary structure. Gap character is .. Args: seq1 (str): Sets the gapped nucleotide sequence for the first RNA sequence structure1 (str): Sets the first secondary structure in Dot-Bracket Notation seq2 (str): Sets the gapped nucleotide sequence for the second sequence strcuture2 (str): Sets the second secondary structure in Doc-Bracket Notation

Source code in src/varnaapi/models.py
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
class Comparison(BasicDraw):
    """Drawing of two aligned RNAs.
    Unlike classic [Structure][varnaapi.Structure] mode,
    both sequences and structures __MUST__ be specified and have the same size.
    Additionally, the merged secondary structures must currently be without any crossing
    interactions (e.g. pseudoknots), and merging them should give a secondary structure.
    Gap character is `.`.
    Args:
        seq1 (str): Sets the gapped nucleotide sequence for the first RNA sequence
        structure1 (str): Sets the first secondary structure in Dot-Bracket Notation
        seq2 (str): Sets the gapped nucleotide sequence for the second sequence
        strcuture2 (str): Sets the second secondary structure in Doc-Bracket Notation
    """

    def __init__(self, seq1, structure1, seq2, structure2):
        if not (len(seq1) == len(structure1) == len(seq2) == len(structure2)):
            raise Exception("All length should be equal")
        super().__init__()

        self.seq1 = seq1
        self.structure1 = structure1
        self.seq2 = seq2
        self.structure2 = structure2
        self.length = len(seq1)

    def _gen_input_cmd(self):
        return ["-comparisonMode", str(True), "-firstSequence", self.seq1, "-firstStructure", self.structure1, "-secondSequence", self.seq2, "-secondStructure", self.structure2]

    def __repr__(self):
        return repr((self.seq1, self.structure1, self.seq2, self.structure2))

Motif

Bases: BasicDraw

Special class for motif drawing. A motif is a rooted ordered tree, similar to a secondary structure, but whose leaves may represent base paired positions, named open base pairs or open paired leaves and denoted by (*), and the root always represents a closing base pair. A motif can also be seen as an union of consecutive loops. The figure below represents ((*)(*)(((*)(*)))).

Motif class inherits from Structure with some pre-set parameters.

  • rotation is set at 180
  • default base pair style is simple
  • base number is hidden by setting default color to white (default background color)

A dummy base pair is added after each open base pair and in front of the root, as shown in the figure below. Therefore, the index of bases is changed after creating the object. For example, the index of first base of root is 1 instead of 0. The default bases style for root is BasesStyle(fill="#606060", outline="#FFFFFF", number="#FFFFFF") and BasesStyle(fill="#DDDDDD", outline="#FFFFFF", number="#FFFFFF") for dummy bases. One can change them using set_root_bases_style and set_dummy_bases_style.

Parameters:

Name Type Description Default
motif str

Motif in Dot-Bracket Notation. (*) is used to represent open base pair.

required
sequence str

Chain of characters for motif. Note that sequence should exactly match with motif, i.e. Equal length and same positions for all *.

None
Source code in src/varnaapi/models.py
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
class Motif(BasicDraw):
    """Special class for motif drawing.
    A motif is a rooted ordered tree, similar to a secondary structure,
    but whose leaves may represent base paired positions, named open base
    pairs or open paired leaves and denoted by `(*)`, and the root always
    represents a closing base pair. A motif can also be seen as an union
    of consecutive loops. The figure below represents `((*)(*)(((*)(*))))`.

    Motif class inherits from [Structure][varnaapi.Structure] with some pre-set
    parameters.

    - rotation is set at `180`
    - default base pair style is `simple`
    - base number is hidden by setting default color to white
    (default background color)

    A dummy base pair is added after each open base pair and in front of
    the root, as shown in the figure below.
    Therefore, the index of bases is changed after creating the object.
    For example, the index of first base of root is `1` instead of `0`.
    The default bases style for root is
    `BasesStyle(fill="#606060", outline="#FFFFFF", number="#FFFFFF")` and
    `BasesStyle(fill="#DDDDDD", outline="#FFFFFF", number="#FFFFFF")` for
    dummy bases. One can change them using
    [set_root_bases_style][varnaapi.Motif.set_root_bases_style] and
    [set_dummy_bases_style][varnaapi.Motif.set_dummy_bases_style].

    Args:
        motif (str): Motif in Dot-Bracket Notation.
            `(*)` is used to represent open base pair.
        sequence (str): Chain of characters for motif. Note that sequence
            should exactly match with motif, _i.e._ Equal length and same
            positions for all `*`.

    """
    def __init__(self, motif, sequence=None):
        super().__init__()

        seq = ""
        struct = ""
        extra_bps = []
        pos = 1
        for i, c in enumerate(motif):
            if c == "*":
                if sequence is not None and not sequence[i] == '*':
                    raise Exception("Motif and sequence are not compatible at position {}".format(i))
                extra_bps.append((pos + 1, pos + 2))
                seq += " & "
                struct += "(&)"
                pos += 2
            else:
                if sequence is not None:
                    w = sequence[i]
                else:
                    w = " "
                if w == '*':
                    raise Exception("Motif and sequence are not compatible at position {}".format(i))
                seq += w
                struct += c
                pos += 1
        seq = " " + seq + " "
        struct = "(" + struct + ")"
        self.sequence = seq
        self.structure = struct
        self.length = pos + 1
        extra_bps.append((1, self.length))
        self.extra_bps = extra_bps

        # Default Bases Styles
        self.rootBasesStyle = BasesStyle(fill="#606060", outline="#FFFFFF")
        self.dummyBasesStyle = BasesStyle(fill="#DDDDDD", outline="#FFFFFF")

        self.update(baseNum="#FFFFFF", bpStyle='simple', rotation=180)

    def _gen_input_cmd(self):
        return ["-sequenceDBN", self.sequence, "-structureDBN", self.structure]

    def set_dummy_bases_style(self, style):
        """Set style for dummy bases. Argument is a [BasesStyle][varnaapi.param.BasesStyle] object.
        """
        if not isinstance(style, BasesStyle):
            raise Exception('The argument should be BasesStyle object')
        self.dummyBasesStyle = style

    def set_root_bases_style(self, style):
        """Set style for root bases. Argument is a [BasesStyle][varnaapi.param.BasesStyle] object.
        """
        if not isinstance(style, BasesStyle):
            raise Exception('The argument should be BasesStyle object')
        self.rootBasesStyle = style

    def savefig(self, output, show=False):
        dummybps = []
        for (i,j) in self.extra_bps:
            dummybps += [i, j]
            self.add_aux_BP(i=i, j=j, color="#DDDDDD")
        self.add_aux_BP(i=2, j=self.length-1, color="#000000", thickness=2)

        self.add_bases_style(self.rootBasesStyle, [2, self.length-1])
        self.add_bases_style(self.dummyBasesStyle, dummybps)
        super().savefig(output, show)

set_dummy_bases_style(style)

Set style for dummy bases. Argument is a BasesStyle object.

Source code in src/varnaapi/models.py
544
545
546
547
548
549
def set_dummy_bases_style(self, style):
    """Set style for dummy bases. Argument is a [BasesStyle][varnaapi.param.BasesStyle] object.
    """
    if not isinstance(style, BasesStyle):
        raise Exception('The argument should be BasesStyle object')
    self.dummyBasesStyle = style

set_root_bases_style(style)

Set style for root bases. Argument is a BasesStyle object.

Source code in src/varnaapi/models.py
551
552
553
554
555
556
def set_root_bases_style(self, style):
    """Set style for root bases. Argument is a [BasesStyle][varnaapi.param.BasesStyle] object.
    """
    if not isinstance(style, BasesStyle):
        raise Exception('The argument should be BasesStyle object')
    self.rootBasesStyle = style

Motif ((*)(*)(((*)(*))))

Motif ((*)(*)(((*)(*))))

Example

Figure above is created with

from varnaapi import Motif
from varnaapi.param import BaseAnnotation
m = Motif("((*)(*)(((*)(*))))", sequence="  *AU* CC *  *    ")
m.add_annotation(BaseAnnotation(" Root", 2))
m.add_annotation(BaseAnnotation("Dummy", 14))
# Show how base indices work for motif.
# Remeber that VARNA is 1-indexed
m.set_default_color(baseNum="#a6a6a6")
m.set_numeric_params(periodNum=4)
m.savefig("motif_ex.png")