Generating code snippets

MP 97: My approach to generating code snippets for presentations.

Note: I've been busy at PyCon this past week. The ongoing series about building a Django project from a single file will continue next week.

I gave a talk at PyCon in Pittsburgh this weekend, and one interesting task was deciding how to place code snippets onto slides. I really like the approach I ended up using, and I'd like to share it in case it's useful to others.

small image of me speaking, with larger image of one slide from the talk
I gave a talk about using Python to monitor for landslide activity in my hometown in southeast Alaska this weekend. The video will be available on YouTube in about a month.

Actual text or images?

There are two main approaches to putting code onto slides. You can paste the actual text onto the slide, or you can use an image of the code. Both have advantages and disadvantages.

Some of the decision about which approach to take can depend on the presentation software you're using. For example reveal.js is designed to have code written directly onto the slides. This is reasonable, because it then applies highlighting, and you can adjust things like font size through CSS rules.

I've tried these kinds of tools, but except for very short presentations I always come back to tools like KeyNote and PowerPoint. My presentations usually use images, and I like to define a few guides so I can drag elements around the slide and easily use the space on each slide exactly as I want to. I'm on macOS, so I use KeyNote for most of my presentations these days. The approach I use for managing code snippets would work just as well in PowerPoint, or any similar presentation tool.

Some people copy code into text boxes on KeyNote. I tried that briefly, but I found images of code snippets to be much easier to work with. No one should be copying and pasting code from my slides, so it seems reasonable to use images.

Using Pygments to generate images

If you haven't heard of it, Pygments is a Python tool that can be used to highlight just about any code or text you want. You can generate images, HTML, RTF, LaTeX, and more. Pygments is used in all kinds of workflows, such as highlighting code that will be presented on a blog.

My workflow

When generating snippets, I don't want to highlight entire program files; I want to highlight just the snippet that will appear on the slide. I also want to be able to regenerate all my snippets at any time. As I'm building out a slideshow, I often realize a slightly different style would make things better for the audience. The ability to easily regenerate all snippets lets you constantly keep an eye out for little improvements like this.

To actually make my snippets, I make a directory called snippets/ alongside my presentation file. In that folder, I make two subdirectories: raw_snippets/ and processed_snippets/. When I want to put a snippet of code onto a slide, I copy that excerpt into a tiny .py file, and save it in raw_snippets/. I then run a program that uses Pygments to generate the image that can be placed onto a slide.

Generating snippets

Let's say this is the snippet I want to put onto a slide:

for reading_set in reading_sets:
    ph.plot_data_static(
        reading_set,
        known_slides=known_slides,
        critical_points=critical_points
    )

a_utils.summarize_results()
plot_data.py

I save this file in raw_snippets/, as plot_data.py. Note that you can't run this file; it's just an excerpt from a much larger .py file. The .py extension will be used by the parser to determine what kind of highlighting to apply.

Here's the first part of the program that generates images from this snippet file. This is saved as pygparser.py, in the snippets/ directory:

from pygments import highlight
from pygments.lexers import PythonLexer, TextLexer, JsonLexer
from pygments.formatters import ImageFormatter, RtfFormatter

from pathlib import Path

# Get all snippets.
raw_dir = Path("raw_snippets")
raw_files = [
    path
    for path in raw_dir.iterdir()
    if path.suffix in [".py", ".json", ".txt"]
    ]
pygparser.py

We first import some tools from Pygments, and then get all the snippet files from the raw_snippets/ directory. The main thing to notice here is that raw_files is a sequence of paths to each raw snippet file. The conditional check in the list comprehension makes sure we only include the kinds of snippet files we're looking for.

Here's the next part:

...
# Highlight all snippets.
hl_dir = Path("processed_snippets")
for file in raw_files:
    code = file.read_text()

    if file.suffix == ".py":
        lexer = PythonLexer()
    elif file.suffix == ".json":
        lexer = JsonLexer()
    else:
        lexer = TextLexer()

We loop over all paths in raw_files, and read the text of each raw snippet. We then look at the suffix of the path in order to determine which of Pygment's lexers to use. A lexer parses the file, and determines the semantic meaning of each element in the file.

Finally, here's the code that generates an image for each snippet:

...
for file in raw_files:
    ...
    hl_filename = file.stem + ".png"
    hl_file = hl_dir / hl_filename
    with open(hl_file, "wb") as f:
        # Set parameters for ImageFormatter.
        formatter = ImageFormatter(
            line_pad=15,
            font_size=36,
            line_numbers=False,
        )

        highlighted_code = highlight(code, lexer, formatter)
        f.write(highlighted_code)
        print("Wrote file:", hl_file.as_posix())

The image file will have the same name as the snippet file, with the extension .png. The formatter object lets you adjust the style of your snippets. Here I'm customizing the padding between lines, the font size, and turning off line-numbering.

The call to highlight() takes in the code we want to highlight, the lexer object, and the formatter object. Because we're using an instance of ImageFormatter, highlight() returns an image object which we can write to a file.

Here's the image that's generated:

The rendered image, which can be pasted onto a slide in KeyNote, or any presentation software.

This image can be pasted into KeyNote, or any other presentation software you might be using.

Advantages

There are a number of strengths of this approach. As you add more snippets to the raw_snippets/ directory, you end up with a growing but consistent set of images in processed_snippets/. If you want to change the style of all of your snippets, you can run pygparser.py again and all your images will be regenerated with the new style.

If you want to modify the text of an individual snippet, you can go to the relevant file in raw_snippets/, change the text, and run pygparser.py again. For example, you might want to replace the arguments in a function call with an ellipsis in order to declutter your slide.

Recommendation

One mistake I made was to use a font size that looked big on my laptop, but ended up looking smaller on the projector screen than I thought it would. I recommend choosing a large font size, which fills your screen using a snippet roughly the size of the one used in this post. Then stick to that size for all your snippets.

If you have a snippet that requires a smaller font size to fit on the slide, you're probably showing more code than your audience can make sense of. It's almost certainly better to find a way to trim the snippet than to use a smaller font size. Can you replace any arguments with an ellipsis? Can you cut some comments? Can you replace the body of a loop with an ellipsis? These kinds of decisions can help your audience make sense of what you're showing them, in the time the slide is on the screen.

small image of me speaking, with larger slide next to me; the code block only fills about half the slide
The font size isn't terrible for this slide, but the code block really should fill more of the space on the screen.

Conclusions

Lots of programmers end up making presentations at some point, either during meetings or at conferences. Everyone who does so has to figure out a workflow for getting code onto slides. I prefer to use images, and I've largely settled on a process that works well for me. Hopefully there's something in here that will help you refine your workflow as well.

If you haven't presented before, I highly recommend finding an interesting aspect of the work you're involved in, and looking for a group to present to. Start with something short like a lightning talk (5 minutes or less). You can present to a group of your colleagues, or you can look for a user group or meetup. Remember there are many smaller regional Python conferences that happen throughout the year, all over the world.

Good luck with your snippets, and good luck with your presentations. :)

Resources

You can find the code from this post in the mostly_python GitHub repository.