Skip to content

Commit 9aec0d2

Browse files
committed
add arch, pie shapes and charts module with donut chart
1 parent e0fc92f commit 9aec0d2

28 files changed

+354
-109
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
# Changelog
22

3+
## 0.2.0 (2025-05-15)
4+
5+
### Added
6+
- New shape `Arch`: a `blockArc`-like ring with configurable thickness, and angular range.
7+
- New shape `Pie`: a filled sector of a circle.
8+
- New module: `charts` with initial support for `DonutChart`, a circular chart with customizable radii, gaps, and total value label.
9+
- New example: `charts/donut_chart.py` showcasing `DonutChart` usage
10+
11+
### Changed
12+
- `Ellipse` now uses `width` and `height` instead of `dx` and `dy` for clarity and consistency.
13+
14+
### Fixed
15+
- Minor internal refactoring for better consistency of shape parameter naming.
16+
17+
318
## 0.1.4 (2025-05-14)
419

520
- Added `family` field to `FontStyle` for specifying font family (default: `"Calibri"`)

README.md

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ with Presentation(presentation_path="empty.pptx") as presentation:
3737
))
3838

3939
presentation.add(shape=Ellipse(
40-
x=20, y=2, dx=4, dy=4,
40+
x=20, y=2, width=4, height=4,
4141
fill=FillStyle(color="#7699d4")
4242
))
4343

@@ -74,9 +74,11 @@ Currently, `pptx-shapes` supports the following geometric shapes:
7474
|------------------------------------------------------------------------------------------------------|-------------|------------------------------------------------------------------------------|
7575
| [Line](https://github.com/dronperminov/pptx-shapes/blob/master/pptx_shapes/shapes/line.py) | `Line` | Straight line between two points |
7676
| [Arrow](https://github.com/dronperminov/pptx-shapes/blob/master/pptx_shapes/shapes/arrow.py) | `Arrow` | Straight arrow between two points |
77-
| [Arc](https://github.com/dronperminov/pptx-shapes/blob/master/pptx_shapes/shapes/arc.py) | `Arc` | Curved segment defined by the bounding box and start/end angles. |
78-
| [Ellipse](https://github.com/dronperminov/pptx-shapes/blob/master/pptx_shapes/shapes/ellipse.py) | `Ellipse` | Ellipse defined by top-left corner, diameters, and rotation angle |
77+
| [Arc](https://github.com/dronperminov/pptx-shapes/blob/master/pptx_shapes/shapes/arc.py) | `Arc` | Curved segment defined by the bounding box and start/end angles |
78+
| [Arch](https://github.com/dronperminov/pptx-shapes/blob/master/pptx_shapes/shapes/arch.py) | `Arch` | Ring-shaped arc defined by the bounding box, thickness and start/end angles |
79+
| [Ellipse](https://github.com/dronperminov/pptx-shapes/blob/master/pptx_shapes/shapes/ellipse.py) | `Ellipse` | Ellipse defined by top-left corner, size, and rotation angle |
7980
| [Rectangle](https://github.com/dronperminov/pptx-shapes/blob/master/pptx_shapes/shapes/rectangle.py) | `Rectangle` | Rectangle defined by top-left corner, size, corner radius and rotation angle |
81+
| [Pie](https://github.com/dronperminov/pptx-shapes/blob/master/pptx_shapes/shapes/pie.py) | `Pie` | Filled sector of a circle, defined by the bounding box and start/end angles |
8082
| [Polygon](https://github.com/dronperminov/pptx-shapes/blob/master/pptx_shapes/shapes/polygon.py) | `Polygon` | Arbitrary polygon defined by a list of points and rotation angle |
8183
| [TextBox](https://github.com/dronperminov/pptx-shapes/blob/master/pptx_shapes/shapes/textbox.py) | `TextBox` | Text container with position, size, rotation, and font style |
8284
| [Group](https://github.com/dronperminov/pptx-shapes/blob/master/pptx_shapes/shapes/group.py) | `Group` | A group of multiple shapes |
@@ -148,24 +150,52 @@ Arc(
148150
* `stroke`: optional `StrokeStyle` for the border
149151

150152

153+
### Arch
154+
155+
A ring-shaped arc defined by the bounding box, thickness, and angular range. It is based on PowerPoint’s `blockArc` shape.
156+
157+
```python
158+
Arch(
159+
x=5, y=5, # center (cm)
160+
width=3, height=3, # sizes (cm)
161+
thickness=1, # ring thickness (cm)
162+
start_angle=0, # start angle (degrees)
163+
end_angle=270, # end angle (degrees)
164+
angle=45, # optional rotation angle (degrees)
165+
fill=..., # optional FillStyle
166+
stroke=... # optional StrokeStyle
167+
)
168+
```
169+
170+
#### Parameters
171+
172+
* `x`, `y`: top-left corner coordinates in centimeters
173+
* `width`, `height`: sizes in centimeters
174+
* `thickness`: width of the ring (distance between outer and inner radius) in centimeters
175+
* `start_angle`, `end_angle`: arc range in degrees (default `0` and `180`)
176+
* `angle`: the rotation angle of the arch in degrees (default is `0`)
177+
* `fill`: optional `FillStyle` to fill the arch
178+
* `stroke`: optional `StrokeStyle` for the border
179+
180+
151181
### Ellipse
152182

153-
An ellipse is defined by its top-left corner and the diameters in horizontal and vertical directions. It can be rotated by a given angle.
183+
An ellipse is defined by its top-left corner and its width and height. It can be rotated by a given angle.
154184

155185
```python
156186
Ellipse(
157-
x=2, y=3.5, # top-left angle (cm)
158-
dx=4, dy=6, # diameters (cm)
159-
angle=30, # optional rotation angle (degrees)
160-
fill=..., # optional FillStyle
161-
stroke=... # optional StrokeStyle
187+
x=2, y=3.5, # top-left angle (cm)
188+
width=4, height=6, # sizes (cm)
189+
angle=30, # optional rotation angle (degrees)
190+
fill=..., # optional FillStyle
191+
stroke=... # optional StrokeStyle
162192
)
163193
```
164194

165195
#### Parameters
166196
* `x`, `y`: top-left corner coordinates in centimeters
167-
* `dx`, `dy`: diameters in centimeters
168-
* `angle`: the rotation angle of the ellipse in degrees (default is `0`).
197+
* `width`, `height`: sizes in centimeters
198+
* `angle`: the rotation angle of the ellipse in degrees (default is `0`)
169199
* `fill`: optional `FillStyle` to fill the ellipse
170200
* `stroke`: optional `StrokeStyle` for the border
171201

@@ -192,6 +222,33 @@ Rectangle(
192222
* `fill`: fill style
193223
* `stroke`: stroke style
194224

225+
226+
### Pie
227+
228+
A filled sector of an ellipse, defined by its bounding box and angular range.
229+
230+
```python
231+
Pie(
232+
x=5, y=5, # center (cm)
233+
width=3, height=3, # sizes (cm)
234+
start_angle=0, # start angle (degrees)
235+
end_angle=270, # end angle (degrees)
236+
angle=45, # optional rotation angle (degrees)
237+
fill=..., # optional FillStyle
238+
stroke=... # optional StrokeStyle
239+
)
240+
```
241+
242+
#### Parameters
243+
244+
* `x`, `y`: top-left corner coordinates in centimeters
245+
* `width`, `height`: sizes in centimeters
246+
* `start_angle`, `end_angle`: the angular range of the slice in degrees (default `0` and `180`)
247+
* `angle`: the rotation angle of the pie in degrees (default is `0`)
248+
* `fill`: optional `FillStyle` to fill the arch
249+
* `stroke`: optional `StrokeStyle` for the border
250+
251+
195252
### Polygon
196253

197254
A polygon can be an arbitrary shape defined by a list of points. It can also be rotated by an angle.
@@ -440,6 +497,16 @@ This example demonstrates how to use different font families and styles in `Text
440497
Download .pptx: [examples/text_boxes.pptx](https://github.com/dronperminov/pptx-shapes/blob/master/examples/text_boxes.pptx)
441498

442499

500+
### Example 6. Donut charts example
501+
502+
This example demonstrates how to use `DonutChart` from `charts` module
503+
([examples/charts/donut_chart.py](https://github.com/dronperminov/pptx-shapes/blob/master/examples/charts/donut_chart.py)).
504+
505+
![Slide example](https://github.com/dronperminov/pptx-shapes/raw/master/examples/charts/donut_chart.png)
506+
507+
Download .pptx: [examples/charts/donut_chart.pptx](https://github.com/dronperminov/pptx-shapes/blob/master/examples/charts/donut_chart.pptx)
508+
509+
443510
## Changelog
444511

445512
See [CHANGELOG.md](https://github.com/dronperminov/pptx-shapes/blob/master/CHANGELOG.md) for version history.

examples/basic.pptx

1 Byte
Binary file not shown.

examples/basic.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def main() -> None:
1818
))
1919

2020
# ellipses
21-
presentation.add(shape=Ellipse(x=20, y=2, dx=4, dy=4, fill=FillStyle(color="#7699d4")))
21+
presentation.add(shape=Ellipse(x=20, y=2, width=4, height=4, fill=FillStyle(color="#7699d4")))
2222

2323
# arrows
2424
presentation.add(shape=Arrow(x1=10, y1=9, x2=14, y2=11, start_type=ArrowType.OVAL, end_type=ArrowType.ARROW, stroke=StrokeStyle(thickness=2)))
@@ -61,9 +61,9 @@ def main() -> None:
6161
]))
6262

6363
presentation.add(shape=Group(shapes=[
64-
Ellipse(x=4.5, y=6.0, dx=2.0, dy=3.5, fill=FillStyle(color="#dd7373", opacity=0.5), stroke=StrokeStyle(color="black", thickness=2, opacity=0.75)),
65-
Ellipse(x=3.0, y=8.5, dx=3.5, dy=2.0, fill=FillStyle(color="#dd7373", opacity=0.25), stroke=StrokeStyle(color="black", opacity=0.25), angle=-45),
66-
Ellipse(x=5.0, y=8.5, dx=3.5, dy=2.0, fill=FillStyle(color="#dd7373", opacity=0.85), stroke=StrokeStyle(color="black", opacity=0.85), angle=45)
64+
Ellipse(x=4.5, y=6.0, width=2.0, height=3.5, fill=FillStyle(color="#dd7373", opacity=0.5), stroke=StrokeStyle(color="black", thickness=2, opacity=0.75)),
65+
Ellipse(x=3.0, y=8.5, width=3.5, height=2.0, fill=FillStyle(color="#dd7373", opacity=0.25), stroke=StrokeStyle(color="black", opacity=0.25), angle=-45),
66+
Ellipse(x=5.0, y=8.5, width=3.5, height=2.0, fill=FillStyle(color="#dd7373", opacity=0.85), stroke=StrokeStyle(color="black", opacity=0.85), angle=45)
6767
]))
6868

6969
presentation.add(shape=Group(shapes=[

examples/charts/donut_chart.png

72.3 KB
Loading

examples/charts/donut_chart.pptx

27.1 KB
Binary file not shown.

examples/charts/donut_chart.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from pptx_shapes import Presentation
2+
from pptx_shapes.charts.donut import DonutChart, DonutChartConfig, GapConfig, LabelConfig
3+
from pptx_shapes.shapes import Group
4+
5+
6+
def main() -> None:
7+
x0, y0 = 1, 2
8+
gap = 1
9+
10+
configs = [
11+
DonutChartConfig(inner_radius=3, outer_radius=5, gap=GapConfig(thickness=2), label=LabelConfig(size=75)),
12+
DonutChartConfig(inner_radius=0, outer_radius=5, gap=GapConfig(thickness=2), label=None),
13+
DonutChartConfig(inner_radius=4.5, outer_radius=5, gap=None, label=None),
14+
]
15+
16+
data = [
17+
{"value": 180, "color": "#f39c12"},
18+
{"value": 95, "color": "#2ecc71"},
19+
{"value": 150, "color": "#e74c3c"},
20+
{"value": 75, "color": "#3498db"}
21+
]
22+
23+
with Presentation(presentation_path="../empty.pptx") as presentation:
24+
for i, config in enumerate(configs):
25+
chart = DonutChart(config=config)
26+
presentation.add(shape=Group(chart.render(data=data, x=x0 + i * (gap + chart.size), y=y0)))
27+
28+
presentation.save("donut_chart.pptx")
29+
30+
31+
if __name__ == "__main__":
32+
main()

examples/histograms.pptx

0 Bytes
Binary file not shown.

examples/polygons.pptx

0 Bytes
Binary file not shown.

examples/scatter.png

-2.24 KB
Loading

0 commit comments

Comments
 (0)