A while loop, user input, and a growing list — your most ambitious script yet
Day 9 of 40
scene_planner.py — a two-day projectwhile True: ... break pattern to run a loop until the user is donenot in to validate user input against a list of allowed values.strip() to clean whitespace from user inputlen() for auto-numbering shotsYesterday's shot filter worked on hardcoded data — the shots were already in the script. Today you build the opposite: a script where the user enters the shots interactively, one at a time, until they're done. The script collects each shot as a dictionary, appends it to a list, and auto-numbers them.
This is a two-day build. Today you write the input loop — the part that collects shots. Tomorrow you add the output and platform summary.
This script is longer and more complex than anything you've built so far. Breaking it into two sessions lets you understand each piece before adding the next one. By the end of Day 10 you'll have a genuinely useful scene planning tool.
Create a new file called scene_planner.py in your Python Training folder.
Here's the full code you'll write today. Read it through completely first.
scene = []
scene_num = input("What scene number is this? ")
print(f"\n=== Planning Scene {scene_num} ===")
print("Add shots one at a time. Type 'done' when finished.\n")
while True:
description = input("Shot description (or 'done'): ")
if description.strip().lower() == "done":
break
platform = input("Platform (Kling / Runway / Veo): ")
valid_platforms = ["kling", "runway", "veo"]
if platform.lower() not in valid_platforms:
print(f" ⚠ Unknown platform '{platform}'. Defaulting to Kling.")
platform = "Kling"
print(" Shot types: wide, medium, close-up, aerial, tracking, slow-mo")
shot_type = input(" Shot type: ")
shot = {
"number": len(scene) + 1,
"description": description,
"platform": platform,
"type": shot_type,
}
scene.append(shot)
print(f" ✓ Shot {shot['number']} added.\n")
scene = []
scene_num = input("What scene number is this? ")
print(f"\n=== Planning Scene {scene_num} ===")
print("Add shots one at a time. Type 'done' when finished.\n")
scene = [] creates an empty list. This is where all the shot dictionaries will go as the user adds them. Starting empty and growing it is the pattern for building data from user input.
scene_num stores the scene number as a string. We don't convert it to an integer because we're only displaying it, never doing math with it.
The === ... === header is just decoration — it makes the terminal output easier to read at a glance.
while True:
description = input("Shot description (or 'done'): ")
if description.strip().lower() == "done":
break
while True: means "loop forever." On its own, this would run until you force-quit the program. The break statement is what makes it stop — it immediately exits the loop when the condition is met.
This pattern — while True: ... break — is the standard way to write "keep going until the user decides to stop." You'll use it constantly in interactive scripts.
description.strip() removes any leading or trailing whitespace the user may have accidentally typed. If someone typed " done " with spaces, .strip() turns it into "done" before the comparison.
.lower() makes the check case-insensitive. "DONE", "Done", and "done" all trigger the break.
Think of while True: as saying "open a conversation loop." break says "end the conversation." Between those two lines, everything runs on repeat until the user triggers the exit condition.
This is different from a for loop, which runs a fixed number of times (once per item in a list). A while True loop runs an unknown number of times — however many times the user keeps adding shots.
# for loop: runs exactly 5 times (once per shot)
for shot in shots:
print(shot)
# while True: runs until the user types 'done'
while True:
answer = input("Keep going? (done to quit): ")
if answer == "done":
break
platform = input("Platform (Kling / Runway / Veo): ")
valid_platforms = ["kling", "runway", "veo"]
if platform.lower() not in valid_platforms:
print(f" ⚠ Unknown platform '{platform}'. Defaulting to Kling.")
platform = "Kling"
valid_platforms is a list of allowed values (all lowercase so comparison is easy). This is a common pattern: define what's allowed, then check against it.
not in is the reverse of in. "pika" not in valid_platforms returns True because "pika" isn't in the list. If the user's input isn't valid, we print a warning and override it with a sensible default ("Kling") so the script keeps running rather than crashing.
Notice we check platform.lower() against the lowercase list — so "KLING", "Kling", and "kling" all pass validation correctly.
The in operator checks if something is inside a collection:
"kling" in ["kling", "runway", "veo"] # True
"sora" in ["kling", "runway", "veo"] # False
"sora" not in ["kling", "runway", "veo"] # True
in and not in work on lists, strings, and (next week) dictionaries. They're one of Python's most readable features — the code says almost exactly what it means in English.
print(" Shot types: wide, medium, close-up, aerial, tracking, slow-mo")
shot_type = input(" Shot type: ")
shot = {
"number": len(scene) + 1,
"description": description,
"platform": platform,
"type": shot_type,
}
scene.append(shot)
print(f" ✓ Shot {shot['number']} added.\n")
After collecting all inputs, we package them into a dictionary called shot. Each key is a string label; each value comes from a variable we collected with input().
"number": len(scene) + 1 — this auto-numbers each shot. Before we append the new shot, len(scene) tells us how many shots are already in the list. If there are 0 shots so far, this shot is #1. If there are 2 shots, this is #3. This is cleaner than manually tracking a counter variable.
scene.append(shot) adds the completed dictionary to the scene list. After this line, the shot is permanently stored in the list, and the loop goes back to the top to ask for the next one.
The trailing comma after shot_type, is valid Python and a common style for multiline dictionaries and lists — it makes adding a new line later easier without forgetting a comma.
$ python scene_planner.py
What scene number is this? 3
=== Planning Scene 3 ===
Add shots one at a time. Type 'done' when finished.
Shot description (or 'done'): Wide establishing, golf course at dawn
Platform (Kling / Runway / Veo): Kling
Shot types: wide, medium, close-up, aerial, tracking, slow-mo
Shot type: wide
✓ Shot 1 added.
Shot description (or 'done'): Close-up on club head at impact
Platform (Kling / Runway / Veo): Sora
⚠ Unknown platform 'Sora'. Defaulting to Kling.
Shot types: wide, medium, close-up, aerial, tracking, slow-mo
Shot type: close-up
✓ Shot 2 added.
Shot description (or 'done'): done
The script ends cleanly after typing "done" — no output yet, because that's tomorrow's work. But the data was being collected and stored correctly the whole time.
If you accidentally created an infinite loop (you typed "done" but it didn't exit), press Ctrl + C in the terminal to force-stop the script. Then re-read the break condition — a typo in the comparison string is the most common cause.
scene_planner.py runs without errorswhile True: ... break pattern in plain Englishnot in doeslen(scene) + 1 auto-numbers the shotsYou'll add the output section to scene_planner.py — the part that prints the final shot list with all fields, then counts how many shots are assigned to each platform. You'll also learn a clever trick for counting: the dictionary .get(key, default) pattern. Then the week wraps up with a vocabulary review.