fix pptx — clean version with slide transitions

This commit is contained in:
Dauren777 2026-07-07 13:11:47 +00:00
parent e5beb94f6d
commit d377502bc1
2 changed files with 185 additions and 349 deletions

View File

@ -1,13 +1,13 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Презентация о Казахстане с 3D-анимациями (fly, zoom, spin, bounce).""" """Презентация о Казахстане — чистый PPTX без кривых XML-анимаций."""
from pptx import Presentation from pptx import Presentation
from pptx.util import Inches, Pt, Emu from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE from pptx.enum.shapes import MSO_SHAPE
import copy from pptx.oxml.ns import qn
from lxml import etree import lxml.etree as etree
# ── Colors ── # ── Colors ──
INK = RGBColor(0x0F, 0x12, 0x18) INK = RGBColor(0x0F, 0x12, 0x18)
@ -23,144 +23,30 @@ prs.slide_height = Inches(7.5)
W = prs.slide_width W = prs.slide_width
H = prs.slide_height H = prs.slide_height
# ── Animation XML builder ── BLANK_LAYOUT = prs.slide_layouts[6]
P = "http://schemas.openxmlformats.org/presentationml/2006/main"
A = "http://schemas.openxmlformats.org/drawingml/2006/main"
R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
MC = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xml_ns = f'xmlns:p="{P}" xmlns:a="{A}" xmlns:r="{R}" xmlns:mc="{MC}"'
def _anim_seq_node():
"""Create <p:timing><p:tnLst><p:par>...</p:par></p:tnLst></p:timing>."""
return etree.SubElement(
etree.Element("dummy"),
f"{{{P}}}timing"
)
def add_animation(slide, anim_type, target_idx, delay_ms=0, dur_ms=500, **kwargs): def set_slide_transition(slide, transition_type='push', speed='med', advance_ms=None):
""" """Set slide transition effect."""
Add entrance animation to shape at target_idx. timing = slide._element.find(qn('p:transition'))
anim_type: 'flyIn', 'zoom', 'spin', 'float', 'bounce', 'appear', 'fade'
"""
nsmap = {'p': P, 'a': A, 'r': R, 'mc': MC}
# Find or create timing element
timing = slide._element.find(f'{{{P}}}timing')
if timing is None: if timing is None:
timing = etree.SubElement(slide._element, f'{{{P}}}timing') timing = etree.SubElement(slide._element, qn('p:transition'))
tnLst = timing.find(f'{{{P}}}tnLst') transitions = {
if tnLst is None: 'push': ('push', {'advClick': '1'}),
tnLst = etree.SubElement(timing, f'{{{P}}}tnLst') 'fade': ('fade', {'advClick': '1'}),
'wipe': ('wipe', {'dir': 'd', 'advClick': '1'}),
par_main = tnLst.find(f'{{{P}}}par') 'cover': ('cover', {'dir': 'l', 'advClick': '1'}),
if par_main is None: 'cut': ('cut', {'advClick': '1'}),
par_main = etree.SubElement(tnLst, f'{{{P}}}par') 'dissolve': ('dissolve', {'advClick': '1'}),
cTn_main = etree.SubElement(par_main, f'{{{P}}}cTn', id='1', dur='indefinite', 'zoom': ('zoom', {'advClick': '1'}),
restart='never', nodeType='timingRoot') }
etree.SubElement(cTn_main, f'{{{P}}}childTnLst') tag_name, attrs = transitions.get(transition_type, transitions['push'])
el = etree.SubElement(timing, qn(f'p:{tag_name}'))
childTnLst = par_main.find(f'{{{P}}}cTn/{{{P}}}childTnLst') for k, v in attrs.items():
el.set(k, v)
# Container par for this animation if advance_ms:
par = etree.SubElement(childTnLst, f'{{{P}}}par') timing.set('advTm', str(advance_ms))
cTn = etree.SubElement(par, f'{{{P}}}cTn', id=str(10 + target_idx * 3),
dur='indefinite', nodeType='withEffect')
stCondLst = etree.SubElement(cTn, f'{{{P}}}stCondLst')
etree.SubElement(stCondLst, f'{{{P}}}cond', delay='0')
childTn = etree.SubElement(cTn, f'{{{P}}}childTnLst')
# Inner par (the actual animation)
par2 = etree.SubElement(childTn, f'{{{P}}}par')
cTn2 = etree.SubElement(par2, f'{{{P}}}cTn', id=str(11 + target_idx * 3),
fill='hold')
stCondLst2 = etree.SubElement(cTn2, f'{{{P}}}stCondLst')
etree.SubElement(stCondLst2, f'{{{P}}}cond', delay=str(delay_ms))
childTn2 = etree.SubElement(cTn2, f'{{{P}}}childTnLst')
# Animation sequence
par3 = etree.SubElement(childTn2, f'{{{P}}}par')
cTn3 = etree.SubElement(par3, f'{{{P}}}cTn', id=str(12 + target_idx * 3),
fill='hold')
stCondLst3 = etree.SubElement(cTn3, f'{{{P}}}stCondLst')
etree.SubElement(stCondLst3, f'{{{P}}}cond', delay='0')
childTn3 = etree.SubElement(cTn3, f'{{{P}}}childTnLst')
# Set (make visible)
par_set = etree.SubElement(childTn3, f'{{{P}}}par')
cTn_set = etree.SubElement(par_set, f'{{{P}}}cTn', id=str(20 + target_idx),
fill='hold')
childTn_set = etree.SubElement(cTn_set, f'{{{P}}}childTnLst')
par_set2 = etree.SubElement(childTn_set, f'{{{P}}}par')
cTn_set2 = etree.SubElement(par_set2, f'{{{P}}}cTn', id=str(21 + target_idx))
stCondLst_set = etree.SubElement(cTn_set2, f'{{{P}}}stCondLst')
etree.SubElement(stCondLst_set, f'{{{P}}}cond', delay='0')
childTn_set2 = etree.SubElement(cTn_set2, f'{{{P}}}childTnLst')
set_el = etree.SubElement(childTn_set2, f'{{{P}}}set')
cBhvr_set = etree.SubElement(set_el, f'{{{P}}}cBhvr')
etree.SubElement(cBhvr_set, f'{{{P}}}cTn', id=str(22 + target_idx), dur='1')
tgtEl = etree.SubElement(cBhvr_set, f'{{{P}}}tgtEl')
spTgt = etree.SubElement(tgtEl, f'{{{P}}}spTgt', spid=str(target_idx + 2))
to = etree.SubElement(set_el, f'{{{P}}}to')
val_el = etree.SubElement(to, f'{{{P}}}val')
val_el.set(f'{{{A}}}val', '1')
# Main animation effect (animEffect)
anim_effect = etree.SubElement(childTn3, f'{{{P}}}animEffect')
anim_effect.set('transition', 'with')
anim_effect.set('filter', 'appear')
cBhvr_ae = etree.SubElement(anim_effect, f'{{{P}}}cBhvr')
etree.SubElement(cBhvr_ae, f'{{{P}}}cTn', id=str(30 + target_idx), dur=str(dur_ms))
tgtEl_ae = etree.SubElement(cBhvr_ae, f'{{{P}}}tgtEl')
spTgt_ae = etree.SubElement(tgtEl_ae, f'{{{P}}}spTgt', spid=str(target_idx + 2))
etree.SubElement(anim_effect, f'{{{P}}}filter', val='appear')
# Add actual motion/animation based on type
if anim_type == 'flyIn':
anim_motion = etree.SubElement(childTn3, f'{{{P}}}animMotion')
anim_motion.set('origin', 'layout')
anim_motion.set('path', 'left')
cBhvr_m = etree.SubElement(anim_motion, f'{{{P}}}cBhvr')
etree.SubElement(cBhvr_m, f'{{{P}}}cTn', id=str(31 + target_idx), dur=str(dur_ms))
etree.SubElement(cBhvr_m, f'{{{P}}}tgtEl')
ptLst = etree.SubElement(anim_motion, f'{{{P}}}ptLst')
pt_from = etree.SubElement(ptLst, f'{{{P}}}pt')
pt_from.set(f'{{{A}}}type', 'from')
from_x = kwargs.get('from_x', Inches(-6))
from_y = kwargs.get('from_y', 0)
to_el = etree.SubElement(ptLst, f'{{{P}}}pt')
to_el.set(f'{{{A}}}type', 'to')
pt_to = etree.SubElement(to_el, f'{{{P}}}pt')
pt_to.set('x', str(from_x))
pt_to.set('y', str(from_y))
elif anim_type == 'zoom':
anim_seq_zoom = etree.SubElement(childTn3, f'{{{P}}}animEffect')
anim_seq_zoom.set('transition', 'with')
anim_seq_zoom.set('filter', 'appear')
cBhvr_z = etree.SubElement(anim_seq_zoom, f'{{{P}}}cBhvr')
etree.SubElement(cBhvr_z, f'{{{P}}}cTn', id=str(31 + target_idx), dur=str(dur_ms))
tgtEl_z = etree.SubElement(cBhvr_z, f'{{{P}}}tgtEl')
etree.SubElement(tgtEl_z, f'{{{P}}}spTgt', spid=str(target_idx + 2))
etree.SubElement(anim_seq_zoom, f'{{{P}}}filter', val='appear')
elif anim_type == 'spin':
anim_spin = etree.SubElement(childTn3, f'{{{P}}}animRotation')
cBhvr_s = etree.SubElement(anim_spin, f'{{{P}}}cBhvr')
etree.SubElement(cBhvr_s, f'{{{P}}}cTn', id=str(31 + target_idx), dur=str(dur_ms))
etree.SubElement(cBhvr_s, f'{{{P}}}tgtEl')
anim_spin_to = etree.SubElement(anim_spin, f'{{{P}}}to')
anim_spin_to.set(f'{{{A}}}val', '3600000')
elif anim_type == 'bounce':
anim_bounce = etree.SubElement(childTn3, f'{{{P}}}animEffect')
anim_bounce.set('transition', 'with')
anim_bounce.set('filter', 'appear')
cBhvr_b = etree.SubElement(anim_bounce, f'{{{P}}}cBhvr')
etree.SubElement(cBhvr_b, f'{{{P}}}cTn', id=str(31 + target_idx), dur=str(dur_ms))
tgtEl_b = etree.SubElement(cBhvr_b, f'{{{P}}}tgtEl')
etree.SubElement(tgtEl_b, f'{{{P}}}spTgt', spid=str(target_idx + 2))
etree.SubElement(anim_bounce, f'{{{P}}}filter', val='appear')
def add_bg(slide, color): def add_bg(slide, color):
@ -186,8 +72,8 @@ def add_text(slide, text, left, top, width, height,
return txBox return txBox
def add_shape_box(slide, left, top, width, height, fill_color=BG_DARK, def add_box(slide, left, top, width, height, fill_color=BG_DARK,
border_color=None, radius=Inches(0.2)): border_color=None):
shape = slide.shapes.add_shape( shape = slide.shapes.add_shape(
MSO_SHAPE.ROUNDED_RECTANGLE, left, top, width, height MSO_SHAPE.ROUNDED_RECTANGLE, left, top, width, height
) )
@ -201,143 +87,124 @@ def add_shape_box(slide, left, top, width, height, fill_color=BG_DARK,
return shape return shape
def add_circle(slide, left, top, size, color):
c = slide.shapes.add_shape(MSO_SHAPE.OVAL, left, top, size, size)
c.fill.solid()
c.fill.fore_color.rgb = color
c.line.fill.background()
return c
def add_rect(slide, left, top, width, height, color):
r = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, left, top, width, height)
r.fill.solid()
r.fill.fore_color.rgb = color
r.line.fill.background()
return r
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
# SLIDE 1 — Title # SLIDE 1 — Title
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(prs.slide_layouts[6]) # blank s = prs.slides.add_slide(BLANK_LAYOUT)
add_bg(slide, BG_DARK) add_bg(s, BG_DARK)
set_slide_transition(s, 'zoom')
# Decorative circle add_circle(s, Inches(9.5), Inches(0.8), Inches(4.5), RGBColor(0x00, 0x3D, 0x5C))
circle = slide.shapes.add_shape(MSO_SHAPE.OVAL, Inches(9.5), Inches(1), Inches(4), Inches(4)) add_circle(s, Inches(10.2), Inches(1.5), Inches(3), CYAN)
circle.fill.solid() add_circle(s, Inches(-0.5), Inches(5.5), Inches(3), RGBColor(0x00, 0x3D, 0x5C))
circle.fill.fore_color.rgb = RGBColor(0x00, 0x3D, 0x5C) add_circle(s, Inches(0), Inches(6), Inches(2), GOLD)
circle.line.fill.background()
s = slide.shapes.add_shape(MSO_SHAPE.OVAL, Inches(10), Inches(1.5), Inches(3), Inches(3)) add_text(s, '🇰🇿', Inches(1), Inches(1.2), Inches(2), Inches(1.5), font_size=80, color=WHITE)
s.fill.solid() add_text(s, 'КАЗАХСТАН', Inches(1), Inches(2.8), Inches(8), Inches(1.5),
s.fill.fore_color.rgb = CYAN font_size=72, bold=True, color=WHITE)
s.line.fill.background() add_text(s, 'Сердце Евразии — девятая по территории страна мира',
add_animation(slide, 'spin', len(slide.shapes) - 1, delay_ms=300, dur_ms=1500) Inches(1), Inches(4.3), Inches(8), Inches(0.8),
font_size=24, color=GRAY)
# Flag emoji stats = [('2.7 млн км²', 'Площадь'), ('20 млн', 'Население'),
flag = add_text(slide, '🇰🇿', Inches(0.8), Inches(1.5), Inches(2), Inches(1.5), ('130+', 'Народов'), ('1 космодром', 'Байконур')]
font_size=80, color=WHITE, align=PP_ALIGN.LEFT) for i, (val, lbl) in enumerate(stats):
add_animation(slide, 'flyIn', len(slide.shapes) - 1, delay_ms=100, dur_ms=800) x = Inches(1 + i * 3)
y = Inches(5.6)
# Title add_box(s, x, y, Inches(2.7), Inches(1.2), RGBColor(0x1C, 0x26, 0x36))
title = add_text(slide, 'КАЗАХСТАН', Inches(0.8), Inches(3), Inches(8), Inches(1.5), add_text(s, val, x + Inches(0.2), y + Inches(0.1), Inches(2.3), Inches(0.6),
font_size=72, bold=True, color=WHITE, font_name='Calibri') font_size=24, bold=True, color=CYAN)
add_animation(slide, 'zoom', len(slide.shapes) - 1, delay_ms=400, dur_ms=1000) add_text(s, lbl, x + Inches(0.2), y + Inches(0.65), Inches(2.3), Inches(0.4),
font_size=13, color=GRAY)
# Subtitle
sub = add_text(slide, 'Сердце Евразии', Inches(0.8), Inches(4.5), Inches(6), Inches(1),
font_size=28, color=GRAY, font_name='Calibri')
add_animation(slide, 'flyIn', len(slide.shapes) - 1, delay_ms=700, dur_ms=800)
# Stats boxes
stats = [
('2.7 млн км²', 'Площадь'),
('20 млн', 'Население'),
('130+', 'Народов'),
('1 космодром', 'Байконур'),
]
for i, (val, label) in enumerate(stats):
x = Inches(0.8 + i * 3.1)
y = Inches(5.8)
box = add_shape_box(slide, x, y, Inches(2.8), Inches(1.2), RGBColor(0x1C, 0x26, 0x36))
add_text(slide, val, x + Inches(0.2), y + Inches(0.15), Inches(2.4), Inches(0.6),
font_size=26, bold=True, color=CYAN, font_name='Calibri')
add_text(slide, label, x + Inches(0.2), y + Inches(0.7), Inches(2.4), Inches(0.4),
font_size=13, color=GRAY, font_name='Calibri')
add_animation(slide, 'bounce', len(slide.shapes) - 1, delay_ms=900 + i * 150, dur_ms=600)
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
# SLIDE 2 — Geography # SLIDE 2 — Geography
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(prs.slide_layouts[6]) s = prs.slides.add_slide(BLANK_LAYOUT)
add_bg(slide, WHITE) add_bg(s, WHITE)
set_slide_transition(s, 'push')
# Section header bar add_rect(s, 0, 0, W, Inches(1.4), BG_DARK)
bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, W, Inches(1.4)) add_text(s, 'ГЕОГРАФИЯ', Inches(0.8), Inches(0.3), Inches(8), Inches(0.8),
bar.fill.solid() font_size=36, bold=True, color=CYAN)
bar.fill.fore_color.rgb = BG_DARK add_text(s, 'От каспийских берегов до алтайских гор', Inches(0.8), Inches(1.4),
bar.line.fill.background() Inches(8), Inches(0.5), font_size=16, color=GRAY)
add_text(slide, 'ГЕОГРАФИЯ', Inches(0.8), Inches(0.3), Inches(6), Inches(0.8),
font_size=36, bold=True, color=CYAN, font_name='Calibri')
add_animation(slide, 'flyIn', len(slide.shapes) - 1, delay_ms=100, dur_ms=600)
geo_items = [ geo = [
('🏔️', 'Горы', 'Тянь-Шань, Алтай. Высшая точка — Хан-Тенгри (7010 м)'), ('🏔️', 'Горы', 'Тянь-Шань, Чу-Илийские горы, Алтай.\nВысшая точка — Хан-Тенгри (7010 м)'),
('🌊', 'Каспийское море', 'Крупнейшее озеро мира. 1894 км побережья'), ('🌊', 'Каспийское море', 'Крупнейшее озеро мира.\nПобережье тянется на 1894 км'),
('🏜️', 'Степи', 'Бескрайние равнины — историческая родина кочевников'), ('🏜️', 'Степи', 'Бескрайние равнины —\nисторическая родина кочевников'),
('🌾', 'Озёра', 'Балхаш, Алакольболее 45 000 озёр'), ('🌾', 'Озёра', 'Балхаш, Алаколь, Маркаколь —\nболее 45 000 озёр'),
] ]
for i, (emoji, name, desc) in enumerate(geo_items): for i, (em, nm, ds) in enumerate(geo):
x = Inches(0.8 + (i % 2) * 6.2) col, row = i % 2, i // 2
y = Inches(1.8 + (i // 2) * 2.6) x = Inches(0.8 + col * 6.2)
box = add_shape_box(slide, x, y, Inches(5.6), Inches(2.2), y = Inches(2.2 + row * 2.5)
RGBColor(0xF5, 0xF7, 0xFA)) add_box(s, x, y, Inches(5.6), Inches(2.2), RGBColor(0xF5, 0xF7, 0xFA))
add_text(slide, emoji, x + Inches(0.3), y + Inches(0.2), Inches(1), Inches(0.8), add_text(s, em, x + Inches(0.3), y + Inches(0.15), Inches(1), Inches(0.7),
font_size=40, font_name='Calibri') font_size=38)
add_text(slide, name, x + Inches(1.2), y + Inches(0.25), Inches(4), Inches(0.5), add_text(s, nm, x + Inches(1.3), y + Inches(0.2), Inches(4), Inches(0.5),
font_size=22, bold=True, color=INK, font_name='Calibri') font_size=22, bold=True, color=INK)
add_text(slide, desc, x + Inches(1.2), y + Inches(0.8), Inches(4.2), Inches(1.2), add_text(s, ds, x + Inches(1.3), y + Inches(0.75), Inches(4), Inches(1.2),
font_size=15, color=GRAY, font_name='Calibri') font_size=14, color=GRAY)
add_animation(slide, 'flyIn', len(slide.shapes) - 1, delay_ms=300 + i * 200, dur_ms=700)
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
# SLIDE 3 — Facts & Figures # SLIDE 3 — Facts
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(prs.slide_layouts[6]) s = prs.slides.add_slide(BLANK_LAYOUT)
add_bg(slide, BG_DARK) add_bg(s, BG_DARK)
set_slide_transition(s, 'fade')
add_text(slide, 'ЦИФРЫ И ФАКТЫ', Inches(0.8), Inches(0.6), Inches(10), Inches(1), add_text(s, 'ЦИФРЫ И ФАКТЫ', Inches(0.8), Inches(0.6), Inches(10), Inches(1),
font_size=40, bold=True, color=WHITE, font_name='Calibri') font_size=40, bold=True, color=WHITE)
add_text(slide, 'Казахстан в цифрах', Inches(0.8), Inches(1.4), Inches(6), Inches(0.6), add_text(s, 'Казахстан в цифрах', Inches(0.8), Inches(1.4), Inches(6), Inches(0.5),
font_size=18, color=GRAY, font_name='Calibri') font_size=18, color=GRAY)
facts = [ facts = [('9', 'по величине\nв мире'), ('1991', 'год\nнезависимости'),
('9', 'по величине\nв мире'), ('70+', 'тонн золота\nв год'), ('1', 'космодром\nБайконур'),
('1991', 'год\nнезависимости'), ('3', 'столицы\nв истории')]
('70+', 'тонн золота\nв год'),
('1', 'космодром\nБайконур'),
('3', 'столицы\nв истории'),
]
for i, (num, txt) in enumerate(facts): for i, (num, txt) in enumerate(facts):
x = Inches(0.6 + i * 2.5) x = Inches(0.5 + i * 2.5)
y = Inches(2.5) add_box(s, x, Inches(2.5), Inches(2.2), Inches(3.5),
box = add_shape_box(slide, x, y, Inches(2.2), Inches(3.5), RGBColor(0x1C, 0x26, 0x36), CYAN)
RGBColor(0x1C, 0x26, 0x36), CYAN) add_text(s, num, x + Inches(0.2), Inches(2.9), Inches(1.8), Inches(1.2),
add_text(slide, num, x + Inches(0.2), y + Inches(0.4), Inches(1.8), Inches(1.2), font_size=52, bold=True, color=CYAN, align=PP_ALIGN.CENTER)
font_size=52, bold=True, color=CYAN, align=PP_ALIGN.CENTER, font_name='Calibri') add_text(s, txt, x + Inches(0.2), Inches(4.2), Inches(1.8), Inches(1.4),
add_text(slide, txt, x + Inches(0.2), y + Inches(1.8), Inches(1.8), Inches(1.4), font_size=14, color=GRAY, align=PP_ALIGN.CENTER)
font_size=14, color=GRAY, align=PP_ALIGN.CENTER, font_name='Calibri')
add_animation(slide, 'zoom', len(slide.shapes) - 1, delay_ms=200 + i * 200, dur_ms=800)
# Bottom decorative line add_rect(s, Inches(0.8), Inches(6.5), Inches(11.7), Inches(0.04), CYAN)
line = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
Inches(0.8), Inches(6.5), Inches(11.7), Inches(0.04))
line.fill.solid()
line.fill.fore_color.rgb = CYAN
line.line.fill.background()
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
# SLIDE 4 — Culture # SLIDE 4 — Culture
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(prs.slide_layouts[6]) s = prs.slides.add_slide(BLANK_LAYOUT)
add_bg(slide, RGBColor(0xF0, 0xF4, 0xF8)) add_bg(s, RGBColor(0xF0, 0xF4, 0xF8))
set_slide_transition(s, 'cover')
bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, W, Inches(1.4)) add_rect(s, 0, 0, W, Inches(1.4), BG_DARK)
bar.fill.solid() add_text(s, 'КУЛЬТУРА И ТРАДИЦИИ', Inches(0.8), Inches(0.3), Inches(10), Inches(0.8),
bar.fill.fore_color.rgb = BG_DARK font_size=36, bold=True, color=GOLD)
bar.line.fill.background()
add_text(slide, 'КУЛЬТУРА И ТРАДИЦИИ', Inches(0.8), Inches(0.3), Inches(10), Inches(0.8),
font_size=36, bold=True, color=GOLD, font_name='Calibri')
culture_items = [ culture = [
('🦅', 'Беркутчи', 'Соколиная охота с орлами — наследие ЮНЕСКО'), ('🦅', 'Беркутчи', 'Соколиная охота с орлами — наследие ЮНЕСКО'),
('🎪', 'Юрта', 'Легко разборное жилище — символ свободы'), ('🎪', 'Юрта', 'Легко разборное жилище — символ свободы'),
('🎶', 'Домбра', 'Двухструнный инструмент — душа кюев'), ('🎶', 'Домбра', 'Двухструнный инструмент — душа кюев'),
@ -345,65 +212,57 @@ culture_items = [
('🎭', 'Наурыз', 'Праздник весны — 7 блюд, 22 марта'), ('🎭', 'Наурыз', 'Праздник весны — 7 блюд, 22 марта'),
('📖', 'Абай', 'Великий поэт, философ, мыслитель'), ('📖', 'Абай', 'Великий поэт, философ, мыслитель'),
] ]
for i, (emoji, name, desc) in enumerate(culture_items): for i, (em, nm, ds) in enumerate(culture):
col = i % 3 col, row = i % 3, i // 3
row = i // 3
x = Inches(0.8 + col * 4.1) x = Inches(0.8 + col * 4.1)
y = Inches(1.8 + row * 2.6) y = Inches(1.8 + row * 2.6)
box = add_shape_box(slide, x, y, Inches(3.7), Inches(2.2), WHITE) add_box(s, x, y, Inches(3.7), Inches(2.2), WHITE)
add_text(slide, emoji, x + Inches(0.3), y + Inches(0.2), Inches(1), Inches(0.8), add_text(s, em, x + Inches(0.3), y + Inches(0.15), Inches(1), Inches(0.7),
font_size=36, font_name='Calibri') font_size=36)
add_text(slide, name, x + Inches(1.2), y + Inches(0.25), Inches(2.2), Inches(0.5), add_text(s, nm, x + Inches(1.2), y + Inches(0.2), Inches(2.2), Inches(0.5),
font_size=20, bold=True, color=INK, font_name='Calibri') font_size=20, bold=True, color=INK)
add_text(slide, desc, x + Inches(0.3), y + Inches(1), Inches(3.2), Inches(1), add_text(s, ds, x + Inches(0.3), y + Inches(1), Inches(3.2), Inches(1),
font_size=14, color=GRAY, font_name='Calibri') font_size=14, color=GRAY)
add_animation(slide, 'flyIn', len(slide.shapes) - 1, delay_ms=250 + i * 180, dur_ms=700)
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
# SLIDE 5 — Cities # SLIDE 5 — Cities
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(prs.slide_layouts[6]) s = prs.slides.add_slide(BLANK_LAYOUT)
add_bg(slide, WHITE) add_bg(s, WHITE)
set_slide_transition(s, 'push')
add_text(slide, 'ГОРОДА', Inches(0.8), Inches(0.6), Inches(10), Inches(1), add_text(s, 'ГОРОДА', Inches(0.8), Inches(0.6), Inches(10), Inches(1),
font_size=40, bold=True, color=INK, font_name='Calibri') font_size=40, bold=True, color=INK)
add_text(slide, 'Мегаполисы Казахстана', Inches(0.8), Inches(1.3), Inches(6), Inches(0.5), add_text(s, 'Мегаполисы Казахстана', Inches(0.8), Inches(1.3), Inches(6), Inches(0.5),
font_size=18, color=GRAY, font_name='Calibri') font_size=18, color=GRAY)
cities = [ cities = [
('АСТАНА', 'Столица с 1997 г.\nБайтерек, Хан-Шатыр,\nсамые высокие фонтаны Евразии', CYAN), ('АСТАНА', 'Столица с 1997 г.\nБайтерек, Хан-Шатыр,\nфонтаны «Танкіри»', CYAN),
('АЛМАТЫ', 'Бывшая столица\nГоры за городом, Медеу,\nШымбулак, алматинские яблоки', GOLD), ('АЛМАТЫ', 'Бывшая столица\nГоры, Медеу, Шымбулак,\nалматинские яблоки', GOLD),
('ШЫМКЕНТ', 'Третий город\nЮжные ворота, жаркий\nклимат, древняя история', RGBColor(0x2A, 0x9D, 0x8F)), ('ШЫМКЕНТ', 'Третий город\nЮжные ворота,\nжаркий климат', RGBColor(0x2A, 0x9D, 0x8F)),
] ]
for i, (name, desc, accent) in enumerate(cities): for i, (nm, ds, clr) in enumerate(cities):
x = Inches(0.8 + i * 4.1) x = Inches(0.8 + i * 4.1)
y = Inches(2.2) add_box(s, x, Inches(2.2), Inches(3.7), Inches(4.5), RGBColor(0xF5, 0xF7, 0xFA))
box = add_shape_box(slide, x, y, Inches(3.7), Inches(4.5), add_rect(s, x, Inches(2.2), Inches(3.7), Inches(0.12), clr)
RGBColor(0xF5, 0xF7, 0xFA)) add_text(s, nm, x + Inches(0.4), Inches(2.7), Inches(3), Inches(0.6),
# Color accent bar at top font_size=28, bold=True, color=INK)
accent_bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, add_text(s, ds, x + Inches(0.4), Inches(3.5), Inches(3), Inches(2.5),
x, y, Inches(3.7), Inches(0.12)) font_size=15, color=GRAY)
accent_bar.fill.solid()
accent_bar.fill.fore_color.rgb = accent
accent_bar.line.fill.background()
add_text(slide, name, x + Inches(0.4), y + Inches(0.5), Inches(3), Inches(0.6),
font_size=28, bold=True, color=INK, font_name='Calibri')
add_text(slide, desc, x + Inches(0.4), y + Inches(1.3), Inches(3), Inches(2.5),
font_size=15, color=GRAY, font_name='Calibri')
add_animation(slide, 'flyIn', len(slide.shapes) - 1, delay_ms=300 + i * 250, dur_ms=800)
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
# SLIDE 6 — Food # SLIDE 6 — Food
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(prs.slide_layouts[6]) s = prs.slides.add_slide(BLANK_LAYOUT)
add_bg(slide, RGBColor(0xFD, 0xF8, 0xEF)) add_bg(s, RGBColor(0xFD, 0xF8, 0xEF))
set_slide_transition(s, 'wipe')
add_text(slide, 'КУХНЯ', Inches(0.8), Inches(0.6), Inches(10), Inches(1), add_text(s, 'КУХНЯ', Inches(0.8), Inches(0.6), Inches(10), Inches(1),
font_size=40, bold=True, color=INK, font_name='Calibri') font_size=40, bold=True, color=INK)
add_text(slide, 'Вкус степи — просто и сытно', Inches(0.8), Inches(1.3), Inches(6), Inches(0.5), add_text(s, 'Вкус степи — просто и сытно', Inches(0.8), Inches(1.3), Inches(6), Inches(0.5),
font_size=18, color=GRAY, font_name='Calibri') font_size=18, color=GRAY)
foods = [ foods = [
('🥟', 'Бешбармак', 'Главное блюдо — мясо с лапшой.\n«Пять пальцев» — едят руками'), ('🥟', 'Бешбармак', 'Главное блюдо — мясо с лапшой.\n«Пять пальцев» — едят руками'),
@ -411,39 +270,32 @@ foods = [
('🫓', 'Баурсаки', 'Жареные тестовые шарики\nна каждом празднике'), ('🫓', 'Баурсаки', 'Жареные тестовые шарики\nна каждом празднике'),
('🍵', 'Чай с молоком', 'С солью — гостям наливают\nпервым, знак уважения'), ('🍵', 'Чай с молоком', 'С солью — гостям наливают\nпервым, знак уважения'),
] ]
for i, (emoji, name, desc) in enumerate(foods): for i, (em, nm, ds) in enumerate(foods):
col = i % 2 col, row = i % 2, i // 2
row = i // 2
x = Inches(0.8 + col * 6.2) x = Inches(0.8 + col * 6.2)
y = Inches(2.2 + row * 2.4) y = Inches(2.2 + row * 2.4)
box = add_shape_box(slide, x, y, Inches(5.6), Inches(2), WHITE, add_box(s, x, y, Inches(5.6), Inches(2), WHITE, GOLD)
RGBColor(0xE8, 0xB7, 0x30)) add_text(s, em, x + Inches(0.3), y + Inches(0.15), Inches(1), Inches(0.7),
add_text(slide, emoji, x + Inches(0.3), y + Inches(0.2), Inches(1), Inches(0.8), font_size=38)
font_size=40, font_name='Calibri') add_text(s, nm, x + Inches(1.3), y + Inches(0.2), Inches(3.5), Inches(0.5),
add_text(slide, name, x + Inches(1.3), y + Inches(0.2), Inches(3.5), Inches(0.5), font_size=22, bold=True, color=INK)
font_size=22, bold=True, color=INK, font_name='Calibri') add_text(s, ds, x + Inches(1.3), y + Inches(0.8), Inches(3.8), Inches(1),
add_text(slide, desc, x + Inches(1.3), y + Inches(0.8), Inches(3.8), Inches(1), font_size=14, color=GRAY)
font_size=14, color=GRAY, font_name='Calibri')
add_animation(slide, 'bounce', len(slide.shapes) - 1, delay_ms=200 + i * 200, dur_ms=700)
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
# SLIDE 7 — History timeline # SLIDE 7 — History
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(prs.slide_layouts[6]) s = prs.slides.add_slide(BLANK_LAYOUT)
add_bg(slide, WHITE) add_bg(s, WHITE)
set_slide_transition(s, 'dissolve')
add_text(slide, 'ИСТОРИЯ', Inches(0.8), Inches(0.6), Inches(10), Inches(1), add_text(s, 'ИСТОРИЯ', Inches(0.8), Inches(0.6), Inches(10), Inches(1),
font_size=40, bold=True, color=INK, font_name='Calibri') font_size=40, bold=True, color=INK)
add_text(slide, 'От кочевников до космоса', Inches(0.8), Inches(1.3), Inches(6), Inches(0.5), add_text(s, 'От кочевников до космоса', Inches(0.8), Inches(1.3), Inches(6), Inches(0.5),
font_size=18, color=GRAY, font_name='Calibri') font_size=18, color=GRAY)
# Vertical line add_rect(s, Inches(1.6), Inches(2.2), Inches(0.06), Inches(4.8), CYAN)
vline = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
Inches(1.6), Inches(2.2), Inches(0.06), Inches(4.8))
vline.fill.solid()
vline.fill.fore_color.rgb = CYAN
vline.line.fill.background()
timeline = [ timeline = [
('1465', 'Казахское ханство', 'Формирование народа и кочевого уклада'), ('1465', 'Казахское ханство', 'Формирование народа и кочевого уклада'),
@ -453,51 +305,35 @@ timeline = [
('1991', 'Независимость', '16 декабря — последняя республика СССР'), ('1991', 'Независимость', '16 декабря — последняя республика СССР'),
('1997', 'Новая столица', 'Перенос столицы в Акмолу (Астану)'), ('1997', 'Новая столица', 'Перенос столицы в Акмолу (Астану)'),
] ]
for i, (year, title, desc) in enumerate(timeline): for i, (yr, ttl, dsc) in enumerate(timeline):
y = Inches(2.2 + i * 0.8) y = Inches(2.2 + i * 0.8)
# Dot dot = add_circle(s, Inches(1.4), y + Inches(0.08), Inches(0.45),
dot = slide.shapes.add_shape(MSO_SHAPE.OVAL, CYAN if i % 2 == 0 else GOLD)
Inches(1.4), y + Inches(0.08), Inches(0.45), Inches(0.45)) add_text(s, yr, Inches(2.2), y, Inches(1.2), Inches(0.6),
dot.fill.solid() font_size=16, bold=True, color=INK)
dot.fill.fore_color.rgb = CYAN if i % 2 == 0 else GOLD add_text(s, f'{ttl}{dsc}', Inches(3.5), y, Inches(9), Inches(0.6),
dot.line.fill.background() font_size=14, color=GRAY)
add_text(slide, year, Inches(2.2), y, Inches(1.2), Inches(0.6),
font_size=16, bold=True, color=INK, font_name='Calibri')
add_text(slide, f'{title}{desc}', Inches(3.5), y, Inches(9), Inches(0.6),
font_size=14, color=GRAY, font_name='Calibri')
add_animation(slide, 'flyIn', len(slide.shapes) - 1, delay_ms=200 + i * 150, dur_ms=600)
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
# SLIDE 8 — Thank You # SLIDE 8 — Thank You
# ══════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(prs.slide_layouts[6]) s = prs.slides.add_slide(BLANK_LAYOUT)
add_bg(slide, BG_DARK) add_bg(s, BG_DARK)
set_slide_transition(s, 'zoom')
# Decorative circles add_circle(s, Inches(10), Inches(0.5), Inches(3), RGBColor(0x00, 0x3D, 0x5C))
for cx, cy, sz, clr in [(10, 0.5, 3, RGBColor(0x00, 0x3D, 0x5C)), add_circle(s, Inches(10.5), Inches(1), Inches(2), CYAN)
(10.5, 1, 2, CYAN), add_circle(s, Inches(0.5), Inches(5), Inches(2.5), RGBColor(0x00, 0x3D, 0x5C))
(0.5, 5, 2.5, RGBColor(0x00, 0x3D, 0x5C)), add_circle(s, Inches(1), Inches(5.5), Inches(1.5), GOLD)
(1, 5.5, 1.5, GOLD)]:
c = slide.shapes.add_shape(MSO_SHAPE.OVAL,
Inches(cx), Inches(cy), Inches(sz), Inches(sz))
c.fill.solid()
c.fill.fore_color.rgb = clr
c.line.fill.background()
add_animation(slide, 'spin', len(slide.shapes) - 1, delay_ms=500, dur_ms=2000)
flag = add_text(slide, '🇰🇿', Inches(5.5), Inches(1.2), Inches(2.5), Inches(1.5), add_text(s, '🇰🇿', Inches(5.5), Inches(1), Inches(2.5), Inches(1.5),
font_size=80, color=WHITE, align=PP_ALIGN.CENTER) font_size=80, color=WHITE, align=PP_ALIGN.CENTER)
add_animation(slide, 'zoom', len(slide.shapes) - 1, delay_ms=200, dur_ms=1000) add_text(s, 'СПАСИБО ЗА ВНИМАНИЕ!', Inches(1), Inches(2.8), Inches(11.3), Inches(1.2),
font_size=48, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
add_text(slide, 'СПАСИБО ЗА ВНИМАНИЕ!', Inches(1), Inches(2.8), Inches(11.3), Inches(1.2), add_text(s, 'Казахстан — древние традиции и современные мегаполисы,\nбескрайние степи и высокие горы',
font_size=48, bold=True, color=WHITE, align=PP_ALIGN.CENTER, font_name='Calibri')
add_animation(slide, 'flyIn', len(slide.shapes) - 1, delay_ms=500, dur_ms=900)
add_text(slide, 'Казахстан — древние традиции и современные мегаполисы,\nбескрайние степи и высокие горы',
Inches(2), Inches(4.2), Inches(9.3), Inches(1.2), Inches(2), Inches(4.2), Inches(9.3), Inches(1.2),
font_size=18, color=GRAY, align=PP_ALIGN.CENTER, font_name='Calibri') font_size=18, color=GRAY, align=PP_ALIGN.CENTER)
add_animation(slide, 'fade', len(slide.shapes) - 1, delay_ms=800, dur_ms=800)
# ── Save ── # ── Save ──