def __init__(self, elements_to_group=None, x='center', y='auto', width=None, height=None, background=None): if width != None: width = width if height != None: height = height self.id = document._global_counter['group'] self.args = {'group_id': self.id, "background": background} self.data = { 'args': self.args, 'id': self.id, 'content': '', 'render': render_group, 'type': 'group' } self.positionner = add_to_slide(self.data, x, y, width, height) document._contents[gcs()]['groups'] += [self.data] document._global_counter['group'] += 1 if elements_to_group != None: eids = [e.id for e in elements_to_group] print("[Beampy][%s] render %i elements in group %i" % (gcs(), len(eids), self.id)) render_group_content(eids, self.data)
def create_element_id( elem, use_args=True, use_render=True, use_content=True, add_slide=True, slide_position=True ): """ create a unique id for the element using element['content'] and element['args'].keys() and element['render'].__name__ """ from beampy.functions import gcs from beampy.document import document ct_to_hash = '' if add_slide: ct_to_hash += gcs() if use_args and 'args' in elem: ct_to_hash += ''.join(['%s:%s'%(k,v) for k,v in elem['args'].items()]) if use_render and 'render' in elem and elem['render'] != None: ct_to_hash += elem['render'].__name__ if use_content and 'content' in elem: ct_to_hash += str(elem['content']) if slide_position: ct_to_hash += str(len(document._contents[gcs()]['element_keys'])) outid = None if ct_to_hash != '': outid = hashlib.md5( ct_to_hash ).hexdigest() if outid in document._contents[gcs()]['element_keys']: print("Id for this element already exist!") sys.exit(0) outid = None #print outid return outid
def endgroup(): """ close the current group then computre the height and width of the group """ if len(document._contents[gcs()]['groups']) > 0: group = document._contents[gcs()]['groups'][-1] group['content_stop'] = len(document._contents[gcs()]['contents']) else: print('Error no begingroup() defined before')
def __enter__(self): #get the position of the first element content_start = len(document._contents[gcs()]['contents']) #Add it to the data dict self.data['content_start'] = content_start return self.positionner
def endgroup(): """ close the current group then computre the height and width of the group """ slide = document._contents[gcs()] if len(slide['groups']) > 0: slide['groups'][-1]['gp'].__exit__(None,None,None)
def register(self): #Function to register the module (run the function add to slide) #Save the id of the current slide for the module self.slide_id = gcs() #Ajout du nom du module self.name = self.get_name() #Create a unique id for this element self.id = create_element_id( self ) #print(elem_id) document._slides[self.slide_id].add_module(self.id, self) self.positionner = positionner( self.x, self.y , self.width, self.height, self.id) #Add anchors for relative positionning self.top = self.positionner.top self.bottom = self.positionner.bottom self.left = self.positionner.left self.right = self.positionner.right self.center = self.positionner.center #Report width, height from positionner to self.width, self.height self.width = self.positionner.width self.height = self.positionner.height #Add the source of the script that run this module start, stop, source = get_command_line( self.name ) self.call_cmd = source self.call_lines = (start, stop)
def __init__(self, title=None, doc=document, style=None): #Add a slide to the global counter if 'slide' in doc._global_counter: doc._global_counter['slide'] += 1 else: doc._global_counter['slide'] = 0 #Init group counter document._global_counter['group'] = 0 out = { 'title': title, 'contents': {}, 'num': doc._global_counter['slide'] + 1, 'groups': [], 'style': style, 'htmlout': '', #store rendered htmlelements inside the slide 'animout': [], #store svg rendered part of animatesvg 'scriptout': '', #store javascript defined in element['script'] 'cpt_anim': 0, 'element_keys': [] #list to store elements id in order } self.id = gcs() document._contents[self.id] = out if title != None: bptitle(title)
def __init__(self, title= None, doc = document, style=None): #Add a slide to the global counter if 'slide' in doc._global_counter: doc._global_counter['slide'] += 1 else: doc._global_counter['slide'] = 0 #Init group counter document._global_counter['group'] = 0 out = {'title':title, 'contents': {}, 'num':doc._global_counter['slide']+1, 'groups': [], 'style': style, 'htmlout': '', #store rendered htmlelements inside the slide 'animout': [], #store svg rendered part of animatesvg 'scriptout': '', #store javascript defined in element['script'] 'cpt_anim': 0, 'element_keys': [] #list to store elements id in order } self.id = gcs() document._contents[self.id] = out if title!= None: bptitle( title )
def run_render(self): """ Run the function self.render if the module is not in cache """ #Get the current slide object slide = document._slides[gcs()] if self.cache and document._cache != None: ct_cache = document._cache.is_cached('slide_%i' % slide.num, self) if ct_cache: #Update the state of the module to rendered self.rendered = True try: print("Elem [%s ...] from cache" % self.call_cmd.strip()[:20]) except: print("Elem %s from cache" % self.name) else: #print("element %i not cached"%ct['positionner'].id) self.render() document._cache.add_to_cache('slide_%i' % slide.num, self) try: print("Elem [%s ...] rendered" % self.call_cmd.strip()[:20]) except: print("Elem %s rendered" % self.name) else: self.render() try: print("Elem [%s ...] rendered" % self.call_cmd.strip()[:20]) except: print("Elem %s rendered" % self.name)
def __init__(self, title=None, **kwargs): # Add a slide to the global counter if 'slide' in document._global_counter: document._global_counter['slide'] += 1 else: document._global_counter['slide'] = 0 # Init group counter document._global_counter['group'] = 0 # check args from THEME self.args = check_function_args(slide, kwargs) # The id for this slide self.id = gcs() self.slide_num = document._global_counter['slide'] # Change from dict to class self.tmpout = '' self.contents = {} self.element_keys = [] self.cpt_anim = 0 self.num = document._global_counter['slide']+1 self.title = title self.curwidth = document._width self.num_layers = 0 # Store the number of layers in this slide # Store all outputs self.svgout = [] self.svgdefout = [] # Store module definition for slide self.htmlout = {} # Html is a dict, each key dict is a layer htmlout[0] = [html, html, html] etc... self.scriptout = [] self.animout = [] self.svgheader = '' self.svgfooter = '\n</svg>\n' self.svglayers = {} # Store slide final svg (without svg defs stored in self.svgdefout) for the given layer # Do we need to render the THEME layout on this slide self.render_layout = True # Add the slide to the document contents list document._slides[self.id] = self # Manage groups inside one slide the lower level of group is 0 and correspond # to the main slide self.groupsid = {} self.cur_group_level = -1 g0 = group(x=0, y=0, width=document._width, height=document._height) self.groupsid[0] = [g0.id] # store groups id objects in this list if title is not None: from beampy.modules.title import title as bptitle self.title_element = bptitle(title) self.ytop = float(convert_unit(self.title.reserved_y)) else: self.ytop = 0 self.title_element = None # Add ytop to the slide main group g0.yoffset = self.ytop
def register(self): #Function to register the module (run the function add to slide) #Save the id of the current slide for the module self.slide_id = gcs() #Ajout du nom du module self.name = self.get_name() #Create a unique id for this element self.id = create_element_id(self) #print(elem_id) document._slides[self.slide_id].add_module(self.id, self) self.positionner = positionner(self.x, self.y, self.width, self.height, self.id) #Add anchors for relative positionning self.top = self.positionner.top self.bottom = self.positionner.bottom self.left = self.positionner.left self.right = self.positionner.right self.center = self.positionner.center #Report width, height from positionner to self.width, self.height self.width = self.positionner.width self.height = self.positionner.height #Add the source of the script that run this module start, stop, source = get_command_line(self.name) self.call_cmd = source self.call_lines = (start, stop)
def __init__(self, titlein, **kwargs): """ Add a title to a slide """ #Set the type as text self.type = 'text' #Check function arguments from THEME self.check_args_from_theme(kwargs) self.content = titlein #Add text arguments because we use the text render self.load_extra_args('text') #Re-compute the title when color or size is changed self.args_for_cache_id = ['color', 'size'] if self.width == None: self.width = document._width self.height = None #Add the title to the slide document._slides[gcs()].title = self #Register this module self.register()
def __init__(self, textin=None, **kwargs): self.type = 'text' self.check_args_from_theme(kwargs) self.content = textin self.svgtext = '' # To store the svg produced by latex # Height of text is None (it need to be computed) self.height = None # Check width if self.width is None: self.width = document._slides[gcs()].curwidth # Add special args for cache id # Text need to be re-rendered from latex if with, color or size are changed self.initial_width = self.width self.args_for_cache_id = [ 'initial_width', 'color', 'size', 'align', 'opacity' ] # Initialise the global store on document._content to store letter if 'svg_glyphs' not in document._contents: document._contents['svg_glyphs'] = {} if self.extra_packages != []: auto_render = True else: auto_render = False # Register the function to the current slide self.register(auto_render=auto_render)
def __init__(self, elements_to_group=None, x='center', y='auto', width = None, height = None, background=None): #Store the input of the module self.width = width self.height = height self.x = x self.y = y self.background = background self.type = 'group' self.content = [] #TODO: remode these to belows #self.args = {'group_id': self.group_id, "background": background} #self.data = {'args': self.args, 'id': self.group_id, 'content':'', # 'render':render_group,'type':'group'} #Add group id from the global group counter self.idg = document._global_counter['group'] document._slides[gcs()].groups += [ self ] document._global_counter['group'] += 1 #Add classic register to the slide self.register() if elements_to_group != None: eids = [e.id for e in elements_to_group] print("[Beampy][%s] render %i elements in group %i"%(self.slide_id, len(eids), self.idg)) self.render_ingroup( eids )
def endgroup(): """ close the current group then computre the height and width of the group """ slide = document._contents[gcs()] if len(slide['groups']) > 0: slide['groups'][-1].__exit__(None, None, None)
def __init__(self, elements_to_group=None, x='center', y='auto', width=None, height=None, background=None): #Store the input of the module self.width = width self.height = height self.x = x self.y = y self.background = background self.type = 'group' self.content = [] #TODO: remode these to belows #self.args = {'group_id': self.group_id, "background": background} #self.data = {'args': self.args, 'id': self.group_id, 'content':'', # 'render':render_group,'type':'group'} #Add group id from the global group counter self.idg = document._global_counter['group'] document._slides[gcs()].groups += [self] document._global_counter['group'] += 1 #Add classic register to the slide self.register() if elements_to_group != None: eids = [e.id for e in elements_to_group] print("[Beampy][%s] render %i elements in group %i" % (self.slide_id, len(eids), self.idg)) self.render_ingroup(eids)
def run_render(self): """ Run the function self.render if the module is not in cache """ #Get the current slide object slide = document._slides[gcs()] if self.cache and document._cache != None: ct_cache = document._cache.is_cached('slide_%i'%slide.num, self) if ct_cache: #Update the state of the module to rendered self.rendered = True try: print("Elem [%s ...] from cache"%self.call_cmd.strip()[:20]) except: print("Elem %s from cache"%self.name) else: #print("element %i not cached"%ct['positionner'].id) self.render() document._cache.add_to_cache('slide_%i'%slide.num, self) try: print("Elem [%s ...] rendered"%self.call_cmd.strip()[:20]) except: print("Elem %s rendered"%self.name) else: self.render() try: print("Elem [%s ...] rendered"%self.call_cmd.strip()[:20]) except: print("Elem %s rendered"%self.name)
def code( codetext, x='center', y='auto', width=None, height=None, langage=None, size="14px" ): """ Color a given code using python pygment option ------ langage[None]: try to infer the lexer from codetext size['9px']: size of the text """ if width == None: width = str(document._width) if height == None: height = str(document._height) args = {"x":str(x), "y": str(y) , "width": str(width), "height": str(height), "langage": langage, 'font-size': size } codeout = {'type': 'code', 'content': codetext, 'args': args, "render": render_code} if is_pigment: document._contents[gcs()]['contents'] += [ codeout ] else: print("Python pygment is not installed, I can't translate code into svg...")
def __init__(self, title= None, **kwargs): #Add a slide to the global counter if 'slide' in document._global_counter: document._global_counter['slide'] += 1 else: document._global_counter['slide'] = 0 #Init group counter document._global_counter['group'] = 0 #check args from THEME self.args = check_function_args(slide, kwargs) out = {'title':title, 'contents': {}, 'num':document._global_counter['slide']+1, 'groups': [], "args": self.args, 'htmlout': '', #store rendered htmlelements inside the slide 'animout': [], #store svg rendered part of animatesvg 'scriptout': '', #store javascript defined in element['script'] 'cpt_anim': 0, 'element_keys': [] #list to store elements id in order } #The id for this slide self.id = gcs() #Change from dict to class self.tmpout = out self.contents = out['contents'] self.element_keys = out['element_keys'] self.cpt_anim = 0 self.num = out['num'] self.title = title #Store all outputs self.svgout = [] self.htmlout = [] self.scriptout = [] self.animout = [] self.svgheader = '' self.svgfooter = '\n</svg>\n' self.cpt_anim = 0 self.groups = [] #Add the slide to the document contents list document._contents[self.id] = out document._slides[self.id] = self if title!= None: from beampy.modules.title import title as bptitle bptitle( title ) self.ytop = float(convert_unit(self.title.reserved_y)) else: self.ytop = 0
def __init__(self, x, y, width, height, elem_id): """ This class need position x and y of the element width and height is the size of the svg or html object (need to be updated after rendering) x, y: could be a dict, a float or a string Default position dict: ====================== {'align': 'left', 'reference': 'slide', 'shift': 0, 'unit': 'rel'} align: define the alignement for the coordinate ('center' (page centering),'left','right','middle', 'top','bottom') reference: 'relative' element for placement or 'slide' relative to slide 'slide' for placement refenreced on slide shift: the value of the shift unit: 'width': relative to page (or group) width 'height': relative to page (or group) height 'cm', 'pt', 'px': shift value unit anchor: 'left', 'right', 'middle', 'top', 'bottom', define the anchor on the object bounding-box """ # Create and id (positition in the dict of this element) self.id = elem_id self.slideid = gcs() try: self.id_index = document._slides[self.slideid].element_keys.index( self.id) except IndexError: print('Element not found in document._content[gcs()].element_keys') self.id_index = -1 self.update_size(width, height) # Make copy if x and y are dict input if type(x) == type(dict()): self.x = x.copy() else: self.x = x if type(y) == type(dict()): self.y = y.copy() else: self.y = y # print({"id":self.id,'y':self.y}) # print(self.x) # Need to convert x, y to a standart dict self.convert_position() # Compute elements anchors self.compute_anchors()
def __exit__(self, type, value, traceback): #link the current slide slide = document._contents[gcs()] if len(slide['groups']) > self.id: #Get the last group group = slide['groups'][self.id] group['content_stop'] = len(slide['contents']) elementids = slide['element_keys'][group['content_start']:group['content_stop']] print("[Beampy][%s] render %i elements in group %i"%(gcs(), len(elementids), self.id)) #render the content of a given group render_group_content(elementids, self.data) else: print('The begining of the group as not been defined') print(slide['groups']) sys.exit(0)
def __init__(self, x, y, width, height, elem_id): """ This class need position x and y of the element width and height is the size of the svg or html object (need to be updated after rendering) x, y: could be a dict, a float or a string Default position dict: ====================== {'align': 'left', 'reference': 'slide', 'shift': 0, 'unit': 'rel'} align: define the alignement for the coordinate ('center' (page centering),'left','right','middle', 'top','bottom') reference: relative element for placement or 'slide' relative to slide shift: the value of the shift unit: 'width': relative to page (or group) width 'height': relative to page (or group) height 'cm', 'pt', 'px': shift value unit """ #Create and id (positition in the dict of this element) self.id = elem_id try: self.id_index = document._contents[gcs()]['element_keys'].index(self.id) except: print('Element not found in document._content[gcs()]') self.id_index = -1 #Content not stored in slide contents (like group) self.width = width self.height = height #Make copy if x and y are dict input if type(x) == type(dict()): self.x = x.copy() else: self.x = x if type(y) == type(dict()): self.y = y.copy() else: self.y = y #print({"id":self.id,'y':self.y}) #print(self.x) #Need to convert x, y to a standart dict self.convert_position() #Compute elements anchors self.compute_anchors()
def __init__(self, iframe_content, **kwargs): self.type = 'svg' # Update arguments for the module self.load_args(kwargs) self.content = iframe_content # Check that a width/height is given or set the curent width/height if self.width is None: self.width = document._slides[gcs()].curwidth _log.info('Set the width to curwidth: %s' % self.width) if self.height is None: self.height = document._slides[gcs()].curheight _log.info('Set the height to curheight: %s' % self.height) # Register the module self.register()
def begingroup(x='center',y='auto', width = None, height = None, background=None): """ start a group """ #check global counter if 'group' in document._global_counter: document._global_counter['group'] += 1 else: document._global_counter['group'] = 0 if width != None: width = str(width) if height != None: height = str(height) args = {'x': str(x), 'y': str(y), 'width': width, 'height': height, 'group_id': document._global_counter['group'], "background": background} tmp = {'args': args, 'content_start': len(document._contents[gcs()]['contents'])} document._contents[gcs()]['groups'] += [ tmp ]
def create_element_id(elem, use_args=True, use_render=True, use_content=True, add_slide=True, slide_position=True): """ create a unique id for the element using element['content'] and element['args'].keys() and element['render'].__name__ """ from beampy.functions import gcs from beampy.document import document ct_to_hash = '' if add_slide: ct_to_hash += gcs() if use_args and 'args' in elem: ct_to_hash += ''.join( ['%s:%s' % (k, v) for k, v in elem['args'].items()]) if use_render and 'render' in elem and elem['render'] != None: ct_to_hash += elem['render'].__name__ if use_content and 'content' in elem: ct_to_hash += str(elem['content']) if slide_position: ct_to_hash += str(len(document._contents[gcs()]['element_keys'])) outid = None if ct_to_hash != '': outid = hashlib.md5(ct_to_hash).hexdigest() if outid in document._contents[gcs()]['element_keys']: print("Id for this element already exist!") sys.exit(0) outid = None #print outid return outid
def __init__(self, elements_to_group=None, x='center', y='auto', width = None, height = None, background=None): if width != None: width = width if height != None: height = height self.id = document._global_counter['group'] self.args = {'group_id': self.id, "background": background} self.data = {'args': self.args, 'id': self.id, 'content':'', 'render':render_group,'type':'group'} self.positionner = add_to_slide(self.data, x, y, width, height) document._contents[gcs()]['groups'] += [ self.data ] document._global_counter['group'] += 1 if elements_to_group != None: eids = [e.id for e in elements_to_group] print("[Beampy][%s] render %i elements in group %i"%(gcs(), len(eids), self.id)) render_group_content( eids, self.data )
def __exit__(self, type, value, traceback): #link the current slide slide = document._contents[gcs()] if len(slide['groups']) > self.id: #Get the last group group = slide['groups'][self.id] group['content_stop'] = len(slide['contents']) elementids = slide['element_keys'][ group['content_start']:group['content_stop']] print("[Beampy][%s] render %i elements in group %i" % (gcs(), len(elementids), self.id)) #render the content of a given group render_group_content(elementids, self.data) else: print('The begining of the group as not been defined') print(slide['groups']) sys.exit(0)
def maketitle(*args, **kwargs): """ Create title content for the presentation. The function could be overwritten by Beampy theme. Theme could add or change the arguments of :py:func:`maketitle` function. The default theme use the set of parameters described below. These arguments should not be modified by other themes. Parameters ---------- titlein : str The title of the presentation. author : str or list of str or None, optional The name of the author, or the list of author names (the default value is None). subtitle : str or None, optional The subtitle for the presentation (the default value is None). date : str or {'Today', 'today', 'now'} or None, optional The date for the presentation (the default value is None). If `date` is a str it will be displayed as the given str. If the `date` is either 'Today' or 'today' or 'now' it will replaced automatically by the current date. title_width : int or float or str or None, optional The width of the `titlein` text (the default is value is None, which implies `tilte_width` = `document._width` * 0.75). If a `title_width` is given it is passed to the `beampy.text()` module to constrain the width of the `titlein` text. vert_space : int or float or str or None, optional The vertical spacing of maketitle elements (the default value is None, which implies `vert_space` = `document._height` * 0.75). `vert_space` could take any values allowed by the positioning system of beampy (see `x` or `y` allowed values for :py:mod:`beampy.base_module`). """ # get_command_line(maketitle) # Check function arguments from THEME # The maketitle disable the layout that could be defined in THEME['slide'] slide = document._slides[gcs()] slide.render_layout = False try: document._theme['maketitle']['template'](*args, **kwargs) except: default_maketitle(*args, **kwargs)
def __init__(self, files_in, **kwargs): # Add type self.type = 'animatesvg' # Check input args for this module self.check_args_from_theme(kwargs) # Cache is useless because we call figure function which handle the cache for each figures self.cache = False slide = document._slides[gcs()] # Add +1 to counter self.anim_num = slide.cpt_anim slide.cpt_anim += 1 input_width = self.width # Save the input width for mpl figures if self.width is None: self.width = slide.curwidth # Read all files from a given wildcard if isinstance(files_in, str): svg_files = glob.glob(files_in) # Need to sort using the first digits finded in the name svg_files = sorted( svg_files, key=lambda x: int(''.join(re.findall(r'\d+', x)))) # If the input is a list of names or mpl figures or other compatible with figure elif isinstance(files_in, list): svg_files = files_in if input_width is None: width_inch, height_inch = files_in[0].get_size_inches() self.width = convert_unit("%fin" % (width_inch)) else: print('Unknown input type for files_folder') sys.exit(0) # check how many images we wants if self.end == 'end': self.end = len(svg_files) # Add content self.content = svg_files[self.start:self.end] # Register the module self.register()
def maketitle(*args, **kwargs): """ Function to create the presentation title slide """ #get_command_line(maketitle) #Check function arguments from THEME #The maketitle disable the layout that could be defined in THEME['slide'] slide = document._slides[gcs()] slide.render_layout = False try: document._theme['maketitle']['template'](*args, **kwargs) except: default_maketitle(*args, **kwargs)
def animatesvg(files_folder, start=0, end='end', x='center',y='auto', width=None, height=None, fps=25, autoplay=False): """ Function to create svg animation from a folder containing svg files - files_folder: Folder containing svg like "./my_folder/" - x['center']: x coordinate of the image 'center': center image relative to document._width '+1cm": place image relative to previous element - y['auto']: y coordinate of the image 'auto': distribute all slide element on document._height 'center': center image relative to document._height (ignore other slide elements) '+3cm': place image relative to previous element """ if width == None: width = str(document._width) if height == None: height = str(document._height) args = {"x":str(x), "y": str(y) , "width": str(width), "height": str(height), "fps": fps, "autoplay": autoplay} #Read all svg files svg_files = glob.glob(files_folder+'*.svg') #Need to sort using the first digits finded in the name svg_files = sorted(svg_files, key=lambda x: int(''.join(re.findall(r'\d+', x)))) #check how many images we wants if end == 'end': end = len(svg_files) svg_files = svg_files[start:end] svgcontent = [] for svgf in svg_files: with open(svgf,'r') as f: svgcontent += [f.read()] animout = {'type': 'animatesvg', 'content': svgcontent, 'args': args, "render": render_animatesvg} document._contents[gcs()]['contents'] += [ animout ]
def register(self): # Function to register the module (run the function add to slide) # Save the id of the current slide for the module self.slide_id = gcs() # Store the list of groups id where the module is finally located (used for html element to # resolve final positionning) self.groups_id = [] # Ajout du nom du module self.name = self.get_name() # Create a unique id for this element self.id = create_element_id( self ) # Store the layers where this module should be printed self.layers = [0] # Store a boolean to know if this module has been exported to slide store self.exported = False # Add module to his slide document._slides[self.slide_id].add_module(self.id, self) self.positionner = positionner( self.x, self.y , self.width, self.height, self.id) # Add anchors for relative positionning self.top = self.positionner.top self.bottom = self.positionner.bottom self.left = self.positionner.left self.right = self.positionner.right self.center = self.positionner.center # Report width, height from positionner to self.width, self.height self.width = self.positionner.width self.height = self.positionner.height # Add the source of the script that run this module try: start, stop, source = get_command_line(self.name) except: start = 0 stop = 0 source = 'None' self.call_cmd = source self.call_lines = (start, stop)
def text( textin, x='center', y='auto', width=None, color="", size="", align='', font="", usetex = True): """ Function to add a text to the current slide Options ------- - x['center']: x coordinate of the image 'center': center image relative to document._width '+1cm": place image relative to previous element - y['auto']: y coordinate of the image 'auto': distribute all slide element on document._height 'center': center image relative to document._height (ignore other slide elements) '+3cm': place image relative to previous element Exemples -------- text('this is my text', '20', '20') """ if width == None: width = str(document._width) else: width = str(width) args = {"x":str(x), "y": str(y) ,"font": font, "width": width, "font-size": size, "fill": color, 'align':align, 'usetex': usetex } #Load theme properties if document._theme != None: if color == "": args['fill'] = document._theme.get('text','color') if size == "": args['font-size'] = int(document._theme.get('text','size')) if font == "": args['font'] = document._theme.get('text','font') textout = {'type': 'text', 'content': textin, 'args': args, "render": render_text} #Add text to the document document._contents[gcs()]['contents'] += [ textout ]
def __init__(self, elements_to_group=None, x='center', y='auto', width=None, height=None, background=None, parendid=None): # Store the input of the module self.width = width self.height = height self.x = x self.y = y self.background = background self.type = 'group' self.content = [] self.xoffset = 0 self.yoffset = 0 self.parentid = parendid # Store the id of the parent group #To store elements id to group self.elementsid = [] self.element_keys = [] self.autoxid = [] self.autoyid = [] self.manualid = [] self.htmlid = [] # Store html module id self.content_layer = {} # Store group rendered svg content by layers # To store layers inside groups # self.layers_elementsid = {} # self.svgout_layers = {} # To store svg output by layers #Add the parentid if level is more than 1 slide = document._slides[gcs()] if slide.cur_group_level >= 0: self.parentid = slide.contents[slide.groupsid[slide.cur_group_level][-1]].id #Add group id from the slide groups counter (and add one because it's a new one slide.cur_group_level += 1 self.grouplevel = slide.cur_group_level # Add classic register to the slide self.register() self.group_id = self.id if elements_to_group is not None: print('TODO: Need to remove these elements from previous group!') for e in elements_to_group: self.add_elements_to_group(e.id, e)
def video(videofile, width=None, height=None, x='center',y='auto', fps=25, autoplay=False): """ Add video in webm format width and height must be specified ! width = None -> document._width heigh = None -> document._height """ #if no width is given get the default width if width == None: width = str(document._width) else: width=str(width) if height != None: width = str(width) #check extension ext = None if '.webm' in videofile.lower(): ext = 'webm' elif '.ogg' in videofile.lower(): ext = 'ogg' elif '.mp4' in videofile.lower(): ext = 'mp4' else: print('Video need to be in webm/ogg/mp4(h.264) format!') if ext != None: args = {"x":str(x), "y": str(y) , "width": width, "height": height, "fps": fps, "autoplay": autoplay, "ext": ext, 'filename': videofile} with open(videofile, 'rb') as fin: datain = base64.b64encode( fin.read() ) #Add video to the document type_nohtml used to remplace video by svg still image when not exported to HTML5 videout = {'type': 'html', 'type_nohtml': 'svg', 'content': datain, 'args': args, "render": render_video} document._contents[gcs()]['contents'] += [ videout ]
def tikz(tikscommands, x='0', y='0', tikz_header=None, tex_packages=None, figure_options=None): """ Function to render tikz commands to svg options: -------- - tikz_header: allow to add extra tikslibraries and style, everything that is included befor \begin{document} exp: tikz_header = " \usetikzlibrary{shapes.geometric} % Vector Styles \tikzstyle{load} = [ultra thick,-latex] \tikzstyle{stress} = [-latex] \tikzstyle{dim} = [latex-latex] \tikzstyle{axis} = [-latex,black!55] " - tex_packages: Add extra \usepackages in tex document. Need to be a list of string expl: tex_packages = ['xolors','tikz-3dplot'] - figure_options: options for \begin{tikzfigure}[options] """ args = {"x":str(x), "y": str(y) } if tikz_header: args['tikzheader'] = tikz_header if tex_packages: args['tex_packages'] = tex_packages if figure_options: args['tikzfigureoptions'] = figure_options textout = {'type': 'tikz', 'content': tikscommands, 'args': args, "render": render_tikz} document._contents[gcs()]['contents'] += [ textout ]
def slide(title= None, doc = document, style=None): """ Function to add a slide to the presentation """ #Add a slide to the global counter if 'slide' in doc._global_counter: doc._global_counter['slide'] += 1 else: doc._global_counter['slide'] = 0 out = {'title':title, 'contents':[], 'num':doc._global_counter['slide']+1, 'groups': [], 'style': style} document._contents[gcs()] = out if title!= None: bptitle( title )
def title( title_text , **kwargs): """ Add a title to a slide """ #Check function arguments from THEME args = check_function_args(title, kwargs) #Add text arguments because we use the text render args = inherit_function_args('text', args) if args['width'] == None: args['width'] = document._width titleout = {'type': 'svg', 'content':title_text, "args":args, "render": render_text } document._contents[gcs()]['title']=titleout out = add_to_slide(titleout, args['x'], args['y'], width=args['width'], height=None)
def __exit__(self, type, value, traceback): #link the current slide slide = document._slides[self.slide_id] if len(slide.groups) > self.idg: #Get the last group self.content_stop = len(slide.contents) elementids = slide.element_keys[self.content_start:self.content_stop] if len(elementids) > 0: print("[Beampy][%s] render %i elements in group %i"%(gcs(), len(elementids), self.idg)) #render the content of a given group self.render_ingroup( elementids ) else: print('The begining of the group as not been defined') print( slide.groups ) sys.exit(0)
def title( title, usetex=True): """ Add a title to a slide """ args = {"x":"0.5cm" , "y": "1.2cm", "reserved_y":"1.4cm", "font": "CMR", "font-size": 28, "fill": "#3333b3", 'usetex': usetex, 'align':'', 'ha':'left', 'va':'baseline' } #Load theme properties if document._theme != None: args['fill'] = document._theme.get('title','color') args['size'] = document._theme.get('title','size') args['x'] = document._theme.get('title','x') args['y'] = document._theme.get('title','y') args['reserved_y'] = document._theme.get('title','yspace') titleout = {'type': 'svg', 'content':title, "args":args, "render": render_text } document._contents[gcs()]['title']=titleout
def __exit__(self, type, value, traceback): #link the current slide slide = document._slides[self.slide_id] if len(slide.groups) > self.idg: #Get the last group self.content_stop = len(slide.contents) elementids = slide.element_keys[self.content_start:self. content_stop] if len(elementids) > 0: print("[Beampy][%s] render %i elements in group %i" % (gcs(), len(elementids), self.idg)) #render the content of a given group self.render_ingroup(elementids) else: print('The begining of the group as not been defined') print(slide.groups) sys.exit(0)
def __init__(self, titlein, **kwargs): """ Add a title to a slide """ # Set the type as text self.type = "text" # Check function arguments from THEME self.check_args_from_theme(kwargs) self.content = titlein # Add text arguments because we use the text render self.load_extra_args("text") if self.width == None: self.width = document._width self.height = None # Add the title to the slide document._slides[gcs()].title = self # Register this module self.register()
def delete(self): #Remove from document document._slides[gcs()].remove_module(self.id) del self
def render(self): """ Latex -> dvi -> svg for tikz image """ tikzcommands = self.content #args = ct['args'] tex_pt_to_px = 96 / 72.27 #replace '\slidewidth' tiktikzcommands = tikzcommands.replace( r'\slidewidth', '%ipt' % (0.75 * document._slides[gcs()].curwidth)) #Include extrac packages for tex if getattr(self, 'tex_packages', False): extra_tex_packages = '\n'.join( ['\\usepackages{%s}' % pkg for pkg in self.tex_packages]) else: extra_tex_packages = '' #Include extra tikz headers if getattr(self, 'tikz_header', False): extra_tex_packages += '\n%s' % (self.tikz_header) #Tikzfigure options in [] if getattr(self, 'figure_options', False): tikz_fig_opts = '[' + self.figure_options + ']' else: tikz_fig_opts = '' # Render to a dvi file pretex = """ \\documentclass[tikz,svgnames]{standalone} \\usepackage[utf8x]{inputenc} %s \\begin{document} \\begin{tikzpicture}%s %s \\end{tikzpicture} \\end{document} """ % (extra_tex_packages, tikz_fig_opts, tikzcommands) #latex2svg svgout = latex2svg(pretex) if svgout != '': #Parse the svg soup = BeautifulSoup(svgout, 'xml') #Find the width and height svgsoup = soup.find('svg') g = soup.find('g') xinit, yinit, tikz_width, tikz_height = svgsoup.get( 'viewBox').split() tikz_width = float(tikz_width) * tex_pt_to_px tikz_height = float(tikz_height) * tex_pt_to_px print(tikz_width, tikz_height) # Default is args['figure_anchor'] == top_left dx = -float(xinit) dy = -float(yinit) print(self.positionner.x, self.positionner.y) if 'bottom' in self.figure_anchor: self.positionner.y['anchor'] = 'bottom' if 'right' in self.figure_anchor: self.positionner.x['anchor'] = 'right' newmatrix = 'scale(%0.3f) translate(%0.1f,%0.1f)' % (tex_pt_to_px, dx, dy) g['transform'] = newmatrix output = svgsoup.renderContents() else: output = '' tikz_height = 0 tikz_width = 0 self.update_size(tikz_width, tikz_height) self.svgout = output self.rendered = True
def create_element_id( bp_mod, use_args=True, use_render=True, use_content=True, add_slide=True, slide_position=True, use_size = False ): """ create a unique id for the element using element['content'] and element['args'].keys() and element['render'].__name__ """ from beampy.functions import gcs from beampy.document import document ct_to_hash = '' if add_slide: ct_to_hash += gcs() if use_args and hasattr(bp_mod, 'args'): ct_to_hash += ''.join(['%s:%s'%(k,v) for k,v in bp_mod.args.items()]) if use_render and bp_mod.name != None: ct_to_hash += bp_mod.name if use_content and bp_mod.content != None: ct_to_hash += str(bp_mod.content) if use_size: if 'height' in bp_mod.args: h = bp_mod.args['height'] else: h = 'None' if 'width' in bp_mod.args: w = bp_mod.args['width'] else: w = 'None' ct_to_hash += '(%s,%s)'%(str(w), str(h)) if slide_position: ct_to_hash += str(len(document._slides[gcs()].element_keys)) if bp_mod.args_for_cache_id != None: for key in bp_mod.args_for_cache_id: try: tmp = getattr(bp_mod, key) ct_to_hash += str(tmp) except: print('No parameters %s for cache id for %s'%(key, bp_mod.name)) outid = None if ct_to_hash != '': #print ct_to_hash outid = hashlib.md5( ct_to_hash ).hexdigest() if outid in document._slides[gcs()].element_keys: print("Id for this element already exist!") sys.exit(0) outid = None #print outid return outid
def create_element_id(bp_mod, use_args=True, use_render=True, use_content=True, add_slide=True, slide_position=True, use_size=False): """ create a unique id for the element using element['content'] and element['args'].keys() and element['render'].__name__ """ from beampy.functions import gcs from beampy.document import document ct_to_hash = '' if add_slide: ct_to_hash += gcs() if use_args and hasattr(bp_mod, 'args'): ct_to_hash += ''.join( ['%s:%s' % (k, v) for k, v in bp_mod.args.items()]) if use_render and bp_mod.name != None: ct_to_hash += bp_mod.name if use_content and bp_mod.content != None: ct_to_hash += str(bp_mod.content) if use_size: if 'height' in bp_mod.args: h = bp_mod.args['height'] else: h = 'None' if 'width' in bp_mod.args: w = bp_mod.args['width'] else: w = 'None' ct_to_hash += '(%s,%s)' % (str(w), str(h)) if slide_position: ct_to_hash += str(len(document._slides[gcs()].element_keys)) if bp_mod.args_for_cache_id != None: for key in bp_mod.args_for_cache_id: try: tmp = getattr(bp_mod, key) ct_to_hash += str(tmp) except: print('No parameters %s for cache id for %s' % (key, bp_mod.name)) outid = None if ct_to_hash != '': #print ct_to_hash outid = hashlib.md5(ct_to_hash).hexdigest() if outid in document._slides[gcs()].element_keys: print("Id for this element already exist!") sys.exit(0) outid = None #print outid return outid
def figure(filename,x='center',y='auto', width=None, height=None, ext=None): """ Add figure to current slide Accepted format: [svg, png, jpeg, bokeh figure] - x['center']: x coordinate of the image 'center': center image relative to document._width '+1cm": place image relative to previous element - y['auto']: y coordinate of the image 'auto': distribute all slide element on document._height 'center': center image relative to document._height (ignore other slide elements) '+3cm': place image relative to previous element - width[None]: Image width, if None: width = document._width - height[None]: Image heigt - ext[None]: Image format, if None, format is guessed from filename. """ #Check if the given filename is a string if type(filename) == type(''): #Check extension if ext == None: if '.svg' in filename.lower(): ext = 'svg' if '.png' in filename.lower(): ext = 'png' if ( '.jpeg' in filename.lower() ) or ( '.jpg' in filename.lower() ): ext = 'jpeg' if '.pdf' in filename.lower(): ext = 'pdf' else: if "bokeh" in str(type(filename)): ext = 'bokeh' ###################################### if ext == None: print("figure format can't be guessed from file name") #Bokeh image elif ext == 'bokeh': #print('I got a bokeh figure') figscript, figdiv = components(filename, wrap_script=False) #Todo get width and height from a bokeh figure if width == None: width = '%ipx'%filename.plot_width if height == None: height = '%ipx'%filename.plot_height #Transform figscript to givea function name load_bokehjs tmp = figscript.splitlines() goodscript = '\n'.join( ['["load_bokeh"] = function() {'] + tmp[1:-1] + ['};\n'] ) args = {"x":str(x), "y": str(y) , "width": str(width), "height": str(height), "ext": ext, 'script':goodscript} figout = {'type': 'html', 'content': figdiv, 'args': args, 'render': render_figure} document._contents[gcs()]['contents'] += [ figout ] #Other filetype images else: if width == None: width = str(document._width) else: width = str(width) if height != None: height = str(height) args = {"x":str(x), "y": str(y) , "width": width, "height": height, "ext": ext, 'filename':filename } if ext == 'pdf' : figdata = convert_pdf_to_svg( filename ) else : with open(filename,"r") as f: figdata = f.read() #If it's png/jpeg figure we need to encode them to base64 if ext in ( 'png', 'jpeg' ): figdata = base64.encodestring(figdata) figout = {'type': 'figure', 'content': figdata, 'args': args, "render": render_figure} document._contents[gcs()]['contents'] += [ figout ]
def __init__(self, title=None, **kwargs): #Add a slide to the global counter if 'slide' in document._global_counter: document._global_counter['slide'] += 1 else: document._global_counter['slide'] = 0 #Init group counter document._global_counter['group'] = 0 #check args from THEME self.args = check_function_args(slide, kwargs) out = { 'title': title, 'contents': {}, 'num': document._global_counter['slide'] + 1, 'groups': [], "args": self.args, 'htmlout': '', #store rendered htmlelements inside the slide 'animout': [], #store svg rendered part of animatesvg 'scriptout': '', #store javascript defined in element['script'] 'cpt_anim': 0, 'element_keys': [] #list to store elements id in order } #The id for this slide self.id = gcs() self.slide_num = document._global_counter['slide'] #Change from dict to class self.tmpout = out self.contents = out['contents'] self.element_keys = out['element_keys'] self.cpt_anim = 0 self.num = out['num'] self.title = title #Store all outputs self.svgout = [] self.htmlout = [] self.scriptout = [] self.animout = [] self.svgheader = '' self.svgfooter = '\n</svg>\n' #Do we need to render the THEME layout on this slide self.render_layout = True #If we want to add background slide decodaration like header-bar or footer informations self.cpt_anim = 0 self.groups = [] #Add the slide to the document contents list document._contents[self.id] = out document._slides[self.id] = self if title != None: from beampy.modules.title import title as bptitle bptitle(title) self.ytop = float(convert_unit(self.title.reserved_y)) else: self.ytop = 0
def render_group_content(content_ids, group): """ Function to render each elements inside one group defined by their id in content_ids """ #link the current slide slide = document._contents[gcs()] #Add the group id to all the elements in this group cptcache = 0 groupsid = content_ids #print(groupsid) #First loop render elements in group allwidth = [] allheight = [] for k in groupsid: elem = slide['contents'][k] #Add group id to the element elem['group_id'] = group['positionner'].id #Render the element or read rendered svg from cache cptcache = render_content(elem, cptcache) #Get element size allwidth += [elem['positionner'].width] allheight += [ elem['positionner'].height + elem['positionner'].y['shift'] ] #Compute group size if needed if group['positionner'].width == None: group['positionner'].width = max(allwidth) if group['positionner'].height == None: group['positionner'].height = sum(allheight) #Re loop over element to place them all_height = {} #To store height of element for automatic placement for i, key in enumerate(groupsid): elem = slide['contents'][key] if elem['positionner'].y['align'] == 'auto': all_height[i] = {"height": elem['positionner'].height, "id": key} else: elem['positionner'].place( (group['positionner'].width, group['positionner'].height)) #Check if we have javascript to output if 'script' in elem['args']: slide['scriptout'] += ct['args']['script'] #Manage autoplacement #print(all_height) if all_height != {}: auto_place_elements( all_height, (group['positionner'].width, group['positionner'].height), 'y', slide['contents'], ytop=0) for key in groupsid: elem = slide['contents'][key] if 'rendered' in elem and elem['type'] not in ['html']: #Add rendered content to groupcontent group['content'], slide['animout'], slide['htmlout'], slide[ 'cpt_anim'] = write_content(elem, group['content'], slide['animout'], slide['htmlout'], slide['cpt_anim']) #remove the rendered part from slide content and set render to None #slide['contents'].pop(key) #slide['element_keys'].pop(slide['element_keys'].index(key)) slide['contents'][key][ 'render'] = None #not processed in the next loop slide['contents'][key].pop('rendered')
def render_group_content(content_ids, group): """ Function to render each elements inside one group defined by their id in content_ids """ #link the current slide slide = document._contents[gcs()] #Add the group id to all the elements in this group cptcache = 0 groupsid = content_ids #print(groupsid) #First loop render elements in group allwidth = [] allheight = [] for k in groupsid: elem = slide['contents'][k] #Add group id to the element elem['group_id'] = group['positionner'].id #Render the element or read rendered svg from cache cptcache = render_content(elem, cptcache) #Get element size allwidth += [elem['positionner'].width] allheight += [elem['positionner'].height+elem['positionner'].y['shift']] #Compute group size if needed if group['positionner'].width == None: group['positionner'].width = max( allwidth ) if group['positionner'].height == None: group['positionner'].height = sum( allheight ) #Re loop over element to place them all_height = {} #To store height of element for automatic placement for i, key in enumerate(groupsid): elem = slide['contents'][key] if elem['positionner'].y['align'] == 'auto': all_height[i] = {"height":elem['positionner'].height, "id":key} else: elem['positionner'].place( (group['positionner'].width, group['positionner'].height) ) #Check if we have javascript to output if 'script' in elem['args']: slide['scriptout'] += ct['args']['script'] #Manage autoplacement #print(all_height) if all_height != {}: auto_place_elements(all_height, (group['positionner'].width, group['positionner'].height), 'y', slide['contents'], ytop=0) for key in groupsid: elem = slide['contents'][key] if 'rendered' in elem and elem['type'] not in ['html']: #Add rendered content to groupcontent group['content'], slide['animout'], slide['htmlout'], slide['cpt_anim'] = write_content(elem, group['content'], slide['animout'], slide['htmlout'], slide['cpt_anim']) #remove the rendered part from slide content and set render to None #slide['contents'].pop(key) #slide['element_keys'].pop(slide['element_keys'].index(key)) slide['contents'][key]['render'] = None #not processed in the next loop slide['contents'][key].pop('rendered')
def __init__(self, content, ext=None, **kwargs): # The type of the module self.type = 'svg' # Add the extra args to the module self.check_args_from_theme(kwargs) self.ext = ext # Register the content self.content = content # Special args for cache id self.args_for_cache_id = ['width', 'ext'] # Check if the given filename is a string if isinstance(self.content, str): # Check extension self.ext = guess_file_type(self.content, self.ext) else: # Check kind of objects that are passed to filename # Bokeh plot if "bokeh" in str(type(self.content)): self.ext = 'bokeh' # Mathplotlib figure if "matplotlib" in str(type(self.content)): self.ext = "matplotlib" ###################################### # Check if the input filename can be treated if self.ext is None: print("figure format can't be guessed.") sys.exit(1) # Bokeh image if self.ext == 'bokeh': self.type = 'html' # Todo get width and height from a bokeh figure if self.width is None: self.width = int(self.content.plot_width) if self.height is None: self.height = int(self.content.plot_height) # Do not cache this element if it's bokeh plot self.cache = False # Mpl figure if self.ext == 'matplotlib': # import close to force the closing of the input figure from matplotlib.pyplot import close close(self.content) #close the figure # Set figure default width when it was not given as arguement if self.width is None: width_inch, height_inch = self.content.get_size_inches() self.width = convert_unit("%fin" % (width_inch)) # Create a special args to create a unique id for caching # Generate the figure (in binary format as jpg) from the canvas with BytesIO() as tmpb: self.content.canvas.print_jpg(tmpb) tmpb.seek(0) md5t = hashlib.md5(tmpb.read()).hexdigest() #print(md5t) # Add this new arg self.args['mpl_fig_hash'] = md5t self.mpl_fig_hash = md5t self.args_for_cache_id += ['mpl_fig_hash'] # Other filetype images if self.ext not in ('matplotlib', 'bokeh'): # Add file timestamp to an arguments for caching fdate = str(os.path.getmtime(self.content)) self.args['filedate'] = fdate self.filedate = fdate self.args_for_cache_id += ['filedate'] if self.width is None and self.height is None: self.width = document._slides[gcs()].curwidth # Add this module to the current slide + add positionner self.register()
def convert_position(self): #Function to convert position of an element tmpx = DEFAULT_X.copy() tmpy = DEFAULT_Y.copy() slidects = document._contents[gcs()]['contents'] #Get the previous content if it exist (to us "+xx" or "-yy" in x, y coords) if self.id_index > 0: prev_ct = slidects[document._contents[gcs()]['element_keys'][self.id_index - 1]] else: prev_ct = None #Check if x or y are only floats if type(self.x) == type(float()) or type(self.x) == type(int()): tmpx['shift'] = self.x elif type(self.x) == type(dict()): tmpx = dict_deep_update(tmpx, self.x) elif type(self.x) == type(str()): converted = False if '+' in self.x: self.x = convert_unit( self.x.replace('+','') ) #Make relative placement if prev_ct != None: dict_old = prev_ct['positionner'].right + float( self.x ) tmpx = dict_deep_update(tmpx, dict_old) else: tmpx['shift'] = float( self.x ) tmpx['unit'] = 'px' converted = True if '-' in self.x: self.x = convert_unit( self.x.replace('-','') ) #Make relative placement if prev_ct != None: dict_old = prev_ct['positionner'].left - float( self.x ) tmpx = dict_deep_update(tmpx, dict_old) else: tmpx['shift'] = float( self.x ) tmpx['unit'] = 'px' converted = True if self.x in ['auto', 'center']: tmpx['shift'] = 0 tmpx['align'] = self.x converted = True if not converted: try: tmpx['shift'] = float( convert_unit(self.x) ) tmpx['unit'] = 'px' except: print('[Error] x position is incorect string format') print(self.x) else: print("[Error] x position need to be a float or a dict") if type(self.y) == type(float()) or type(self.y) == type(int()): tmpy['shift'] = self.y elif type(self.y) == type(dict()): tmpy = dict_deep_update(tmpy, self.y) elif type(self.y) == type(str()): converted = False if '+' in self.y: self.y = convert_unit( self.y.replace('+','') ) #Make relative placement if prev_ct != None: dict_old = prev_ct['positionner'].bottom + float(self.y) tmpy = dict_deep_update(tmpy, dict_old) else: tmpy['shift'] = float( self.y ) tmpy['unit'] = 'px' converted = True if '-' in self.y: self.y = convert_unit( self.y.replace('-','') ) #Make relative placement if prev_ct != None : dict_old = prev_ct['positionner'].top - float(self.y) tmpy = dict_deep_update(tmpy, dict_old) else: tmpy['shift'] = float( self.y ) tmpy['unit'] = 'px' converted = True if self.y in ['auto', 'center']: tmpy['shift'] = 0 tmpy['align'] = self.y converted = True if not converted: try: tmpy['shift'] = float( convert_unit(self.y) ) tmpy['unit'] = 'px' except: print('[Error] y position is incorect string format') print self.y else: print("[Error] y position need to be a float or an int or a dict") #Store the dict for positions self.x = tmpx self.y = tmpy #Convert position unit to pt if self.x['unit'] in ['cm', 'pt', 'mm']: self.x['shift'] = float( convert_unit( '%f%s'%(self.x['shift'], self.x['unit']) ) ) if self.y['unit'] in ['cm', 'pt', 'mm']: self.y['shift'] = float( convert_unit( '%f%s'%(self.y['shift'], self.y['unit']) ) ) if type(self.width) == type(str()): self.width = float( convert_unit(self.width) ) if type(self.height) == type(str()): self.height = float( convert_unit(self.height) )