Source code for gwsumm.plot.mixins

# -*- coding: utf-8 -*-
# Copyright (C) Duncan Macleod (2013)
#
# This file is part of GWSumm.
#
# GWSumm is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GWSumm is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GWSumm.  If not, see <http://www.gnu.org/licenses/>.

"""
"""

import re
import abc
import os.path
from io import StringIO

from lxml import etree

from gwdetchar.plot import texify

re_bit_label = re.compile(r'\[(?P<idx>.*)\] (?P<label>.*)')
re_source_label = re.compile(r'(?P<label>.*) \[(?P<flag>.*)\]')

HOVERSCRIPT = """
<script type="text/ecmascript">
<![CDATA[    function init(evt) {
      if ( window.svgDocument == null ) {
        svgDocument = evt.target.ownerDocument;
      }
      var labels = svgDocument.getElementsByClassName('mpl-label');
      for (var i=0; labels[i]; i++) {
        labels[i].setAttribute('visibility', 'hidden');
      }
    }

    function ShowLabel(obj) {
      var cur = obj.id.substr(obj.id.lastIndexOf('_')+1);
      var tip = svgDocument.getElementById('label_' + cur);
      tip.setAttribute('visibility',"visible")
    }

    function HideLabel(obj) {
      var cur = obj.id.substr(obj.id.lastIndexOf('_')+1);
      var tip = svgDocument.getElementById('label_' + cur);
      tip.setAttribute('visibility',"hidden")
    }]]>
  </script>

"""

HTML_WRAPPER = """
<html>
<body>
  <style>
    body { margin: 0 !important }
  </style>
</head>
<div>
<object type="image/svg+xml" data="%s"
Your browser cannot display this SVG
</object>
</div>
</body>
</html>
"""


[docs] class SvgMixin(object, metaclass=abc.ABCMeta): def __init__(self, *args, **kwargs): super(SvgMixin, self).__init__(*args, **kwargs) self.preview_labels = False
[docs] def finalize(self, outputfile=None, close=True, **savekwargs): if outputfile is None: outputfile = self.outputfile # make SVG if outputfile.endswith('.svg'): self.draw_svg(outputfile) # or continue as normal else: return super(SvgMixin, self).finalize( outputfile=outputfile, close=close, **savekwargs)
[docs] @abc.abstractmethod def draw_svg(self, outputfile): pass
[docs] def finalize_svg(self, tree, outputfile, script=None): if script: tree.insert(0, etree.XML(script)) etree.ElementTree(tree).write(outputfile) # write HTML wrapper html = self.outputfile.replace('.svg', '.html') with open(html, 'w') as f: f.write(HTML_WRAPPER % os.path.basename(self.outputfile)) return html
[docs] class DataLabelSvgMixin(SvgMixin):
[docs] def draw_svg(self, outputfile): for ax in self.plot.axes: for line in ax.lines: line.set_rasterized(True) # render image super(SvgMixin, self).finalize( outputfile=outputfile.replace('.svg', '.png'), close=False) # make new text labels for the channel names ax = self.plot.axes[0] leg = ax.legend_ texts = [] if leg is not None: for i, (text, line) in enumerate( zip(leg.get_texts(), leg.get_lines())): try: label, source = re_source_label.match( text.get_text()).groups() except (AttributeError, ValueError): continue channel = texify(str(source)) text.set_text(label) t2 = ax.text( 0.994, 1.02, channel, ha='right', va='bottom', fontsize=text.get_fontsize(), zorder=1000, transform=ax.transAxes, bbox={'facecolor': 'white', 'edgecolor': 'lightgray', 'pad': 10.}) text.set_gid('leg_text_%d' % i) line.set_gid('leg_patch_%d' % i) t2.set_gid('label_%d' % i) texts.append(t2) # tmp save f = StringIO() self.plot.save(f, format='svg') # parse svg tree, xmlid = etree.XMLID(f.getvalue()) tree.set('onload', 'init(evt)') # add effects for i in range(len(texts)): pl = xmlid['leg_patch_%d' % i] ll = xmlid['leg_text_%d' % i] tl = xmlid['label_%d' % i] pl.set('cursor', 'pointer') pl.set('onmouseover', "ShowLabel(this)") pl.set('onmouseout', "HideLabel(this)") ll.set('cursor', 'pointer') ll.set('onmouseover', "ShowLabel(this)") ll.set('onmouseout', "HideLabel(this)") tl.set('class', 'mpl-label') tl.set('visibility', 'hidden') return self.finalize_svg(tree, outputfile, script=HOVERSCRIPT)
[docs] class SegmentLabelSvgMixin(SvgMixin):
[docs] def draw_svg(self, outputfile): # render image super(SvgMixin, self).finalize(outputfile=StringIO(), close=False) ax = self.plot.axes[0] collections = [c for c in ax.collections if hasattr(c, '_ignore')] # reset labels labels = {} for i, t in enumerate(ax.get_yaxis().get_ticklabels()): text = t.get_text() if not text: continue x, y = t.get_position() m1 = re_bit_label.match(text) m2 = re_source_label.match(text) if m1: idx, label = m1.groups() t2 = ax.text(0.01, y, label, ha='left', fontsize=t.get_fontsize(), va='center', transform=t.get_transform()) t2.set_bbox({'alpha': 0.5, 'facecolor': 'white', 'edgecolor': 'none'}) t.set_text(idx) t.set_bbox(None) t.set_position((0, y)) t.set_ha('right') t.set_fontsize('14') labels[text] = (idx, t2) elif m2: label, flag = m2.groups() t2 = ax.text(x, y, text, ha='left', bbox=t._bbox.copy(), fontsize=t.get_fontsize(), va='center', transform=t.get_transform()) t.set_text(label) labels[text] = (label, t2) ax._insetlabels = None j = 0 for i, collection in enumerate(collections): try: idx, tickl = labels[collection.get_label()] except KeyError: continue else: labels[collection] = tickl collection.set_label(idx) if not tickl.get_gid(): tickl.set_gid('label_%d' % j) j += 1 collection.set_gid('collection_%d_%s' % (i, tickl.get_gid())) # render as and parse SVG f = StringIO() self.plot.save(f, format='svg') tree, xmlid = etree.XMLID(f.getvalue()) tree.set('onload', 'init(evt)') # set mouse events for visible labels for i, collection in enumerate(collections): try: tickl = labels[collection] except KeyError: continue cel = xmlid[collection.get_gid()] cel.set('cursor', 'pointer') cel.set('onmouseover', "ShowLabel(this)") cel.set('onmouseout', "HideLabel(this)") tel = xmlid[tickl.get_gid()] tel.set('class', 'mpl-label') if not self.preview_labels: tel.set('visibility', 'hidden') # ignore hover on text events for key in xmlid: if key.startswith('text_') or key.startswith('label_'): xmlid[key].set('style', 'pointer-events: none;') return self.finalize_svg(tree, outputfile, script=HOVERSCRIPT)