Skip to main content

A Python package that allows you to create interactive presentations using Python, Bootstrap and Reveal.js. Charts from Altair and Plotly can also be added

Project description

respysive-slide


A Python package that allows you to create interactive presentations using Python, Bootstrap and Reveal.js. Charts from Matplotlib Altair and Plotly can be easily added.

respysiv.png

You will find a live example here


Installation

With PyPI

pip install respysive-slide

Upgrade

pip install respysive-slide --upgrade

You can also clone the repo and import respysive as a module


Usage

The package consists of two main classes: Presentation and Slide.

Presentation is the main instance, containing your slides.

Slide is used to create a unique slide. You can add various elements to it such as text, headings, images, cards etc.

Each Slide instance is added to the Presentation instance for final rendering.

Creating a new presentation

Here's an example of how to use respysive-slide

from respysive import Slide, Presentation

# Create a new presentation
p = Presentation()

# Create the first slide with a centered layout
slide1 = Slide(center=True)

# Content for the title page
logo_url = "https://upload.wikimedia.org/wikipedia/commons/4/4d/Fractal_canopy.svg"
title_page_content = {
    'title': 'Your presentation title',
    'subtitle': 'Your subtitle',
    'authors': 'Author 1, Author 2',
    'logo': logo_url
}

# Styles for the title page content in the same order as content
styles = [
    {'color': '#e63946', 'class': 'r-fit-text border-top'},  # title
    {},  # subtitle style by default
    {},  # authors style by default
    {'filter': 'invert(100%) opacity(30%)'},  # logo
]

# Add the title page to the slide
slide1.add_title_page(title_page_content, styles)

You can pass CSS styles and classes as kwargs. For example, in the code below, the add_title method takes a dictionary kwarg styles containing :

  • one or several CSS styles as key : values
  • and class as a unique key:

slide1.png

Split title page layout

As an alternative to the classic title page, you can create a split layout with the title content on one side and custom content (logo, image, chart) on the other side using add_split_title_page():

# Create a slide with split title page layout
slide1b = Slide(center=True)

# Content for the split title page
split_title_content = {
    'title': 'Your presentation title',
    'subtitle': 'Your subtitle',
    'authors': 'Author 1, Author 2',
}

# Styles for the title elements  
title_styles = [
    {'color': '#e63946', 'font-weight': 'bold', 'font-size': '60px'},  # title
    {'color': '#457b9d', 'font-size': '40px'},  # subtitle
    {'color': '#457b9d', 'font-size': '25px'},  # authors
]

# Style for the title column 
title_column_style = {
    'background-color': '#1d3557', 
    'color': '#f1faee',             
    'padding': '40px',              
    'border-radius': '10px 0 0 10px'  
}

# Style for the custom content column 
image_style = {
    'text-align': 'center',
    'padding': '30px',
    'background-color': '#e63946',
    'border-radius': '0 10px 10px 0'  
}

# Add the split title page - title on left (8 cols), logo on right (4 cols)
slide1b.add_split_title_page(
    title_page_content=split_title_content,
    custom_content=logo_url,
    title_column_width=8,
    custom_column_width=4,
    title_page_class="split-intro",
    custom_content_style=image_style,
    title_styles=title_styles,
    title_column_style=title_column_style  
)

split_title.png

A simple text slide

Now, lets create a simple slide with a title and some content.

Markdown is more intuitive, so we will use it, but it's not mandatory.

# Create the second slide
slide2 = Slide()

# Add a title to the slide with a fontawesome icon
slide2.add_title("Your title with a fontawesome icon", icon="fas fa-infinity fa-beat")

# Create some text in markdown format
txt = markdown("""
This is some dummy text 

- and it's easier to use Markdown
<ul><li>but it's ok to use HTML tag</li></ul>
""")

# Add the text to the slide in a new Bootstrap column with a width of 12 (default)
slide2.add_content([txt], columns=[12])

Note that for the add_title() method, Fontawesome icons can be added.

slide2.png

A two columns slide with text and image

Let's add two columns :

  • the first with some text
  • the second with an image

respysive-slide will try to find automatically the content type (txt, image, chart from json). You only have to pass the content list with the add_content() method

# Create a new slide
slide3 = Slide()

text = markdown("""
En cosmologie, le modèle de l'univers fractal désigne un modèle cosmologique 
dont la structure et la répartition de la matière possèdent une dimension fractale, 
et ce, à plusieurs niveaux. 

De façon plus générale, il correspond à l'usage ou 
l'apparence de fractales dans l'étude de l'Univers et de la matière qui le compose.
Ce modèle présente certaines lacunes lorsqu'il est utilisé à de très grandes ou de 
très petites échelles.

""")

# Add image url
url = "./assets/img/Univers_Fractal_J.H..jpg"

# Add title to slide
slide3.add_title("Bootstrap powering")

# Add styles to slide
css_txt = [
   {'font-size': '70%', 'text-align': 'justify', 'class': 'bg-warning'},  # text style
   None  # url style is mandatory even it is None
]

# Add content to slide, where text and url are added to the slide with 7 and 5 columns respectively
# css_txt is added as styles
slide3.add_content([text, url], columns=[7, 5], styles=css_txt)

Note that class can include Reveal.js fragments for step-by-step content reveal.

slide3.png

Plotly, Altair and Matplotlib

Plotly, Altair or Matplotlib graphs can be easily added with add_content(). Interactivity is fully functional for Plotly and Altair.

slide4 = Slide()
slide4.add_title("Plotly")

# import plotly express for creating scatter plot
import plotly.express as px

# load iris data
df = px.data.iris()

# create scatter plot
fig = px.scatter(df, x="sepal_width", y="sepal_length",
                 color="species", size="petal_length", hover_data=["petal_width"])

# update layout
fig.update_layout(autosize=True)

# Export the figure to json format
j = fig.to_json()

# apply css to the figure
css_txt = [{'class': 'stretch'}]

# add the scatter plot to the slide
slide4.add_content([j], columns=[12], styles=css_txt)

slide4.png

Sharing GeoJSON data between multiple Plotly charts

You can optimize performance by sharing large data (like GeoJSON) between multiple charts across the entire presentation.

Simple example:

# Add shared data once
p.add_global_geojson("my_data", geojson_dict)
# Use in multiple charts with shared_data_ids parameter  
slide.add_content([chart1, chart2],
                  columns=[6, 6],
                  shared_data_ids=["my_data", "my_data"])

Complete example:

from respysive import Presentation, Slide

# Create presentation
p = Presentation()

# Load GeoJSON data
from urllib.request import urlopen
import json
with urlopen('https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json') as response:
    counties = json.load(response)

# Load unemployment data
import pandas as pd
df_counties = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/fips-unemp-16.csv", dtype={"fips": str})

# Add additional columns with pandas
import random
random.seed(42)
df_counties['population'] = [random.randint(10000, 500000) for _ in range(len(df_counties))]
df_counties['income'] = [random.randint(25000, 85000) for _ in range(len(df_counties))]
# Add area data for density calculation
df_counties['area_sq_miles'] = [random.randint(200, 2000) for _ in range(len(df_counties))]
df_counties['density'] = (df_counties['population'] / df_counties['area_sq_miles']).round(1)

# Add global geojson data to presentation
p.add_global_geojson("us_counties", counties)

slide_maps = Slide()
slide_maps.add_title("Sharing geojson data between multiple plotly charts", **{'class': 'r-fit-text'})

# Add context text
context_text = """
Counties geojson data (~2MB) is shared across the presentation
"""

css_txt = [{'text-align': 'center', 'font-size': "70%"}]

slide_maps.add_content([context_text], columns=[12], styles=css_txt)

# Create population density map
population_config = {
    "data": [{
        "type": "choropleth",
        "locations": df_counties['fips'].tolist(),
        "z": df_counties['density'].tolist(),
        "colorscale": "Blues",
        "colorbar": {
            "title": "Density<br>(per sq mi)",
            "thickness": 15,
            "len": 0.7
        },
        "hovertemplate": (
            "<b>County {text}</b><br>" +
            "Population: %{customdata[0]:,.0f}<br>" +
            "Area: %{customdata[1]:,.0f} sq mi<br>" + 
            "Density: <b>%{z:.1f} per sq mi</b>" +
            "<extra></extra>"
        ),
        "text": df_counties['fips'].tolist(),
        "customdata": list(zip(df_counties['population'], df_counties['area_sq_miles']))
    }],
    "layout": {
        "geo": {
            "projection": {"type": "albers usa"}, 
            "scope": "usa",
            "showlakes": True,
            "lakecolor": "lightblue"
        },
        "title": {
            "text": "Population Density by County (Random Data)",
            "x": 0.5,
            "xanchor": "center",
            "font": {"size": 16}
        },
        "margin": {"r":10,"t":50,"l":10,"b":10}
    }
}

# Create choropleth map for income
income_config = {
    "data": [{
        "type": "choroplethmap",
        "locations": df_counties['fips'].tolist(),
        "z": df_counties['income'].tolist(),
        "colorscale": [
            [0.0, "#ffffb2"],
            [0.2, "#fed976"],
            [0.4, "#feb24c"],
            [0.6, "#fd8d3c"],
            [0.8, "#f03b20"],
            [1.0, "#bd0026"]
        ],
        "colorbar": {
            "title": {
                "text": "Median Income ($)",
                "font": {"size": 14, "family": "Arial Black"}
            },
            "thickness": 20,
            "len": 0.8,
            "x": 1.02,
            "tickmode": "array",
            "tickvals": [25000, 40000, 55000, 70000, 85000],
            "ticktext": ["$25K", "$40K", "$55K", "$70K", "$85K"],
            "tickfont": {"size": 11}
        },
        "hovertemplate": (
            "<b>County FIPS: %{location}</b><br>" +
            "Median Income: <b>$%{z:,.0f}</b><br>" +
            "Population: <b>%{customdata:,.0f}</b>" +
            "<extra></extra>"
        ),
        "customdata": df_counties['population'].tolist(),
        "marker": {
            "line": {"color": "white", "width": 0.5},
            "opacity": 0.9
        }
    }],
    "layout": {
        "map": {
            "style": "carto-positron",
            "zoom": 3.2,
            "center": {"lat": 38.0, "lon": -97.0}
        },
        "title": {
            "text": "Median Household Income by County (Random Data)",
            "x": 0.5,
            "xanchor": "center",
            "font": {
                "size": 18,
                "family": "Arial Black", 
                "color": "#2E86AB"
            },
            "pad": {"t": 20}
        },
        "annotations": [{
            "text": "Data source: Simulated random data",
            "showarrow": False,
            "x": 0.99,
            "y": 0.01,
            "xref": "paper",
            "yref": "paper",
            "xanchor": "right",
            "yanchor": "bottom",
            "font": {"size": 10, "color": "gray"}
        }],
        "margin": {"r":80,"t":80,"l":20,"b":40},
        "paper_bgcolor": "rgba(248, 249, 250, 0.95)"
    }
}

# Add the two maps side by side using shared GeoJSON data
slide_maps.add_content(
    [population_config, income_config], 
    columns=[6, 6],
    shared_data_ids=["us_counties", "us_counties"]
)

This approach stores the large counties GeoJSON data (~2MB) only once globally in the presentation. Multiple maps across any slides can then reference this shared data, avoiding duplication.

Compatible map types:

  • choropleth - Standard geographic choropleth maps
  • choroplethmap - Modern tile-based choropleth maps
  • scattermap/scattermapbox - Scatter plots on maps
  • Line plots on maps (using scattermap with mode: "lines")

slide4b.png

Altair

slide5 = Slide()
slide5.add_title("Altair")

# import altair for creating scatter plot
import altair as alt

source = px.data.iris()

# create scatter plot
chart = (
    alt.Chart(source)
    .mark_circle(size=60)
    .encode(
        x="sepal_width", y="sepal_length", color="species",
        tooltip=["species", "sepal_length", "sepal_width"],
    )
    .interactive()
    .properties(width=900, height=500)
)

# Export the figure to json format
j = chart.to_json()

# add the scatter plot to the slide
slide5.add_content([j], columns=[12])

slide5.png

Matplotlib fig are automatically converted to svg

slide5_fig = Slide()
slide5_fig.add_title("Matplotlib")

import numpy as np
import matplotlib.pyplot as plt

x = np.arange(0,4*np.pi-1,0.1)   # start,stop,step
y = np.sin(x)
z = np.cos(x)

plt.rcParams["figure.figsize"] = (8, 5)
fig, ax = plt.subplots()
plt.plot(x,y,x,z)
plt.xlabel('x values')
plt.title('sin and cos ')
plt.legend(['sin(x)', 'cos(x)'])

# add the  plot to the slide
slide5_fig.add_content([fig], columns=[12])

slide5_fig.png

It is highly recommended to set chart's width and height manually

LaTeX support

You can use LaTeX mathematical expressions in your slides. The package automatically detects and processes LaTeX syntax:

slide6 = Slide()
slide6.add_title("Mathematical Equations")

# Text with LaTeX expressions
math_content = """
The Gaussian function $f(x) = e^{-x^2}$ or in display mode:

$$f(x) = e^{-x^2}$$
"""

slide6.add_content([math_content], columns=[12])

The LaTeX processing is automatic when you include $ or $$ delimiters in your text content.

Bootstrap cards

Bootstrap Cards can also be added with add_card() method.

slide7 = Slide()

# card 1 content
txt_card1 = markdown("""
- list 1
- list 2

""")

# card 1 image
univ_url = "https://upload.wikimedia.org/wikipedia/commons/b/b5/Mandel_zoom_04_seehorse_tail.jpg"

# list of cards. These orders will be the same on the HTML page
cards = [{'text': txt_card1, 'image': univ_url},  # Only text and image
         {'image': logo_url, 'text': "Card text 2", 'title': "Card Title 2", },  # Image, text and title
         {'title': "Card Title 3", 'text': "Card text 3"}]  # Title and text

# styles for each cards
styles_list = [{'font-size': '20px', 'color': '#1d3557', 'class': 'bg-danger'},
               {'font-size': '20px', 'color': '#e63946', 'class': 'bg-warning'},
               {'font-size': '20px', 'color': '#f1faee', 'class': 'bg-info'}]

# add title and card to slide
slide7.add_title("Bootstrap cards can be added")
slide7.add_card(cards, styles_list)

slide6.png

Background image

Reveal.js Slide Backgrounds by passing a class data-background-* to the Slide() method with a kwarg

bckgnd_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Frost_patterns_2.jpg/1920px-Frost_patterns_2.jpg"

# Create a dictionary with slide kwargs
slide_kwargs = {
    'data-background-image': bckgnd_url,
    'data-background-size': 'cover',  # more options here : https://revealjs.com/backgrounds/
}

# Create a slide object with slide kwargs
slide8 = Slide(center=True, **slide_kwargs)

css_background = {"class": "text-center", "color": "#e63946", "background-color": "#f1faee"}
slide8.add_title("Image  background", **css_background)

slide7.png

Vertical slides

You can add vertical slides. First, let's create slide 9 (horizontal one) and slide 10 (vertical one)

slide9 = Slide()
text = markdown("""Press arrow down to show vertical slide""")
slide9.add_title("Horizontal and vertical slides")
slide9.add_content([text])

slide10 = Slide(center=True)
slide10.add_title("Horizontal and vertical slides")
text = markdown("""This is a vertical slide""")
slide10.add_content([text])

They will be added as list in the next method to export your presentation

slide8_9.png

Speaker notes

You can add speaker notes to your slides which will be visible in the speaker view:

slide11 = Slide()
slide11.add_title("Speaker view")
text = markdown("""Press S for Speaker View""")

# Add speaker notes
speaker_notes = markdown("""
  <aside class="notes">
    This is a test for speaker view
  </aside>
""")

slide11.add_content([text])
slide11.add_content([speaker_notes])

Press 'S' during the presentation to open the speaker view with your notes.

Presentation rendering

Last step in rendering your Reveal.js presentation with respysive-slide as HTML The Presentation.add_slide() method is used

# Adding slide to the presentation
p.add_slide([slide1, slide1b, slide2, slide3, slide4, slide_maps, slide5, slide5_fig, slide6, slide7, slide8, [slide9, slide10], slide11])

# Saving the presentation in HTML format
p.save_html("readme_example.html")

As you can see, slides 9 and 10 are inside a list. That tells respysive-slide to create vertical slide

Different Reveal.js theme and parameters can be added :

p.save_html(file_name,
            theme="moon",
            width=960,
            height=600,
            minscale=0.2,
            maxscale=1.5,
            margin=0.1,
            custom_theme=None,  # If theme="custom", pass here the custom css url 
            center=True,        # Vertical centering of slides (default: True)
            embedded=False)     # Embedded presentation mode (default: False)

Vertical centering and layout options

  • center=True (default): Slides are vertically centered on screen based on content
  • center=False: Slides remain at fixed height, content aligns to top - useful for presentations with lots of content
  • embedded=True: Presentation adapts to its container size - useful for embedding in web pages
  • embedded=False (default): Presentation covers full browser viewport

PDF export

The slide can be exported with the classic Reveal.js method.

Just add ?print-pdf at the end of the url and open the in-browser print dialog : https://raw.githack.com/fbxyz/respysive-slide/master/readme_example.html?print-pdf .

Best results are obtained with Chrome or Chromium

Future features

  • add method for speaker view
  • offline presentation
  • prettify the final rendering

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

respysive_slide-1.1.15.tar.gz (26.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

respysive_slide-1.1.15-py3-none-any.whl (22.9 kB view details)

Uploaded Python 3

File details

Details for the file respysive_slide-1.1.15.tar.gz.

File metadata

  • Download URL: respysive_slide-1.1.15.tar.gz
  • Upload date:
  • Size: 26.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.5

File hashes

Hashes for respysive_slide-1.1.15.tar.gz
Algorithm Hash digest
SHA256 1923daa9e3fd242f2430459f9356dd09fc0eb3d1e8034de65714959b14316880
MD5 17452a22111bccdf9a0610473718b556
BLAKE2b-256 7e26d9a6a5fa84c50ea0dc2dde01c3bccc29fcde74196bd98919c4cf1b4570af

See more details on using hashes here.

File details

Details for the file respysive_slide-1.1.15-py3-none-any.whl.

File metadata

File hashes

Hashes for respysive_slide-1.1.15-py3-none-any.whl
Algorithm Hash digest
SHA256 2847805e8f3291d912cf38f1b8baac43eae3c9a62729edf7d307202170c291c6
MD5 59e592b7cf3b07428d9345b412b18030
BLAKE2b-256 89d20dfdeb456adda2736e2a3e0ab4fff048859bf95cce7c202f5f221e84ddc8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page