1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
|
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/sequencer.py
__version__=''' $Id: sequencer.py 2423 2004-08-24 11:36:22Z rgbecker $ '''
"""This module defines a single public class, Sequencer, which aids in
numbering and formatting lists."""
#
# roman numbers conversion thanks to
#
# fredrik lundh, november 1996 (based on a C hack from 1984)
#
# fredrik@pythonware.com
# http://www.pythonware.com
_RN_TEMPLATES = [ 0, 01, 011, 0111, 012, 02, 021, 0211, 02111, 013 ]
_RN_LETTERS = "IVXLCDM"
from string import lower
def _format_I(value):
if value < 0 or value > 3999:
raise ValueError, "illegal value"
str = ""
base = -1
while value:
value, index = divmod(value, 10)
tmp = _RN_TEMPLATES[index]
while tmp:
tmp, index = divmod(tmp, 8)
str = _RN_LETTERS[index+base] + str
base = base + 2
return str
def _format_i(num):
return lower(_format_I(num))
def _format_123(num):
"""The simplest formatter"""
return str(num)
def _format_ABC(num):
"""Uppercase. Wraps around at 26."""
n = (num -1) % 26
return chr(n+65)
def _format_abc(num):
"""Lowercase. Wraps around at 26."""
n = (num -1) % 26
return chr(n+97)
class _Counter:
"""Private class used by Sequencer. Each counter
knows its format, and the IDs of anything it
resets, as well as its value. Starts at zero
and increments just before you get the new value,
so that it is still 'Chapter 5' and not 'Chapter 6'
when you print 'Figure 5.1'"""
def __init__(self):
self._base = 0
self._value = self._base
self._formatter = _format_123
self._resets = []
def setFormatter(self, formatFunc):
self._formatter = formatFunc
def reset(self, value=None):
if value:
self._value = value
else:
self._value = self._base
def next(self):
self._value = self._value + 1
v = self._value
for counter in self._resets:
counter.reset()
return v
def _this(self):
return self._value
def nextf(self):
"""Returns next value formatted"""
return self._formatter(self.next())
def thisf(self):
return self._formatter(self._this())
def chain(self, otherCounter):
if not otherCounter in self._resets:
self._resets.append(otherCounter)
class Sequencer:
"""Something to make it easy to number paragraphs, sections,
images and anything else. The features include registering
new string formats for sequences, and 'chains' whereby
some counters are reset when their parents.
It keeps track of a number of
'counters', which are created on request:
Usage:
>>> seq = layout.Sequencer()
>>> seq.next('Bullets')
1
>>> seq.next('Bullets')
2
>>> seq.next('Bullets')
3
>>> seq.reset('Bullets')
>>> seq.next('Bullets')
1
>>> seq.next('Figures')
1
>>>
"""
def __init__(self):
self._counters = {} #map key to current number
self._defaultCounter = None
self._formatters = {
# the formats it knows initially
'1':_format_123,
'A':_format_ABC,
'a':_format_abc,
'I':_format_I,
'i':_format_i,
}
def _getCounter(self, counter=None):
"""Creates one if not present"""
try:
return self._counters[counter]
except KeyError:
cnt = _Counter()
self._counters[counter] = cnt
return cnt
def _this(self, counter=None):
"""Retrieves counter value but does not increment. For
new counters, sets base value to 1."""
if not counter:
counter = self._defaultCounter
return self._getCounter(counter)._this()
def next(self, counter=None):
"""Retrieves the numeric value for the given counter, then
increments it by one. New counters start at one."""
if not counter:
counter = self._defaultCounter
return self._getCounter(counter).next()
def thisf(self, counter=None):
if not counter:
counter = self._defaultCounter
return self._getCounter(counter).thisf()
def nextf(self, counter=None):
"""Retrieves the numeric value for the given counter, then
increments it by one. New counters start at one."""
if not counter:
counter = self._defaultCounter
return self._getCounter(counter).nextf()
def setDefaultCounter(self, default=None):
"""Changes the key used for the default"""
self._defaultCounter = default
def registerFormat(self, format, func):
"""Registers a new formatting function. The funtion
must take a number as argument and return a string;
fmt is a short menmonic string used to access it."""
self._formatters[format] = func
def setFormat(self, counter, format):
"""Specifies that the given counter should use
the given format henceforth."""
func = self._formatters[format]
self._getCounter(counter).setFormatter(func)
def reset(self, counter=None, base=0):
if not counter:
counter = self._defaultCounter
self._getCounter(counter)._value = base
def chain(self, parent, child):
p = self._getCounter(parent)
c = self._getCounter(child)
p.chain(c)
def __getitem__(self, key):
"""Allows compact notation to support the format function.
s['key'] gets current value, s['key+'] increments."""
if key[-1:] == '+':
counter = key[:-1]
return self.nextf(counter)
else:
return self.thisf(key)
def format(self, template):
"""The crowning jewels - formats multi-level lists."""
return template % self
def dump(self):
"""Write current state to stdout for diagnostics"""
counters = self._counters.items()
counters.sort()
print 'Sequencer dump:'
for (key, counter) in counters:
print ' %s: value = %d, base = %d, format example = %s' % (
key, counter._this(), counter._base, counter.thisf())
"""Your story builder needs to set this to"""
_sequencer = None
def getSequencer():
global _sequencer
if _sequencer is None:
_sequencer = Sequencer()
return _sequencer
def setSequencer(seq):
global _sequencer
s = _sequencer
_sequencer = seq
return s
def test():
s = Sequencer()
print 'Counting using default sequence: %d %d %d' % (s.next(),s.next(), s.next())
print 'Counting Figures: Figure %d, Figure %d, Figure %d' % (
s.next('figure'), s.next('figure'), s.next('figure'))
print 'Back to default again: %d' % s.next()
s.setDefaultCounter('list1')
print 'Set default to list1: %d %d %d' % (s.next(),s.next(), s.next())
s.setDefaultCounter()
print 'Set default to None again: %d %d %d' % (s.next(),s.next(), s.next())
print
print 'Creating Appendix counter with format A, B, C...'
s.setFormat('Appendix', 'A')
print ' Appendix %s, Appendix %s, Appendix %s' % (
s.nextf('Appendix'), s.nextf('Appendix'),s.nextf('Appendix'))
def format_french(num):
return ('un','deux','trois','quatre','cinq')[(num-1)%5]
print
print 'Defining a custom format with french words:'
s.registerFormat('french', format_french)
s.setFormat('FrenchList', 'french')
print ' ',
for i in range(1,6):
print s.nextf('FrenchList'),
print
print 'Chaining H1 and H2 - H2 goes back to one when H1 increases'
s.chain('H1','H2')
print ' H1 = %d' % s.next('H1')
print ' H2 = %d' % s.next('H2')
print ' H2 = %d' % s.next('H2')
print ' H2 = %d' % s.next('H2')
print ' H1 = %d' % s.next('H1')
print ' H2 = %d' % s.next('H2')
print ' H2 = %d' % s.next('H2')
print ' H2 = %d' % s.next('H2')
print
print 'GetItem notation - append a plus to increment'
print ' seq["Appendix"] = %s' % s["Appendix"]
print ' seq["Appendix+"] = %s' % s["Appendix+"]
print ' seq["Appendix+"] = %s' % s["Appendix+"]
print ' seq["Appendix"] = %s' % s["Appendix"]
print
print 'Finally, string format notation for nested lists. Cool!'
print 'The expression ("Figure %(Chapter)s.%(Figure+)s" % seq) gives:'
print ' Figure %(Chapter)s.%(Figure+)s' % s
print ' Figure %(Chapter)s.%(Figure+)s' % s
print ' Figure %(Chapter)s.%(Figure+)s' % s
if __name__=='__main__':
test()
|