#!/usr/bin/env python3 """Folie 3 – LIS mit Umsystemen (Adapter, links/rechts) und andockenden Modulen (unten), unter einer Steuerungs-/Governance-Schicht; weitere LIS angedeutet.""" 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 INK = RGBColor(0x2A, 0x2F, 0x3A) NAVY = RGBColor(0x1F, 0x3A, 0x5F) ACCENT = RGBColor(0xC0, 0x49, 0x2F) TEAL = RGBColor(0x2F, 0x8F, 0x83) AMBER = RGBColor(0xA9, 0x73, 0x22) MUTE = RGBColor(0x8A, 0x93, 0xA0) LINEC = RGBColor(0xC3, 0xC9, 0xD1) HINT = RGBColor(0xEE, 0xF1, 0xF4) HINTL = RGBColor(0xCB, 0xD2, 0xDA) TINT_T = RGBColor(0xDD, 0xEC, 0xEA) TINT_A = RGBColor(0xF3, 0xE8, 0xD2) UMS = RGBColor(0xEC, 0xEE, 0xF1) BG = RGBColor(0xFF, 0xFF, 0xFF) WHITE = RGBColor(0xFF, 0xFF, 0xFF) F = "Calibri" prs = Presentation() prs.slide_width = Inches(13.333) prs.slide_height = Inches(7.5) s = prs.slides.add_slide(prs.slide_layouts[6]) bgr = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, prs.slide_height) bgr.fill.solid(); bgr.fill.fore_color.rgb = BG; bgr.line.fill.background() bgr.shadow.inherit = False def shp(kind, x, y, w, h, fill=None, line=None, lw=Pt(1), rot=0): sh = s.shapes.add_shape(kind, x, y, w, h) if fill is None: sh.fill.background() else: sh.fill.solid(); sh.fill.fore_color.rgb = fill if line is None: sh.line.fill.background() else: sh.line.color.rgb = line; sh.line.width = lw if rot: sh.rotation = rot sh.shadow.inherit = False return sh def boxtext(sh, t, size=14, color=INK, bold=False): tf = sh.text_frame; tf.word_wrap = True tf.vertical_anchor = MSO_ANCHOR.MIDDLE tf.margin_left = tf.margin_right = Pt(3); tf.margin_top = tf.margin_bottom = Pt(1) p = tf.paragraphs[0]; p.alignment = PP_ALIGN.CENTER r = p.add_run(); r.text = t r.font.size = Pt(size); r.font.bold = bold; r.font.name = F; r.font.color.rgb = color def txt(x, y, w, h, t, size=14, color=INK, bold=False, italic=False, align=PP_ALIGN.LEFT, spc=None): tb = s.shapes.add_textbox(x, y, w, h); tf = tb.text_frame tf.word_wrap = True; tf.vertical_anchor = MSO_ANCHOR.MIDDLE tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = Pt(0) p = tf.paragraphs[0]; p.alignment = align r = p.add_run(); r.text = t r.font.size = Pt(size); r.font.bold = bold; r.font.italic = italic r.font.name = F; r.font.color.rgb = color if spc: r.font._rPr.set('spc', str(spc)) return tb def hline(x, y, w, color=LINEC, weight=Pt(1.4)): shp(MSO_SHAPE.RECTANGLE, x, y - weight / 2, w, weight, fill=color) def vline(x, y, h, color=LINEC, weight=Pt(1.4)): shp(MSO_SHAPE.RECTANGLE, x - weight / 2, y, weight, h, fill=color) # ---- Kopf ------------------------------------------------------------------ txt(Inches(0.95), Inches(0.32), Inches(8), Inches(0.35), "ARCHITEKTUR", size=12.5, color=ACCENT, bold=True, spc=240) txt(Inches(0.95), Inches(0.62), Inches(11), Inches(0.85), "Offen – und gesteuert", size=31, color=NAVY, bold=True) shp(MSO_SHAPE.RECTANGLE, Inches(0.95), Inches(1.34), Inches(0.6), Pt(4), fill=ACCENT) x0, x1 = Inches(0.95), Inches(12.38) # =========================================================================== # GOVERNANCE – Icon-Karten + Strich # =========================================================================== def card(cx, label): cw_, ch_ = Inches(2.05), Inches(1.25) x, y = cx - cw_ / 2, Inches(1.5) shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, y, cw_, ch_, fill=BG, line=LINEC, lw=Pt(1.25)) txt(x, y + ch_ - Inches(0.4), cw_, Inches(0.34), label, size=14, color=NAVY, bold=True, align=PP_ALIGN.CENTER) return cx, y + Inches(0.42) def regler(cx, cy): tw = Inches(0.62) left = cx - tw / 2 for j, frac in enumerate([0.30, 0.68, 0.48]): ty = cy - Inches(0.18) + j * Inches(0.18) shp(MSO_SHAPE.ROUNDED_RECTANGLE, left, ty - Inches(0.022), tw, Inches(0.045), fill=LINEC) kx = left + Inches(0.62 * frac) shp(MSO_SHAPE.OVAL, kx - Inches(0.07), ty - Inches(0.07), Inches(0.14), Inches(0.14), fill=NAVY) cx1, cy1 = card(Inches(4.75), "Steuerung") cx2, cy2 = card(Inches(6.95), "Reporting") cx3, cy3 = card(Inches(9.15), "Governance") regler(cx1, cy1) # Reporting – Zahlenreihen nb = s.shapes.add_textbox(cx2 - Inches(0.85), cy2 - Inches(0.36), Inches(1.7), Inches(0.72)) tf = nb.text_frame; tf.word_wrap = False; tf.vertical_anchor = MSO_ANCHOR.MIDDLE tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = Pt(0) for i, row in enumerate(["3 8 1 4 7", "6 0 9 2 5", "5 7 2 8 1"]): p = tf.paragraphs[0] if i == 0 else tf.add_paragraph() p.alignment = PP_ALIGN.CENTER; p.line_spacing = 0.95 r = p.add_run(); r.text = row r.font.size = Pt(11); r.font.name = "Consolas"; r.font.color.rgb = MUTE # Governance – § gtb = s.shapes.add_textbox(cx3 - Inches(0.6), cy3 - Inches(0.42), Inches(1.2), Inches(0.8)) gtf = gtb.text_frame; gtf.vertical_anchor = MSO_ANCHOR.MIDDLE gp = gtf.paragraphs[0]; gp.alignment = PP_ALIGN.CENTER gr = gp.add_run(); gr.text = "§"; gr.font.size = Pt(34); gr.font.bold = True gr.font.name = F; gr.font.color.rgb = NAVY gov_y = Inches(3.05) shp(MSO_SHAPE.ROUNDED_RECTANGLE, x0, gov_y - Inches(0.07), x1 - x0, Inches(0.14), fill=NAVY) for cc in (cx1, cx2, cx3): vline(cc, Inches(2.75), gov_y - Inches(0.07) - Inches(2.75), color=LINEC, weight=Pt(1)) # =========================================================================== # LIS + Umsysteme (Adapter links/rechts) + Module (andocken, unten) # =========================================================================== core_x, core_w = Inches(3.7), Inches(2.3) core_y, core_h = Inches(4.3), Inches(1.2) core_cx = core_x + core_w / 2 core_by = core_y + core_h def adapter(xc, yc): shp(MSO_SHAPE.ROUNDED_RECTANGLE, xc - Inches(0.10), yc - Inches(0.11), Inches(0.16), Inches(0.22), fill=ACCENT) shp(MSO_SHAPE.RECTANGLE, xc + Inches(0.06), yc - Inches(0.055), Inches(0.1), Pt(2.4), fill=ACCENT) shp(MSO_SHAPE.RECTANGLE, xc + Inches(0.06), yc + Inches(0.03), Inches(0.1), Pt(2.4), fill=ACCENT) def dock(xc, yc): shp(MSO_SHAPE.ROUNDED_RECTANGLE, xc - Inches(0.1), yc - Inches(0.1), Inches(0.2), Inches(0.2), fill=NAVY) shp(MSO_SHAPE.OVAL, xc - Inches(0.055), yc - Inches(0.055), Inches(0.11), Inches(0.11), fill=ACCENT) def icon_truck(ix, iy): # Anlieferung shp(MSO_SHAPE.ROUNDED_RECTANGLE, ix - Inches(0.20), iy - Inches(0.10), Inches(0.24), Inches(0.16), fill=NAVY) shp(MSO_SHAPE.ROUNDED_RECTANGLE, ix + Inches(0.05), iy - Inches(0.035), Inches(0.12), Inches(0.095), fill=NAVY) for wx in (ix - Inches(0.11), ix + Inches(0.08)): shp(MSO_SHAPE.OVAL, wx - Inches(0.038), iy + Inches(0.05), Inches(0.076), Inches(0.076), fill=NAVY) def icon_device(ix, iy): # Gerät shp(MSO_SHAPE.ROUNDED_RECTANGLE, ix - Inches(0.16), iy - Inches(0.14), Inches(0.32), Inches(0.28), fill=NAVY) shp(MSO_SHAPE.RECTANGLE, ix - Inches(0.11), iy - Inches(0.10), Inches(0.22), Inches(0.085), fill=RGBColor(0x34, 0x4E, 0x70)) shp(MSO_SHAPE.OVAL, ix - Inches(0.085), iy + Inches(0.03), Inches(0.055), Inches(0.055), fill=TEAL) shp(MSO_SHAPE.OVAL, ix + Inches(0.03), iy + Inches(0.03), Inches(0.055), Inches(0.055), fill=AMBER) def icon_dfu(ix, iy): # DFÜ shp(MSO_SHAPE.RIGHT_ARROW, ix - Inches(0.16), iy - Inches(0.12), Inches(0.32), Inches(0.095), fill=NAVY) shp(MSO_SHAPE.LEFT_ARROW, ix - Inches(0.16), iy + Inches(0.025), Inches(0.32), Inches(0.095), fill=ACCENT) # Governance -> LIS vline(core_cx, gov_y + Inches(0.07), core_y - (gov_y + Inches(0.07)), color=LINEC, weight=Pt(1)) uw, uh = Inches(1.55), Inches(0.6) def ums_box(bx, yc, name, icon_fn): shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx, yc - uh / 2, uw, uh, fill=UMS, line=LINEC) icon_fn(bx + Inches(0.32), yc) txt(bx + Inches(0.58), yc - Inches(0.16), uw - Inches(0.64), Inches(0.32), name, size=12, color=INK, align=PP_ALIGN.LEFT) # Umsysteme links lx = Inches(0.8) ums_box(lx, Inches(4.6), "Anlieferung", icon_truck) ums_box(lx, Inches(5.2), "DFÜ", icon_dfu) for yc in (Inches(4.6), Inches(5.2)): hline(lx + uw, yc, core_x - (lx + uw)) adapter(core_x - Inches(0.16), yc) # Umsystem rechts gx = Inches(6.65) ums_box(gx, Inches(4.9), "Geräte", icon_device) hline(core_x + core_w, Inches(4.9), gx - (core_x + core_w)) adapter(core_x + core_w + Inches(0.16), Inches(4.9)) # LIS (kompakt, solide) shp(MSO_SHAPE.ROUNDED_RECTANGLE, core_x, core_y, core_w, core_h, fill=NAVY) sh = shp(MSO_SHAPE.ROUNDED_RECTANGLE, core_x, core_y, core_w, core_h, fill=NAVY) boxtext(sh, "LIS", size=22, color=WHITE, bold=True) # Module unten (andockend) mw, mh = Inches(1.7), Inches(0.6) my = Inches(6.05) for nm, fill, tc, mcx in [("Hochdurchsatz", TINT_T, TEAL, Inches(3.85)), ("Spezial", TINT_A, AMBER, Inches(5.85))]: vline(mcx, core_by, my - core_by, color=LINEC, weight=Pt(1)) dock(mcx, core_by + (my - core_by) / 2) sh = shp(MSO_SHAPE.ROUNDED_RECTANGLE, mcx - mw / 2, my, mw, mh, fill=fill, line=tc, lw=Pt(1.25)) boxtext(sh, nm, size=12.5, color=tc, bold=True) txt(Inches(2.9), my + mh + Inches(0.12), Inches(4.0), Inches(0.3), "eigene Module", size=12, color=MUTE, bold=True, align=PP_ALIGN.CENTER) # =========================================================================== # WEITERE LIS – überlappend, nur angedeutet # =========================================================================== hw_, hh_ = Inches(1.8), Inches(2.0) def mini_lis(hx, hy, faded=False): fill = RGBColor(0xF4, 0xF6, 0xF8) if faded else HINT shp(MSO_SHAPE.ROUNDED_RECTANGLE, hx, hy, hw_, hh_, fill=fill, line=HINTL, lw=Pt(1.25)) hdc = shp(MSO_SHAPE.ROUNDED_RECTANGLE, hx, hy, hw_, Inches(0.46), fill=HINTL if not faded else RGBColor(0xDF, 0xE4, 0xEA)) boxtext(hdc, "LIS", size=14, color=NAVY, bold=True) for k in range(3): yy = hy + Inches(0.66) + k * Inches(0.4) shp(MSO_SHAPE.ROUNDED_RECTANGLE, hx + Inches(0.2), yy, hw_ - Inches(0.4), Inches(0.28), fill=WHITE, line=HINTL, lw=Pt(0.75)) mini_lis(Inches(10.5), Inches(4.35), faded=True) mini_lis(Inches(10.0), Inches(4.75), faded=False) vline(Inches(10.0) + hw_ / 2, gov_y + Inches(0.07), Inches(4.75) - (gov_y + Inches(0.07)), color=LINEC, weight=Pt(1)) txt(Inches(9.9), Inches(4.35) + hh_ + Inches(0.2), Inches(2.4), Inches(0.35), "weitere LIS", size=13, color=MUTE, align=PP_ALIGN.CENTER) # ---- Mini-Legende ---------------------------------------------------------- ly = Inches(7.05) adapter(Inches(7.35), ly) txt(Inches(7.55), ly - Inches(0.16), Inches(2.1), Inches(0.32), "Adapter (Umsysteme)", size=11.5, color=MUTE) dock(Inches(9.65), ly) txt(Inches(9.85), ly - Inches(0.16), Inches(2.1), Inches(0.32), "Andocken (Module)", size=11.5, color=MUTE) out = "/home/marcuh/claude-projects/limbach/Folie3.pptx" prs.save(out) print("saved", out)