Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a188b6c

Browse files
committed
fix to empty datetime bug; add datetime tests
svn path=/trunk/matplotlib/; revision=7661
1 parent 6e9ba37 commit a188b6c

6 files changed

Lines changed: 163 additions & 97 deletions

File tree

lib/matplotlib/axes.py

Lines changed: 6 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2299,73 +2299,18 @@ def xaxis_date(self, tz=None):
22992299
23002300
*tz* is the time zone to use in labeling dates. Defaults to rc value.
23012301
"""
2302-
2303-
xmin, xmax = self.dataLim.intervalx
2304-
if xmin==0.:
2305-
# no data has been added - let's set the default datalim.
2306-
# We should probably use a better proxy for the datalim
2307-
# have been updated than the ignore setting
2308-
dmax = today = datetime.date.today()
2309-
dmin = today-datetime.timedelta(days=10)
2310-
self._process_unit_info(xdata=(dmin, dmax))
2311-
dmin, dmax = self.convert_xunits([dmin, dmax])
2312-
self.viewLim.intervalx = dmin, dmax
2313-
self.dataLim.intervalx = dmin, dmax
2314-
2315-
locator = self.xaxis.get_major_locator()
2316-
if not isinstance(locator, mdates.DateLocator):
2317-
locator = mdates.AutoDateLocator(tz)
2318-
self.xaxis.set_major_locator(locator)
2319-
2320-
# the autolocator uses the viewlim to pick the right date
2321-
# locator, but it may not have correct viewlim before an
2322-
# autoscale. If the viewlim is still zero..1, set it to the
2323-
# datalim and the autoscaler will update it on request
2324-
if self.viewLim.intervalx[0]==0.:
2325-
self.viewLim.intervalx = tuple(self.dataLim.intervalx)
2326-
locator.refresh()
2327-
2328-
formatter = self.xaxis.get_major_formatter()
2329-
if not isinstance(formatter, mdates.DateFormatter):
2330-
formatter = mdates.AutoDateFormatter(locator, tz)
2331-
self.xaxis.set_major_formatter(formatter)
2302+
# should be enough to inform the unit conversion interface
2303+
# dates are comng in
2304+
self.xaxis.update_units(datetime.date(2009,1,1))
23322305

23332306
def yaxis_date(self, tz=None):
23342307
"""Sets up y-axis ticks and labels that treat the y data as dates.
23352308
23362309
*tz* is the time zone to use in labeling dates. Defaults to rc value.
23372310
"""
2338-
ymin, ymax = self.dataLim.intervaly
2339-
if ymin==0.:
2340-
# no data has been added - let's set the default datalim.
2341-
# We should probably use a better proxy for the datalim
2342-
# have been updated than the ignore setting
2343-
dmax = today = datetime.date.today()
2344-
dmin = today-datetime.timedelta(days=10)
2345-
self._process_unit_info(ydata=(dmin, dmax))
2346-
2347-
dmin, dmax = self.convert_yunits([dmin, dmax])
2348-
self.viewLim.intervaly = dmin, dmax
2349-
self.dataLim.intervaly = dmin, dmax
2350-
2351-
2352-
locator = self.yaxis.get_major_locator()
2353-
if not isinstance(locator, mdates.DateLocator):
2354-
locator = mdates.AutoDateLocator(tz)
2355-
self.yaxis.set_major_locator(locator)
2356-
2357-
# the autolocator uses the viewlim to pick the right date
2358-
# locator, but it may not have correct viewlim before an
2359-
# autoscale. If the viewlim is still zero..1, set it to the
2360-
# datalim and the autoscaler will update it on request
2361-
if self.viewLim.intervaly[0]==0.:
2362-
self.viewLim.intervaly = tuple(self.dataLim.intervaly)
2363-
locator.refresh()
2364-
2365-
formatter = self.xaxis.get_major_formatter()
2366-
if not isinstance(formatter, mdates.DateFormatter):
2367-
formatter = mdates.AutoDateFormatter(locator, tz)
2368-
self.yaxis.set_major_formatter(formatter)
2311+
# should be enough to inform the unit conversion interface
2312+
# dates are comng in
2313+
self.yaxis.update_units(datetime.date(2009,1,1))
23692314

23702315
def format_xdata(self, x):
23712316
"""

lib/matplotlib/axis.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,19 @@ def set_data_interval(self):
666666
'Set the axis data limits'
667667
raise NotImplementedError('Derived must override')
668668

669+
def set_default_intervals(self):
670+
'set the default limits for the axis data and view interval if they are not mutated'
671+
672+
# this is mainly in support of custom object plotting. For
673+
# example, if someone passes in a datetime object, we do not
674+
# know automagically how to set the default min/max of the
675+
# data and view limits. The unit conversion AxisInfo
676+
# interface provides a hook for custom types to register
677+
# default limits through the AxisInfo.default_limits
678+
# attribute, and the derived code below will check for that
679+
# and use it if is available (else just use 0..1)
680+
pass
681+
669682
def _set_artist_props(self, a):
670683
if a is None: return
671684
a.set_figure(self.figure)
@@ -1010,6 +1023,7 @@ def _update_axisinfo(self):
10101023
self.set_label_text(info.label)
10111024
self.isDefault_label = True
10121025

1026+
self.set_default_intervals()
10131027

10141028
def have_units(self):
10151029
return self.converter is not None or self.units is not None
@@ -1420,6 +1434,25 @@ def set_data_interval(self, vmin, vmax, ignore=False):
14201434
self.axes.dataLim.intervalx = min(vmin, Vmin), max(vmax, Vmax)
14211435

14221436

1437+
def set_default_intervals(self):
1438+
'set the default limits for the axis interval if they are not mutated'
1439+
xmin, xmax = 0., 1.
1440+
dataMutated = self.axes.dataLim.mutatedx()
1441+
viewMutated = self.axes.viewLim.mutatedx()
1442+
if not dataMutated or not viewMutated:
1443+
if self.converter is not None:
1444+
info = self.converter.axisinfo(self.units, self)
1445+
if info.default_limits is not None:
1446+
valmin, valmax = info.default_limits
1447+
xmin = self.converter.convert(valmin, self.units, self)
1448+
xmax = self.converter.convert(valmax, self.units, self)
1449+
if not dataMutated:
1450+
self.axes.dataLim.intervalx = xmin, xmax
1451+
if not viewMutated:
1452+
self.axes.viewLim.intervalx = xmin, xmax
1453+
1454+
1455+
14231456
class YAxis(Axis):
14241457
__name__ = 'yaxis'
14251458
axis_name = 'y'
@@ -1665,3 +1698,22 @@ def set_data_interval(self, vmin, vmax, ignore=False):
16651698
else:
16661699
Vmin, Vmax = self.get_data_interval()
16671700
self.axes.dataLim.intervaly = min(vmin, Vmin), max(vmax, Vmax)
1701+
1702+
def set_default_intervals(self):
1703+
'set the default limits for the axis interval if they are not mutated'
1704+
ymin, ymax = 0., 1.
1705+
dataMutated = self.axes.dataLim.mutatedy()
1706+
viewMutated = self.axes.viewLim.mutatedy()
1707+
if not dataMutated or not viewMutated:
1708+
if self.converter is not None:
1709+
info = self.converter.axisinfo(self.units, self)
1710+
if info.default_limits is not None:
1711+
valmin, valmax = info.default_limits
1712+
ymin = self.converter.convert(valmin, self.units, self)
1713+
ymax = self.converter.convert(valmax, self.units, self)
1714+
if not dataMutated:
1715+
self.axes.dataLim.intervaly = ymin, ymax
1716+
if not viewMutated:
1717+
self.axes.viewLim.intervaly = ymin, ymax
1718+
1719+

lib/matplotlib/dates.py

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,41 +1062,14 @@ class DateConverter(units.ConversionInterface):
10621062
def axisinfo(unit, axis):
10631063
'return the unit AxisInfo'
10641064
# make sure that the axis does not start at 0
1065-
if axis:
1066-
ax = axis.axes
1067-
1068-
if axis is ax.get_xaxis():
1069-
xmin, xmax = ax.dataLim.intervalx
1070-
if xmin==0.:
1071-
# no data has been added - let's set the default datalim.
1072-
# We should probably use a better proxy for the datalim
1073-
# have been updated than the ignore setting
1074-
dmax = today = datetime.date.today()
1075-
dmin = today-datetime.timedelta(days=10)
1076-
1077-
ax._process_unit_info(xdata=(dmin, dmax))
1078-
dmin, dmax = ax.convert_xunits([dmin, dmax])
1079-
1080-
ax.viewLim.intervalx = dmin, dmax
1081-
ax.dataLim.intervalx = dmin, dmax
1082-
elif axis is ax.get_yaxis():
1083-
ymin, ymax = ax.dataLim.intervaly
1084-
if ymin==0.:
1085-
# no data has been added - let's set the default datalim.
1086-
# We should probably use a better proxy for the datalim
1087-
# have been updated than the ignore setting
1088-
dmax = today = datetime.date.today()
1089-
dmin = today-datetime.timedelta(days=10)
1090-
1091-
ax._process_unit_info(ydata=(dmin, dmax))
1092-
dmin, dmax = ax.convert_yunits([dmin, dmax])
1093-
1094-
ax.viewLim.intervaly = dmin, dmax
1095-
ax.dataLim.intervaly = dmin, dmax
10961065

10971066
majloc = AutoDateLocator(tz=unit)
10981067
majfmt = AutoDateFormatter(majloc, tz=unit)
1099-
return units.AxisInfo( majloc=majloc, majfmt=majfmt, label='' )
1068+
datemin = datetime.date(2000, 1, 1)
1069+
datemax = datetime.date(2010, 1, 1)
1070+
1071+
return units.AxisInfo( majloc=majloc, majfmt=majfmt, label='',
1072+
default_limits=(datemin, datemax))
11001073

11011074
@staticmethod
11021075
def convert(value, unit, axis):

lib/matplotlib/tests/test_dates.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import datetime
2+
import numpy as np
3+
import matplotlib
4+
matplotlib.use('Agg')
5+
from matplotlib.testing.decorators import image_comparison
6+
import matplotlib.pyplot as plt
7+
8+
@image_comparison(baseline_images=['empty_datetime.png'])
9+
def test_empty_datetime():
10+
# make sure mpl does the right thing when told to plot dates even
11+
# if no date data has been presented, cf
12+
# http://sourceforge.net/tracker/?func=detail&aid=2850075&group_id=80706&atid=560720
13+
fig = plt.figure()
14+
ax = fig.add_subplot(1,1,1)
15+
ax.xaxis_date()
16+
fig.savefig('empty_datetime.png')
17+
18+
@image_comparison(baseline_images=['date_axhspan.png'])
19+
def test_date_axhspan():
20+
# test ax hspan with date inputs
21+
t0 = datetime.datetime(2009, 1, 20)
22+
tf = datetime.datetime(2009, 1, 21)
23+
fig = plt.figure()
24+
ax = fig.add_subplot(1,1,1)
25+
ax.axhspan( t0, tf, facecolor="blue", alpha=0.25 )
26+
ax.set_xlim(t0-datetime.timedelta(days=5),
27+
tf+datetime.timedelta(days=5))
28+
fig.autofmt_xdate()
29+
fig.savefig('date_axhspan.png')
30+
31+
@image_comparison(baseline_images=['date_axvspan.png'])
32+
def test_date_axvspan():
33+
# test ax hspan with date inputs
34+
t0 = datetime.datetime(2000, 1, 20)
35+
tf = datetime.datetime(2010, 1, 21)
36+
fig = plt.figure()
37+
ax = fig.add_subplot(1,1,1)
38+
ax.axvspan( t0, tf, facecolor="blue", alpha=0.25 )
39+
ax.set_xlim(t0-datetime.timedelta(days=5),
40+
tf+datetime.timedelta(days=5))
41+
fig.autofmt_xdate()
42+
fig.savefig('date_axvspan.png')
43+
44+
45+
@image_comparison(baseline_images=['date_axhline.png'])
46+
def test_date_axhline():
47+
# test ax hline with date inputs
48+
t0 = datetime.datetime(2009, 1, 20)
49+
tf = datetime.datetime(2009, 1, 31)
50+
fig = plt.figure()
51+
ax = fig.add_subplot(1,1,1)
52+
ax.axhline( t0, tf, facecolor="blue", lw=3)
53+
ax.set_xlim(t0-datetime.timedelta(days=5),
54+
tf+datetime.timedelta(days=5))
55+
fig.autofmt_xdate()
56+
fig.savefig('date_axhline.png')
57+
58+
@image_comparison(baseline_images=['date_axvline.png'])
59+
def test_date_axvline():
60+
# test ax hline with date inputs
61+
t0 = datetime.datetime(2000, 1, 20)
62+
tf = datetime.datetime(2010, 1, 21)
63+
fig = plt.figure()
64+
ax = fig.add_subplot(1,1,1)
65+
ax.axvline( t0, tf, facecolor="blue", lw=3)
66+
ax.set_xlim(t0-datetime.timedelta(days=5),
67+
tf+datetime.timedelta(days=5))
68+
fig.autofmt_xdate()
69+
fig.savefig('date_axvline.png')
70+
71+
72+
if __name__=='__main__':
73+
import nose
74+
nose.runmodule(argv=['-s','--with-doctest'], exit=False)
75+
76+

lib/matplotlib/transforms.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,10 @@ def __init__(self, points):
711711
self._points = np.asarray(points, np.float_)
712712
self._minpos = np.array([0.0000001, 0.0000001])
713713
self._ignore = True
714-
714+
# it is helpful in some contexts to know if the bbox is a
715+
# default or has been mutated; we store the orig points to
716+
# support the mutated methods
717+
self._points_orig = self._points.copy()
715718
if DEBUG:
716719
___init__ = __init__
717720
def __init__(self, points):
@@ -939,6 +942,21 @@ def set(self, other):
939942
self._points = other.get_points()
940943
self.invalidate()
941944

945+
def mutated(self):
946+
'return whether the bbox has changed since init'
947+
return self.mutatedx() or self.mutatedy()
948+
949+
def mutatedx(self):
950+
'return whether the x-limits have changed since init'
951+
return (self._points[0,0]!=self._points_orig[0,0] or
952+
self._points[1,0]!=self._points_orig[1,0])
953+
def mutatedy(self):
954+
'return whether the y-limits have changed since init'
955+
return (self._points[0,1]!=self._points_orig[0,1] or
956+
self._points[1,1]!=self._points_orig[1,1])
957+
958+
959+
942960

943961
class TransformedBbox(BboxBase):
944962
"""

lib/matplotlib/units.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,23 @@ def default_units(x, axis):
4646
from matplotlib.cbook import iterable, is_numlike, is_string_like
4747

4848
class AxisInfo:
49-
'information to support default axis labeling and tick labeling'
49+
'information to support default axis labeling and tick labeling, and default limits'
5050
def __init__(self, majloc=None, minloc=None,
51-
majfmt=None, minfmt=None, label=None):
51+
majfmt=None, minfmt=None, label=None,
52+
default_limits=None):
5253
"""
5354
majloc and minloc: TickLocators for the major and minor ticks
5455
majfmt and minfmt: TickFormatters for the major and minor ticks
5556
label: the default axis label
56-
57+
default_limits: the default min, max of the axis if no data is present
5758
If any of the above are None, the axis will simply use the default
5859
"""
5960
self.majloc = majloc
6061
self.minloc = minloc
6162
self.majfmt = majfmt
6263
self.minfmt = minfmt
6364
self.label = label
65+
self.default_limits = default_limits
6466

6567

6668
class ConversionInterface:

0 commit comments

Comments
 (0)