# Copyright (C) 2003-2006 Alexei Gilchrist and Paul Cochrane # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ''' PyScript presentation library (posters and talks) ''' from pyscript.defaults import defaults from pyscript import Group, Rectangle, Color, TeX, Area, Align, Page, \ VAlign, Page, Text, P, Bbox, Pages __revision__ = '$Revision: 1.8 $' # ---------------------------------------------------------------------- # First some useful components # ---------------------------------------------------------------------- class Box(Group, Rectangle): ''' Draws a box around an object, the box can be placed acording to standard Area tags @cvar pad: padding around object @cvar width: overide the width of the box @cvar height: override the height of the box ''' # set these preferences different from Rectangle: fg = Color(0) bg = Color(1) pad = .2 width = None height = None def __init__(self, obj, **options): Rectangle.__init__(self, **options) Group.__init__(self, **options) bbox = obj.bbox() self.object = obj w = bbox.width+2*self.pad h = bbox.height+2*self.pad # overide the width and height if supplied if self.width is None: self.width = options.get('width', w) if self.height is None: self.height = options.get('height', h) self.append( Rectangle(width=self.width, height=self.height, bg=self.bg, fg=self.fg, c=obj.c, r=self.r, linewidth=self.linewidth, dash=self.dash), obj, ) # ---------------------------------------------------------------------- class TeXArea(Group): ''' Typeset some LaTeX within a fixed width minipage environment. @cvar width: the width of the environment @type width: float @cvar iscale: initial scale of the tex @type iscale: float @cvar align: alignment of the LaTeX to box if it iss smaller than the full width @type align: string (anchor point) @cvar fg: color of TeX @type fg: L{Color} object ''' # has to be different from groups width attribute width = 9.4 iscale = 1 fg = Color(0) align = "w" def __init__(self, text, **options): Group.__init__(self, **options) # set up tex width ... this relies on latex notion of # a point being accurate ... adjust for tex_scale too width_pp = int(self.width/float(self.iscale)*defaults.units) t = TeX(r'\begin{minipage}{%dpt}%s\end{minipage}'%(width_pp, text), fg=self.fg, iscale=self.iscale) # use this for alignment as the latex bounding box may be smaller # than the full width a = Area(width=self.width, height=0) Align(t, a, a1=self.align, a2=self.align, space=0) self.append(a, t) #apply(self, (), options) # ---------------------------------------------------------------------- # Poster class # ---------------------------------------------------------------------- class Poster(Page, VAlign): ''' A poster class @cvar size: the size of the poster eg A0 @cvar orientation: portrait or landscape @cvar space: space between vertically aligned objects appended to poster @cvar topspace: initial space at top of poster @cvar bg: background color of poster (unless background() method is overiden) ''' size = "A0" orientation = "portrait" bg = Color('DarkSlateBlue') space = 1 topspace = 2 def __init__(self, *objects, **options): Page.__init__(self, **options) VAlign.__init__(self, **options) back = self.background() # use Page's append so background doesn't get aligned Page.append(self, back) # add invisible area at top to start alignment a = Area(width=0, height=self.topspace-self.space, n=self.area().n) self.append(a) def background(self): ''' Return background for poster ''' area = self.area() signature = Text( 'Created with PyScript. http://pyscript.sourceforge.net', size=14, fg=Color(1)) signature.se = area.se+P(-.5, .5) return Group( Rectangle(width=area.width, height=area.height, fg=None, bg=self.bg), signature, ) # ---------------------------------------------------------------------- # Talk Class # ---------------------------------------------------------------------- class Pause(object): ''' A marker object to split slides to simulate a pause can appear anywhere in the talk ''' def bbox(self): """ Return the bounding box """ return Bbox() # ---------------------------------------------------------------------- class Talk(Pages): """ A Talk class for seminar presentations """ def append(self, *slides_raw): """ Append slides to the Talk @param slides_raw: list of slides to append to the talk @type slides_raw: list """ slides = [] pg = 1 for slide in slides_raw: slide(label = str(pg)) pg += 1 # find any Pauses present pauses = [] f = slide.flatten() for ii in range(len(f)): if isinstance(f[ii][0], Pause): pauses.append(ii) for pause in pauses: # create a copy and remove everything from Pause onwards print "Found Pause(): splitting slide in two" s = slide.copy() fs = s.flatten() for obj, group in fs[pause:]: group.objects.remove(obj) slides.append(s) slides.append(slide) for slide in slides: Pages.append(self, slide) def write(self, fp, title="PyScriptPS"): """ Write the talk out to file @param fp: the file handle of the file to write to @type fp: filehandle @param title: the title to use in the output postscript @type title: string """ tot = len(self) for pp in range(tot): self[pp].make(page=pp, total=tot) Pages.write(self, fp, title) # ---------------------------------------------------------------------- class EmptySlide(Page): """ An empty slide class """ title = None orientation = "Landscape" size = "screen" def flatten(self, thegroup=None, objects=[]): ''' Return a flattened list of objects ''' if thegroup is None: objects = [] objects = self.flatten(self.objects, objects) else: for obj in thegroup: objects.append((obj, thegroup)) if isinstance(obj, Group): objects = self.flatten(obj, objects) return objects def append(self, *items, **options): """ Append items to the slide @param items: list of PyScript objects to append @type items: list @param options: dictionary of options (where to append object etc) @type options: dict """ a1 = options.get('a1', None) a2 = options.get('a2', None) if (a1 is not None) and (a2 is not None) and len(items)>0: assert a1 in ["n", "ne", "e", "se", "s", "sw", "w", "nw", "c"] assert a2 in ["n", "ne", "e", "se", "s", "sw", "w", "nw", "c"] area = self.main() setattr(items[0], a1, getattr(area, a2)) return Page.append(self, *items) def append_n(self, *items): """ Append items using the "north" attribute of the objects @param items: list of objects to append @type items: list """ return apply(self.append, items, {'a1':'n', 'a2':'n'}) def append_s(self, *items): """ Append items using the "south" attribute of the objects @param items: list of objects to append @type items: list """ return apply(self.append, items, {'a1':'s', 'a2':'s'}) def append_e(self, *items): """ Append items using the "east" attribute of the objects @param items: list of objects to append @type items: list """ return apply(self.append, items, {'a1':'e', 'a2':'e'}) def append_w(self, *items): """ Append items using the "west" attribute of the objects @param items: list of objects to append @type items: list """ return apply(self.append, items, {'a1':'w', 'a2':'w'}) def append_c(self, *items): """ Append items using the "centre" attribute of the objects @param items: list of objects to append @type items: list """ return apply(self.append, items, {'a1':'c', 'a2':'c'}) def main(self): """ Return the bounding box of the slide """ bbox = self.bbox() return bbox def make_back(self): """ Make the background of the slide """ return None def make_title(self): """ Make the title of the slide """ return None def clear(self): """ Clear the slide of objects """ Page.clear(self) def make(self, page=1, total=1): """ Make the slide @param page: the page in the sequence of slides to make (default=1) @type page: int @param total: the total number of slides in the talk (default=1) @type total: int """ self.page = page self.total = total b = self.make_back() if b is not None: self.insert(0, b) t = self.make_title() if t is not None: self.append(t) # vim: expandtab shiftwidth=4: