PPTXHandler generates PPTX

PowerPoint presentations are the most common way businesses communicate data. Many, in fact, copy-paste screenshots from their BI tools into PowerPoint.

PPTXHandler makes it easy for users to:

Here are examples of what PPTXHandler can create. Click to see details.

Bar chart race Rate of entrepreneurship Causes of death App store sales FMCG revenue breakup

About

There are two versions of PPTXHandler.

Here is an example that shows how to change the text of a slide from data:

url:
  pptxhandler/sales-funnel:
    pattern: /$YAMLURL/output.pptx
    handler: PPTXHandler
    kwargs:
      version: 2                                  # Use PPTXHandler v2 instead of v1
      source: $YAMLPATH/template.pptx             # Template to use
      data:
        products: {url: $YAMLPATH/products.yaml}  # Load products data from products.yaml
      rules:
        - Visitors:                               # Find all shapes named 'Visitors'
            text: products['visitors']            # Replace text with value from data
          Leads:
            text: products['leads']               # Same for leads
          Cart:
            text: products['cart']                # ... and cart
      headers:
        Content-Disposition: attachment; filename=output.pptx

This takes the following PPTX as the source or template:

Source or template PPTX

… and this dataset: products.yaml:

visitors: 839
leads: 420
cart: 142

… and creates this presentation:

Output PPTX

Tutorials

Here are some tutorials:

Bar chart race

Create a bar chart race in PowerPoint from data.

Bar chart race

Rate of Entrepreneurship

Re-create a portion of the Kauffman Indicators of Rate of New Entrepreneurship in PowerPoint.

Rate of entrepreneurship

Causes of death

Re-create the Shifting Causes of Death visualization in PowerPoint.

Shifting causes of death

Bestselling Pharmaceutical Drugs

Visualize the Bestselling Pharmaceutical Drugs of 2017-18 data in PowerPoint.

Bestselling Pharmaceutical Drugs

Usage

PPTXHandler accepts a set of keys as kwargs. The most used keys are:

Less used keys are:

Command line

Run slidesense to run PPTXHandler from the command line. It renders any gramex.yaml in the current folder, creating an output.pptx (or the file specified by target: in gramex.yaml). It also opens the target PPTX in PowerPoint, if possible.

SlideSense command line usage

You can specify the path to your gramex.yaml, and also which PPTXHandler to run as the command-line arguments. For example:

slidesense                      # Render first PPTXHandler in gramex.yaml
slidesense gramex.yaml my-url   # Render first PPTXHandler in gramex.yaml matching *my-url*

The second command above will render the 2nd PPTXHandler in this configuration:

url:
  first-pattern:
    ...
  my-url-pattern:
    pattern: ...
    handler: PPTXHandler
    kwargs: ...

Run slidesense config.yaml to render config.yaml with a simplified configuration that just contains the kwargs of a PPTXHandler. Here is a sample config.yaml:

source: $YAMLPATH/template.pptx
rules:
  - Title 1: {text: New title}

You can override configurations by adding these options at the end:

Examples:

slidesense --source=my-template.pptx
slidesense config.yaml --target=my-output.pptx --no-open
slidesense gramex.yaml my-url --data=my-data.xlsx

Reference


Rules

A rule defines how to modify the source presentation. For example:

rules:                              # Apply these rules
  - Title 1:                        # Take the shape named "Title 1"
      text: f'Total sales is {sales["sales"].sum()}'  # Replace text with data template
      fill-opacity: 0.5             # Make shape 50% transparent
  - Rectangle 1:                    # Take the shape named "Rectangle 1"
      fill: f'#ffff00'              # Make its background yellow

A rule can pick one or more shape names, and apply one or more commands to each shape.

Shapes

Each rule in rules: is a dictionary of shape names.

All shapes in PowerPoint have names. To see them in PowerPoint, select Home tab > Drawing group > Arrange drop-down > Selection pane. Or press Alt + F10.

Selection pane

To change shape names, double-click on the name in the selection pane.

Shapes shouldn’t have the same name as commands, e.g. don’t name a shape “text”. Start shape names with a capital letter, e.g. “Text” instead of “text”.

You can use wildcards in a shape name. Use ? to match a single character, and * to match anything. For example:

rules:
  - 'TextBox ?':      # Select TextBox 1, TextBox 2, ... but not TextBox 10, ...
      color: f'red'  #     ... and set font color to red
  - 'Text *':         # Select ALL shapes starting with Text (case-sensitive)
      color: f'red'
  - '*box*':          # Select ALL shapes with "box" anywhere in the text (case-sensitive)
      color: f'red'

You can also change the shape name with the name command, e.g. name: New TextBox. This is particularly useful with the “morph” transitions, which matches shape names beginning with !!. For example:

rules:
  - Bar:
      name: f'!!Bar'    # Renames the shape "Bar" to "!!Bar"
      text: f'New text'

This ensures that the morph will match the shape even if it’s text changes. Source

Groups

Groups are shapes that contain other shapes. You can apply commands to the group itself (e.g. change its size or position), or to the shapes inside a group, like this:

rules:
  - Group 1:                            # Take the shape named "Group 1"
      left: 5                           # Set the group's left position to 5 (inches)
      Caption:                          # Find the shape named "Caption" inside it
        text: f'New caption'            #   Change its text to "New caption"
      Picture:                          # Find the shape named "Picture" inside it
        image: f'$YAMLPATH/sample.png'  #   Replace the image with sample.png

Slide filters

By default, rules are applied to all slides. You can restrict changes to specific slides with:

For example:

source: input.pptx        # optional path to source. Default to blank PPT with 1 slide
target: output.pptx       # required path to save output as
rules:
  - slide-number: [2, 4]            # Pick the 2nd and 4th slide (not the 3rd)
    Title: {text: f'X'}
  - slide-title: f'Business Update' # Pick the slide titled "Business Update" (case-insensitive)
    Title: {text: f'X'}
  - slide-title: f'Day ?'           # Picks the slide titled "Day 1" or "Day 2", etc, not "Day 10"
    Title: {text: f'X'}

Transition

Add transition: to a rule to set the transition for all applicable slides.

For example:

rules:
  - transition: f'fade'   # All slides have fade transition (0.3 seconds duration)
  - slide-number: 1       # Slide 1: Morph transition for 1.5s
    transition:
      type: f'morph'
      duration: 1.5
  - slide-number: 3       # Slide 3: Glitter - Diamonds from Left transition for 3s
    transition:
      type: f'glitter diamond left'
      duration: 3
  - slide-number: 4       # Remove all transitions from slide 4. Auto-advance in 2s
    transition:
      type: f'none'
      advance: 2

You can specify transitions by name (with space-separated options). The transition names and their options are below, e.g. transition: f'airplane left', transition: f'fly-through in bounce'. These are similar to PowerPoint’s UI.

You can also specify transitions as a dict with 3 keys:

Copy slides

You can repeat a slide, changing the layouts or content based on data. This is used to:

copy-slide: repeats the selected slides based on data. For example:

rules:
  - slide-number: 1
    copy-slide: [A, B, C]
    Title 1:
      text: f'Copy {copy.key} {copy.val}'

This repeats slide 1 three times, with titles “Copy 0: A”, “Copy 1: B” and “Copy 2: C”.

copy-slide: must be an expression that returns one of the following types. When copying, the values of (copy.key, copy.val) are set as follows (just like clone-shape:):

For each copied slide, the data variable copy is set. It has these attributes:

Commands

Shapes can be changed using 1 or more commands. These commands can change the shape’s style and content, or add new content (like charts). Here are some common commands:

Position

Style

Image

link and hover can be specified as a:

Text

Text format

The text for text: may include <p> tags for for paragraphs, which may contain <a> tags for runs. Similarly, the new text for replace: may include a single <a> tags for the run. (These look like HTML, but they’re not.)

Paragraphs (<p>) can have attributes like <p align="right" level="2">. Valid attributes are:

Runs (<a>) can have attributes like <a href="https://gramener.com/" bold="y" italic="y">. Valid attributes are:

You can’t use <p> inside another <p>, nor an <a> inside another <a>. If you do, the previous tag is closed.

Note: Don’t use run attributes (like bold=, color=) on paragraphs. They work on paragraphs, but are over-ridden by run attributes. E.g. If your source PPTX had a bold run, setting bold: n on the para has no visible effect, since the bold run overrides it.

Table

To update table text or style using data, use the table: command. Example:

data:
  products: {url: $YAMLPATH/products.csv}
rules:
  - Table 1:
      table:                                          # Apply table command
        data: products.head(10)                       # Show top 10 rows from products dataset
        text:
          Sales: f'<p>{cell.val:.0} $mn</p>'          # Format sales column as $mn
          Margin: f'<p>{cell.val:0.1%}</p>'           # Format margin column as %
        fill: 'red' if cell.row.Margin < 0 else 'green'   # Negative margin rows are colored red

The table: command supports these sub-commands:

You can set each cell’s properties with these sub-commands:

You can set the above cell properties either with a single expression, or an expression per column:

rules:
  - Table 1:
      table:
        # You can set every cell's property with an expression.
        # E.g. this makes every row red if the "Margin" column is negative, else it's green
        fill: 'red' if cell.row.Margin < 0 else 'green'
        # You can set a column's property with an expression, too.
        # E.g. this formats the Sales column and Margin column differently
        text:
          Sales: f'<p>{cell.val:.0} $mn</p>'          # Format sales column as $mn
          Margin: f'<p>{cell.val:0.1%}</p>'           # Format margin column as %

For each cell, the data variable cell is set. It has these attributes:

Chart

To update a chart’s data, use the chart-data: command. Example:

data:
  products: {url: $YAMLPATH/products.csv}
rules:
  - Chart 1:
      chart-data: products.set_index('Name')

The dataset must be formatted with data for each series in a column. For a 1-series column chart like this:

Column chart with 1 series

… the data needs to look like this:

Column chart with 1 series data

The 1st column must be the index of the DataFrame, and the 2nd must be a column, like this:

data = pd.DataFrame({
  'Categories': ['A', 'B', 'C', 'D'],
  'Series 1': [3, 4, 2, 1]
}).set_index('Categories')

Note: The first column name “Categories” is not used. It’s just a placeholder for the index name.

Clone shapes

Use clone-shape: to clone a shape for as many times specified. For example:

rules:
  - Rectangle 1:
      clone-shape: [A, B, C]
      text: f'Clone {clone.key} {clone.val}'

This repeats the shape three times, with text “Clone 0: A”, “Clone 1: B” and “Clone 2: C”.

clone-shape: must be an expression that returns one of these types. When cloning, the variables (clone.key, clone.val) are set as follows (just like copy-slide:):

For each cloned shape, the data variable clone is set. It has these attributes:

Debug

If PPTXHandler fails, a good way to debug is to

Register

Register let you create your own custom commands. It can run any Python code using 3 variables:

  1. shape: the PPTX shape)
  2. spec: whatever configuration value you pass to the command
  3. data: dictionary that has whatever data is available to the command

For example, here are some custom commands:

register:
  rename: setattr(shape, "name", spec)      # Add a "rename" command that changes the shape's name
  turn: setattr(shape, "rotation", spec)    # Add a "turn:" command that changes the shape's angle
  custom: my_method(shape, spec, data)      # my_method can do anything with the shape and val
rules:
  - Shape 1:                                # Take the shape named Shape 1
      rotate: 45                            # Rotate clockwise 45
      rename: New Shape 1                   # Change the shape name to New Shape 1
      my_method: ...                        # Any object can be passed to my_method

Reference

Expressions

PPTXHandler is mostly used to change presentations using data. So values are evaluated as Python expressions. For example:

data:
  widths: [1, 2, 3]
  colors: [red, blue, green]
rules:
  - Rectangle 1:
      # Type in YAML strings almost like you would type it in Python
      width: sum(widths)      # Python expression using "widths" as a variable. Returns 1+2+3=6
      fill: colors[0]         # Python expression using "colors" as a variable. Returns "red"
      left: 10                # Numbers in YAML are numbers in Python too. This sets left: 10 inches
      stroke: [255, 0, 0]     # Lists in YAML are lists in Python too. This sets the stroke to red

What won’t work are literal strings. Instead, use f'...' – a Python format-string. For example:

      # Incorrect
      color: red              # This is NOT 'red'. This is the VARIABLE red (which is not
      color: 'red'            # This is NOT 'red'. This is the VARIABLE red (which is not defined)
      # Correct
      color: f'red'           # Use formatted strings inside.
      text: f'Color {colors[0]}'  # These can be templates. This sets text to 'Color red'

To use literal values, you can also use {value: ...}. For example:

      # Incorrect
      color: 'red'            # This is NOT 'red'. This is the VARIABLE red (which is not defined)
      # Correct
      color: {value: red}     # Strings inside value: are treated literally
      text: {value: 'Color {colors[0]}'}  # These can be templates. This sets text to 'Color red'

To fully switch to literal values instead of expressions, use mode: literal. For example:

mode: literal
rules:
  - color: red              # This sets the color to the string "red"

Literals

By default, values passed to pptgen() are used as-is. For example:

widths = [1, 2, 3]
colors = ['red', 'blue', 'green']
pptgen('source.pptx', rules=[
  'Rectangle 1': {
    'width': sum(widths),   # Values are passed as-is. Returns 1+2+3=6
    'fill': colors[0],      # Values are passed as-is. Returns "red"
    'left': 10,             # Numbers are passed as-is. This sets left: 10 inches
    'stroke': [255, 0, 0],  # Lists are passed as-is. This sets the stroke to red
    'color': 'red',         # Strings can be passed as-is
    'text': 'Shape {shape.name}'  # Strings are formatted with data. This sets text to 'Shape Rectangle 1'
  }
])

To use expressions instead of literals, use {expr: ...}. For example:

pptgen('source.pptx', rules=[
  'Rectangle 1': {
    'clone-shape': widths,                # Clone the shape for each entry in "widths"
    'width': {'expr': '1 + clone.val'},   # The value is an expression. Sets width to 1 + width
  }
])

To fully switch to expressions instead of literals, use mode='expr'. For example:

pptgen('source.pptx', mode='expr', rules=yaml_config['rules'])

Data

PPTGen can change presentations with data from various sources. It uses the same syntax as FormHandler. It supports these keys:

You can also specify the data as a function using function:. This can be any Python expression.

Example:

data:
  cities: {url: cities.csv}                         # Load cities.csv into "cities" key
  sales: {url: sales.xlsx, sheet: Sheet1}           # Load Sheet1 from sales.xlsx into "sales" key
  tweets: {url: tweets.json}                        # Load JSON data into "tweets" key
  sample: {url: mysql://server/db, table: sample}   # Load sample data from MySQL
  filter:
    url: cities.csv                                 # Load cities.csv
    args:                                           # Filter results
      city: [Oslo, Kiev]                            # WHERE column "city" is Oslo or Kiev
      population>~: 100000                          # AND column population is 100,000+
  custom1:
    function: gramex.cache.open('data.xlsx')        # Run a custom function
  custom2:
    function: gramex.cache.open(handler.get_arg('file'))  # Functions can accept handler as argument
  big_cities:
    function: cities[cities.population > 100000]    # You can also access loaded datasets

These datasets are available in expressions as variables. For example, you can use:

rules:
  - Rectangle 1:
      text: cities.columns[0]
  - Rectangle 2:
      text: tweets[0]['text']

Expressions can also use these pre-defined variables. (DON’T create a variable with these names. They will be over-ridden.)

They may also use these variables where available:

PPTGen Library

You can access the pptgen library to change presentations programmatically. PPTXHandler is just a wrapper around pptgen. The keyword arguments for pptgen() are the same as for PPTXHandler. Here’s an example:

from gramex.pptgen2 import pptgen
target = pptgen(
  source='input.pptx',                # Input file to load
  data={
    'sales': {'url': 'sales.csv'}     # Load sales data from sales.csv
  },
  rules=[                             # Apply these rules
    {
      'slide-number': 1,              # Take only the first slide
      'Title 1': {                    # Find all shapes named "Title 1"
        'text': 'Total sales is {sales["sales"].sum()}'   # Replace text with data template
      }
    }
  ]
)
target.save('slide1.pptx')  # Save the target

The Python library does not treat strings as expressions. So 'red' means the string “red”, not the variable red. You can specify string values as-is. (If you want to use the variable red in Python, use data['red']).

But if you need to use expressions (e.g. to specify copy.val in a copy-slide:), you can use {'expr': '...'}. For example:

from gramex.pptgen2 import pptgen
target = pptgen(
  source='input.pptx',                # Input file to load
  rules=[                             # Apply these rules
    {
      'slide-number': 1,              # Take only the first slide
      'copy-slide': ['A', 'B'],       # Copy it twice
      'Title 1': {                    # Find all shapes named "Title 1"
        'add-top': {'expr': 'copy.key * 10'}, # Add 0 * 10 to slide A's top, 1 * 10 to slide B's top
      }
    }
  ]
)
target.save('slide1.pptx')  # Save the target

Length units

Any command that sets a length, e.g. width: 5, uses “inches” by default. You can change the unit to “cm” using width: 5 cm. Or, you can change the default unit from “inches” to “cm” by passing unit: inches to the PPTXHandler configuration.

Valid length units are:

Color units

Any command that sets a color, e.g. fill: f'red', accepts colors in one of these formats:

Support

PPTGen currently cannot handle: