Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions examples/add_shape_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Example of adding shapes with background and text."""

from pathlib import Path

from pptx.enum.shapes import MSO_AUTO_SHAPE_TYPE

import tppt


def main():
"""Create a PowerPoint with box shapes that have background color and text."""
tppt.Presentation.builder().slide(
lambda slide: slide.BlankLayout()
.builder()
.add_shape(
MSO_AUTO_SHAPE_TYPE.RECTANGLE,
lambda shape: (
shape.fill.solid().fore_color.set_rgb("#0066ff"),
shape.set_text("Sample Box with Text"),
)[-1],
left=(1, "in"),
top=(1, "in"),
width=(4, "in"),
height=(2, "in"),
)
.add_shape(
MSO_AUTO_SHAPE_TYPE.ROUNDED_RECTANGLE,
lambda shape: (
shape.fill.solid().fore_color.set_rgb("#ff6600"),
shape.set_text("Rounded Box"),
)[-1],
left=(6, "in"),
top=(1, "in"),
width=(3, "in"),
height=(1.5, "in"),
)
.add_shape(
MSO_AUTO_SHAPE_TYPE.OVAL,
lambda shape: (
shape.fill.solid().fore_color.set_rgb("#00ff00"),
shape.set_text("Circle"),
)[-1],
left=(1, "in"),
top=(4, "in"),
width=(2, "in"),
height=(2, "in"),
)
).save(Path(__file__).with_suffix(".pptx"))


if __name__ == "__main__":
main()
19 changes: 18 additions & 1 deletion src/tppt/pptx/dml/color.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Literal, assert_never, cast
from typing import Literal, Self, assert_never, cast

from lxml.etree import _Element
from pptx.dml.color import ColorFormat as PptxColorFormat
Expand Down Expand Up @@ -92,6 +92,11 @@ def brightness(self) -> float:
def brightness(self, value: float) -> None:
self._pptx.brightness = value

def set_brightness(self, value: float) -> Self:
"""Set brightness value and return self for method chaining."""
self.brightness = value
return self

@property
def rgb(self) -> Color:
solid_fill = cast(
Expand All @@ -118,6 +123,11 @@ def rgb(self, color: Color | LiteralColor | PptxRGBColor):
if alpha := srgbClr.find("a:alpha", namespace):
srgbClr.remove(alpha)

def set_rgb(self, color: Color | LiteralColor | PptxRGBColor) -> Self:
"""Set RGB color value and return self for method chaining."""
self.rgb = color
return self

@property
def theme_color(self) -> MSO_THEME_COLOR | None:
"""Theme color value of this color.
Expand All @@ -133,3 +143,10 @@ def theme_color(self) -> MSO_THEME_COLOR | None:
@theme_color.setter
def theme_color(self, value: LiteralThemeColor | MSO_THEME_COLOR | None) -> None:
self._pptx.theme_color = to_pptx_theme_color(value)

def set_theme_color(
self, value: LiteralThemeColor | MSO_THEME_COLOR | None
) -> Self:
"""Set theme color value and return self for method chaining."""
self.theme_color = value
return self
20 changes: 20 additions & 0 deletions src/tppt/pptx/dml/fill.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ def pattern(self) -> MSO_PATTERN_TYPE:
def pattern(self, value: MSO_PATTERN_TYPE) -> None:
self._pptx.pattern = value

def set_pattern(self, value: MSO_PATTERN_TYPE) -> "FillFormat":
"""Set pattern."""
self.pattern = value
return self

def patterned(self) -> "PattFill":
"""Set the fill type to patterned."""
self._pptx.patterned()
Expand Down Expand Up @@ -78,6 +83,11 @@ def gradient_angle(self) -> Angle | None:
def gradient_angle(self, value: Angle) -> None:
self._pptx.gradient_angle = to_pptx_angle(value)

def set_gradient_angle(self, value: Angle) -> "GradFill":
"""Set gradient angle."""
self.gradient_angle = value
return self

@property
def gradient_stops(self) -> "GradientStops":
"""Gradient stops."""
Expand Down Expand Up @@ -110,6 +120,11 @@ def pattern(self) -> MSO_PATTERN_TYPE:
def pattern(self, value: LiteralPatternType | MSO_PATTERN_TYPE) -> None:
self._pptx.pattern = to_pptx_pattern_type(value)

def set_pattern(self, value: LiteralPatternType | MSO_PATTERN_TYPE) -> "PattFill":
"""Set pattern."""
self.pattern = value
return self


class SolidFill(Fill[PptxSolidFill]):
"""Solid fill."""
Expand Down Expand Up @@ -152,3 +167,8 @@ def position(self) -> float:
@position.setter
def position(self, value: float) -> None:
self._pptx.position = value

def set_position(self, value: float) -> "GradientStop":
"""Set position."""
self.position = value
return self
37 changes: 36 additions & 1 deletion src/tppt/pptx/shape/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Shape wrapper implementation."""

from typing import TYPE_CHECKING, TypedDict
from typing import TYPE_CHECKING, Self, TypedDict

from pptx.shapes import Subshape as PptxSubshape
from pptx.shapes.autoshape import Shape as PptxShape
Expand Down Expand Up @@ -66,6 +66,11 @@ def height(self) -> Length:
def height(self, value: Length | LiteralLength | PptxLength) -> None:
self._pptx.height = to_pptx_length(value)

def set_height(self, value: Length | LiteralLength | PptxLength) -> Self:
"""Set height value and return self for method chaining."""
self.height = value
return self

@property
def left(self) -> Length:
return to_length(self._pptx.left)
Expand All @@ -74,6 +79,11 @@ def left(self) -> Length:
def left(self, value: Length | LiteralLength | PptxLength) -> None:
self._pptx.left = to_pptx_length(value)

def set_left(self, value: Length | LiteralLength | PptxLength) -> Self:
"""Set left position value and return self for method chaining."""
self.left = value
return self

@property
def name(self) -> str:
return self._pptx.name
Expand All @@ -82,6 +92,11 @@ def name(self) -> str:
def name(self, value: str) -> None:
self._pptx.name = value

def set_name(self, value: str) -> Self:
"""Set name value and return self for method chaining."""
self.name = value
return self

@property
def part(self) -> "PptxBaseSlidePart":
return self._pptx.part
Expand All @@ -98,6 +113,11 @@ def rotation(self) -> float:
def rotation(self, value: float) -> None:
self._pptx.rotation = value

def set_rotation(self, value: float) -> Self:
"""Set rotation value and return self for method chaining."""
self.rotation = value
return self

@property
def shadow(self) -> "ShadowFormat":
from tppt.pptx.dml.effect import ShadowFormat
Expand All @@ -120,6 +140,11 @@ def top(self) -> Length:
def top(self, value: Length | LiteralLength | PptxLength) -> None:
self._pptx.top = to_pptx_length(value)

def set_top(self, value: Length | LiteralLength | PptxLength) -> Self:
"""Set top position value and return self for method chaining."""
self.top = value
return self

@property
def width(self) -> Length:
return to_length(self._pptx.width)
Expand All @@ -128,6 +153,11 @@ def width(self) -> Length:
def width(self, value: Length | LiteralLength | PptxLength) -> None:
self._pptx.width = to_pptx_length(value)

def set_width(self, value: Length | LiteralLength | PptxLength) -> Self:
"""Set width value and return self for method chaining."""
self.width = value
return self


class Shape(BaseShape[GenericPptxShape]):
@property
Expand Down Expand Up @@ -158,6 +188,11 @@ def text(self) -> str:
def text(self, text: str) -> None:
self._pptx.text = text

def set_text(self, text: str) -> Self:
"""Set text value and return self for method chaining."""
self.text = text
return self

@property
def text_frame(self) -> "TextFrame":
from tppt.pptx.text.text_frame import TextFrame
Expand Down
48 changes: 47 additions & 1 deletion src/tppt/pptx/slide.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@
from tppt.types import FilePath

from .converter import PptxConvertible, to_pptx_length
from .shape import BaseShape, RangeProps
from .shape import BaseShape, RangeProps, Shape
from .shape.picture import Picture, PictureData, PictureProps
from .shape.placeholder import SlidePlaceholder
from .shape.text import Text, TextData, TextProps
from .slide_layout import SlideLayout
from .table.table import DataFrame, Table, TableData, TableProps, dataframe2list

if TYPE_CHECKING:
from pptx.enum.shapes import MSO_AUTO_SHAPE_TYPE

from tppt.pptx.shape.background import Background

from .notes_slide import NotesSlide
Expand Down Expand Up @@ -355,6 +357,50 @@ def _register(slide: Slide) -> Chart:

return self

@overload
def add_shape(
self,
shape_type: "MSO_AUTO_SHAPE_TYPE",
/,
**kwargs: Unpack[RangeProps],
) -> Self: ...

@overload
def add_shape(
self,
shape_type: "MSO_AUTO_SHAPE_TYPE",
shape: Callable[[Shape], Shape],
/,
**kwargs: Unpack[RangeProps],
) -> Self: ...

def add_shape(
self,
shape_type: "MSO_AUTO_SHAPE_TYPE",
shape: Callable[[Shape], Shape] | None = None,
/,
**kwargs: Unpack[RangeProps],
) -> Self:
"""Add a shape to the slide."""

def _register(slide: Slide) -> Shape:
shape_obj = Shape(
slide.to_pptx().shapes.add_shape(
shape_type,
to_pptx_length(kwargs["left"]),
to_pptx_length(kwargs["top"]),
to_pptx_length(kwargs["width"]),
to_pptx_length(kwargs["height"]),
)
)
if shape is not None:
return shape(shape_obj)
else:
return shape_obj

self._shape_registry.append(_register)
return self

def _build(self, slide: PptxSlide) -> Slide:
tppt_slide = Slide(slide)

Expand Down
Loading