From: <ry...@us...> - 2009-10-21 18:09:58
|
Revision: 7898 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=7898&view=rev Author: ryanmay Date: 2009-10-21 18:09:45 +0000 (Wed, 21 Oct 2009) Log Message: ----------- Make AutoDateLocator more configurable by moving hard-coded behavior to a structure the user can tweak. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/dates.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2009-10-21 13:28:26 UTC (rev 7897) +++ trunk/matplotlib/CHANGELOG 2009-10-21 18:09:45 UTC (rev 7898) @@ -1,7 +1,13 @@ -2009-10-19 Add "path_effects" support for Text and Patch. See +2009-10-21 Make AutoDateLocator more configurable by adding options + to control the maximum and minimum number of ticks. Also + add control of the intervals to be used for ticking. This + does not change behavior but opens previously hard-coded + behavior to runtime modification`. - RMM + +2009-10-19 Add "path_effects" support for Text and Patch. See examples/pylab_examples/patheffect_demo.py -JJL -2009-10-19 Add "use_clabeltext" option to clabel. If True, clabels +2009-10-19 Add "use_clabeltext" option to clabel. If True, clabels will be created with ClabelText class, which recalculates rotation angle of the label during the drawing time. -JJL Modified: trunk/matplotlib/lib/matplotlib/dates.py =================================================================== --- trunk/matplotlib/lib/matplotlib/dates.py 2009-10-21 13:28:26 UTC (rev 7897) +++ trunk/matplotlib/lib/matplotlib/dates.py 2009-10-21 18:09:45 UTC (rev 7898) @@ -603,11 +603,79 @@ :class:`MultipleDateLocator` to set the view limits and the tick locations. """ - def __init__(self, tz=None): + def __init__(self, tz=None, minticks=5, maxticks=None, + interval_multiples=False): + """ + *minticks* is the minimum number of ticks desired, which is used to + select the type of ticking (yearly, monthly, etc.). + + *maxticks* is the maximum number of ticks desired, which controls + any interval between ticks (ticking every other, every 3, etc.). + For really fine-grained control, this can be a dictionary mapping + individual rrule frequency constants (YEARLY, MONTHLY, etc.) + to their own maximum number of ticks. This can be used to keep + the number of ticks appropriate to the format chosen in + class:`AutoDateFormatter`. Any frequency not specified in this + dictionary is given a default value. + + *tz* is a :class:`tzinfo` instance. + + *interval_multiples* is a boolean that indicates whether ticks + should be chosen to be multiple of the interval. This will lock + ticks to 'nicer' locations. For example, this will force the + ticks to be at hours 0,6,12,18 when hourly ticking is done at + 6 hour intervals. + + The AutoDateLocator has an interval dictionary that maps the + frequency of the tick (a constant from dateutil.rrule) and a + multiple allowed for that ticking. The default looks like this:: + + self.intervald = { + YEARLY : [1, 2, 4, 5, 10], + MONTHLY : [1, 2, 3, 4, 6], + DAILY : [1, 2, 3, 7, 14], + HOURLY : [1, 2, 3, 4, 6, 12], + MINUTELY: [1, 5, 10, 15, 30], + SECONDLY: [1, 5, 10, 15, 30] + } + + The interval is used to specify multiples that are appropriate for + the frequency of ticking. For instance, every 7 days is sensible + for daily ticks, but for minutes/seconds, 15 or 30 make sense. + You can customize this dictionary by doing:: + + locator = AutoDateLocator() + locator.intervald[HOURLY] = [3] # only show every 3 hours + """ DateLocator.__init__(self, tz) self._locator = YearLocator() self._freq = YEARLY + self._freqs = [YEARLY, MONTHLY, DAILY, HOURLY, MINUTELY, SECONDLY] + self.minticks = minticks + self.maxticks = {YEARLY : 16, MONTHLY : 12, DAILY : 11, HOURLY : 16, + MINUTELY : 11, SECONDLY : 11} + if maxticks is not None: + try: + self.maxticks.update(maxticks) + except TypeError: + # Assume we were given an integer. Use this as the maximum + # number of ticks for every frequency and create a + # dictionary for this + self.maxticks = dict(zip(self._freqs, + [maxticks]*len(self._freqs))) + self.interval_multiples = interval_multiples + self.intervald = { + YEARLY : [1, 2, 4, 5, 10], + MONTHLY : [1, 2, 3, 4, 6], + DAILY : [1, 2, 3, 7, 14], + HOURLY : [1, 2, 3, 4, 6, 12], + MINUTELY: [1, 5, 10, 15, 30], + SECONDLY: [1, 5, 10, 15, 30] + } + self._byranges = [None, range(1, 13), range(1, 32), range(0, 24), + range(0, 60), range(0, 60)] + def __call__(self): 'Return the locations of the ticks' self.refresh() @@ -659,89 +727,59 @@ numMinutes = (numHours * 60.0) + delta.minutes numSeconds = (numMinutes * 60.0) + delta.seconds - numticks = 5 + nums = [numYears, numMonths, numDays, numHours, numMinutes, numSeconds] - # self._freq = YEARLY - interval = 1 - bymonth = 1 - bymonthday = 1 - byhour = 0 - byminute = 0 - bysecond = 0 + # Default setting of bymonth, etc. to pass to rrule + # [unused (for year), bymonth, bymonthday, byhour, byminute, bysecond] + byranges = [None, 1, 1, 0, 0, 0] - if ( numYears >= numticks ): - self._freq = YEARLY - elif ( numMonths >= numticks ): - self._freq = MONTHLY - bymonth = range(1, 13) - if ( (0 <= numMonths) and (numMonths <= 14) ): - interval = 1 # show every month - elif ( (15 <= numMonths) and (numMonths <= 29) ): - interval = 3 # show every 3 months - elif ( (30 <= numMonths) and (numMonths <= 44) ): - interval = 4 # show every 4 months - else: # 45 <= numMonths <= 59 - interval = 6 # show every 6 months - elif ( numDays >= numticks ): - self._freq = DAILY - bymonth = None - bymonthday = range(1, 32) - if ( (0 <= numDays) and (numDays <= 9) ): - interval = 1 # show every day - elif ( (10 <= numDays) and (numDays <= 19) ): - interval = 2 # show every 2 days - elif ( (20 <= numDays) and (numDays <= 49) ): - interval = 3 # show every 3 days - elif ( (50 <= numDays) and (numDays <= 99) ): - interval = 7 # show every 1 week - else: # 100 <= numDays <= ~150 - interval = 14 # show every 2 weeks - elif ( numHours >= numticks ): - self._freq = HOURLY - bymonth = None - bymonthday = None - byhour = range(0, 24) # show every hour - if ( (0 <= numHours) and (numHours <= 14) ): - interval = 1 # show every hour - elif ( (15 <= numHours) and (numHours <= 30) ): - interval = 2 # show every 2 hours - elif ( (30 <= numHours) and (numHours <= 45) ): - interval = 3 # show every 3 hours - elif ( (45 <= numHours) and (numHours <= 68) ): - interval = 4 # show every 4 hours - elif ( (68 <= numHours) and (numHours <= 90) ): - interval = 6 # show every 6 hours - else: # 90 <= numHours <= 120 - interval = 12 # show every 12 hours - elif ( numMinutes >= numticks ): - self._freq = MINUTELY - bymonth = None - bymonthday = None - byhour = None - byminute = range(0, 60) - if ( numMinutes > (10.0 * numticks) ): - interval = 10 - # end if - elif ( numSeconds >= numticks ): - self._freq = SECONDLY - bymonth = None - bymonthday = None - byhour = None - byminute = None - bysecond = range(0, 60) - if ( numSeconds > (10.0 * numticks) ): - interval = 10 - # end if + # Loop over all the frequencies and try to find one that gives at + # least a minticks tick positions. Once this is found, look for + # an interval from an list specific to that frequency that gives no + # more than maxticks tick positions. Also, set up some ranges + # (bymonth, etc.) as appropriate to be passed to rrulewrapper. + for i, (freq, num) in enumerate(zip(self._freqs, nums)): + # If this particular frequency doesn't give enough ticks, continue + if num < self.minticks: + # Since we're not using this particular frequency, set + # the corresponding by_ to None so the rrule can act as + # appropriate + byranges[i] = None + continue + + # Find the first available interval that doesn't give too many ticks + for interval in self.intervald[freq]: + if num <= interval * (self.maxticks[freq] - 1): + break + else: + # We went through the whole loop without breaking, default to 1 + interval = 1 + + # Set some parameters as appropriate + self._freq = freq + + if self._byranges[i] and self.interval_multiples: + byranges[i] = self._byranges[i][::interval] + interval = 1 + else: + byranges[i] = self._byranges[i] + + # We found what frequency to use + break else: + # We couldn't find a good frequency. # do what? # microseconds as floats, but floats from what reference point? - pass + byranges = [None, 1, 1, 0, 0, 0] + interval = 1 + unused, bymonth, bymonthday, byhour, byminute, bysecond = byranges + del unused - rrule = rrulewrapper( self._freq, interval=interval, \ - dtstart=dmin, until=dmax, \ - bymonth=bymonth, bymonthday=bymonthday, \ - byhour=byhour, byminute = byminute, \ + rrule = rrulewrapper( self._freq, interval=interval, + dtstart=dmin, until=dmax, + bymonth=bymonth, bymonthday=bymonthday, + byhour=byhour, byminute = byminute, bysecond=bysecond ) locator = RRuleLocator(rrule, self.tz) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |