diff --git a/Slides/presentation_helper.py b/Slides/presentation_helper.py index cf6020c..e910192 100644 --- a/Slides/presentation_helper.py +++ b/Slides/presentation_helper.py @@ -1,324 +1,326 @@ #!/usr/bin/env pyton ################################################################ import base64 import sympy import PyPDF2 import io import IPython from IPython.display import display from IPython.display import HTML import os import fnmatch import shutil import subprocess import matplotlib.pyplot as plt import matplotlib.figure from IPython.core.interactiveshell import InteractiveShell from wand.image import Image as WImage from wand.color import Color from io import BytesIO from tempfile import NamedTemporaryFile from tempfile import mkdtemp ################################################################ def findImageFile(filename): if os.path.isfile(filename): return filename tool_dir = os.path.dirname(__file__) image_dir = os.path.join(tool_dir, '..', 'images') pattern = os.path.basename(filename) found = [] for root, dirnames, filenames in os.walk(image_dir): for filename in fnmatch.filter(filenames, pattern): found.append(os.path.join(root, filename)) if len(found) == 0: raise Exception("file not found: {0}".format(filename)) if len(found) == 1: return found[0] raise Exception("Several files were found:\n{0}".format(found)) ################################################################ def _print_latex_png(o): o = o._repr_latex_() dvioptions = None exprbuffer = BytesIO() # from IPython.core.debugger import Tracer # Tracer()() sympy.preview(o, output='png', viewer='BytesIO', outputbuffer=exprbuffer, packages=('xcolor',), dvioptions=dvioptions) return exprbuffer.getvalue() ################################################################ def _print_latex_svg(o): o = o._repr_latex_() dvioptions = None exprbuffer = BytesIO() # from IPython.core.debugger import Tracer # Tracer()() sympy.preview(o, output='svg', viewer='BytesIO', outputbuffer=exprbuffer, packages=('xcolor',), dvioptions=dvioptions) return exprbuffer.getvalue().decode() ################################################################ def registerFormatter(_type, _format, formatter_functor): f = InteractiveShell.instance().display_formatter.formatters[_format] f.for_type(_type, func=formatter_functor) ################################################################ def init_printing(): sympy.init_printing() registerFormatter(IPython.core.display.Math, 'image/svg+xml', _print_latex_svg) registerFormatter(IPython.core.display.Math, 'image/png', _print_latex_png) ################################################################ VIDEO_TAG = """""" def anim_to_html(anim): if not hasattr(anim, '_encoded_video'): with NamedTemporaryFile(suffix='.mp4') as f: anim.save(f.name, fps=1./(anim._interval*1e-3), extra_args=['-vcodec', 'libx264', '-pix_fmt', 'yuv420p']) video = open(f.name, "rb").read() toto = base64.b64encode(video).decode() # print anim._encoded_video return VIDEO_TAG.format(toto) ################################################################ def display_animation(anim): plt.close(anim._fig) return HTML(anim_to_html(anim)) ################################################################ def display_images(objs, display_flag=True, **kwargs): htmls = [display_image(o, display_flag=False, **kwargs) for o in objs] width = 100/len(htmls) htmls_withdiv = ["" for h in htmls] for i, h in enumerate(htmls): htmls_withdiv[i] = '
{0}
'.format(h) html = '\n'.join(htmls_withdiv) html = '
' + html + '
' if display_flag: display(HTML(html)) return html -def display_image(obj, resolution=150, width=None, height=None, - display_flag=True): +def display_image(obj, resolution=200, width=None, height=None, + display_flag=True, center=False): if type(obj) == matplotlib.figure.Figure: png = InteractiveShell.instance().display_formatter.format(obj) elif type(obj) == str: try: fname = findImageFile(obj) except Exception as e: return str(e) img = WImage(filename=fname, resolution=resolution) img.trim() with Color('#FDFDFD') as white: twenty_percent = int(65535 * 0.2) img.transparent_color(white, alpha=0.0, fuzz=twenty_percent) if (width is None) and (height is None): display(img) return img png = InteractiveShell.instance().display_formatter.format(img) else: raise Exception('unknown obj type for an image') size_tag = "" if width is not None: size_tag += 'width="{0}"'.format(width) if height is not None: size_tag += 'height="{0}"'.format(height) data = png[0]['image/png'] data = base64.b64encode(data).decode('utf-8') - html = r'You cannot see the image with this browser/mailer{html}' # print(html) if display_flag: display(HTML(html)) return html ################################################################ def display_graph(obj, **kwargs): if not isinstance(obj, str): raise RuntimeError( 'Graphs should be passed as a str object storing the graph' ' in dot format') with NamedTemporaryFile(suffix='.svg') as f: p = subprocess.Popen('dot -Tsvg -o' + f.name, shell=True, stdin=subprocess.PIPE) p.communicate(input=obj.encode()) display_image(f.name, **kwargs) ################################################################ def makeAnimationFromImageFiles(filenames, framerate): temp_dir = mkdtemp() ext = None for idx, f in enumerate(filenames): _dir = os.path.dirname(f) base = os.path.basename(f) base, _ext = os.path.splitext(base) ext = _ext if ext == '.pdf': _png = os.path.join(_dir, base+".png") subprocess.call('convert {0} {1}'.format(f, _png), shell=True) ext = ".png" f = _png new_name = "file{0:05d}{1}".format(idx, ext) new_name = os.path.join(temp_dir, new_name) # print base, ext # print new_name # print "copy {0} to {1}".format(f,new_name) shutil.copyfile(f, new_name) pattern = os.path.join(temp_dir, 'file%05d'+ext) with NamedTemporaryFile(suffix='.mp4') as f: cmd = ('ffmpeg -y -framerate {2} -i {0} -vf "scale=trunc(iw/2)*2:' 'trunc(ih/2)*2" -c:v libx264 -r 30 ' '-pix_fmt yuv420p {1}').format(pattern, f.name, framerate) subprocess.call(cmd, shell=True) video = open(f.name, "rb").read() toto = base64.b64encode(video).decode() shutil.rmtree(temp_dir) return HTML(VIDEO_TAG.format(toto)) ################################################################ def displayClassDiagram(classes, resolution=150, width=None, height=None): png = makeClassDiagramImage(classes, resolution, width, height) img = WImage(filename=png, resolution=resolution) with Color('#FDFDFD') as white: twenty_percent = int(65535 * 0.2) img.transparent_color(white, alpha=0.0, fuzz=twenty_percent) png = InteractiveShell.instance().display_formatter.format(img) size_tag = "" if width is not None: size_tag += 'width="{0}"'.format(width) if height is not None: size_tag += 'height="{0}"'.format(height) data = png[0]['image/png'] html = (r'You cannot see the image with this browser/mailer'.format( base64.b64encode(data).decode('utf8'), size_tag)) display(HTML(html)) return html ################################################################ def makeClassDiagramImage(classes, resolution=150, width=None, height=None): with NamedTemporaryFile(suffix='.classes', delete=False) as f: f.write(classes.encode('utf-8')) f.flush() fout = os.path.splitext(f.name)[0] + '.svg' _command = ('class_dumper_dot.py --collaboration_no ' '-c {0} -o {1} -f svg'.format(f.name, fout)) # print(_command) ret = subprocess.call(_command, shell=True) if ret: raise RuntimeError('could not launch commad:', _command) return fout ################################################################ def pdf_page_to_png(src_filename, pagenums=None, resolution=72): """ Returns specified PDF page as wand.image.Image png. :param PyPDF2.PdfFileReader src_pdf: PDF from which to take pages. :param int pagenum: Page number to take. :param int resolution: Resolution for resulting png in DPI. """ with open(src_filename, "rb") as f: src_pdf = PyPDF2.PdfFileReader(f) # print dir(src_pdf) # print src_pdf.numPages if pagenums is None: pagenums = [0] res = [] for n in pagenums: dst_pdf = PyPDF2.PdfFileWriter() dst_pdf.addPage(src_pdf.getPage(n)) pdf_bytes = io.BytesIO() dst_pdf.write(pdf_bytes) pdf_bytes.seek(0) img = WImage(file=pdf_bytes, resolution=resolution) img.convert("png") res.append(img) return res ################################################################ init_printing() diff --git a/Slides/snippet_helper.py b/Slides/snippet_helper.py index cf91845..022b8db 100644 --- a/Slides/snippet_helper.py +++ b/Slides/snippet_helper.py @@ -1,514 +1,517 @@ #!/usr/bin/env python3 import subprocess import os import re import abc from pygments import highlight from pygments.lexers import CppLexer from pygments.lexers import PythonLexer from pygments.lexers import MatlabLexer from pygments.formatters import HtmlFormatter from pygments.formatters import LatexFormatter from pygments import token from IPython.display import HTML from tempfile import NamedTemporaryFile ################################################################ class SlicedSnippet: """ When you slice a snippet, this object is returned so that all your operations can apply directly to the snippet if need be. """ def __init__(self, snippet, _slice): self.start = _slice.start - if slice.start is None: + if _slice.start is None: self.start = 1 - self.end = slice.stop-1 + if _slice.stop is None: + self.end = None + else: + self.end = _slice.stop-1 self.snippet = snippet def pigment(self, **kwargs): kwargs['start'] = self.start kwargs['end'] = self.end return self.snippet.pigment(**kwargs) def extract(self): return self.snippet.extract(start=self.start, end=self.end) class Snippet(abc.ABC): """ Snippet objects manipulate codes. """ default_output = 'html' default_line_numbering = False def __getitem__(self, _slice): return SlicedSnippet(self, _slice) def __init__(self, inp, format_flag=False, compile_flag=False, working_dir='/tmp'): self.working_dir = working_dir try: self.loadfile(inp) except Exception: self.content = inp.strip() if format_flag: self.format() if compile_flag: self._compile() # elif language == 'matlab': # self.lexer = MatlabLexer() self.lexer = None def save(self, filename): """ Saves the snippet in another file :param filename: The filename to save the snippet to """ f = open(filename, 'w') f.write(self.content) f.close() def loadfile(self, inp): filename = inp full_filename = os.path.realpath(filename) f = open(full_filename) self.content = f.read() f.close() def getTemporaryFile(self, **kwargs): f = NamedTemporaryFile(dir=self.working_dir, **kwargs) f.write(self.content.encode()) f.flush() return f @abc.abstractmethod def format(self): raise RuntimeError('pure virtual routine') @abc.abstractmethod def execute(self, working_dir='/tmp', args='', libraries=''): raise RuntimeError('pure virtual routine') @staticmethod def _compile(working_dir='/tmp'): raise RuntimeError('pure virtual routine') def get_lines(self): return self.content.split('\n') def get_content(self): return self.content def __str__(self): _content = self.content.split('\n') ret = "" for i, line in enumerate(_content): ret += '{0}: {1}\n'.format(i, line) return ret class KeywordLexer: def __init__(self, keyword): if keyword == 'curly_brackets': keyword = '{|}' self.keyword = keyword def get_tokens(self, text): pattern = '(' + self.keyword + ')' splits = re.split(pattern, text) if len(splits) == 1: return [(token.Text, text)] res = [] # print(splits) # print(token.Keyword.Type) for count, s in enumerate(splits): # print(count, s) if count % 2 == 0: res.append((token.Text, s)) else: res.append((token.Keyword.Type, s)) # print(res) return res class LineLexer: def __init__(self, line_number): self.line_number = line_number def get_tokens(self, text): splits = text.split('\n') res = [] splits1 = '\n'.join(splits[:self.line_number]) + '\n' splits2 = splits[self.line_number] + '\n' splits3 = '\n' + '\n'.join(splits[self.line_number + 1:]) + '\n' splits = [splits1] + [splits2] + [splits3] # print(splits) for count, s in enumerate(splits): if count == 1: res.append((token.Keyword.Type, s)) else: res.append((token.Text, s)) # print(res) return res class CustomLatexFormatter(LatexFormatter): def __init__(self, **kwargs): LatexFormatter.__init__(self, **kwargs) def format(self, tokens, outfile): res = [] for t, s in tokens: if t == token.Keyword.Type: import pygments.formatters.latex as la s = la.escape_tex(s, self.commandprefix) s = r'\color{red}{\huge{' + s + '}}' t = token.Escape res.append((t, s)) LatexFormatter.format(self, res, outfile) class CustomHtmlFormatter(HtmlFormatter): def __init__(self, **kwargs): HtmlFormatter.__init__(self, **kwargs) def format(self, tokens, outfile_final): # HtmlFormatter.format(self, tokens, outfile_final) # return from io import StringIO outfile = StringIO() res = [] ss = [] for t, s in tokens: if t == token.Keyword.Type: ss.append(s) s = 'A' * 24 t = token.Text res.append((t, s)) # print(res) HtmlFormatter.format(self, res, outfile) final = outfile.getvalue() while ss: s = ss.pop(0) final = final.replace( 'A' * 24, '{0}'.format( s), 1) outfile_final.write(final) def extract(self, start=None, end=None): _content = self.content.split('\n') _content = [(c + '\n') for c in _content] if start is None: start = 1 if end is None: end = len(_content) snip = _content[start - 1:end] snip = ''.join(snip) return snip def pigment(self, start=None, end=None, keyword=None, line_highlight=None, output=None, line_numbering=None, **kwargs): if start is None: start = 1 if end is None: end = 100000000 if output is None: output = self.default_output lexer = self.lexer if keyword is not None: lexer = self.KeywordLexer(keyword) elif line_highlight is not None: if line_highlight < start or line_highlight > end: raise RuntimeError('line_highlight out of the sub snippet') line_highlight -= start lexer = self.LineLexer(line_highlight) if line_numbering is None: line_numbering = self.default_line_numbering if output == 'html': formatter = HtmlFormatter( full=False, linenos=line_numbering, linenostart=start) elif output == 'latex': formatter = LatexFormatter( full=False, linenos=line_numbering, linenostart=start) if (keyword is not None) or (line_highlight is not None): format_class = self.CustomHtmlFormatter if output == 'latex': format_class = self.CustomLatexFormatter formatter = format_class( full=False, linenos=line_numbering, linenostart=start) snip = self.extract(start=start, end=end) colored_snippet = highlight(snip, lexer, formatter) # print(colored_snippet) if output == 'html': HTML_TEMPLATE = """ {} """ css = formatter.get_style_defs() colored_snippet = HTML(HTML_TEMPLATE.format(css, colored_snippet)) return colored_snippet @staticmethod def getLatexStyleDefs(): return LatexFormatter(full=True).get_style_defs() ################################################################ class CPPSnippet(Snippet): """ Snippet objects manipulate Cpp codes. At construction it can be formatted using clang-format and it can be compiled to check for consistency. """ class CompilationError(Exception): """ Compilation error """ def __init__(self, inp, include_paths=None, **kwargs): if include_paths is None: self.include_paths = [] else: self.include_paths = [ os.path.abspath(i) for i in include_paths] Snippet.__init__(self, inp, **kwargs) self.lexer = CppLexer() def format(self): f = self.getTemporaryFile() p = subprocess.Popen( 'clang-format {0}'.format(f.name), shell=True, stdout=subprocess.PIPE) stdout, stderr = p.communicate() if p.returncode: raise RuntimeError('Failed to launch clang-format') formatted_file = stdout self.content = formatted_file.decode('utf-8') def execute(self, args='', libraries=''): self._compile() f = self.getTemporaryFile(suffix='.cpp', delete=False) working_dir = os.path.dirname(f.name) # file_dir = os.path.dirname(self.full_filename) previous_dir = os.getcwd() os.chdir(working_dir) include_paths = self.include_paths + [working_dir] include_paths = ['-I {0}'.format(a) for a in include_paths] include_paths = ' '.join(include_paths) p = subprocess.Popen( 'g++ -Wall -Wextra {1} {2} -o exec {0}'.format( f.name, include_paths, libraries), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() ret = p.returncode if ret != 0: return ret, stdout, stderr p = subprocess.Popen( './exec {0}'.format(args), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() ret = p.returncode os.chdir(previous_dir) return ret, stdout, stderr def _compile(self): f = self.getTemporaryFile(suffix='.cpp', delete=False) working_dir = os.path.dirname(f.name) # file_dir = os.path.dirname(self.full_filename) previous_dir = os.getcwd() os.chdir(self.working_dir) include_paths = self.include_paths + [working_dir] include_paths = ['-I {0}'.format(a) for a in include_paths] include_paths = ' '.join(include_paths) cmd = 'g++ -Wall -Wextra {1} -c {0}'.format(f.name, include_paths) p = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE) stdout, stderr = p.communicate() ret = p.returncode os.chdir(previous_dir) if ret != 0: raise CPPSnippet.CompilationError('compilation failed\n' + cmd + '\n' + stderr.decode('utf-8')) os.remove(f.name) o_file = os.path.splitext(f.name)[0] + '.o' os.remove(o_file) ################################################################ class PythonSnippet(Snippet): """ Snippet objects manipulate Python codes. At construction it can be formatted using autopep8. """ def __init__(self, inp, **kwargs): Snippet.__init__(self, inp, **kwargs) self.lexer = PythonLexer() def format(self): f = self.getTemporaryFile() p = subprocess.Popen( 'autopep8 {0}'.format(f.name), shell=True, stdout=subprocess.PIPE) stdout, stderr = p.communicate() if p.returncode: raise RuntimeError('Failed to launch autopep8') formatted_file = stdout self.content = formatted_file.decode('utf-8') def execute(self, args=[]): f = self.getTemporaryFile(suffix='.py') cmd = 'python3 {0} {1}'.format(f.name, ' '.join(args)) previous_dir = os.getcwd() os.chdir(self.working_dir) p = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() os.chdir(previous_dir) if p.returncode: raise RuntimeError('Failed to run snippet\n' + cmd + '\n' + stderr.decode()) return stdout ################################################################ class SnippetCollection: def __init__(self): self.functions = dict() self.func_names = [] def addFunction(self, f_name, code, language='cpp'): if f_name in self.functions: raise RuntimeError('function already declared') self.func_names.append(f_name) if language == 'cpp': _snippet_class = CPPSnippet if language == 'python': _snippet_class = PythonSnippet self.functions[f_name] = _snippet_class(code, format_flag=True) self._compile() def __getitem__(self, key): return self.functions[key] def flatten(self, language='cpp'): global_input = """ #include #include #include #include """ + '/' * 70 + '\n\n' for f_name in self.func_names: global_input += "void {0}(){{\n".format(f_name) global_input += self.functions[f_name].content global_input += '\n}\n\n' + '/' * 70 + '\n\n' global_input += """ int main(int argc, char ** argv){\n""" for f_name in self.func_names: global_input += f_name + '();\n' global_input += '\n}\n' if language == 'cpp': _snippet_class = CPPSnippet if language == 'python': _snippet_class = PythonSnippet flat = _snippet_class(global_input, format_flag=True) return flat def _compile(self, **kwargs): global_snippet = self.flatten() global_snippet._compile(**kwargs) def save(self, filename): global_snippet = self.flatten() open(filename, 'w').write(global_snippet.content) def pigment(self, *args): return self.flatten().pigment(*args) def last(self): return self.functions[self.func_names[-1]] ################################################################ # def printMatlab(code): # # html_snippet = highlight( # code, MatlabLexer(), # HtmlFormatter(full=True, style='colorful', # classprefix='matlab_')) # # display(HTML(html_snippet)) # # return html_snippet # ## testing # Snippet.default_output = 'latex' # Snippet.default_line_numbering = True # # snippet = SnippetCollection() # snippet.addFunction('if_syntax', ''' # int p, q; # if (p > q) { # /* # Statement1; # Statement2; # */ # } # ''') # # print (snippet['if_syntax'].pigment())