diff --git a/create_pptx.py b/create_pptx.py index 82012f6..cb30602 100644 --- a/create_pptx.py +++ b/create_pptx.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 -"""Презентация о Казахстане с 3D-анимациями (fly, zoom, spin, bounce).""" +"""Презентация о Казахстане — чистый PPTX без кривых XML-анимаций.""" from pptx import Presentation from pptx.util import Inches, Pt, Emu from pptx.dml.color import RGBColor from pptx.enum.text import PP_ALIGN, MSO_ANCHOR from pptx.enum.shapes import MSO_SHAPE -import copy -from lxml import etree +from pptx.oxml.ns import qn +import lxml.etree as etree # ── Colors ── INK = RGBColor(0x0F, 0x12, 0x18) @@ -23,144 +23,30 @@ prs.slide_height = Inches(7.5) W = prs.slide_width H = prs.slide_height -# ── Animation XML builder ── -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}"' +BLANK_LAYOUT = prs.slide_layouts[6] -def _anim_seq_node(): - """Create ....""" - return etree.SubElement( - etree.Element("dummy"), - f"{{{P}}}timing" - ) -def add_animation(slide, anim_type, target_idx, delay_ms=0, dur_ms=500, **kwargs): - """ - Add entrance animation to shape at target_idx. - 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') +def set_slide_transition(slide, transition_type='push', speed='med', advance_ms=None): + """Set slide transition effect.""" + timing = slide._element.find(qn('p:transition')) 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') - if tnLst is None: - tnLst = etree.SubElement(timing, f'{{{P}}}tnLst') - - par_main = tnLst.find(f'{{{P}}}par') - if par_main is None: - par_main = etree.SubElement(tnLst, f'{{{P}}}par') - cTn_main = etree.SubElement(par_main, f'{{{P}}}cTn', id='1', dur='indefinite', - restart='never', nodeType='timingRoot') - etree.SubElement(cTn_main, f'{{{P}}}childTnLst') - - childTnLst = par_main.find(f'{{{P}}}cTn/{{{P}}}childTnLst') - - # Container par for this animation - par = etree.SubElement(childTnLst, f'{{{P}}}par') - 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') + transitions = { + 'push': ('push', {'advClick': '1'}), + 'fade': ('fade', {'advClick': '1'}), + 'wipe': ('wipe', {'dir': 'd', 'advClick': '1'}), + 'cover': ('cover', {'dir': 'l', 'advClick': '1'}), + 'cut': ('cut', {'advClick': '1'}), + 'dissolve': ('dissolve', {'advClick': '1'}), + 'zoom': ('zoom', {'advClick': '1'}), + } + tag_name, attrs = transitions.get(transition_type, transitions['push']) + el = etree.SubElement(timing, qn(f'p:{tag_name}')) + for k, v in attrs.items(): + el.set(k, v) + if advance_ms: + timing.set('advTm', str(advance_ms)) def add_bg(slide, color): @@ -186,8 +72,8 @@ def add_text(slide, text, left, top, width, height, return txBox -def add_shape_box(slide, left, top, width, height, fill_color=BG_DARK, - border_color=None, radius=Inches(0.2)): +def add_box(slide, left, top, width, height, fill_color=BG_DARK, + border_color=None): shape = slide.shapes.add_shape( 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 +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 = prs.slides.add_slide(prs.slide_layouts[6]) # blank -add_bg(slide, BG_DARK) +s = prs.slides.add_slide(BLANK_LAYOUT) +add_bg(s, BG_DARK) +set_slide_transition(s, 'zoom') -# Decorative circle -circle = slide.shapes.add_shape(MSO_SHAPE.OVAL, Inches(9.5), Inches(1), Inches(4), Inches(4)) -circle.fill.solid() -circle.fill.fore_color.rgb = RGBColor(0x00, 0x3D, 0x5C) -circle.line.fill.background() +add_circle(s, Inches(9.5), Inches(0.8), Inches(4.5), RGBColor(0x00, 0x3D, 0x5C)) +add_circle(s, Inches(10.2), Inches(1.5), Inches(3), CYAN) +add_circle(s, Inches(-0.5), Inches(5.5), Inches(3), RGBColor(0x00, 0x3D, 0x5C)) +add_circle(s, Inches(0), Inches(6), Inches(2), GOLD) -s = slide.shapes.add_shape(MSO_SHAPE.OVAL, Inches(10), Inches(1.5), Inches(3), Inches(3)) -s.fill.solid() -s.fill.fore_color.rgb = CYAN -s.line.fill.background() -add_animation(slide, 'spin', len(slide.shapes) - 1, delay_ms=300, dur_ms=1500) +add_text(s, '🇰🇿', Inches(1), Inches(1.2), Inches(2), Inches(1.5), font_size=80, color=WHITE) +add_text(s, 'КАЗАХСТАН', Inches(1), Inches(2.8), Inches(8), Inches(1.5), + font_size=72, bold=True, color=WHITE) +add_text(s, 'Сердце Евразии — девятая по территории страна мира', + Inches(1), Inches(4.3), Inches(8), Inches(0.8), + font_size=24, color=GRAY) -# Flag emoji -flag = add_text(slide, '🇰🇿', Inches(0.8), Inches(1.5), Inches(2), Inches(1.5), - font_size=80, color=WHITE, align=PP_ALIGN.LEFT) -add_animation(slide, 'flyIn', len(slide.shapes) - 1, delay_ms=100, dur_ms=800) - -# Title -title = add_text(slide, 'КАЗАХСТАН', Inches(0.8), Inches(3), Inches(8), Inches(1.5), - font_size=72, bold=True, color=WHITE, font_name='Calibri') -add_animation(slide, 'zoom', len(slide.shapes) - 1, delay_ms=400, dur_ms=1000) - -# 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) +stats = [('2.7 млн км²', 'Площадь'), ('20 млн', 'Население'), + ('130+', 'Народов'), ('1 космодром', 'Байконур')] +for i, (val, lbl) in enumerate(stats): + x = Inches(1 + i * 3) + y = Inches(5.6) + add_box(s, x, y, Inches(2.7), Inches(1.2), RGBColor(0x1C, 0x26, 0x36)) + add_text(s, val, x + Inches(0.2), y + Inches(0.1), Inches(2.3), Inches(0.6), + font_size=24, bold=True, color=CYAN) + add_text(s, lbl, x + Inches(0.2), y + Inches(0.65), Inches(2.3), Inches(0.4), + font_size=13, color=GRAY) # ══════════════════════════════════════════════════════════════ # SLIDE 2 — Geography # ══════════════════════════════════════════════════════════════ -slide = prs.slides.add_slide(prs.slide_layouts[6]) -add_bg(slide, WHITE) +s = prs.slides.add_slide(BLANK_LAYOUT) +add_bg(s, WHITE) +set_slide_transition(s, 'push') -# Section header bar -bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, W, Inches(1.4)) -bar.fill.solid() -bar.fill.fore_color.rgb = BG_DARK -bar.line.fill.background() -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) +add_rect(s, 0, 0, W, Inches(1.4), BG_DARK) +add_text(s, 'ГЕОГРАФИЯ', Inches(0.8), Inches(0.3), Inches(8), Inches(0.8), + font_size=36, bold=True, color=CYAN) +add_text(s, 'От каспийских берегов до алтайских гор', Inches(0.8), Inches(1.4), + Inches(8), Inches(0.5), font_size=16, color=GRAY) -geo_items = [ - ('🏔️', 'Горы', 'Тянь-Шань, Алтай. Высшая точка — Хан-Тенгри (7010 м)'), - ('🌊', 'Каспийское море', 'Крупнейшее озеро мира. 1894 км побережья'), - ('🏜️', 'Степи', 'Бескрайние равнины — историческая родина кочевников'), - ('🌾', 'Озёра', 'Балхаш, Алаколь — более 45 000 озёр'), +geo = [ + ('🏔️', 'Горы', 'Тянь-Шань, Чу-Илийские горы, Алтай.\nВысшая точка — Хан-Тенгри (7010 м)'), + ('🌊', 'Каспийское море', 'Крупнейшее озеро мира.\nПобережье тянется на 1894 км'), + ('🏜️', 'Степи', 'Бескрайние равнины —\nисторическая родина кочевников'), + ('🌾', 'Озёра', 'Балхаш, Алаколь, Маркаколь —\nболее 45 000 озёр'), ] -for i, (emoji, name, desc) in enumerate(geo_items): - x = Inches(0.8 + (i % 2) * 6.2) - y = Inches(1.8 + (i // 2) * 2.6) - box = add_shape_box(slide, 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), - font_size=40, font_name='Calibri') - add_text(slide, name, x + Inches(1.2), y + Inches(0.25), Inches(4), Inches(0.5), - font_size=22, bold=True, color=INK, font_name='Calibri') - add_text(slide, desc, x + Inches(1.2), y + Inches(0.8), Inches(4.2), Inches(1.2), - font_size=15, color=GRAY, font_name='Calibri') - add_animation(slide, 'flyIn', len(slide.shapes) - 1, delay_ms=300 + i * 200, dur_ms=700) +for i, (em, nm, ds) in enumerate(geo): + col, row = i % 2, i // 2 + x = Inches(0.8 + col * 6.2) + y = Inches(2.2 + row * 2.5) + add_box(s, x, y, Inches(5.6), Inches(2.2), RGBColor(0xF5, 0xF7, 0xFA)) + add_text(s, em, x + Inches(0.3), y + Inches(0.15), Inches(1), Inches(0.7), + font_size=38) + add_text(s, nm, x + Inches(1.3), y + Inches(0.2), Inches(4), Inches(0.5), + font_size=22, bold=True, color=INK) + add_text(s, ds, x + Inches(1.3), y + Inches(0.75), Inches(4), Inches(1.2), + font_size=14, color=GRAY) # ══════════════════════════════════════════════════════════════ -# SLIDE 3 — Facts & Figures +# SLIDE 3 — Facts # ══════════════════════════════════════════════════════════════ -slide = prs.slides.add_slide(prs.slide_layouts[6]) -add_bg(slide, BG_DARK) +s = prs.slides.add_slide(BLANK_LAYOUT) +add_bg(s, BG_DARK) +set_slide_transition(s, 'fade') -add_text(slide, 'ЦИФРЫ И ФАКТЫ', Inches(0.8), Inches(0.6), Inches(10), Inches(1), - font_size=40, bold=True, color=WHITE, font_name='Calibri') -add_text(slide, 'Казахстан в цифрах', Inches(0.8), Inches(1.4), Inches(6), Inches(0.6), - font_size=18, color=GRAY, font_name='Calibri') +add_text(s, 'ЦИФРЫ И ФАКТЫ', Inches(0.8), Inches(0.6), Inches(10), Inches(1), + font_size=40, bold=True, color=WHITE) +add_text(s, 'Казахстан в цифрах', Inches(0.8), Inches(1.4), Inches(6), Inches(0.5), + font_size=18, color=GRAY) -facts = [ - ('9', 'по величине\nв мире'), - ('1991', 'год\nнезависимости'), - ('70+', 'тонн золота\nв год'), - ('1', 'космодром\nБайконур'), - ('3', 'столицы\nв истории'), -] +facts = [('9', 'по величине\nв мире'), ('1991', 'год\nнезависимости'), + ('70+', 'тонн золота\nв год'), ('1', 'космодром\nБайконур'), + ('3', 'столицы\nв истории')] for i, (num, txt) in enumerate(facts): - x = Inches(0.6 + i * 2.5) - y = Inches(2.5) - box = add_shape_box(slide, x, y, Inches(2.2), Inches(3.5), - RGBColor(0x1C, 0x26, 0x36), CYAN) - 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_name='Calibri') - 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_name='Calibri') - add_animation(slide, 'zoom', len(slide.shapes) - 1, delay_ms=200 + i * 200, dur_ms=800) + x = Inches(0.5 + i * 2.5) + add_box(s, x, Inches(2.5), Inches(2.2), Inches(3.5), + RGBColor(0x1C, 0x26, 0x36), CYAN) + add_text(s, num, x + Inches(0.2), Inches(2.9), Inches(1.8), Inches(1.2), + font_size=52, bold=True, color=CYAN, align=PP_ALIGN.CENTER) + add_text(s, txt, x + Inches(0.2), Inches(4.2), Inches(1.8), Inches(1.4), + font_size=14, color=GRAY, align=PP_ALIGN.CENTER) -# Bottom decorative line -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() +add_rect(s, Inches(0.8), Inches(6.5), Inches(11.7), Inches(0.04), CYAN) # ══════════════════════════════════════════════════════════════ # SLIDE 4 — Culture # ══════════════════════════════════════════════════════════════ -slide = prs.slides.add_slide(prs.slide_layouts[6]) -add_bg(slide, RGBColor(0xF0, 0xF4, 0xF8)) +s = prs.slides.add_slide(BLANK_LAYOUT) +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)) -bar.fill.solid() -bar.fill.fore_color.rgb = BG_DARK -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') +add_rect(s, 0, 0, W, Inches(1.4), BG_DARK) +add_text(s, 'КУЛЬТУРА И ТРАДИЦИИ', Inches(0.8), Inches(0.3), Inches(10), Inches(0.8), + font_size=36, bold=True, color=GOLD) -culture_items = [ +culture = [ ('🦅', 'Беркутчи', 'Соколиная охота с орлами — наследие ЮНЕСКО'), ('🎪', 'Юрта', 'Легко разборное жилище — символ свободы'), ('🎶', 'Домбра', 'Двухструнный инструмент — душа кюев'), @@ -345,65 +212,57 @@ culture_items = [ ('🎭', 'Наурыз', 'Праздник весны — 7 блюд, 22 марта'), ('📖', 'Абай', 'Великий поэт, философ, мыслитель'), ] -for i, (emoji, name, desc) in enumerate(culture_items): - col = i % 3 - row = i // 3 +for i, (em, nm, ds) in enumerate(culture): + col, row = i % 3, i // 3 x = Inches(0.8 + col * 4.1) y = Inches(1.8 + row * 2.6) - box = add_shape_box(slide, 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), - font_size=36, font_name='Calibri') - add_text(slide, name, x + Inches(1.2), y + Inches(0.25), Inches(2.2), Inches(0.5), - font_size=20, bold=True, color=INK, font_name='Calibri') - add_text(slide, desc, x + Inches(0.3), y + Inches(1), Inches(3.2), Inches(1), - font_size=14, color=GRAY, font_name='Calibri') - add_animation(slide, 'flyIn', len(slide.shapes) - 1, delay_ms=250 + i * 180, dur_ms=700) + add_box(s, x, y, Inches(3.7), Inches(2.2), WHITE) + add_text(s, em, x + Inches(0.3), y + Inches(0.15), Inches(1), Inches(0.7), + font_size=36) + 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) + add_text(s, ds, x + Inches(0.3), y + Inches(1), Inches(3.2), Inches(1), + font_size=14, color=GRAY) # ══════════════════════════════════════════════════════════════ # SLIDE 5 — Cities # ══════════════════════════════════════════════════════════════ -slide = prs.slides.add_slide(prs.slide_layouts[6]) -add_bg(slide, WHITE) +s = prs.slides.add_slide(BLANK_LAYOUT) +add_bg(s, WHITE) +set_slide_transition(s, 'push') -add_text(slide, 'ГОРОДА', Inches(0.8), Inches(0.6), Inches(10), Inches(1), - font_size=40, bold=True, color=INK, font_name='Calibri') -add_text(slide, 'Мегаполисы Казахстана', Inches(0.8), Inches(1.3), Inches(6), Inches(0.5), - font_size=18, color=GRAY, font_name='Calibri') +add_text(s, 'ГОРОДА', Inches(0.8), Inches(0.6), Inches(10), Inches(1), + font_size=40, bold=True, color=INK) +add_text(s, 'Мегаполисы Казахстана', Inches(0.8), Inches(1.3), Inches(6), Inches(0.5), + font_size=18, color=GRAY) cities = [ - ('АСТАНА', 'Столица с 1997 г.\nБайтерек, Хан-Шатыр,\nсамые высокие фонтаны Евразии', CYAN), - ('АЛМАТЫ', 'Бывшая столица\nГоры за городом, Медеу,\nШымбулак, алматинские яблоки', GOLD), - ('ШЫМКЕНТ', 'Третий город\nЮжные ворота, жаркий\nклимат, древняя история', RGBColor(0x2A, 0x9D, 0x8F)), + ('АСТАНА', 'Столица с 1997 г.\nБайтерек, Хан-Шатыр,\nфонтаны «Танкіри»', CYAN), + ('АЛМАТЫ', 'Бывшая столица\nГоры, Медеу, Шымбулак,\nалматинские яблоки', GOLD), + ('ШЫМКЕНТ', 'Третий город\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) - y = Inches(2.2) - box = add_shape_box(slide, x, y, Inches(3.7), Inches(4.5), - RGBColor(0xF5, 0xF7, 0xFA)) - # Color accent bar at top - accent_bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, - x, y, Inches(3.7), Inches(0.12)) - 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) + add_box(s, x, Inches(2.2), Inches(3.7), Inches(4.5), RGBColor(0xF5, 0xF7, 0xFA)) + add_rect(s, x, Inches(2.2), Inches(3.7), Inches(0.12), clr) + add_text(s, nm, x + Inches(0.4), Inches(2.7), Inches(3), Inches(0.6), + font_size=28, bold=True, color=INK) + add_text(s, ds, x + Inches(0.4), Inches(3.5), Inches(3), Inches(2.5), + font_size=15, color=GRAY) # ══════════════════════════════════════════════════════════════ # SLIDE 6 — Food # ══════════════════════════════════════════════════════════════ -slide = prs.slides.add_slide(prs.slide_layouts[6]) -add_bg(slide, RGBColor(0xFD, 0xF8, 0xEF)) +s = prs.slides.add_slide(BLANK_LAYOUT) +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), - font_size=40, bold=True, color=INK, font_name='Calibri') -add_text(slide, 'Вкус степи — просто и сытно', Inches(0.8), Inches(1.3), Inches(6), Inches(0.5), - font_size=18, color=GRAY, font_name='Calibri') +add_text(s, 'КУХНЯ', Inches(0.8), Inches(0.6), Inches(10), Inches(1), + font_size=40, bold=True, color=INK) +add_text(s, 'Вкус степи — просто и сытно', Inches(0.8), Inches(1.3), Inches(6), Inches(0.5), + font_size=18, color=GRAY) foods = [ ('🥟', 'Бешбармак', 'Главное блюдо — мясо с лапшой.\n«Пять пальцев» — едят руками'), @@ -411,39 +270,32 @@ foods = [ ('🫓', 'Баурсаки', 'Жареные тестовые шарики\nна каждом празднике'), ('🍵', 'Чай с молоком', 'С солью — гостям наливают\nпервым, знак уважения'), ] -for i, (emoji, name, desc) in enumerate(foods): - col = i % 2 - row = i // 2 +for i, (em, nm, ds) in enumerate(foods): + col, row = i % 2, i // 2 x = Inches(0.8 + col * 6.2) y = Inches(2.2 + row * 2.4) - box = add_shape_box(slide, x, y, Inches(5.6), Inches(2), WHITE, - RGBColor(0xE8, 0xB7, 0x30)) - add_text(slide, emoji, x + Inches(0.3), y + Inches(0.2), Inches(1), Inches(0.8), - font_size=40, font_name='Calibri') - 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_name='Calibri') - add_text(slide, desc, x + Inches(1.3), y + Inches(0.8), Inches(3.8), Inches(1), - font_size=14, color=GRAY, font_name='Calibri') - add_animation(slide, 'bounce', len(slide.shapes) - 1, delay_ms=200 + i * 200, dur_ms=700) + add_box(s, x, y, Inches(5.6), Inches(2), WHITE, GOLD) + add_text(s, em, x + Inches(0.3), y + Inches(0.15), Inches(1), Inches(0.7), + font_size=38) + add_text(s, nm, x + Inches(1.3), y + Inches(0.2), Inches(3.5), Inches(0.5), + font_size=22, bold=True, color=INK) + add_text(s, ds, x + Inches(1.3), y + Inches(0.8), Inches(3.8), Inches(1), + font_size=14, color=GRAY) # ══════════════════════════════════════════════════════════════ -# SLIDE 7 — History timeline +# SLIDE 7 — History # ══════════════════════════════════════════════════════════════ -slide = prs.slides.add_slide(prs.slide_layouts[6]) -add_bg(slide, WHITE) +s = prs.slides.add_slide(BLANK_LAYOUT) +add_bg(s, WHITE) +set_slide_transition(s, 'dissolve') -add_text(slide, 'ИСТОРИЯ', Inches(0.8), Inches(0.6), Inches(10), Inches(1), - font_size=40, bold=True, color=INK, font_name='Calibri') -add_text(slide, 'От кочевников до космоса', Inches(0.8), Inches(1.3), Inches(6), Inches(0.5), - font_size=18, color=GRAY, font_name='Calibri') +add_text(s, 'ИСТОРИЯ', Inches(0.8), Inches(0.6), Inches(10), Inches(1), + font_size=40, bold=True, color=INK) +add_text(s, 'От кочевников до космоса', Inches(0.8), Inches(1.3), Inches(6), Inches(0.5), + font_size=18, color=GRAY) -# Vertical line -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() +add_rect(s, Inches(1.6), Inches(2.2), Inches(0.06), Inches(4.8), CYAN) timeline = [ ('1465', 'Казахское ханство', 'Формирование народа и кочевого уклада'), @@ -453,51 +305,35 @@ timeline = [ ('1991', 'Независимость', '16 декабря — последняя республика СССР'), ('1997', 'Новая столица', 'Перенос столицы в Акмолу (Астану)'), ] -for i, (year, title, desc) in enumerate(timeline): +for i, (yr, ttl, dsc) in enumerate(timeline): y = Inches(2.2 + i * 0.8) - # Dot - dot = slide.shapes.add_shape(MSO_SHAPE.OVAL, - Inches(1.4), y + Inches(0.08), Inches(0.45), Inches(0.45)) - dot.fill.solid() - dot.fill.fore_color.rgb = CYAN if i % 2 == 0 else GOLD - dot.line.fill.background() - 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) + dot = add_circle(s, Inches(1.4), y + Inches(0.08), Inches(0.45), + CYAN if i % 2 == 0 else GOLD) + add_text(s, yr, Inches(2.2), y, Inches(1.2), Inches(0.6), + font_size=16, bold=True, color=INK) + add_text(s, f'{ttl} — {dsc}', Inches(3.5), y, Inches(9), Inches(0.6), + font_size=14, color=GRAY) # ══════════════════════════════════════════════════════════════ # SLIDE 8 — Thank You # ══════════════════════════════════════════════════════════════ -slide = prs.slides.add_slide(prs.slide_layouts[6]) -add_bg(slide, BG_DARK) +s = prs.slides.add_slide(BLANK_LAYOUT) +add_bg(s, BG_DARK) +set_slide_transition(s, 'zoom') -# Decorative circles -for cx, cy, sz, clr in [(10, 0.5, 3, RGBColor(0x00, 0x3D, 0x5C)), - (10.5, 1, 2, CYAN), - (0.5, 5, 2.5, RGBColor(0x00, 0x3D, 0x5C)), - (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) +add_circle(s, Inches(10), Inches(0.5), Inches(3), RGBColor(0x00, 0x3D, 0x5C)) +add_circle(s, Inches(10.5), Inches(1), Inches(2), CYAN) +add_circle(s, Inches(0.5), Inches(5), Inches(2.5), RGBColor(0x00, 0x3D, 0x5C)) +add_circle(s, Inches(1), Inches(5.5), Inches(1.5), GOLD) -flag = add_text(slide, '🇰🇿', Inches(5.5), Inches(1.2), Inches(2.5), Inches(1.5), - 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(slide, 'СПАСИБО ЗА ВНИМАНИЕ!', Inches(1), Inches(2.8), Inches(11.3), Inches(1.2), - 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бескрайние степи и высокие горы', +add_text(s, '🇰🇿', Inches(5.5), Inches(1), Inches(2.5), Inches(1.5), + font_size=80, color=WHITE, align=PP_ALIGN.CENTER) +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(s, 'Казахстан — древние традиции и современные мегаполисы,\nбескрайние степи и высокие горы', Inches(2), Inches(4.2), Inches(9.3), Inches(1.2), - font_size=18, color=GRAY, align=PP_ALIGN.CENTER, font_name='Calibri') -add_animation(slide, 'fade', len(slide.shapes) - 1, delay_ms=800, dur_ms=800) + font_size=18, color=GRAY, align=PP_ALIGN.CENTER) # ── Save ── diff --git a/Казахстан_Презентация.pptx b/Казахстан_Презентация.pptx index 0785ce5..1bd222f 100644 Binary files a/Казахстан_Презентация.pptx and b/Казахстан_Презентация.pptx differ