mirror of
https://github.com/oDinZu/callirhoe.git
synced 2025-02-22 00:04:52 -05:00
renamed itoa->atoi (typo!!)
added documentation @rtype git-svn-id: https://callirhoe.googlecode.com/svn/trunk@161 81c8bb96-aa45-f2e2-0eef-c4fa4a15c6df
This commit is contained in:
parent
97bcec2027
commit
8e0cf7ec54
36
callirhoe.py
36
callirhoe.py
@ -75,6 +75,7 @@ def import_plugin(plugin_paths, cat, longcat, longcat2, listopt, preset):
|
||||
@param longcat2: long category name in plural form
|
||||
@param listopt: option name
|
||||
@param preset: default value
|
||||
@rtype: module
|
||||
|
||||
@note: Aimed for internal use with I{lang}, I{style}, I{geom}, I{layouts}.
|
||||
"""
|
||||
@ -135,12 +136,13 @@ def add_list_option(parser, opt):
|
||||
parser.add_option("--list-%s" % opt, action="store_true", dest="list_%s" % opt, default=False,
|
||||
help="list available %s" % opt)
|
||||
|
||||
def itoa(s, lower_bound=None, upper_bound=None, prefix=''):
|
||||
"""convert integer to string, exiting on error (for cmdline parsing)
|
||||
def atoi(s, lower_bound=None, upper_bound=None, prefix=''):
|
||||
"""convert string to integer, exiting on error (for cmdline parsing)
|
||||
|
||||
@param lower_bound: perform additional check so that value >= I{lower_bound}
|
||||
@param upper_bound: perform additional check so that value <= I{upper_bound}
|
||||
@param prefix: output prefix for error reporting
|
||||
@rtype: int
|
||||
"""
|
||||
try:
|
||||
k = int(s);
|
||||
@ -155,31 +157,40 @@ def itoa(s, lower_bound=None, upper_bound=None, prefix=''):
|
||||
return k
|
||||
|
||||
def _parse_month(mstr):
|
||||
"""get a month value (0-12) from I{mstr}, exiting on error (for cmdline parsing)"""
|
||||
m = itoa(mstr,lower_bound=0,upper_bound=12,prefix='month: ')
|
||||
"""get a month value (0-12) from I{mstr}, exiting on error (for cmdline parsing)
|
||||
|
||||
@rtype: int
|
||||
"""
|
||||
m = atoi(mstr,lower_bound=0,upper_bound=12,prefix='month: ')
|
||||
if m == 0: m = time.localtime()[1]
|
||||
return m
|
||||
|
||||
def parse_month_range(s):
|
||||
"""return (Month,Span) by parsing range I{Month}, I{Month1}-I{Month2} or I{Month}:I{Span}"""
|
||||
"""return (Month,Span) by parsing range I{Month}, I{Month1}-I{Month2} or I{Month}:I{Span}
|
||||
|
||||
@rtype: (int,int)
|
||||
"""
|
||||
if ':' in s:
|
||||
t = s.split(':')
|
||||
if len(t) != 2: raise Abort("invalid month range '" + s + "'")
|
||||
Month = _parse_month(t[0])
|
||||
MonthSpan = itoa(t[1],lower_bound=0,prefix='month span: ')
|
||||
MonthSpan = atoi(t[1],lower_bound=0,prefix='month span: ')
|
||||
elif '-' in s:
|
||||
t = s.split('-')
|
||||
if len(t) != 2: raise Abort("invalid month range '" + s + "'")
|
||||
Month = _parse_month(t[0])
|
||||
MonthSpan = itoa(t[1],lower_bound=Month+1,prefix='month range: ') - Month + 1
|
||||
MonthSpan = atoi(t[1],lower_bound=Month+1,prefix='month range: ') - Month + 1
|
||||
else:
|
||||
Month = _parse_month(s)
|
||||
MonthSpan = 1
|
||||
return (Month,MonthSpan)
|
||||
|
||||
def parse_year(ystr):
|
||||
"""get a year value (>=0) from I{ystr}, exiting on error (for cmdline parsing)"""
|
||||
y = itoa(ystr,lower_bound=0,prefix='year: ')
|
||||
"""get a year value (>=0) from I{ystr}, exiting on error (for cmdline parsing)
|
||||
|
||||
@rtype: int
|
||||
"""
|
||||
y = atoi(ystr,lower_bound=0,prefix='year: ')
|
||||
if y == 0: y = time.localtime()[0]
|
||||
return y
|
||||
|
||||
@ -193,6 +204,8 @@ def extract_parser_args(arglist, parser, pos = -1):
|
||||
if I{pos}<0 then all positional arguments are extracted, otherwise,
|
||||
only I{pos} arguments are extracted. arglist[0] (usually sys.argv[0]) is also positional
|
||||
argument!
|
||||
|
||||
@rtype: ([str,...],[str,...])
|
||||
@return: tuple (argv1,argv2) with extracted argument list and remaining argument list
|
||||
"""
|
||||
argv = [[],[]]
|
||||
@ -221,7 +234,10 @@ def extract_parser_args(arglist, parser, pos = -1):
|
||||
return tuple(argv)
|
||||
|
||||
def get_parser():
|
||||
"""get the argument parser object"""
|
||||
"""get the argument parser object
|
||||
|
||||
@rtype: optparse.OptionParser
|
||||
"""
|
||||
parser = optparse.OptionParser(usage="usage: %prog [options] [[MONTH[-MONTH2|:SPAN]] YEAR] FILE",
|
||||
description="High quality calendar rendering with vector graphics. "
|
||||
"By default, a calendar of the current year in pdf format is written to FILE. "
|
||||
|
64
calmagick.py
64
calmagick.py
@ -34,14 +34,14 @@ import optparse
|
||||
import Queue
|
||||
import threading
|
||||
|
||||
from callirhoe import extract_parser_args, parse_month_range, parse_year, itoa, Abort, _version, _copyright
|
||||
from callirhoe import extract_parser_args, parse_month_range, parse_year, atoi, Abort, _version, _copyright
|
||||
from lib.geom import rect_rel_scale
|
||||
|
||||
# MAYBE-TODO
|
||||
# move to python 3?
|
||||
# check ImageMagick availability/version
|
||||
# convert input to ImageMagick native format for faster re-access
|
||||
# report error on parse-float (like itoa())
|
||||
# report error on parse-float (like atoi())
|
||||
# abort --range only on KeyboardInterrupt?
|
||||
|
||||
_prog_im = os.getenv('CALLIRHOE_IM', 'convert')
|
||||
@ -54,12 +54,16 @@ def run_callirhoe(style, size, args, outfile):
|
||||
@param size: tuple (I{width},I{height}) for output calendar size (in pixels)
|
||||
@param args: (extra) argument list to pass to callirhoe
|
||||
@param outfile: output calendar file
|
||||
@return: subprocess exit code
|
||||
@rtype: subprocess.Popen
|
||||
@return: Popen object
|
||||
"""
|
||||
return subprocess.Popen(['callirhoe', '-s', style, '--paper=-%d:-%d' % size] + args + [outfile])
|
||||
|
||||
def _bound(x, lower, upper):
|
||||
"""return the closest number to M{x} that lies in [M{lower,upper}]"""
|
||||
"""return the closest number to M{x} that lies in [M{lower,upper}]
|
||||
|
||||
@rtype: type(x)
|
||||
"""
|
||||
if x < lower: return lower
|
||||
if x > upper: return upper
|
||||
return x
|
||||
@ -111,7 +115,10 @@ class PNMImage(object):
|
||||
self.xsum = [map(lambda x: self._rsum(y,x), range(w+1)) for y in range(0,h)]
|
||||
|
||||
def _rsum(self,y,x):
|
||||
"""running sum with cache"""
|
||||
"""running sum with cache
|
||||
|
||||
@rtype: int
|
||||
"""
|
||||
if self._rsum_cache[0] == y and self._rsum_cache[1] == x:
|
||||
s = self._rsum_cache[2] + self.data[y][x-1]
|
||||
else:
|
||||
@ -120,11 +127,15 @@ class PNMImage(object):
|
||||
return s
|
||||
|
||||
def block_avg(self, x, y, szx, szy):
|
||||
"""returns the average intensity of a block of size M{(szx,szy)} at pos (top-left) M{(x,y)}"""
|
||||
"""returns the average intensity of a block of size M{(szx,szy)} at pos (top-left) M{(x,y)}
|
||||
|
||||
@rtype: float
|
||||
"""
|
||||
return float(sum([(self.xsum[y][x+szx] - self.xsum[y][x]) for y in range(y,y+szy)]))/(szx*szy)
|
||||
|
||||
def lowest_block_avg(self, szx, szy, at_least = 0):
|
||||
"""returns the M{(szx,szy)}-sized block with intensity as close to M{at_least} as possible
|
||||
@rtype: (float,(float,float),(int,int),(int,int))
|
||||
@return: R=tuple M({avg, (szx_ratio,szy_ratio), (x,y), (szx,szy))}: R[0] is the
|
||||
average intensity of the block found, R[1] is the block size ratio with respect to the whole image,
|
||||
R[2] is the block position (top-left) and R[3] is the block size
|
||||
@ -154,6 +165,8 @@ class PNMImage(object):
|
||||
Calendar rectangle ratio over Photo ratio. If M{r>1} then calendar rectangle, when scaled, fits
|
||||
M{x} dimension first. Conversely, if M{r<1}, scaling touches the M{y} dimension first. When M{r=1},
|
||||
calendar rectangle can fit perfectly within the photo at 100% size.
|
||||
|
||||
@rtype: (float,(float,float),(int,int),(int,int),float)
|
||||
"""
|
||||
w,h = self.size
|
||||
sz_lo = _bound(int(w*size_range[0]+0.5),1,w)
|
||||
@ -172,11 +185,14 @@ class PNMImage(object):
|
||||
# we do not use at_least because we want the best possible option, for bigger sizes
|
||||
cur = self.lowest_block_avg(*sz)
|
||||
if cur[0] <= entropy_thres: return cur + (best[0],)
|
||||
return best + (best[0],) # avg, sz_ratio, x, y, sz, best_avg
|
||||
return best + (best[0],) # avg, (szx_ratio,szy_ratio), (x,y), (szx,szy), best_avg
|
||||
|
||||
|
||||
def get_parser():
|
||||
"""get the argument parser object"""
|
||||
"""get the argument parser object
|
||||
|
||||
@rtype: optparse.OptionParser
|
||||
"""
|
||||
parser = optparse.OptionParser(usage="usage: %prog IMAGE [options] [callirhoe-options] [--pre-magick ...] [--in-magick ...] [--post-magick ...]",
|
||||
description="""High quality photo calendar composition with automatic minimal-entropy placement.
|
||||
If IMAGE is a single file, then a calendar of the current month is overlayed. If IMAGE contains wildcards,
|
||||
@ -303,6 +319,7 @@ def parse_magick_args():
|
||||
ImageMagick-specific arguments should be defined between arguments C{--pre-magick},
|
||||
C{--in-magick}, C{--post-magick} is this order
|
||||
|
||||
@rtype: [[str,...],[str,...],[str,...]]
|
||||
@return: 3-element list of lists containing the [pre,in,post]-options
|
||||
"""
|
||||
magickargs = [[],[],[]]
|
||||
@ -332,13 +349,19 @@ def parse_magick_args():
|
||||
return magickargs
|
||||
|
||||
def mktemp(ext=''):
|
||||
"""get temporary file name with optional extension"""
|
||||
"""get temporary file name with optional extension
|
||||
|
||||
@rtype: str
|
||||
"""
|
||||
f = tempfile.NamedTemporaryFile(suffix=ext, delete=False)
|
||||
f.close()
|
||||
return f.name
|
||||
|
||||
def get_outfile(infile, outdir, base_prefix, format, hint=None):
|
||||
"""get output file name taking into account output directory, format and prefix, avoiding overwriting the input file"""
|
||||
"""get output file name taking into account output directory, format and prefix, avoiding overwriting the input file
|
||||
|
||||
@rtype: str
|
||||
"""
|
||||
if hint:
|
||||
outfile = hint
|
||||
else:
|
||||
@ -352,7 +375,10 @@ def get_outfile(infile, outdir, base_prefix, format, hint=None):
|
||||
return outfile
|
||||
|
||||
def _IM_get_image_size(img, args):
|
||||
"""extract tuple(width,height) from image file using ImageMagick"""
|
||||
"""extract tuple(width,height) from image file using ImageMagick
|
||||
|
||||
@rtype: (int,int)
|
||||
"""
|
||||
info = subprocess.check_output([_prog_im, img] + args + ['-format', '%w %h', 'info:']).split()
|
||||
return tuple(map(int, info))
|
||||
|
||||
@ -360,7 +386,10 @@ _IM_lum_args = "-colorspace Lab -channel R -separate +channel -set colorspace Gr
|
||||
"""IM colorspace conversion arguments to extract image luminance"""
|
||||
|
||||
def _IM_get_image_luminance(img, args, geometry = None):
|
||||
"""get average image luminance as a float in [0,255], using ImageMagick"""
|
||||
"""get average image luminance as a float in [0,255], using ImageMagick
|
||||
|
||||
@rtype: float
|
||||
"""
|
||||
return 255.0*float(subprocess.check_output([_prog_im, img] + args +
|
||||
(['-crop', '%dx%d+%d+%d' % geometry] if geometry else []) +
|
||||
_IM_lum_args + ['-format', '%[fx:mean]', 'info:']))
|
||||
@ -375,7 +404,10 @@ _IM_entropy_tail = "-colorspace Lab -channel R -separate +channel -set colorspac
|
||||
#_IM_entropy_tail = "-colorspace Lab -channel R -separate +channel -normalize -scale".split()
|
||||
|
||||
def _IM_entropy_args(alt=False):
|
||||
"""IM entropy computation arguments, depending on default or alternate algorithm"""
|
||||
"""IM entropy computation arguments, depending on default or alternate algorithm
|
||||
|
||||
@rtype: [str,...]
|
||||
"""
|
||||
return _IM_entropy_head + _IM_entropy_alg[alt] + _IM_entropy_tail
|
||||
|
||||
def _entropy_placement(img, size, args, options, r):
|
||||
@ -386,6 +418,7 @@ def _entropy_placement(img, size, args, options, r):
|
||||
@param args: ImageMagick pre-processing argument list (see C{--pre-magick})
|
||||
@param options: (command-line) options object
|
||||
@param r: rectangle ratio, 0=match input ratio
|
||||
@rtype: (int,int,int,int)
|
||||
@return: IM geometry tuple(I{width,height,x,y})
|
||||
"""
|
||||
w,h = size
|
||||
@ -417,6 +450,7 @@ def _manual_placement(size, options, r):
|
||||
@param size: image size tuple(I{width,height})
|
||||
@param options: (command-line) options object
|
||||
@param r: rectangle ratio, 0=match input ratio
|
||||
@rtype: (int,int,int,int)
|
||||
@return: IM geometry tuple(I{width,height,x,y})
|
||||
"""
|
||||
w,h = size
|
||||
@ -449,6 +483,7 @@ _mutex = threading.Lock()
|
||||
def get_cache(num_photos, num_months):
|
||||
"""returns a reference to the cache object, or None if caching is disabled
|
||||
|
||||
@rtype: dict
|
||||
@note: caching is enabled only when more than 1/6 of photos is going to be re-used
|
||||
"""
|
||||
q,r = divmod(num_months, num_photos)
|
||||
@ -488,7 +523,7 @@ def compose_calendar(img, outimg, options, callirhoe_args, magick_args, stats=No
|
||||
|
||||
if '/' in options.ratio:
|
||||
tmp = options.ratio.split('/')
|
||||
calratio = float(itoa(tmp[0],1))/itoa(tmp[1],1)
|
||||
calratio = float(atoi(tmp[0],1))/atoi(tmp[1],1)
|
||||
else:
|
||||
calratio = float(options.ratio)
|
||||
if options.placement == 'min' or options.placement == 'max':
|
||||
@ -556,6 +591,7 @@ def parse_range(s,hint=None):
|
||||
|
||||
@param s: range string in format I{Month1-Month2/Year} or I{Month:Span/Year}
|
||||
@param hint: span value to be used, when M{Span=0}
|
||||
@rtype: [(int,int),...]
|
||||
@return: list of (I{Month,Year}) tuples for every month specified
|
||||
"""
|
||||
if '/' in s:
|
||||
|
@ -26,6 +26,7 @@ def get_parser(layout_name):
|
||||
"""get the parser object for the layout command-line arguments
|
||||
|
||||
@param layout_name: corresponding python module (.py file)
|
||||
@rtype: optparse.OptionParser
|
||||
"""
|
||||
lname = layout_name.split(".")[1]
|
||||
parser = optparse.OptionParser(usage="%prog (...) --layout " + lname + " [options] (...)",add_help_option=False)
|
||||
|
@ -28,7 +28,10 @@ import _base
|
||||
parser = _base.get_parser(__name__)
|
||||
|
||||
def _weekrows_of_month(year, month):
|
||||
"""returns the number of Monday-Sunday ranges (or subsets of) that a month contains, which are 4, 5 or 6"""
|
||||
"""returns the number of Monday-Sunday ranges (or subsets of) that a month contains, which are 4, 5 or 6
|
||||
|
||||
@rtype: int
|
||||
"""
|
||||
day,span = calendar.monthrange(year, month)
|
||||
if day == 0 and span == 28: return 4
|
||||
if day == 5 and span == 31: return 6
|
||||
|
@ -33,6 +33,7 @@ def get_parser(layout_name):
|
||||
"""get the parser object for the layout command-line arguments
|
||||
|
||||
@param layout_name: corresponding python module (.py file)
|
||||
@rtype: optparse.OptionParser
|
||||
"""
|
||||
lname = layout_name.split(".")[1]
|
||||
parser = optparse.OptionParser(usage="%prog (...) --layout " + lname + " [options] (...)",add_help_option=False)
|
||||
|
105
lib/geom.py
105
lib/geom.py
@ -23,7 +23,10 @@
|
||||
# *****************************************
|
||||
|
||||
def rect_ratio(r):
|
||||
"""returns the ratio of rect I{r} which is defined as M{width/height}"""
|
||||
"""returns the ratio of rect I{r} which is defined as M{width/height}
|
||||
|
||||
@rtype: float
|
||||
"""
|
||||
return r[2]*1.0/r[3]
|
||||
|
||||
def rect_rel_scale(r, fw, fh, align_x = 0, align_y = 0):
|
||||
@ -40,33 +43,49 @@ def rect_rel_scale(r, fw, fh, align_x = 0, align_y = 0):
|
||||
linear interpolation.
|
||||
@type align_y: float in [-1,1]
|
||||
@param align_y: Performs vertical (top-bottom) alignment similarly to L{align_x}.
|
||||
@rtype: (float,float,float,float)
|
||||
"""
|
||||
x, y, w, h = r
|
||||
return (x + (align_x + 1.0)*w*(1 - fw)/2.0,
|
||||
y + (align_y + 1.0)*h*(1 - fh)/2.0, w*fw, h*fh)
|
||||
|
||||
def rect_pad(r, pad):
|
||||
"""returns a padded rect by reducing border by the I{pad} tuple (top,left,bottom,right)"""
|
||||
"""returns a padded rect by reducing border by the I{pad} tuple (top,left,bottom,right)
|
||||
|
||||
@rtype: (float,float,float,float)
|
||||
"""
|
||||
x, y, w, h = r
|
||||
t_, l_, b_, r_ = pad
|
||||
return (x + l_, y + t_, w - r_ - l_, h - t_ - b_)
|
||||
|
||||
def rect_to_abs(r):
|
||||
"""get absolute coordinates (x0,y0,x1,y1) from rect definition (x,y,w,h)"""
|
||||
"""get absolute coordinates (x0,y0,x1,y1) from rect definition (x,y,w,h)
|
||||
|
||||
@rtype: (float,float,float,float)
|
||||
"""
|
||||
x, y, w, h = r
|
||||
return (x, y, x + w, y + h)
|
||||
|
||||
def abs_to_rect(a):
|
||||
"""get rect definition (x,y,w,h) from absolute coordinates (x0,y0,x1,y1)"""
|
||||
"""get rect definition (x,y,w,h) from absolute coordinates (x0,y0,x1,y1)
|
||||
|
||||
@rtype: (float,float,float,float)
|
||||
"""
|
||||
x1, y1, x2, y2 = a
|
||||
return (x1, y1, x2 - x1, y2 - y1)
|
||||
|
||||
def rect_from_origin(r):
|
||||
"""returns a similar rect with top-left corner at (0,0)"""
|
||||
"""returns a similar rect with top-left corner at (0,0)
|
||||
|
||||
@rtype: (float,float,float,float)
|
||||
"""
|
||||
return (0, 0, r[2], r[3])
|
||||
|
||||
def rect_hull(r1,r2):
|
||||
"""returns the smallest rect containing r1 and r2"""
|
||||
"""returns the smallest rect containing r1 and r2
|
||||
|
||||
@rtype: (float,float,float,float)
|
||||
"""
|
||||
x1, y1, x2, y2 = rect_to_abs(r1)
|
||||
x3, y3, x4, y4 = rect_to_abs(r2)
|
||||
return abs_to_rect((min(x1,x3), min(y1,y3), max(x2,x4), max(y2,y4)))
|
||||
@ -77,6 +96,7 @@ def rect_hsplit(r, f = 0.5, fdist = 0.0):
|
||||
@type f: float in [0,1]
|
||||
@param f: split fraction
|
||||
@param fdist: fraction of space to discard before splitting (free space)
|
||||
@rtype: ((float,float,float,float),(float,float,float,float))
|
||||
@return: tuple (r1,r2) with splits and free space evenly distributed
|
||||
before r1, between r1 and r2 and after r2
|
||||
"""
|
||||
@ -87,7 +107,10 @@ def rect_hsplit(r, f = 0.5, fdist = 0.0):
|
||||
return (r1, r2)
|
||||
|
||||
def rect_vsplit(r, f = 0.5, fdist = 0.0):
|
||||
"""split a rect vertically, similarly to L{rect_hsplit}"""
|
||||
"""split a rect vertically, similarly to L{rect_hsplit}
|
||||
|
||||
@rtype: ((float,float,float,float),(float,float,float,float))
|
||||
"""
|
||||
x, y, w, h = r
|
||||
rh = h*(1.0 - fdist)
|
||||
r1 = (x, y + h*fdist/3.0, w, rh*f)
|
||||
@ -99,6 +122,7 @@ def color_mix(a, b, frac):
|
||||
|
||||
@type frac: float in [0,1]
|
||||
@param frac: amount of first color
|
||||
@rtype: tuple
|
||||
"""
|
||||
return map(lambda (x,y): x*frac + y*(1 - frac), zip(a,b))
|
||||
|
||||
@ -107,11 +131,15 @@ def color_scale(a, frac):
|
||||
|
||||
@type frac: float
|
||||
@param frac: scale amount (to be multiplied)
|
||||
@rtype: tuple
|
||||
"""
|
||||
return map(lambda x: min(1.0,x*frac), a)
|
||||
|
||||
def color_auto_fg(bg, light = (1,1,1), dark = (0,0,0)):
|
||||
"""return I{light} or I{dark} foreground color based on an ad-hoc evaluation of I{bg}"""
|
||||
"""return I{light} or I{dark} foreground color based on an ad-hoc evaluation of I{bg}
|
||||
|
||||
@rtype: tuple
|
||||
"""
|
||||
return light if (bg[0] + 1.5*bg[1] + bg[2]) < 1.0 else dark
|
||||
|
||||
# ********* layout managers ***********
|
||||
@ -129,7 +157,10 @@ class VLayout(object):
|
||||
self.pad = pad
|
||||
|
||||
def count(self):
|
||||
"""return maximum number of items in the layout"""
|
||||
"""return maximum number of items in the layout
|
||||
|
||||
@rtype: int
|
||||
"""
|
||||
return self.nitems
|
||||
|
||||
def resize(self, k):
|
||||
@ -141,7 +172,10 @@ class VLayout(object):
|
||||
self.nitems += delta
|
||||
|
||||
def item(self, i = 0):
|
||||
"""get rect for item I{i}"""
|
||||
"""get rect for item I{i}
|
||||
|
||||
@rtype: (float,float,float,float)
|
||||
"""
|
||||
x, y, w, h = self.rect
|
||||
h *= 1.0/self.nitems
|
||||
y += i*h
|
||||
@ -152,12 +186,16 @@ class VLayout(object):
|
||||
|
||||
@param n: first item
|
||||
@param k: number of items, -1 for all remaining items
|
||||
@rtype: (float,float,float,float)
|
||||
"""
|
||||
if k < 0: k = (self.count() - n) // 2
|
||||
return rect_hull(self.item(k), self.item(k + n - 1))
|
||||
|
||||
def items(self):
|
||||
"""returns a sequence of all items"""
|
||||
"""returns a sequence of all items
|
||||
|
||||
@rtype: (float,float,float,float),...
|
||||
"""
|
||||
return map(self.item, range(self.count()))
|
||||
|
||||
class HLayout(VLayout):
|
||||
@ -167,6 +205,10 @@ class HLayout(VLayout):
|
||||
nitems, (pad[1], pad[0], pad[3], pad[2]))
|
||||
|
||||
def item(self, i = 0):
|
||||
"""get rect for item I{i}
|
||||
|
||||
@rtype: (float,float,float,float)
|
||||
"""
|
||||
t = super(HLayout,self).item(i)
|
||||
return (t[1], t[0], t[3], t[2])
|
||||
|
||||
@ -189,15 +231,24 @@ class GLayout(object):
|
||||
self.hrep = HLayout((rect[0], rect[1], t[2], t[3]), ncols, (0.0, pad[1], 0.0, pad[3]))
|
||||
|
||||
def row_count(self):
|
||||
"""get (max) number of rows in the grid"""
|
||||
"""get (max) number of rows in the grid
|
||||
|
||||
@rtype: int
|
||||
"""
|
||||
return self.vrep.count()
|
||||
|
||||
def col_count(self):
|
||||
"""get (max) number of columns in the grid"""
|
||||
"""get (max) number of columns in the grid
|
||||
|
||||
@rtype: int
|
||||
"""
|
||||
return self.hrep.count()
|
||||
|
||||
def count(self):
|
||||
"""get total number of cells in the grid (which is M{rows*cols})"""
|
||||
"""get total number of cells in the grid (which is M{rows*cols})
|
||||
|
||||
@rtype: int
|
||||
"""
|
||||
return self.row_count()*self.col_count()
|
||||
|
||||
def resize(self, rows, cols):
|
||||
@ -207,13 +258,19 @@ class GLayout(object):
|
||||
self.hrep = HLayout(t[0:2], t[2:4], cols, (0.0, pad[1], 0.0, pad[3]))
|
||||
|
||||
def item(self, row, col):
|
||||
"""get rect of cell at position I{row,col}"""
|
||||
"""get rect of cell at position I{row,col}
|
||||
|
||||
@rtype: (float,float,float,float)
|
||||
"""
|
||||
ty = self.vrep.item(row)
|
||||
tx = self.hrep.item(col)
|
||||
return (tx[0], ty[1], tx[2], tx[3])
|
||||
|
||||
def item_seq(self, k, column_wise = False):
|
||||
"""get rect of cell at position I{k} column-wise or row-wise"""
|
||||
"""get rect of cell at position I{k} column-wise or row-wise
|
||||
|
||||
@rtype: (float,float,float,float)
|
||||
"""
|
||||
if not column_wise:
|
||||
row, col = k // self.col_count(), k % self.col_count()
|
||||
else:
|
||||
@ -221,15 +278,24 @@ class GLayout(object):
|
||||
return self.item(row, col)
|
||||
|
||||
def items(self, column_wise = False):
|
||||
"""get sequence of rects of cells column-wise or row-wise"""
|
||||
"""get sequence of rects of cells column-wise or row-wise
|
||||
|
||||
@rtype: (float,float,float,float),...
|
||||
"""
|
||||
return map(self.item_seq, range(self.count()))
|
||||
|
||||
def row_items(self, row):
|
||||
"""get sequence of cell rects of a row"""
|
||||
"""get sequence of cell rects of a row
|
||||
|
||||
@rtype: (float,float,float,float),...
|
||||
"""
|
||||
return map(lambda x: self.item(row, x), range(self.col_count()))
|
||||
|
||||
def col_items(self, col):
|
||||
"""get sequence of cell rects of a column"""
|
||||
"""get sequence of cell rects of a column
|
||||
|
||||
@rtype: (float,float,float,float),...
|
||||
"""
|
||||
return map(lambda x: self.item(x, col), range(self.row_count()))
|
||||
|
||||
|
||||
@ -240,6 +306,7 @@ class GLayout(object):
|
||||
@param nc: number of spanning columns
|
||||
@param row: starting row, -1 for vertically centered
|
||||
@param col: starting column, -1 for horizontally centered
|
||||
@rtype: (float,float,float,float)
|
||||
"""
|
||||
if row < 0: row = (self.row_count() - nr) // 2
|
||||
if col < 0: col = (self.col_count() - nc) // 2
|
||||
|
@ -25,7 +25,9 @@
|
||||
from datetime import date, timedelta
|
||||
|
||||
def _get_orthodox_easter(year):
|
||||
"""compute date of orthodox easter"""
|
||||
"""compute date of orthodox easter
|
||||
@rtype: datetime.date
|
||||
"""
|
||||
y1, y2, y3 = year % 4 , year % 7, year % 19
|
||||
a = 19*y3 + 15
|
||||
y4 = a % 30
|
||||
@ -37,7 +39,10 @@ def _get_orthodox_easter(year):
|
||||
# return res
|
||||
|
||||
def _get_catholic_easter(year):
|
||||
"""compute date of catholic easter"""
|
||||
"""compute date of catholic easter
|
||||
|
||||
@rtype: datetime.date
|
||||
"""
|
||||
a, b, c = year % 19, year // 100, year % 100
|
||||
d, e = divmod(b,4)
|
||||
f = (b + 8) // 25
|
||||
@ -50,11 +55,17 @@ def _get_catholic_easter(year):
|
||||
return date(year, emonth, edate+1)
|
||||
|
||||
def _strip_empty(sl):
|
||||
"""strip empty strings from list I{sl}"""
|
||||
"""strip empty strings from list I{sl}
|
||||
|
||||
@rtype: [str,...]
|
||||
"""
|
||||
return filter(lambda z: z, sl) if sl else []
|
||||
|
||||
def _flatten(sl):
|
||||
"""join list I{sl} into a comma-separated string"""
|
||||
"""join list I{sl} into a comma-separated string
|
||||
|
||||
@rtype: str
|
||||
"""
|
||||
if not sl: return None
|
||||
return ', '.join(sl)
|
||||
|
||||
@ -94,19 +105,31 @@ class Holiday(object):
|
||||
self.flags |= hol.flags
|
||||
|
||||
def header(self):
|
||||
"""return a comma-separated string for L{header_list}"""
|
||||
"""return a comma-separated string for L{header_list}
|
||||
|
||||
@rtype: str
|
||||
"""
|
||||
return _flatten(self.header_list)
|
||||
|
||||
def footer(self):
|
||||
"""return a comma-separated string for L{footer_list}"""
|
||||
"""return a comma-separated string for L{footer_list}
|
||||
|
||||
@rtype: str
|
||||
"""
|
||||
return _flatten(self.footer_list)
|
||||
|
||||
def __str__(self):
|
||||
"""string representation for debugging purposes"""
|
||||
"""string representation for debugging purposes
|
||||
|
||||
@rtype: str
|
||||
"""
|
||||
return str(self.footer()) + ':' + str(self.header()) + ':' + str(self.flags)
|
||||
|
||||
def _parse_flags(self, fstr):
|
||||
"""return a bit combination of flags, from a comma-separated string list"""
|
||||
"""return a bit combination of flags, from a comma-separated string list
|
||||
|
||||
@rtype: int
|
||||
"""
|
||||
if not fstr: return 0
|
||||
fs = fstr.split(',')
|
||||
val = 0
|
||||
@ -130,6 +153,8 @@ def _decode_date_str(ddef):
|
||||
|
||||
If C{ddef} is of the form "YYYYMMDD" then tuple (YYYY,MM,DD) is returned, which
|
||||
stands for year YYYY - month MM - day DD.
|
||||
|
||||
@rtype: (int,int,int)
|
||||
"""
|
||||
if len(ddef) == 2:
|
||||
return (0,0,int(ddef))
|
||||
@ -189,11 +214,12 @@ class HolidayProvider(object):
|
||||
def _parse_day_record(self, fields):
|
||||
"""return tuple (etype,ddef,footer,header,flags)
|
||||
|
||||
@rtype: (char,type(ddef),str,str,int)
|
||||
@note: I{ddef} is one of the following:
|
||||
None
|
||||
int
|
||||
((y,m,d),)
|
||||
((y,m,d),(y,m,d))
|
||||
- None
|
||||
- int
|
||||
- ((y,m,d),)
|
||||
- ((y,m,d),(y,m,d))
|
||||
"""
|
||||
if len(fields) != 5:
|
||||
raise ValueError("Too many fields: " + str(fields))
|
||||
@ -239,6 +265,7 @@ class HolidayProvider(object):
|
||||
@param header: passed as C{[header]} of the generated L{Holiday} object
|
||||
@param footer: passed as C{[footer]} of the generated L{Holiday} object
|
||||
@param flags: C{flags} of the generated L{Holiday} object
|
||||
@rtype: (Holiday,Holiday,Holiday,Holiday)
|
||||
"""
|
||||
if header:
|
||||
if self.multiday_markers:
|
||||
@ -341,6 +368,7 @@ class HolidayProvider(object):
|
||||
def get_holiday(self, y, m, d):
|
||||
"""return a L{Holiday} object for the specified date (y,m,d) or C{None} if no holiday is defined
|
||||
|
||||
@rtype: Holiday
|
||||
@note: If year I{y} has not been requested before, the cache is updated first
|
||||
with all holidays that belong in I{y}, indexed by C{date()} objects.
|
||||
"""
|
||||
@ -388,6 +416,7 @@ class HolidayProvider(object):
|
||||
def get_style(self, flags, dow):
|
||||
"""return appropriate style object, depending on I{flags} and I{dow}
|
||||
|
||||
@rtype: Style
|
||||
@param flags: bit combination of holiday flags
|
||||
@param dow: day of week
|
||||
"""
|
||||
@ -400,6 +429,7 @@ class HolidayProvider(object):
|
||||
def __call__(self, year, month, dom, dow):
|
||||
"""returns (header,footer,day_style)
|
||||
|
||||
@rtype: (str,str,Style)
|
||||
@param month: month (0-12)
|
||||
@param dom: day of month (1-31)
|
||||
@param dow: day of week (0-6)
|
||||
|
@ -29,6 +29,7 @@ import glob
|
||||
def available_files(parent, dir, fmatch = None):
|
||||
"""find parent/dir/*.py files to be used for plugins
|
||||
|
||||
@rtype: [str,...]
|
||||
@note:
|
||||
1. __init__.py should exist
|
||||
2. files starting with underscore are ignored
|
||||
@ -50,7 +51,10 @@ def available_files(parent, dir, fmatch = None):
|
||||
return res if good else []
|
||||
|
||||
def plugin_list(cat):
|
||||
"""return a sequence of available plugins, using L{available_files()} and L{get_plugin_paths()}"""
|
||||
"""return a sequence of available plugins, using L{available_files()} and L{get_plugin_paths()}
|
||||
|
||||
@rtype: [str,...]
|
||||
"""
|
||||
plugin_paths = get_plugin_paths()
|
||||
return available_files(plugin_paths[0], cat) + available_files(plugin_paths[1], cat)
|
||||
|
||||
@ -61,5 +65,8 @@ def plugin_list(cat):
|
||||
# preset = "EN"
|
||||
|
||||
def get_plugin_paths():
|
||||
"""return the plugin search paths"""
|
||||
"""return the plugin search paths
|
||||
|
||||
@rtype: [str,str]
|
||||
"""
|
||||
return [ os.path.expanduser("~/.callirhoe"), sys.path[0] if sys.path[0] else "." ]
|
||||
|
@ -43,6 +43,8 @@ def page_spec(spec = None):
|
||||
Paper type can be an ISO paper type (a0..a9 or a0w..a9w) or of the
|
||||
form W:H; positive values correspond to W or H mm, negative values correspond to
|
||||
-W or -H pixels; 'w' suffix swaps width & height; None defaults to A4 paper
|
||||
|
||||
@rtype: (int,int)
|
||||
"""
|
||||
if not spec:
|
||||
return (ISOPAGE[5], ISOPAGE[4])
|
||||
@ -62,11 +64,17 @@ def page_spec(spec = None):
|
||||
return (w,h)
|
||||
|
||||
def mm_to_dots(mm):
|
||||
"""convert millimeters to dots"""
|
||||
"""convert millimeters to dots
|
||||
|
||||
@rtype: float
|
||||
"""
|
||||
return mm/25.4 * XDPI
|
||||
|
||||
def dots_to_mm(dots):
|
||||
"""convert dots to millimeters"""
|
||||
"""convert dots to millimeters
|
||||
|
||||
@rtype: float
|
||||
"""
|
||||
return dots*25.4/XDPI
|
||||
|
||||
class Page(object):
|
||||
@ -205,7 +213,10 @@ def set_color(cr, rgba):
|
||||
cr.set_source_rgba(*rgba)
|
||||
|
||||
def extract_font_name(f):
|
||||
"extract the font name from a string or from a tuple (fontname, slant, weight)"""
|
||||
"""extract the font name from a string or from a tuple (fontname, slant, weight)
|
||||
|
||||
@rtype: str
|
||||
"""
|
||||
return f if type(f) is str else f[0]
|
||||
|
||||
def make_sloppy_rect(cr, rect, sdx = 0.0, sdy = 0.0, srot = 0.0):
|
||||
@ -233,8 +244,6 @@ def draw_shadow(cr, rect, thickness = None, shadow_color = (0,0,0,0.3)):
|
||||
@param thickness: if C{None} nothing is drawn
|
||||
@param shadow_color: shadow color
|
||||
"""
|
||||
|
||||
|
||||
if thickness is None: return
|
||||
fx = mm_to_dots(thickness[0])
|
||||
fy = mm_to_dots(thickness[1])
|
||||
|
Loading…
x
Reference in New Issue
Block a user