[Python-modules-commits] [python-mplexporter] 73/135: PlotlyRenderer updated for 'text_type' annotation options.

Wolfgang Borgert debacle at moszumanska.debian.org
Tue Sep 23 21:19:05 UTC 2014


This is an automated email from the git hooks/post-receive script.

debacle pushed a commit to branch master
in repository python-mplexporter.

commit 7873bf6b756aa2fb5fdde92df31bfb33d9c9ad83
Author: theengineear <andseier at gmail.com>
Date:   Sun Mar 2 20:39:46 2014 -0800

    PlotlyRenderer updated for 'text_type' annotation options.
    
    PlotlyRenderer now basically uses the draw_text method as a switch statement acting on the 'text_type' key in the props dictionary. To allow for proper subplot titles, all axes titles from matplotlib are actually imported as general annotations. The xlabel and ylabel are still placed into the layout dictionary as xaxis and yaxis titles.
    
    Because plotly CENTERS text annotations when using data coordinates, all annotations are just reverted to 'paper' coordinates so that annotations don't move around unexpectedly. The goal right now is exact renderings of mpl figures.
    
    The function convert_to_paper was added to plotly_utils, it converts
    (x, y) pairs in mpl's 'display' (pixel) coordinates to plotly's 'paper' coordinates, which are fairly similar to mpl's 'figure' coordinates.
---
 mplexporter/renderers/plotly/plotly_renderer.py | 114 ++++++++++++++++++------
 mplexporter/renderers/plotly/plotly_utils.py    |  21 +++++
 2 files changed, 108 insertions(+), 27 deletions(-)

diff --git a/mplexporter/renderers/plotly/plotly_renderer.py b/mplexporter/renderers/plotly/plotly_renderer.py
index 926869f..bc5a56e 100644
--- a/mplexporter/renderers/plotly/plotly_renderer.py
+++ b/mplexporter/renderers/plotly/plotly_renderer.py
@@ -11,7 +11,6 @@ Attributes:
 """
 from . import plotly_utils
 from .. base import Renderer
-from ... import utils
 from ... exporter import Exporter
 
 
@@ -59,9 +58,18 @@ class PlotlyRenderer(Renderer):
         self.axis_ct = 0
 
     def open_figure(self, fig, props):
-        """Creates a new figure by beginning to fill out layout dict."""
+        """Creates a new figure by beginning to fill out layout dict.
+
+        Currently, margins are set to zero to reconcile differences between
+        mpl and plotly without complicated transforms. This will be changed
+        in future revisions. Autosize is set to false so that the figure will
+        mirror sizes set by mpl.
+
+        """
         self.layout['width'] = int(props['figwidth']*props['dpi'])
         self.layout['height'] = int(props['figheight']*props['dpi'])
+        self.layout['margin'] = {'l': 0, 'r': 0, 't': 0, 'b': 0, 'pad': 0}
+        self.layout['autosize'] = False
 
     def close_figure(self, fig):
         """Closes figure by cleaning up data and layout dictionaries.
@@ -105,30 +113,30 @@ class PlotlyRenderer(Renderer):
         """
         self.axis_ct += 1
         layout = {
-            'title': props['title'], # this will currently get overwritten!
+            # 'title': props['title'], # this will currently get overwritten!
             'xaxis{}'.format(self.axis_ct): {
                 'range': props['xlim'],
-                'title': props['xlabel'],
-                'showgrid': props['xgrid'],
+                # 'title': props['xlabel'],
+                'showgrid': props['axes'][1]['grid']['gridOn'],
                 'domain': plotly_utils.get_x_domain(props['bounds']),
                 'anchor': 'y{}'.format(self.axis_ct)
             },
             'yaxis{}'.format(self.axis_ct): {
                 'range': props['ylim'],
-                'title': props['ylabel'],
-                'showgrid': props['ygrid'],
+                # 'title': props['ylabel'],
+                'showgrid': props['axes'][0]['grid']['gridOn'],
                 'domain': plotly_utils.get_y_domain(props['bounds']),
                 'anchor': 'x{}'.format(self.axis_ct)
             }
         }
-        if props['xlabel'] not in [None, 'None', 'none', '']:
-            style = utils.get_style(ax.xaxis.get_label())
-            titlefont = {'size': style['fontsize'], 'color': style['color']}
-            layout['xaxis{}'.format(self.axis_ct)]['titlefont'] = titlefont
-        if props['ylabel'] not in [None, 'None', 'none', '']:
-            style = utils.get_style(ax.yaxis.get_label())
-            titlefont = {'size': style['fontsize'], 'color': style['color']}
-            layout['yaxis{}'.format(self.axis_ct)]['titlefont'] = titlefont
+        # if props['xlabel'] not in [None, 'None', 'none', '']:
+        #     style = utils.get_style(ax.xaxis.get_label())
+        #     titlefont = {'size': style['fontsize'], 'color': style['color']}
+        #     layout['xaxis{}'.format(self.axis_ct)]['titlefont'] = titlefont
+        # if props['ylabel'] not in [None, 'None', 'none', '']:
+        #     style = utils.get_style(ax.yaxis.get_label())
+        #     titlefont = {'size': style['fontsize'], 'color': style['color']}
+        #     layout['yaxis{}'.format(self.axis_ct)]['titlefont'] = titlefont
         for key, value in layout.items():
             self.layout[key] = value
 
@@ -216,23 +224,75 @@ class PlotlyRenderer(Renderer):
         """
         if 'annotations' not in self.layout:
             self.layout['annotations'] = []
+        if props['text_type'] == 'xlabel':
+            self.draw_xlabel(**props)
+        elif props['text_type'] == 'ylabel':
+            self.draw_ylabel(**props)
+        elif props['text_type'] == 'title':
+            self.draw_title(**props)
+        else:  # just a regular text annotation...
+            if True:  # props['coordinates'] is not 'data':
+                x_px, y_px = props['mplobj'].get_transform().transform(
+                    props['position'])
+                x, y = plotly_utils.convert_to_paper(x_px, y_px, self.layout)
+                xref = 'paper'
+                yref = 'paper'
+            else:
+                x, y = props['position']
+                xref = 'x{}'.format(self.axis_ct)
+                yref = 'y{}'.format(self.axis_ct)
+            annotation = {
+                'text': props['text'],
+                'x': x,
+                'y': y,
+                'xref': xref,
+                'yref': yref,
+                'font': {'color': props['style']['color'],
+                         'size': props['style']['fontsize']
+                },
+                'showarrow': False  # change this later?
+            }
+            self.layout['annotations'] += annotation,
+
+    def draw_title(self, **props):
+        """Add a title to the current subplot in layout dictionary.
+
+        Currently, titles are added as annotations.
+
+        """
+        # put x and y into mpl's 'display' coordinates
+        x_px, y_px = props['mplobj'].get_transform().transform(props[
+            'position'])
+        x, y = plotly_utils.convert_to_paper(x_px, y_px, self.layout)
         annotation = {
             'text': props['text'],
-            'font': {'color': props['style']['color'], 'size': props['style']['fontsize']},
-            'xref': 'x{}'.format(self.axis_ct),
-            'yref': 'y{}'.format(self.axis_ct),
-            'x': props['position'][0],
-            'y': props['position'][1],
-            'showarrow': False  # change this later?
+            'font': {'color': props['style']['color'],
+                     'size': props['style']['fontsize']
+            },
+            'xref': 'paper',
+            'yref': 'paper',
+            'x': x,
+            'y': y,
+            'showarrow': False  # no arrow for a title!
         }
-        if props['coordinates'] == 'points':
-            data_pos = self._current_ax.transData.inverted().transform(props['position'])
-            annotation['x'], annotation['y'] = data_pos[0], data_pos[1]
-        if props['coordinates'] == 'figure':
-            data_pos = self._current_ax.transFigure.inverted().transform(props['position'])
-            annotation['x'], annotation['y'] = data_pos[0], data_pos[1]
         self.layout['annotations'] += annotation,
 
+    def draw_xlabel(self, **props):
+        """Add an xaxis label to the current subplot in layout dictionary."""
+        self.layout['xaxis{}'.format(self.axis_ct)]['title'] = props['text']
+        titlefont = {'size': props['style']['fontsize'],
+                     'color': props['style']['color']
+        }
+        self.layout['xaxis{}'.format(self.axis_ct)]['titlefont'] = titlefont
+
+    def draw_ylabel(self, **props):
+        """Add a yaxis label to the current subplot in layout dictionary."""
+        self.layout['yaxis{}'.format(self.axis_ct)]['title'] = props['text']
+        titlefont = {'size': props['style']['fontsize'],
+                     'color': props['style']['color']
+        }
+        self.layout['yaxis{}'.format(self.axis_ct)]['titlefont'] = titlefont
+
 
 def fig_to_plotly(fig, username=None, api_key=None, notebook=False):
     """Convert a matplotlib figure to plotly dictionary and send.
diff --git a/mplexporter/renderers/plotly/plotly_utils.py b/mplexporter/renderers/plotly/plotly_utils.py
index 6a34b37..659ca39 100644
--- a/mplexporter/renderers/plotly/plotly_utils.py
+++ b/mplexporter/renderers/plotly/plotly_utils.py
@@ -24,6 +24,27 @@ def get_y_domain(bounds):
     return [bounds[1], bounds[1] + bounds[3]]
 
 
+def convert_to_paper(x, y, layout):
+    """Convert mpl display coordinates to plotly paper coordinates.
+
+    Plotly references object positions with an (x, y) coordinate pair in either
+    'data' or 'paper' coordinates which reference actual data in a plot or
+    the entire plotly axes space where the bottom-left of the bottom-left
+    plot has the location (x, y) = (0, 0) and the top-right of the top-right
+    plot has the location (x, y) = (1, 1). Display coordinates in mpl reference
+    objects with an (x, y) pair in pixel coordinates, where the bottom-left
+    corner is at the location (x, y) = (0, 0) and the top-right corner is at
+    the location (x, y) = (figwidth*dpi, figheight*dpi). Here, figwidth and
+    figheight are in inches and dpi are the dots per inch resolution.
+
+    """
+    num_x = x - layout['margin']['l']
+    den_x = layout['width'] - (layout['margin']['l'] + layout['margin']['r'])
+    num_y = y - layout['margin']['b']
+    den_y = layout['height'] - (layout['margin']['b'] + layout['margin']['t'])
+    return num_x/den_x, num_y/den_y
+
+
 def clean_dict(node, parent=None, node_key=None):
     """Remove None, 'none', 'None', and {} from a dictionary obj.
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-mplexporter.git



More information about the Python-modules-commits mailing list