Skip to content

Commit 91fc1e9

Browse files
committed
refactor: reorganize table module and enhance angle type handling
1 parent 8741cf0 commit 91fc1e9

File tree

12 files changed

+496
-17
lines changed

12 files changed

+496
-17
lines changed

src/tppt/pptx/converter.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
"""Type definitions for pptx wrapper."""
22

3-
from typing import Protocol, Self, TypeVar, assert_never, overload, runtime_checkable
3+
from typing import (
4+
Protocol,
5+
Self,
6+
TypeAlias,
7+
TypeVar,
8+
assert_never,
9+
overload,
10+
runtime_checkable,
11+
)
412

513
from pptx.dml.color import RGBColor as PptxRGBColor
614
from pptx.util import Cm as PptxCm
@@ -10,6 +18,7 @@
1018
from pptx.util import Mm as PptxMm
1119
from pptx.util import Pt as PptxPt
1220

21+
from tppt.types._angle import Angle, Degrees, LiteralAngle
1322
from tppt.types._color import Color, LiteralColor, to_color
1423
from tppt.types._length import (
1524
CentiMeters,
@@ -107,3 +116,38 @@ def to_tppt_color(color: PptxRGBColor | None) -> Color | None: ...
107116

108117
def to_tppt_color(color: PptxRGBColor | None) -> Color | None:
109118
return Color(*color) if color else None
119+
120+
121+
PptxAngle: TypeAlias = float
122+
123+
124+
@overload
125+
def to_pptx_angle(angle: Angle | LiteralAngle) -> PptxAngle: ...
126+
127+
128+
@overload
129+
def to_pptx_angle(angle: Angle | LiteralAngle | None) -> PptxAngle | None: ...
130+
131+
132+
def to_pptx_angle(angle: Angle | LiteralAngle | None) -> PptxAngle | None:
133+
match angle:
134+
case Degrees():
135+
return angle._value
136+
case tuple():
137+
return angle[0]
138+
case None:
139+
return None
140+
case _:
141+
assert_never(angle)
142+
143+
144+
@overload
145+
def to_tppt_angle(angle: PptxAngle) -> Angle: ...
146+
147+
148+
@overload
149+
def to_tppt_angle(angle: PptxAngle | None) -> Angle | None: ...
150+
151+
152+
def to_tppt_angle(angle: PptxAngle | None) -> Angle | None:
153+
return Degrees(angle) if angle else None

src/tppt/pptx/dml/color.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Self
22

33
from pptx.dml.color import ColorFormat as PptxColorFormat
4+
from pptx.enum.dml import MSO_THEME_COLOR
45

56
from tppt.pptx.converter import PptxConvertible, to_pptx_rgb_color, to_tppt_color
67
from tppt.types import Color, LiteralColor
@@ -10,6 +11,19 @@ class ColorFormat(PptxConvertible[PptxColorFormat]):
1011
def __init__(self, pptx_obj: PptxColorFormat) -> None:
1112
self._pptx = pptx_obj
1213

14+
@property
15+
def brightness(self) -> float:
16+
"""
17+
Read/write float value between -1.0 and 1.0 indicating the brightness
18+
adjustment for this color, e.g. -0.25 is 25% darker and 0.4 is 40%
19+
lighter. 0 means no brightness adjustment.
20+
"""
21+
return self._pptx.brightness
22+
23+
@brightness.setter
24+
def brightness(self, value: float) -> None:
25+
self._pptx.brightness = value
26+
1327
@property
1428
def rgb(self) -> Color | None:
1529
return to_tppt_color(self._pptx.rgb)
@@ -18,6 +32,22 @@ def rgb(self) -> Color | None:
1832
def rgb(self, color: Color | LiteralColor):
1933
self._pptx.rgb = to_pptx_rgb_color(color)
2034

35+
@property
36+
def theme_color(self) -> MSO_THEME_COLOR:
37+
"""Theme color value of this color.
38+
39+
Value is a member of :ref:`MsoThemeColorIndex`, e.g.
40+
``MSO_THEME_COLOR.ACCENT_1``. Raises AttributeError on access if the
41+
color is not type ``MSO_COLOR_TYPE.SCHEME``. Assigning a member of
42+
:ref:`MsoThemeColorIndex` causes the color's type to change to
43+
``MSO_COLOR_TYPE.SCHEME``.
44+
"""
45+
return self._pptx.theme_color
46+
47+
@theme_color.setter
48+
def theme_color(self, value: MSO_THEME_COLOR) -> None:
49+
self._pptx.theme_color = value
50+
2151
def to_pptx(self) -> PptxColorFormat:
2252
return self._pptx
2353

src/tppt/pptx/dml/fill.py

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
from typing import Self, cast
2+
3+
from pptx.dml.fill import FillFormat as PptxFillFormat
4+
from pptx.dml.fill import _GradFill as PptxGradFill
5+
from pptx.dml.fill import _GradientStop as PptxGradientStop
6+
from pptx.dml.fill import _GradientStops as PptxGradientStops
7+
from pptx.dml.fill import _NoFill as PptxNoFill
8+
from pptx.dml.fill import _PattFill as PptxPattFill
9+
from pptx.dml.fill import _SolidFill as PptxSolidFill
10+
from pptx.enum.dml import MSO_PATTERN_TYPE
11+
12+
from tppt.pptx.converter import PptxConvertible, to_pptx_angle, to_tppt_angle
13+
from tppt.pptx.dml.color import ColorFormat
14+
from tppt.types._angle import Angle
15+
16+
17+
class FillFormat(PptxConvertible[PptxFillFormat]):
18+
"""Fill format."""
19+
20+
def __init__(self, pptx_obj: PptxFillFormat) -> None:
21+
self._pptx = pptx_obj
22+
23+
@property
24+
def fore_color(self) -> ColorFormat:
25+
"""Fore color."""
26+
return ColorFormat(self._pptx.fore_color)
27+
28+
@property
29+
def back_color(self) -> ColorFormat:
30+
"""Back color."""
31+
return ColorFormat(self._pptx.back_color)
32+
33+
@property
34+
def pattern(self) -> MSO_PATTERN_TYPE:
35+
"""Pattern."""
36+
return self._pptx.pattern
37+
38+
@pattern.setter
39+
def pattern(self, value: MSO_PATTERN_TYPE) -> None:
40+
self._pptx.pattern = value
41+
42+
def patterned(self) -> "PattFill":
43+
"""Set the fill type to patterned."""
44+
self._pptx.patterned()
45+
return PattFill(cast(PptxPattFill, self._pptx._fill))
46+
47+
def background(self) -> "NoFill":
48+
"""Set the fill type to noFill, i.e. transparent."""
49+
self._pptx.background()
50+
return NoFill(cast(PptxNoFill, self._pptx._fill))
51+
52+
def solid(self) -> "SolidFill":
53+
"""Set the fill type to solid."""
54+
self._pptx.solid()
55+
return SolidFill(cast(PptxSolidFill, self._pptx._fill))
56+
57+
def gradient(self) -> "GradFill":
58+
"""Sets the fill type to gradient."""
59+
self._pptx.gradient()
60+
return GradFill(cast(PptxGradFill, self._pptx._fill))
61+
62+
def to_pptx(self) -> PptxFillFormat:
63+
"""Convert to pptx fill format."""
64+
return self._pptx
65+
66+
@classmethod
67+
def from_pptx(cls, pptx_obj: PptxFillFormat) -> Self:
68+
"""Create from pptx fill format."""
69+
return cls(pptx_obj)
70+
71+
72+
class NoFill(PptxConvertible[PptxNoFill]):
73+
"""No fill."""
74+
75+
def __init__(self, pptx_obj: PptxNoFill) -> None:
76+
self._pptx = pptx_obj
77+
78+
def to_pptx(self) -> PptxNoFill:
79+
"""Convert to pptx no fill."""
80+
return self._pptx
81+
82+
@classmethod
83+
def from_pptx(cls, pptx_obj: PptxNoFill) -> Self:
84+
"""Create from pptx no fill."""
85+
return cls(pptx_obj)
86+
87+
88+
class PattFill(PptxConvertible[PptxPattFill]):
89+
"""Pattern fill."""
90+
91+
def __init__(self, pptx_obj: PptxPattFill) -> None:
92+
self._pptx = pptx_obj
93+
94+
def to_pptx(self) -> PptxPattFill:
95+
"""Convert to pptx pattern fill."""
96+
return self._pptx
97+
98+
@classmethod
99+
def from_pptx(cls, pptx_obj: PptxPattFill) -> Self:
100+
"""Create from pptx pattern fill."""
101+
return cls(pptx_obj)
102+
103+
104+
class SolidFill(PptxConvertible[PptxSolidFill]):
105+
"""Solid fill."""
106+
107+
def __init__(self, pptx_obj: PptxSolidFill) -> None:
108+
self._pptx = pptx_obj
109+
110+
def to_pptx(self) -> PptxSolidFill:
111+
"""Convert to pptx solid fill."""
112+
return self._pptx
113+
114+
@classmethod
115+
def from_pptx(cls, pptx_obj: PptxSolidFill) -> Self:
116+
"""Create from pptx solid fill."""
117+
return cls(pptx_obj)
118+
119+
120+
class GradFill(PptxConvertible[PptxGradFill]):
121+
"""Gradient fill."""
122+
123+
def __init__(self, pptx_obj: PptxGradFill) -> None:
124+
self._pptx = pptx_obj
125+
126+
@property
127+
def gradient_angle(self) -> Angle | None:
128+
"""Angle in float degrees of line of a linear gradient."""
129+
return to_tppt_angle(cast(float | None, self._pptx.gradient_angle))
130+
131+
@gradient_angle.setter
132+
def gradient_angle(self, value: Angle) -> None:
133+
self._pptx.gradient_angle = to_pptx_angle(value)
134+
135+
@property
136+
def gradient_stops(self) -> "GradientStops":
137+
"""Gradient stops."""
138+
return GradientStops(self._pptx.gradient_stops)
139+
140+
def to_pptx(self) -> PptxGradFill:
141+
"""Convert to pptx gradient fill."""
142+
return self._pptx
143+
144+
@classmethod
145+
def from_pptx(cls, pptx_obj: PptxGradFill) -> Self:
146+
"""Create from pptx gradient fill."""
147+
return cls(pptx_obj)
148+
149+
150+
class GradientStops(PptxConvertible[PptxGradientStops]):
151+
"""Gradient stops."""
152+
153+
def __init__(self, pptx_obj: PptxGradientStops) -> None:
154+
self._pptx = pptx_obj
155+
156+
def __getitem__(self, idx: int) -> "GradientStop":
157+
return GradientStop(self._pptx[idx])
158+
159+
def __len__(self) -> int:
160+
return len(self._pptx)
161+
162+
def to_pptx(self) -> PptxGradientStops:
163+
"""Convert to pptx gradient stops."""
164+
return self._pptx
165+
166+
@classmethod
167+
def from_pptx(cls, pptx_obj: PptxGradientStops) -> Self:
168+
"""Create from pptx gradient stops."""
169+
return cls(pptx_obj)
170+
171+
172+
class GradientStop(PptxConvertible[PptxGradientStop]):
173+
"""Gradient stop."""
174+
175+
def __init__(self, pptx_obj: PptxGradientStop) -> None:
176+
self._pptx = pptx_obj
177+
178+
@property
179+
def color(self) -> ColorFormat:
180+
"""Color."""
181+
return ColorFormat(self._pptx.color)
182+
183+
@property
184+
def position(self) -> float:
185+
"""Location of stop in gradient path as float between 0.0 and 1.0.
186+
187+
The value represents a percentage, where 0.0 (0%) represents the
188+
start of the path and 1.0 (100%) represents the end of the path. For
189+
a linear gradient, these would represent opposing extents of the
190+
filled area.
191+
"""
192+
return self._pptx.position
193+
194+
@position.setter
195+
def position(self, value: float) -> None:
196+
self._pptx.position = value
197+
198+
def to_pptx(self) -> PptxGradientStop:
199+
"""Convert to pptx gradient stop."""
200+
return self._pptx
201+
202+
@classmethod
203+
def from_pptx(cls, pptx_obj: PptxGradientStop) -> Self:
204+
"""Create from pptx gradient stop."""
205+
return cls(pptx_obj)

src/tppt/pptx/slide.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
from .placeholder import SlidePlaceholder
1212
from .shape import RangeProps, Shape
1313
from .shape.picture import Picture, PictureData, PictureProps
14-
from .shape.table import DataFrame, Table, TableData, TableProps, dataframe2list
1514
from .shape.text import Text, TextData, TextProps
1615
from .slide_layout import SlideLayout
1716
from .snotes_slide import NotesSlide
17+
from .table.table import DataFrame, Table, TableData, TableProps, dataframe2list
1818

1919
if TYPE_CHECKING:
2020
pass
@@ -88,7 +88,7 @@ def __init__(
8888
placeholder_registry: Callable[[Slide], None],
8989
) -> None:
9090
self._slide_layout = slide_layout
91-
self._shape_registry: list[Callable[[Slide], Shape[Any]]] = []
91+
self._shape_registry: list[Callable[[Slide], Any]] = []
9292
self._placeholder_registry = placeholder_registry
9393

9494
@overload
@@ -218,14 +218,16 @@ def table(
218218

219219
def _register(slide: Slide) -> Table:
220220
table_obj = Table(
221-
slide.to_pptx().shapes.add_table(
221+
slide.to_pptx()
222+
.shapes.add_table(
222223
rows,
223224
cols,
224225
to_pptx_length(table_data["left"]),
225226
to_pptx_length(table_data["top"]),
226227
to_pptx_length(table_data["width"]),
227228
to_pptx_length(table_data["height"]),
228-
),
229+
)
230+
.table,
229231
table_data,
230232
)
231233

src/tppt/pptx/table/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .table import Table as Table
2+
from .table import TableCellStyle as TableCellStyle

0 commit comments

Comments
 (0)