Skip to content

Project 1: Event poster series

Schools, clubs and festivals need posters. They need a lot of them, and they should all look like a family. Computer code is good at that. You write one poster, and the code makes the rest. In this project the poster is a small designer: you flip through the events, type a new headline, change the colours and save the files.

How it works

The full poster designer in the gallery has a few ideas at its core.

The layers. The poster is three layers: background, art and words. Each has its own function. It wipes the layer with f.clear() and draws it again. A layer keeps its drawing, so a set of "dirty" names says which layers have changed. Only those are drawn again. The tick boxes call f.hide_layer() and f.show_layer().

def background_layer(event):
    top, bottom, accent, ink = theme_of(event)
    with f.layer("background"):
        f.clear()                                  # a layer keeps its drawing, so wipe it first
        f.fill(f.linear_gradient(0, 0, 0, HEIGHT, [top, bottom]))
        f.rect(0, 0, WIDTH, HEIGHT)

if "art" in dirty:                                 # in draw(): only what changed
    art_layer(event)

The typed headline. The title lives in the event's dictionary. key_typed() adds a letter to it and key_pressed() takes one away on Backspace. Each change marks the words layer as dirty, so the headline is drawn again. A FormattedString makes the first word white and the rest gold.

def key_typed():
    event = EVENTS[index]
    if (f.key.isalnum() or f.key == " ") and len(event["title"]) < MAX_TITLE:
        event["title"] += f.key
        title_changed()                            # the words layer is drawn again

The save buttons. A poster in an animated sketch is one page. So "save PDF" puts one job per event in a list, and draw() does one job on each frame: it draws that event, then calls f.save(). The layers and the real text go into the file. The last button passes text="shapes".

jobs.append((index, "poster.svg", "live"))         # selectable text, for editing
jobs.append((index, "poster_final.svg", "shapes")) # every letter a shape, for handing over

number, name, mode = jobs.pop(0)                   # one job for each frame
f.save(name, text=mode)

Stage 1: make it work

A list of events, one poster, and two buttons to flip through them. The background is a gradient. The headline is text. The panel is a rounded rectangle. The buttons sit in a panel under the canvas. The left and right arrow keys flip the events too.

import funground as f

EVENTS = [
    {"title": "Book Fair", "date": "Sat 14 March, 10 am", "colour": (38, 70, 140)},
    {"title": "Music Night", "date": "Fri 21 March, 7 pm", "colour": (20, 90, 90)},
    {"title": "Spring Mela", "date": "Sun 30 March, 11 am", "colour": (170, 40, 90)},
]
index = 0


def setup():
    global previous, next_one
    f.size(480, 680)
    previous = f.create_button("previous")
    next_one = f.create_button("next")


def key_pressed():
    global index
    if f.key == "left":
        index = (index - 1) % len(EVENTS)
    if f.key == "right":
        index = (index + 1) % len(EVENTS)


def draw():
    global index
    if previous.clicked():                  # true once for each click
        index = (index - 1) % len(EVENTS)
    if next_one.clicked():
        index = (index + 1) % len(EVENTS)
    event = EVENTS[index]

    f.no_stroke()
    f.fill(f.linear_gradient(0, 0, 0, 680, [event["colour"], (120, 70, 170)]))
    f.rect(0, 0, 480, 680)
    f.fill(255, 196, 61)                    # a big yellow circle
    f.circle(240, 250, 300)
    f.fill(255, 255, 255, 235)              # a rounded panel for the details
    f.rect(36, 440, 408, 200, 34)
    f.fill(38, 40, 90)
    f.text_align("center", "top")
    f.text_size(24)
    f.text(event["date"], 240, 480)
    f.fill(255)
    f.text_size(54)
    f.text(event["title"], 240, 40)


f.run()

Run it and press the buttons. Each event is a dictionary, so a new event is one more line in the list.

Stage 2: make it yours

Here are some ways to make it more yours.

Type the headline. key_typed() is called for each letter you type. Add it to the title. Backspace arrives in key_pressed() as "backspace". The edit goes into the event itself, so anything you save later uses it.

def key_typed():
    event = EVENTS[index]
    if f.key.isalnum() or f.key == " ":
        event["title"] += f.key


def key_pressed():
    if f.key == "backspace":
        EVENTS[index]["title"] = EVENTS[index]["title"][:-1]

Add sliders. A slider can turn the colours or change the size of the sun. Make it once in setup() and read it in draw(). Keep each setting in the event, so every event can look different.

hue = f.create_slider(-180, 180, 0, step=1, label="hue")
sun = f.create_slider(90, 190, 150, step=1, label="sun size")

Use layers. A layer is a see-through sheet. In a PDF or SVG it stays a layer, so a designer can switch it on and off or change it. Put the background, the artwork and the words on their own layers. A layer keeps what you drew on it, so you only draw it again when something it shows changes. That keeps the poster quick. f.hide_layer() and f.show_layer() switch a layer on and off. Add a tick box for each one, and you can see what each layer does.

with f.layer("art"):
    f.clear()                               # wipe the layer, then draw it again
    f.circle(240, 250, 300)
f.hide_layer("art")                         # it keeps its drawing; it is just not shown

Use mixed text. A FormattedString can hold a bold word and an italic word in two colours.

headline = f.FormattedString()
headline.append("Spring ", size=54, style="bold", color="white")
headline.append("Mela", size=54, style="bold_italic", color="gold")
f.text(headline, 240, 40)

Use more scripts. The text in a poster does not have to be English. Add a line in Hindi, and an emoji. funground's fallback fonts draw them. A "Hindi line" tick box can switch the line on and off.

f.text("किताबें पढ़ो, सपने गढ़ो", 240, 600)
f.text("📚", 240, 250)

Wrap the details. f.text_box(details, x, y, width, height) wraps long text inside a box.

Make shapes with booleans. A path can be joined to another with |, cut with - and overlapped with &. A circle with a smaller circle cut out of it is a ring or a crescent.

crescent = f.path().circle(240, 250, 200) - f.path().circle(290, 215, 160)
f.draw_path(crescent)

Shuffle the art. f.random_seed(n) makes the random numbers the same each time, for the same n. Keep a seed in each event. A "shuffle art" button adds one to it and draws the confetti again.

if shuffle.clicked():
    event["seed"] += 1
    f.random_seed(event["seed"])
    x, y = f.random(20, 460), f.random(20, 420)

Save the files. f.save("poster.svg") in an animated sketch writes the file at the end of the frame, with the canvas and its layers as they are. A button can call it. The files keep the layers, and the text stays real text, which you can select and search for.

A page belongs to a script, not to an animated sketch, so f.new_page() does not work here. To save every event, draw one event on each frame and save a file for it: events_1.pdf, events_2.pdf and so on. Keep a list of jobs, and do one job on each frame.

jobs = [(0, "events_1.pdf"), (1, "events_2.pdf"), (2, "events_3.pdf")]

def draw():
    if jobs:
        number, name = jobs.pop(0)
        draw_poster(EVENTS[number])         # draw everything for this event
        f.save(name)                        # written at the end of this frame
        return

Hand it over. Save one copy for yourself and one copy to give away. The copy with text="shapes" turns every letter into a shape, so it looks right on any computer, even one without your fonts. See chapter 13 for the golden rule of handoffs.

f.save("poster.svg")                        # live text, for editing
f.save("poster_final.svg", text="shapes")   # for handing over

If you would rather make a printed series in one go, a script can do it. A script can have many pages. Loop over the events, call f.new_page(480, 680) for each, and f.save("events.pdf") writes one PDF with every page.

Stage 3: make it shine

The full version is in the gallery: examples/gallery/projects/01_event_posters.py. It is a poster designer. The buttons and the arrow keys flip the events, and typing changes the headline, with a blinking caret. Sliders turn the hue and change the size of the sun and the number of its rays. A tick box shows or hides the Hindi line. A button shuffles the confetti. Three tick boxes show or hide the background, the art and the words, so you can see the layers. The burst of shapes is made with booleans. The headline has two styles, the details sit in a text box, and there is a Hindi line and an emoji. Three buttons save the files: events_1.pdf, events_2.pdf and events_3.pdf (the posters, with layers and real text), poster.svg (live text) and poster_final.svg (letters as shapes). A note on the poster says where the files went. Run it, then open the files in a PDF viewer or in Inkscape.

Challenge cards

  • Can you add a fourth event, with its own colours? You should only have to change the list.
  • Can you let the date be edited as well as the title? Press Enter to switch between them.
  • Can you add a slider for the size of the confetti?
  • Can you add a layer called "notes" with a reminder for the person who prints the poster? Hide it so it is in the file but not on the page.
  • Can you save a landscape poster as well? A script can give new_page two different sizes.

Credits and ideas

Event posters are an everyday kind of design, and this project has no single source or inspiration to credit. The code is written for funground (CC0). See CREDITS.md.

Back to: 18. Projects