Skip to content

Instantly share code, notes, and snippets.

@cormullion
Created January 15, 2021 10:46
Show Gist options
  • Save cormullion/36e4a365c89c61569e667ea126e4cd2c to your computer and use it in GitHub Desktop.
Save cormullion/36e4a365c89c61569e667ea126e4cd2c to your computer and use it in GitHub Desktop.
Pluto slideshow blogpost by https://andreaskroepelin.de/blog/
### A Pluto.jl notebook ###
# v0.12.15
using Markdown
using InteractiveUtils
# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
quote
local el = $(esc(element))
global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
el
end
end
# ╔═╡ 6708f926-571d-11eb-0f47-93990ee66e51
begin
using PlutoUI
struct TwoColumn{L, R}
left::L
right::R
end
function Base.show(io, mime::MIME"text/html", tc::TwoColumn)
write(io, """<div style="display: flex;"><div style="flex: 50%;">""")
show(io, mime, tc.left)
write(io, """</div><div style="flex: 50%;">""")
show(io, mime, tc.right)
write(io, """</div></div>""")
end
TwoColumn(md"Note the kink at ``x=0``!", Resource("https://julialang.org/assets/infra/logo.svg"))
end
# ╔═╡ a014538e-571b-11eb-1c3b-cda45e4e95b8
using Luxor
# ╔═╡ b1a1f30c-5715-11eb-09cf-23d5cc6abaa7
html"<button onclick='present()'>present</button>"
# ╔═╡ 8a7f14a6-5715-11eb-1c99-d15d6a3538fa
md"""
# What is a Pluto notebook?
If you haven't heard of it before, the Pluto.jl package is a very powerful contender to Jupyter notebooks. While Jupyter is somewhat language-agnostic (can you spot the three programming languages hiding in its name?), Pluto is made specifically for Julia. One of its core features is knowing what other cells a cell depends on and updating the dependents accordingly! Say, one cell defines ```x = 3``` and another ```y = x + 4```, then ```y``` is automatically updated whenever you change ```x```.
Another nicety is the possibility to hide code cells (by clicking on the eye-button) and/or hide outputs (by appending a ; to the code). This is very useful if you're concerned with how things look, as you probably are when it comes to slides.
Definitely go check this package out if you haven't already, it's probably one of the greatest contributions to the Julia ecosystem so far, in my opinion.
"""
# ╔═╡ e7cb06f6-5715-11eb-34f9-e9229ccc0305
md"""
## Slides
As obvious as this goal is, it's actually not (yet) trivial in Pluto. It boils down to what we can find in this issue.
Pluto has an undocumented feature: It starts a hidden presentation mode when the JavaScript function ```present()``` is called (not the Julia function!). This has the following effects:
- Space is inserted before headings of first and second order (```<h1>...</h1>``` and ```<h2>...</h2>``` in HTML or ```# ...``` and ```## ...``` in Markdown), such that consecutive ones have a distance of more than one screen height. (My impression is that first order headings are good for your title slide or maybe other slides with almost no other content, while second order headings work well as slide titles.)
- Navigation symbols (next/previous slide) become visible in the bottom right corner.
- The Live docs button is hidden.
This may feel like a hack and it surely is one but it does work quite nicely. For a real slide-appearance, though, I found it necessary to manually zoom in in the browser (200 % worked for me) and put my browser into fullscreen mode (press F11 or something like that (yeah, you probably know that yourself, I just recently found out there is a <kbd> tag in HTML and wanted to brag with that skill)).
"""
# ╔═╡ 72ea05d8-5717-11eb-3c82-7fbe9ef2f79a
md"""
To make calling that JavaScript function convenient, you can add the following to the beginning of your notebook:
```
html"<button onclick='present()'>present</button>"
```
Now you only have to click a button. Clicking it again will leave presentation mode.
Taking the traditional route, your slides will have a good amount of textual information, so the `md""` (or `md ` for multiline content) string macro is your valuable companion. Using it effectively is the goal of the next section.
"""
# ╔═╡ 16a822fe-5718-11eb-0a02-0358fabbdb09
md"""
## Textual information
Markdown supports quite a range of typography, especially headings, lists, and highlighting. Use it as it suits your needs (keep text on slides compact, you know the deal). Here is what I found to be particularly useful:
Strong text (inside ** **) is nice for highlighting single words but can be too weak for really important statements. Use low order headings (order 5 or 6) for that.
A symbol can express in maximal brevity what you would, otherwise, need many words for. Make use of Julia's excellent Unicode support and Pluto's tab-completion! Need an arrow? \rightarrowTab is your friend. Or what about \rightwavearrow or \downzigzagarrow for your fancy proof by contradiction?
Speaking of proofs, Julia's markdown parser supports maths typesetting. With ``\sin(x \cdot \pi)`` for inline maths and
```math
\int \mathrm{e}^{-x^2} \; \mathrm{d}x
```
for display style maths, you can summon the powers of LaTeX (or MathJax, rather).
Sometimes you want to have text that clearly stands out from its surrounding, be it for a more/less important note, an interjection, or whatever comes to your mind. Pluto formats blockquotes using a rounded box with gray background so you can use that:
I don't really care if you actually read this...
> But this is important!
> I will ask this in the examn!
And now you can go back to playing on your phone.
"""
# ╔═╡ 9282b678-5718-11eb-1ba9-c7e49bcbafec
md"""
## Layouting
While Pluto strictly places content as a large vertical stack, it also allows for arbitrary HTML which we can use for advanced layouting. I present two ideas how we can utilise Julia's type- and IO-system to make our lifes easier.
- foldable content
- side by side
"""
# ╔═╡ 16519f4c-571d-11eb-2114-31174b95c7d8
md"""
## Foldable content
You might want to have content that is only visible after explicitly clicking a certain element. HTML offers the <details> tag that does exactly that:
```
<details>
<summary> A secret </summary>
<p> Pluto is fun </p>
</details>
```
"""
# ╔═╡ 32278380-571d-11eb-3df5-3ff57f8aef6b
md"becomes"
# ╔═╡ f525ab5a-5718-11eb-0291-c1185b211524
html"
<details>
<summary> A secret </summary>
<p> Pluto is fun </p>
</details>
"
# ╔═╡ e59ed710-5718-11eb-3972-8b2af4ad0228
md"""
Now let's define a type Foldable that represents a title and some content, as well as telling Julia how we would like to have it displayed by overriding Base.show:
```
struct Foldable{C}
title::String
content::C
end
function Base.show(io, mime::MIME"text/html", fld::Foldable)
write(io,"<details><summary>$(fld.title)</summary><p>")
show(io, mime, fld.content)
write(io,"</p></details>")
end
```
It basically says, whenever a Foldable is supposed to be shown on a display capable of HTML (as Pluto is), first write this HTML, then do whatever its content does when it's displayed as HTML, and finally close the HTML tags. We can now just write
```
Foldable("What is the gravitational acceleration?", md"Correct, it's ``\pi^2``.")
```
or even
```
Foldable("Some cool plot:", plot(0:10, x -> x^2))
```
"""
# ╔═╡ 48d47b4c-571d-11eb-0207-9f1b39d732a3
begin
struct Foldable{C}
title::String
content::C
end
function Base.show(io, mime::MIME"text/html", fld::Foldable)
write(io,"<details><summary>$(fld.title)</summary><p>")
show(io, mime, fld.content)
write(io,"</p></details>")
end
Foldable("What is the gravitational acceleration?", md"Correct, it's ``\pi^2``.")
end
# ╔═╡ 3ca4a06c-5719-11eb-3b58-356062fc5235
md"""
## Side by side
Having content arranged in two columns, like some text on the left and an image on the right, can look quite nice. The HTML makes use of CSS flexbox this time, but the rest is very similar to the above:
```
struct TwoColumn{L, R}
left::L
right::R
end
function Base.show(io, mime::MIME"text/html", tc::TwoColumn)
write(io, "" "<div style="display: flex;"><div style="flex: 50%;">"" ")
show(io, mime, tc.left)
write(io, "" "</div><div style="flex: 50%;">"" ")
show(io, mime, tc.right)
write(io, "" "</div></div>"" ")
end
```
You can use it as ```TwoColumn(md"Note the kink at ``x=0``!", plot(-5:5, abs))```.
Varying the column widths or introducing a further parameter for that is possible, of course.
"""
# ╔═╡ 8682d4be-571a-11eb-1853-fbaa2baa4825
md"""
## Interactivity
Until now, you might wonder why you shouldn't just use LaTeX Beamer (or even PowerPoint *shiver*). And if your slides are rather static there's really no point in using Pluto for them. But if you want cool interactivity, then Pluto has your back!
One cannot overstate how cool Pluto's reactiveness is. As said in the beginning, whenever you change some piece of code, everything dependent on that is updated. You might, for example, have this kind of code:
```
result = some_fancy_computation(parameters)
```
followed by this cell:
```
md"Some text where $result is interpolated."
```
Now changing parameters automatically changes this Markdown text.
"""
# ╔═╡ a59fade0-571a-11eb-121f-ada06ee3fa1e
md"""
## Showing code
While you probably hide most of the code cells in a presentation notebook (especially those cosmetic ones we considered so far), Julia's syntax is quite presentable and can be displayed to show directly what happens. Why not reveal a cell that says
```
plot(data.time, data.gdp)
```
and be able to change data.gdp to, say, data.avg_income if you had not planned to present this but are asked to do so by someone in the audience? Or, if your code did not turn out so concise, you can just wrap it in a simple function with few parameters (Clean interfaces, everyone! It always pays off! :p).
I think displaying code is generally helpful for discussions like "Nice finding, but what if we do …?" during a talk.
"""
# ╔═╡ bb6d1482-571a-11eb-2d4d-e7855c26a929
md"""
## PlutoUI
The Pluto package has a little sister, [PlutoUI](https://github.com/fonsp/PlutoUI.jl), which makes interacting with the code visually much more appealing. Check out its documentation for all the features, I'll just gloss over what I have found most useful for presentations.
The fundamental concept here is Pluto's ```@bind``` macro. With ```@bind x some_ui_element```, you can get arbitrarily complex UI elements that the user can interact with to change ```x```. Most of the time, you will probably use
```
@bind x Slider(1:100, default = 42)
```
which will give you a [slider].
If you need to chose a category instead, try
```
@bind cat Select(["yesterday", "today", "tomorrow"])
```
It happens that you'd like to change a variable at mutliple places. While Pluto does not allow redefining a variable, you can have
```
slider = @bind x Slider(10:20)
```
and then put slider into any slide you need. This spares you going back and forth a few slides just to adjust ```x```, but note that the appearance of the UI element is not automatically updated at all sites, so it will look wrong!
"""
# ╔═╡ fb213cb6-571a-11eb-34b5-95a41550242c
md"""
## Graphics
No need to convince you of their importance, right? I guess you generally have three types of graphics:
- preexisting images
- drawings
- plots
- animations
## Preexisting images
This is best done using PlutoUI again. You either use
```
Resource("https://julialang.org/assets/infra/logo.svg")
```
for online sources (preferred) or
```
LocalResource("../some/local/image.png")
```
for local files (discouraged, unless you somehow make sure to always distribute them with the notebook). These two types are not restricted to images, by the way, you can display audio and video as well.
"""
# ╔═╡ 60018304-571e-11eb-3687-65fb5b9a7545
Resource("https://julialang.org/assets/infra/logo.svg")
# ╔═╡ 37571f7a-571b-11eb-189c-f71f7646c482
md"""
## Graphics: Drawings
If you're the kind of person that heavily uses TikZ or diagrams in slides, you might look for something like [Luxor.jl](https://github.com/JuliaGraphics/Luxor.jl). It allows for a concise, imperative style of building graphics, quite a bit simpler than TikZ, I would say. I recommend using its ```@draw``` macro (instead of ```@png``` or ```@svg```) to prevent your working directory from being cluttered with graphics files:
```
@draw begin
text("A", Point(0, 0), halign=:center)
arrow(Point(0, 10), Point(0, 40))
text("B", Point(0, 50), halign=:center)
end
```
"""
# ╔═╡ f9422a6c-571b-11eb-02e0-5527800c5d00
@draw begin
fontsize(30)
text("A", Point(0, 0), halign=:center)
arrow(Point(0, 10), Point(40, 50))
text("B", Point(50, 60), halign=:center)
end 600 160
# ╔═╡ 77a213ac-571e-11eb-162c-dfbc1d708d01
md"""
For networky stuff consider GraphPlot.jl as well.
"""
# ╔═╡ 419e84fa-571b-11eb-001a-01d117f6eae2
md"""
## Graphics: Plots
You probably know the deal with plots.
Use the Plots.jl package and its vast ecosystem. When it comes to choosing a backend, Plotly is nice for its interactivity while GR is faster (I think) and supports animations, see the next section.
Of course, it's a matter of taste to some extent, so consider the other backends as well. (PlutoUI has a feature to display terminal output if you insist on using UnicodePlots, for example ;))
"""
# ╔═╡ 4b2b1a04-571b-11eb-34ea-1f2809b4235b
md"""
## Graphics: Animations
Your plots don't need to be static! Maybe you have a dynamic simulation or you need that extra time dimension to visualise your high dimensional data? There are basically two possibilities here:
The easiest thing is to have an automatic, looping animation. All you need is the ```@gif``` macro from Plots:
```
@gif for φ in 0 : .1 : 2π
plot(0 : .1 : 2π, t -> sin(t + φ))
end
```
Or, if you need more control, you can use ```@animate``` together with the ```gif()``` function:
```
let
# show the first 10 builtin colours:
animation = @animate for i in 1:10
scatter([0], [0], msize=10, shape=:hexagon, mcolour=i)
end
gif(animation, fps=10)
end
```
Suppose you have a fairly complex dynamics you'd like to demonstrate and you want to step through your animation manually. You could combine the following three cells to achieve this:
```
@bind plot_time Slider(1:42)
plots = let
state = init_my_complex_system()
collect(
let
update_state!(state, t)
x, y = extract_x_and_y(state)
plot(x, y)
end
for t in 1:42
)
end;
plots[plot_time]
```
Now you can take your time discussing each step in the greatest detail without "Oh and it's gone. Let's wait a moment until it comes again … Ah, here! Nope, gone again."
"""
# ╔═╡ Cell order:
# ╟─b1a1f30c-5715-11eb-09cf-23d5cc6abaa7
# ╟─8a7f14a6-5715-11eb-1c99-d15d6a3538fa
# ╟─e7cb06f6-5715-11eb-34f9-e9229ccc0305
# ╟─72ea05d8-5717-11eb-3c82-7fbe9ef2f79a
# ╟─16a822fe-5718-11eb-0a02-0358fabbdb09
# ╟─9282b678-5718-11eb-1ba9-c7e49bcbafec
# ╟─16519f4c-571d-11eb-2114-31174b95c7d8
# ╟─32278380-571d-11eb-3df5-3ff57f8aef6b
# ╟─f525ab5a-5718-11eb-0291-c1185b211524
# ╟─e59ed710-5718-11eb-3972-8b2af4ad0228
# ╟─48d47b4c-571d-11eb-0207-9f1b39d732a3
# ╟─3ca4a06c-5719-11eb-3b58-356062fc5235
# ╟─6708f926-571d-11eb-0f47-93990ee66e51
# ╟─8682d4be-571a-11eb-1853-fbaa2baa4825
# ╟─a59fade0-571a-11eb-121f-ada06ee3fa1e
# ╟─bb6d1482-571a-11eb-2d4d-e7855c26a929
# ╠═fb213cb6-571a-11eb-34b5-95a41550242c
# ╠═60018304-571e-11eb-3687-65fb5b9a7545
# ╠═a014538e-571b-11eb-1c3b-cda45e4e95b8
# ╟─37571f7a-571b-11eb-189c-f71f7646c482
# ╠═f9422a6c-571b-11eb-02e0-5527800c5d00
# ╠═77a213ac-571e-11eb-162c-dfbc1d708d01
# ╟─419e84fa-571b-11eb-001a-01d117f6eae2
# ╟─4b2b1a04-571b-11eb-34ea-1f2809b4235b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment