diff --git a/plotly/matplotlylib/mpltools.py b/plotly/matplotlylib/mpltools.py index bc000df894d..bf09791cb1e 100644 --- a/plotly/matplotlylib/mpltools.py +++ b/plotly/matplotlylib/mpltools.py @@ -7,6 +7,8 @@ import math import warnings +import datetime +import matplotlib.dates def check_bar_match(old_bar, new_bar): """Check if two bars belong in the same collection (bar chart). @@ -416,6 +418,17 @@ def prep_ticks(ax, index, ax_type, props): return dict() # get tick label formatting information formatter = axis.get_major_formatter().__class__.__name__ + if ax_type == 'x' and formatter == 'DateFormatter': + axis_dict['type'] = 'date' + try: + axis_dict['tick0'] = mpl_dates_to_datestrings(axis_dict['tick0']) + except KeyError: + pass + finally: + axis_dict.pop('dtick', None) + axis_dict.pop('autotick', None) + axis_dict['range'] = mpl_dates_to_datestrings(props['xlim']) + if formatter == 'LogFormatterMathtext': axis_dict['exponentformat'] = 'e' return axis_dict @@ -442,6 +455,22 @@ def prep_xy_axis(ax, props, x_bounds, y_bounds): yaxis.update(prep_ticks(ax, 1, 'y', props)) return xaxis, yaxis +def mpl_dates_to_datestrings(mpl_dates, format_string="%Y-%m-%d %H:%M:%S"): + try: + # make sure we have a list + mpl_date_list = list(mpl_dates) + epoch_times = matplotlib.dates.num2epoch(mpl_date_list) + date_times = [datetime.datetime.utcfromtimestamp(epoch_time) + for epoch_time in epoch_times] + time_strings = [date_time.strftime(format_string) + for date_time in date_times] + if len(time_strings) > 1: + return time_strings + else: + return time_strings[0] + except TypeError: + return mpl_dates + DASH_MAP = { '10,0': 'solid', diff --git a/plotly/matplotlylib/renderer.py b/plotly/matplotlylib/renderer.py index 21f13fdfac6..070b600ac0f 100644 --- a/plotly/matplotlylib/renderer.py +++ b/plotly/matplotlylib/renderer.py @@ -54,6 +54,7 @@ def __init__(self): self.bar_containers = None self.current_bars = [] self.axis_ct = 0 + self.x_is_mpl_date = False self.mpl_x_bounds = (0, 1) self.mpl_y_bounds = (0, 1) self.msg = "Initialized PlotlyRenderer\n" @@ -176,6 +177,10 @@ def open_axes(self, ax, props): self.plotly_fig['layout']['xaxis{0}'.format(self.axis_ct)] = xaxis self.plotly_fig['layout']['yaxis{0}'.format(self.axis_ct)] = yaxis + # let all subsequent dates be handled properly if required + if xaxis.get('type') == 'date': + self.x_is_mpl_date = True + def close_axes(self, ax): """Close the axes object and clean up. @@ -190,6 +195,7 @@ def close_axes(self, ax): """ self.draw_bars(self.current_bars) self.msg += " Closing axes\n" + self.x_is_mpl_date = False def draw_bars(self, bars): @@ -250,6 +256,9 @@ def draw_bar(self, coll): y = [bar['y1'] for bar in trace] bar_gap = mpltools.get_bar_gap([bar['x0'] for bar in trace], [bar['x1'] for bar in trace]) + if self.x_is_mpl_date: + x = [bar['x0'] for bar in trace] + x = mpltools.mpl_dates_to_datestrings(x) else: self.msg += " Attempting to draw a horizontal bar chart\n" old_rights = [bar_props['x1'] for bar_props in trace] @@ -357,6 +366,10 @@ def draw_marked_line(self, **props): yaxis='y{0}'.format(self.axis_ct), line=line, marker=marker) + if self.x_is_mpl_date: + marked_line['x'] = mpltools.mpl_dates_to_datestrings( + marked_line['x'] + ) self.plotly_fig['data'] += marked_line, self.msg += " Heck yeah, I drew that line\n" else: