344 lines
17 KiB
Python
344 lines
17 KiB
Python
#!/usr/bin/env python3
|
||
"""Презентация о Казахстане — чистый 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
|
||
from pptx.oxml.ns import qn
|
||
import lxml.etree as etree
|
||
|
||
# ── Colors ──
|
||
INK = RGBColor(0x0F, 0x12, 0x18)
|
||
CYAN = RGBColor(0x00, 0xE5, 0xFF)
|
||
GOLD = RGBColor(0xE8, 0xB7, 0x30)
|
||
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
|
||
GRAY = RGBColor(0x5B, 0x65, 0x73)
|
||
BG_DARK = RGBColor(0x14, 0x1C, 0x28)
|
||
|
||
prs = Presentation()
|
||
prs.slide_width = Inches(13.333)
|
||
prs.slide_height = Inches(7.5)
|
||
W = prs.slide_width
|
||
H = prs.slide_height
|
||
|
||
BLANK_LAYOUT = prs.slide_layouts[6]
|
||
|
||
|
||
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, qn('p:transition'))
|
||
|
||
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):
|
||
bg = slide.background
|
||
fill = bg.fill
|
||
fill.solid()
|
||
fill.fore_color.rgb = color
|
||
|
||
|
||
def add_text(slide, text, left, top, width, height,
|
||
font_size=24, bold=False, color=INK, align=PP_ALIGN.LEFT,
|
||
font_name='Calibri'):
|
||
txBox = slide.shapes.add_textbox(left, top, width, height)
|
||
tf = txBox.text_frame
|
||
tf.word_wrap = True
|
||
p = tf.paragraphs[0]
|
||
p.text = text
|
||
p.font.size = Pt(font_size)
|
||
p.font.bold = bold
|
||
p.font.color.rgb = color
|
||
p.font.name = font_name
|
||
p.alignment = align
|
||
return txBox
|
||
|
||
|
||
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
|
||
)
|
||
shape.fill.solid()
|
||
shape.fill.fore_color.rgb = fill_color
|
||
if border_color:
|
||
shape.line.color.rgb = border_color
|
||
shape.line.width = Pt(2)
|
||
else:
|
||
shape.line.fill.background()
|
||
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
|
||
# ══════════════════════════════════════════════════════════════
|
||
s = prs.slides.add_slide(BLANK_LAYOUT)
|
||
add_bg(s, BG_DARK)
|
||
set_slide_transition(s, 'zoom')
|
||
|
||
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)
|
||
|
||
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)
|
||
|
||
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
|
||
# ══════════════════════════════════════════════════════════════
|
||
s = prs.slides.add_slide(BLANK_LAYOUT)
|
||
add_bg(s, WHITE)
|
||
set_slide_transition(s, 'push')
|
||
|
||
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 = [
|
||
('🏔️', 'Горы', 'Тянь-Шань, Чу-Илийские горы, Алтай.\nВысшая точка — Хан-Тенгри (7010 м)'),
|
||
('🌊', 'Каспийское море', 'Крупнейшее озеро мира.\nПобережье тянется на 1894 км'),
|
||
('🏜️', 'Степи', 'Бескрайние равнины —\nисторическая родина кочевников'),
|
||
('🌾', 'Озёра', 'Балхаш, Алаколь, Маркаколь —\nболее 45 000 озёр'),
|
||
]
|
||
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
|
||
# ══════════════════════════════════════════════════════════════
|
||
s = prs.slides.add_slide(BLANK_LAYOUT)
|
||
add_bg(s, BG_DARK)
|
||
set_slide_transition(s, 'fade')
|
||
|
||
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в истории')]
|
||
for i, (num, txt) in enumerate(facts):
|
||
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)
|
||
|
||
add_rect(s, Inches(0.8), Inches(6.5), Inches(11.7), Inches(0.04), CYAN)
|
||
|
||
|
||
# ══════════════════════════════════════════════════════════════
|
||
# SLIDE 4 — Culture
|
||
# ══════════════════════════════════════════════════════════════
|
||
s = prs.slides.add_slide(BLANK_LAYOUT)
|
||
add_bg(s, RGBColor(0xF0, 0xF4, 0xF8))
|
||
set_slide_transition(s, 'cover')
|
||
|
||
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 = [
|
||
('🦅', 'Беркутчи', 'Соколиная охота с орлами — наследие ЮНЕСКО'),
|
||
('🎪', 'Юрта', 'Легко разборное жилище — символ свободы'),
|
||
('🎶', 'Домбра', 'Двухструнный инструмент — душа кюев'),
|
||
('🤼', 'Байге', 'Конные скачки на длинные дистанции'),
|
||
('🎭', 'Наурыз', 'Праздник весны — 7 блюд, 22 марта'),
|
||
('📖', 'Абай', 'Великий поэт, философ, мыслитель'),
|
||
]
|
||
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)
|
||
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
|
||
# ══════════════════════════════════════════════════════════════
|
||
s = prs.slides.add_slide(BLANK_LAYOUT)
|
||
add_bg(s, WHITE)
|
||
set_slide_transition(s, 'push')
|
||
|
||
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)),
|
||
]
|
||
for i, (nm, ds, clr) in enumerate(cities):
|
||
x = Inches(0.8 + i * 4.1)
|
||
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
|
||
# ══════════════════════════════════════════════════════════════
|
||
s = prs.slides.add_slide(BLANK_LAYOUT)
|
||
add_bg(s, RGBColor(0xFD, 0xF8, 0xEF))
|
||
set_slide_transition(s, 'wipe')
|
||
|
||
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«Пять пальцев» — едят руками'),
|
||
('🫕', 'Кумыс', 'Ферментированное кобылье\nмолоко — тонизирующий напиток'),
|
||
('🫓', 'Баурсаки', 'Жареные тестовые шарики\nна каждом празднике'),
|
||
('🍵', 'Чай с молоком', 'С солью — гостям наливают\nпервым, знак уважения'),
|
||
]
|
||
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)
|
||
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
|
||
# ══════════════════════════════════════════════════════════════
|
||
s = prs.slides.add_slide(BLANK_LAYOUT)
|
||
add_bg(s, WHITE)
|
||
set_slide_transition(s, 'dissolve')
|
||
|
||
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)
|
||
|
||
add_rect(s, Inches(1.6), Inches(2.2), Inches(0.06), Inches(4.8), CYAN)
|
||
|
||
timeline = [
|
||
('1465', 'Казахское ханство', 'Формирование народа и кочевого уклада'),
|
||
('XVIII', 'Российская империя', 'Присоединение, оседлый образ жизни'),
|
||
('1955', 'Байконур', 'Запуск первого космодрома в мире'),
|
||
('1961', 'Гагарин', 'Первый полёт в космос с Байконура'),
|
||
('1991', 'Независимость', '16 декабря — последняя республика СССР'),
|
||
('1997', 'Новая столица', 'Перенос столицы в Акмолу (Астану)'),
|
||
]
|
||
for i, (yr, ttl, dsc) in enumerate(timeline):
|
||
y = Inches(2.2 + i * 0.8)
|
||
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
|
||
# ══════════════════════════════════════════════════════════════
|
||
s = prs.slides.add_slide(BLANK_LAYOUT)
|
||
add_bg(s, BG_DARK)
|
||
set_slide_transition(s, 'zoom')
|
||
|
||
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)
|
||
|
||
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)
|
||
|
||
|
||
# ── Save ──
|
||
out = '/srv/opencode/workspaces/users/literally.ww/kazakhstan/Казахстан_Презентация.pptx'
|
||
prs.save(out)
|
||
print(f'✅ Saved: {out}')
|
||
print(f' Slides: {len(prs.slides)}')
|