def _draw_legend(self, data, lines, wordspacing, linespacing, *args, **kwargs): """ Draw a legend by iterating through all the lines. The labels are drawn on the same line as the corresponding token. :data: The text data as a dictionary. This is used to look for `label` attributes. :type data: dict :param lines: The drawn lines. :type lines: list of list of :class:`matplotlib.text.Text` :param wordspacing: The space between tokens. :type wordspacing: float :param linespacing: The space between lines. :type linespacing: float :return: A list of lines, each containing a list of labels on that line. :rtype: list of list of :class:`matplotlib.text.Text` """ labels = [] figure = self.drawable.figure axis = self.drawable.axis """ Iterate through each line, and then through each token in that line. """ drawn_labels = [] i = 0 for line, line_tokens in enumerate(lines): line_labels = [] for token in line_tokens: label, style = data[i].get('label', ''), data[i].get('style', { }) i += 1 """ If the token has a label associated with it, draw it the first time it appears. """ if label and label not in drawn_labels: drawn_labels.append(label) token = text_util.draw_token(figure, axis, label, 0, line, style, wordspacing, va='top', *args, **kwargs) line_labels.append(token) """ After drawing the labels on each line, re-align the legend. The labels are aligned to the right. They are reversed so that the first label appears on the left. """ util.align(figure, axis, line_labels[::-1], 'right', wordspacing * 4, (-1, - wordspacing * 4)) labels.append(line_labels) return labels
def _draw_tokens(self, tokens, x, y, wordspacing, lineheight, align, va, transform=None, *args, **kwargs): """ Draw the tokens on the plot. :param tokens: The text tokens to draw. The method expects a `list` of tokens, each one a `dict`. :type tokens: list of str :param x: The start and end x-position of the annotation. :type x: tuple :param y: The starting y-position of the annotation. :type y: float :param wordspacing: The space between words. If `None` is given, the space is calculated based on the height of the line. :type wordspacing: float or None :param lineheight: The space between lines. :type lineheight: float :param align: The text's alignment. Possible values: - left - center - right - justify - justify-start (or justify-left) - justify-center - justify-end or (justify-right) :type align: str :param va: The vertical alignment, can be one of `top` or `bottom`. If the vertical alignment is `bottom`, the annotation grows up. If the vertical alignment is `top`, the annotation grows down. :type va: str :param transform: The bounding box transformation. If `None` is given, the data transformation is used. :type transform: None or :class:`matplotlib.transforms.TransformNode` :return: The drawn lines. Each line is made up of the text tokens. :rtype: list of list of :class:`matplotlib.text.Text` """ figure = self.drawable.figure axis = self.drawable.axis transform = transform if transform is not None else axis.transData linespacing = util.get_linespacing( figure, axis, wordspacing, transform=transform, *args, ** kwargs) * lineheight if wordspacing is None: wordspacing = linespacing / 10. """ Go through each token and draw it on the axis. """ drawn_lines, line_tokens = [], [] offset = x[0] for token in tokens: """ Draw the text token. If the vertical alignment is top, the annotation grows downwards: one line after the other. If the vertical alignment is bottom, the annotation grows upwards. When the vertical alignment is bottom, new text is always added to the same place. New lines push previous lines up. Note that the center alignment is not considered here. There is no way of knowing how many lines there will be in advance. Therefore lines are centered at a later stage. """ va = 'top' if va == 'center' else va text = text_util.draw_token( figure, axis, token.get('text'), offset, y - len(drawn_lines) * linespacing if va == 'top' else y, token.get('style', {}), wordspacing, va=va, transform=transform, *args, **kwargs) line_tokens.append(text) """ If the token exceeds the x-limit, break it into a new line. The offset is reset to the left, and a new line is added. The token is moved to this new line. Lines do not break on punctuation marks. Note that lists are passed by reference. Therefore when the last token is removed from drawn lines when create a new line, the change is reflected here. """ bb = util.get_bb(figure, axis, text, transform=transform) if bb.x1 > x[1] and token.get('text') not in string.punctuation: self._newline(line_tokens, drawn_lines, linespacing, x[0], y, va, transform=transform) util.align(figure, axis, line_tokens, xpad=wordspacing, align=util.get_alignment(align), xlim=x, va=va, transform=transform) offset = x[0] line_tokens = [text] offset += bb.width + wordspacing """ Align the last line. """ drawn_lines.append(line_tokens) util.align(figure, axis, line_tokens, xpad=wordspacing, align=util.get_alignment(align, end=True), xlim=x, va=va, transform=transform) return drawn_lines