54d7b85bed
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
139 lines
4.2 KiB
Python
139 lines
4.2 KiB
Python
from pptx import Presentation
|
|
from pptx.util import Inches, Pt
|
|
from pptx.dml.color import RGBColor
|
|
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
|
|
from pptx.enum.shapes import MSO_SHAPE
|
|
|
|
OUT_PATH = r"C:\Users\jhodgkin\Documents\dev\primary-song-slides\songs\anytime-anywhere\AnytimeAnywhereFlowchart.pptx"
|
|
|
|
SLIDE_W = Inches(13.333)
|
|
SLIDE_H = Inches(7.5)
|
|
|
|
TWILIGHT = RGBColor(0x1C, 0x2E, 0x4A)
|
|
GOLD = RGBColor(0xE8, 0xC5, 0x6A)
|
|
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
|
|
|
|
TITLE = "Anytime, Anywhere"
|
|
|
|
# Flow: A -> R, then R <-> B loop (R -> B -> back to R)
|
|
# The refrain box R appears once; both other lines feed into it.
|
|
BOX_R = "Anytime,\nanywhere"
|
|
|
|
# (verse label, box A text, box B text)
|
|
VERSES = [
|
|
("Verse 1",
|
|
"I can pray to my\nHeav'nly Father",
|
|
"For He always cares,\nand He hears my prayers"),
|
|
("Verse 2",
|
|
"I can listen for\nheav'nly guidance",
|
|
"The Spirit will guide\nas I seek His light"),
|
|
]
|
|
|
|
|
|
def add_box(slide, left, top, w, h, text, is_refrain):
|
|
box = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, left, top, w, h)
|
|
box.fill.solid()
|
|
box.fill.fore_color.rgb = GOLD if is_refrain else WHITE
|
|
box.line.color.rgb = GOLD
|
|
box.line.width = Pt(2.5)
|
|
|
|
tf = box.text_frame
|
|
tf.word_wrap = True
|
|
tf.vertical_anchor = MSO_ANCHOR.MIDDLE
|
|
for i, line in enumerate(text.split("\n")):
|
|
p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
|
|
p.alignment = PP_ALIGN.CENTER
|
|
run = p.add_run()
|
|
run.text = line
|
|
run.font.size = Pt(28)
|
|
run.font.bold = True
|
|
run.font.color.rgb = TWILIGHT
|
|
return box
|
|
|
|
|
|
def add_arrow(slide, shape_type, left, top, w, h):
|
|
arrow = slide.shapes.add_shape(shape_type, left, top, w, h)
|
|
arrow.fill.solid()
|
|
arrow.fill.fore_color.rgb = GOLD
|
|
arrow.line.fill.background()
|
|
return arrow
|
|
|
|
|
|
def add_flow_slide(prs, verse_label, box_a, box_b):
|
|
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
|
|
|
fill = slide.background.fill
|
|
fill.solid()
|
|
fill.fore_color.rgb = TWILIGHT
|
|
|
|
# Title
|
|
title_box = slide.shapes.add_textbox(Inches(0.5), Inches(0.35), SLIDE_W - Inches(1.0), Inches(0.9))
|
|
p = title_box.text_frame.paragraphs[0]
|
|
p.alignment = PP_ALIGN.CENTER
|
|
run = p.add_run()
|
|
run.text = TITLE
|
|
run.font.size = Pt(40)
|
|
run.font.color.rgb = GOLD
|
|
run.font.bold = True
|
|
|
|
# Verse label under the title
|
|
label_box = slide.shapes.add_textbox(Inches(0.5), Inches(1.05), SLIDE_W - Inches(1.0), Inches(0.45))
|
|
p2 = label_box.text_frame.paragraphs[0]
|
|
p2.alignment = PP_ALIGN.CENTER
|
|
run2 = p2.add_run()
|
|
run2.text = verse_label
|
|
run2.font.size = Pt(20)
|
|
run2.font.color.rgb = WHITE
|
|
run2.font.italic = True
|
|
|
|
# Layout: A on top of the left column, R below it, B to the right of R.
|
|
# A flows down into R; R and B loop (R -> B on top, B -> R underneath).
|
|
box_w = Inches(4.8)
|
|
gap_x = Inches(1.7)
|
|
left_x = (SLIDE_W - (box_w * 2 + gap_x)) / 2
|
|
right_x = left_x + box_w + gap_x
|
|
|
|
a_y = Inches(1.7)
|
|
a_h = Inches(1.6)
|
|
r_y = Inches(4.3)
|
|
r_h = Inches(1.9)
|
|
|
|
add_box(slide, left_x, a_y, box_w, a_h, box_a, False)
|
|
add_box(slide, left_x, r_y, box_w, r_h, BOX_R, True)
|
|
add_box(slide, right_x, r_y, box_w, r_h, box_b, False)
|
|
|
|
# A -> R: down arrow
|
|
arrow_w = Inches(0.7)
|
|
arrow_l = Inches(0.85)
|
|
gap_y = r_y - (a_y + a_h)
|
|
add_arrow(slide, MSO_SHAPE.DOWN_ARROW,
|
|
left_x + (box_w - arrow_w) / 2,
|
|
a_y + a_h + (gap_y - arrow_l) / 2,
|
|
arrow_w, arrow_l)
|
|
|
|
# R <-> B loop: right arrow above, left arrow below
|
|
loop_arrow_w = Inches(1.3)
|
|
loop_x = left_x + box_w + (gap_x - loop_arrow_w) / 2
|
|
add_arrow(slide, MSO_SHAPE.RIGHT_ARROW,
|
|
loop_x, r_y + r_h * 0.22 - arrow_w / 2, loop_arrow_w, arrow_w)
|
|
add_arrow(slide, MSO_SHAPE.LEFT_ARROW,
|
|
loop_x, r_y + r_h * 0.78 - arrow_w / 2, loop_arrow_w, arrow_w)
|
|
|
|
print(f" Added: {verse_label}")
|
|
|
|
|
|
def main():
|
|
prs = Presentation()
|
|
prs.slide_width = SLIDE_W
|
|
prs.slide_height = SLIDE_H
|
|
|
|
for verse_label, box_a, box_b in VERSES:
|
|
add_flow_slide(prs, verse_label, box_a, box_b)
|
|
|
|
prs.save(OUT_PATH)
|
|
print(f"\nSaved: {OUT_PATH} ({len(VERSES)} slides)")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|