Files
marcus hinz 93bb19abf0 initial
2026-06-20 14:40:33 +02:00

284 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Folie 3 LIS mit Umsystemen (Adapter, links/rechts) und andockenden Modulen
(unten), unter einer Steuerungs-/Governance-Schicht; weitere LIS angedeutet."""
import os
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
from adesso_style import (WHITE, INK, BLUE, TEAL, GREEN, CORAL, LIGHT, MUTE, RECF,
F, FC, paint_background, chrome)
# Rollen-Mapping auf die adesso-Palette
NAVY = INK # dunkle Strukturelemente (Bar, LIS-Kern)
ACCENT = CORAL # Adapter-Linien
AMBER = GREEN # zweites Modul
LINEC = LIGHT # Verbinder / feine Linien
HINT = RGBColor(0xE6, 0xEF, 0xF8) # helle "weitere LIS"-Karten
HINTL = RGBColor(0xC4, 0xD6, 0xE8)
TINT_T = WHITE # Modul-Boxen weiß
TINT_A = WHITE
UMS = WHITE # Umsystem-Boxen weiß
BG = WHITE
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
s = prs.slides.add_slide(prs.slide_layouts[6])
paint_background(s, prs.slide_width, prs.slide_height)
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 ------------------------------------------------------------------
chrome(s, "Offen und gesteuert", 3)
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
# Adapter-Schicht: echte Schicht als Hülle um den Kern (Trennung)
lay_pad_x, lay_pad_t = Inches(0.24), Inches(0.30)
lay_x = core_x - lay_pad_x
lay_w = core_w + lay_pad_x * 2
lay_y = core_y - lay_pad_t
lay_h = core_h + lay_pad_t
lay_right = lay_x + lay_w
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)
g = Inches(0.10) # Abstand Adapter-Linie <-> LIS
th = Inches(0.08) # Dicke der roten Adapter-Linie
# Governance -> obere Adapter-Linie am Kern
vline(core_cx, gov_y + Inches(0.07), (core_y - g - th) - (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 -> Adapter-Schichten am linken Kernrand
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 - g - th) - (lx + uw))
# Umsystem rechts -> Adapter-Linie am rechten Kernrand
gx = Inches(6.65)
ums_box(gx, Inches(4.9), "Geräte", icon_device)
hline(core_x + core_w + g + th, Inches(4.9), gx - (core_x + core_w + g + th))
# ---- LIS-Kern -------------------------------------------------------------
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)
# ---- 5 Adapter-Linien je Schnittstelle eine, mit Abstand zum Kern -------
def rbar_v(outer_x, yc, h=Inches(0.55)): # vertikal, linker/rechter Rand
shp(MSO_SHAPE.RECTANGLE, outer_x, yc - h / 2, th, h, fill=ACCENT)
def rbar_h(xc, outer_y, w): # horizontal, oben/unten
shp(MSO_SHAPE.RECTANGLE, xc - w / 2, outer_y, w, th, fill=ACCENT)
rbar_v(core_x - g - th, Inches(4.6)) # 1) Anlieferung
rbar_v(core_x - g - th, Inches(5.2)) # 2) DFÜ
rbar_h(core_cx, core_y - g - th, Inches(1.15)) # 3) nach oben (Steuerung)
rbar_v(core_x + core_w + g, Inches(4.9)) # 4) Geräte
rbar_h(core_cx, core_by + g, core_w - Inches(0.2)) # 5) Fachliche Verarbeitung
# Fachliche Verarbeitung (unten): Module an der unteren Adapter-Linie
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 + g + th, my - (core_by + g + th), color=LINEC, weight=Pt(1))
sh = shp(MSO_SHAPE.ROUNDED_RECTANGLE, mcx - mw / 2, my, mw, mh, fill=fill, line=tc, lw=Pt(1.5))
boxtext(sh, nm, size=12.5, color=INK, bold=True)
txt(Inches(2.9), my + mh + Inches(0.12), Inches(4.0), Inches(0.3), "Fachliche Verarbeitung",
size=12, color=LIGHT, 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=LIGHT, align=PP_ALIGN.CENTER)
# ---- Mini-Legende ----------------------------------------------------------
ly = Inches(7.05)
shp(MSO_SHAPE.RECTANGLE, Inches(7.55), ly - Inches(0.11), th, Inches(0.22), fill=ACCENT)
txt(Inches(7.8), ly - Inches(0.16), Inches(3.2), Inches(0.32),
"Adapter-Schicht je Schnittstelle eine", size=11.5, color=LIGHT)
out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Folie3.pptx")
prs.save(out)
print("saved", out)