#################################
# Public Domain Image Sources
#################################
'''
1936, Jesse Owens: https://upload.wikimedia.org/wikipedia/commons/1/19/Jesse_Owens_1936.jpg
1920, Louis Armstrong: https://upload.wikimedia.org/wikipedia/commons/thumb/0/0e/Louis_Armstrong_restored.jpg/1280px-Louis_Armstrong_restored.jpg
1963, Martin Luther King Jr.: https://en.wikipedia.org/wiki/Martin_Luther_King_Jr.#/media/File:Martin_Luther_King_Jr_St_Paul_Campus_U_MN.jpg
'''
#################################
# Event Information
#################################
'''
This list stores all event information.
date_list[i][0] is the display date (what appears on the timeline).
date_list[i][1] is the numerical date (used to plot the event on the timeline).
date_list[i][2] is the information about the timeline event.
date_list[i][3] is the image URL or image label (optional).
'''
date_list = [
["~1600", 1600, "European colonists begin enslaving and transporting African people to the Americas. 1619 marks the first documented record of slaves brought to Virginia."],
["1808", 1808, "Congress bans importing slaves."],
["1820", 1820, "The Missouri Compromise bans slavery in the northern states."],
["1849", 1859, "Harriet Tubman escapes to Philadelphia. She would go on to return to Maryland to help her family and over 300 enslaved people escape using the Underground Railroad."],
["1861", 1861, "Southern states secede from the union and the Civil War begins."],
["1865", 1865, "The Civil War ends."],
["1865", 1865, "The Thirteenth Amendment to the U.S. Constitution abolishes slavery."],
["1868", 1868, "The Fourteenth Amendment to the U.S. Constitution grants citizenship and equal rights to Black Americans."],
["1916", 1916, "The Great Migration begins. Over the next 50 years, millions of Black Americans move from southern states to urban centers in the North and West."],
["~1920", 1920, "The Jazz Age starts led by talented musicians like Louis Armstrong, Jelly Roll Morton, Fletcher Henderson, and Duke Ellington.", "louis_armstrong_b7e"],
["~1924", 1924, "The Harlem Renaissance begins, starting a cultural movement in Harlem, NYC led by black writers, artists, and musicians."],
["1936", 1936, "Jesse Owens wins four gold medals at the 1936 Berlin Olympics in Nazi Germany. He wins gold in the 100m dash, 200m dash, long jump, and 4x100m Relay.", "jesse_owens_da8"],
["1947", 1947, "Jackie Robinson joins the Brooklyn Dodgers and becomes the first Black major league baseball player."],
["1955", 1955, "Rosa Parks refuses to give up her seat to a white person, leading to the Montgomery bus boycott."],
["1960", 1960, "Sit ins and become a nonviolent protest tactic, started by college students in North Carolina."],
["1963", 1963, 'Martin Luther King Jr. writes "Letter from a Birmingham Jail" and later in the year leads a march on D.C. where he makes his famous speech before 200,000 people.', "mlkjr_708"],
["1966", 1966, "The Black Panther Party is founded in Oakland, CA by Huey Newton and Bobby Seale to protect people from police brutality."],
]
#################################
# Scrolling functionality
#################################
stage.disable_all_walls()
scroll_sprites = [] # List to store all sprites that can scroll
def up_arrow_press():
for s in scroll_sprites:
s.set_y_speed(-10)
def up_arrow_release():
for s in scroll_sprites:
s.set_y_speed(0)
def down_arrow_press():
for s in scroll_sprites:
s.set_y_speed(10)
def down_arrow_release():
for s in scroll_sprites:
s.set_y_speed(0)
def disable_event():
pass
def turn_controls_off():
for s in scroll_sprites:
s.set_y_speed(0)
stage.event_key("up", disable_event)
stage.event_key_press("up", disable_event)
stage.event_key_release("up", disable_event)
stage.event_key("down", disable_event)
stage.event_key_press("down", disable_event)
stage.event_key_release("down", disable_event)
def turn_controls_on():
stage.event_key_press("up", up_arrow_press)
stage.event_key_release("up", up_arrow_release)
stage.event_key_press("down", down_arrow_press)
stage.event_key_release("down", down_arrow_release)
#################################
# Click Events
#################################
# Click event for each event sprite
def date_click(sprite):
# Turn off event until info box is closed
for s in event_sprites:
s.event_click(None)
info_box.glide_to(info_box_x, info_box.get_y())
info_date.set_text(sprite.info[0])
info_text.show()
info_text.set_text(sprite.info[2])
info_text.set_text_width(info_box_width-50)
close_button.go_to(195, 190)
close_button_x.go_to(195, close_button.get_y()-2)
# Check if there's an image and show toggle if yes
if sprite.image != None:
switch.go_to(switch_x, switch_y)
switch_left_label.go_to(switch_x-25, switch_y)
switch_right_label.go_to(switch_x+25, switch_y)
toggle.go_to(switch_x-25, switch_y)
toggle.linked_image = sprite.image
toggle.event_click(image_text_toggle)
close_button.linked_image = sprite.image
close_button.event_click(close_info_box)
# Click event for image/text toggle
def image_text_toggle():
toggle.event_click(None) # Disable event while performing it
toggle.set_speed(10)
# Perform actions to show the image
if toggle.get_x() == switch_x-25:
toggle.glide_to(switch_x+25, switch_y)
info_text.hide()
toggle.linked_image.go_to(info_box_x, info_box.get_y())
toggle.linked_image.show()
# Perform actions to show the text
else:
toggle.glide_to(switch_x-25, switch_y)
toggle.linked_image.hide()
info_text.show()
toggle.event_click(image_text_toggle) # Enable event
# Click event for close button
def close_info_box():
close_button.event_click(None)
# Move sprites offstage to hide them
if close_button.linked_image != None:
close_button.linked_image.go_to(0, offstage_below)
info_date.set_text("")
info_text.set_text("")
close_button.go_to(0, offstage_below)
close_button_x.go_to(0, offstage_below)
switch.go_to(switch_x, offstage_below)
switch_left_label.go_to(switch_x-25, offstage_below)
switch_right_label.go_to(switch_x+25, offstage_below)
toggle.go_to(switch_x-25, offstage_below)
info_box.glide_to(400, info_box.get_y())
# Enable the event clicking after close
for s in event_sprites:
s.event_click(date_click)
# Click event for NEW button
def new_click():
# Collect user input
display_date = stage.ask("What date information would you like to appear on the timeline? (ex. 1950, ~1730)")
numerical_date = int(stage.ask("What is the closest true date of the event? (This must be an integer)"))
event_info = stage.ask("What information would you like to include about this event?")
image_info = stage.ask("Paste the URL for an image here (optional):")
confirmation = stage.ask("Enter yes to confirm entry or no to discard.")
required_list = [display_date, numerical_date, event_info, confirmation] # These inputs are required
if confirmation.lower() in ["n", "no"] or "" in required_list:
print("Try again to make a new entry.")
return # Exit event and cancel new entry if chosen or missing required inputs
else:
new_entry = [display_date, numerical_date, event_info] # Create new event entry
if image_info != "":
new_entry.append(image_info) # Add image URL if included
date_list.append(new_entry) # Add new entry to date_list
# Sort date_list using second item of inner lists
date_list.sort(key=lambda x: x[1])
# Remove current event sprites to recreate timeline
for s in event_sprites:
stage.remove_sprite(s.text)
stage.remove_sprite(s)
event_sprites.clear()
plot_dates(date_list) # Replot and recreate event sprites
for s in event_sprites:
s.event_click(date_click)
# Provide instructions for user to save inputted information
print("Copy the list below and paste it into\nyour code as a new item in date_list.\nClick save so that it appears the next time you run your code!\n")
print(new_entry)
# Click event for START button
def start_click():
start_button.hide()
start_button_text.hide()
stage_shade.hide()
instructions_text.hide()
turn_controls_on() # Allow scrolling after start
for s in event_sprites:
s.event_click(date_click)
new_button.event_click(new_click)
#################################
# Helper Functions
#################################
# Helper function to dynamically resize images
def resize_sprite(sprite, max_dimension):
if sprite.get_width() >= sprite.get_height():
if sprite.get_width() >= max_dimension:
while sprite.get_width() > max_dimension:
sprite.set_size(.95)
else:
while sprite.get_width() < max_dimension:
sprite.set_size(1.05)
else:
if sprite.get_height() >= max_dimension:
while sprite.get_height() > max_dimension:
sprite.set_size(.95)
else:
while sprite.get_height() < max_dimension:
sprite.set_size(1.05)
# Create event sprite and add multiple properties to store data
def make_event(event_info, y_position):
rand_color = random.choice(["red", "orange", "yellow", "blue", "purple", "indigo", "violet", "green", "cyan", "pink"])
event_ellipse = codesters.Ellipse(timeline_x, y_position, 125, 35, rand_color, "black")
event_date = codesters.Text(event_info[0], event_ellipse.get_x(), event_ellipse.get_y())
event_ellipse.info = event_info # Store info text as property on event sprite
event_ellipse.text = event_date # Store date text sprite as property on event sprite
# If the item includes an image URL, create and resize the image offstage
if len(event_info) > 3:
event_ellipse.image = codesters.Sprite(event_info[-1], 0, offstage_below)
resize_sprite(event_ellipse.image, 290)
else:
event_ellipse.image = None
if rand_color in ["blue", "purple", "indigo", "green"]:
event_date.set_color("white")
scroll_sprites.extend([event_ellipse, event_date])
event_sprites.append(event_ellipse)
# Calculate relative position of each event item
def plot_dates(dates):
global last_event_y # Changes to store the position of the last created event sprite
for i, val in enumerate(dates):
if i == 0:
event_y = first_event_y
else:
event_y = last_event_y - (dates[i][1] - dates[i-1][1])*scale
if last_event_y - event_y < 40:
event_y -= 25
last_event_y = event_y # Store position of last created event sprite to plot the next one
make_event(val, event_y) # Create an event sprite
# Turn physics on so that event sprites can scroll
for s in scroll_sprites:
s.set_physics_on()
#################################
# Main Stage Setup
#################################
stage.set_color("cornflowerblue")
offstage_below = -1000 # Use as y value to create objects below the stage
event_sprites = [] # store all the event sprites to turn events on and off
first_event_y = 200 # initial position of the earliest event
last_event_y = first_event_y # store position of last created event sprite
scale = 2 # determines spacing between events on the timeline
timeline_x = -175
timeline = codesters.Rectangle(timeline_x, 0, 5, 1000, "black")
# Information display sprites
info_box_width = 300
info_box_x = 70
info_box = codesters.Rectangle(400, 10, info_box_width, 400, "orange", "black")
info_box.set_opacity(.75)
info_date = codesters.Text("", info_box_x, 190, "black")
info_date.set_text_weight("bold")
info_text = codesters.Text("", info_box_x, 0, "black")
close_button = codesters.Circle(0, offstage_below, 20, "red", "black")
close_button.linked_image = None
close_button_x = codesters.Text("X", 0, offstage_below, "black")
close_button_x.set_text_size(18)
switch_x = 70
switch_y = -165
switch = codesters.Rectangle(switch_x, offstage_below, 100, 25, "black")
switch_left_label = codesters.Text("TEXT", switch_x-25, offstage_below, "yellow")
switch_left_label.set_text_size(10)
switch_right_label = codesters.Text("IMAGE", switch_x+25, offstage_below, "yellow")
switch_right_label.set_text_size(10)
toggle = codesters.Rectangle(switch_x-25, offstage_below, 45, 20, "white")
toggle.linked_image = None
toggle.set_opacity(.5)
# Create event sprites
plot_dates(date_list)
new_button = codesters.Rectangle(70, -220, 100, 35, "green", "black")
new_button_text = codesters.Text("NEW", new_button.get_x(), new_button.get_y())
# Show instructions and start button
stage_shade = codesters.Square(0, 0, 500, "blue")
stage_shade.set_opacity(.3)
instructions = '''Use the UP and DOWN arrow keys to navigate the timeline!
\n\nClick on a date to learn what event took place at that time.
\n\nClick NEW and follow the instructions to add more events!'''
instructions_text = codesters.Text(instructions, 70, 50)
instructions_text.set_text_width(300)
start_button = codesters.Rectangle(70, -110, 250, 100, "orange", "black")
start_button_text = codesters.Text("START", start_button.get_x(), start_button.get_y())
start_button.event_click(start_click)