Skip to content

Commit

Permalink
borders
Browse files Browse the repository at this point in the history
  • Loading branch information
daizutabi committed Jul 7, 2024
1 parent 414d9a5 commit 4e1eb86
Show file tree
Hide file tree
Showing 10 changed files with 314 additions and 51 deletions.
12 changes: 8 additions & 4 deletions src/pptxlib/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ def active(self) -> Presentation:

@dataclass(repr=False)
class Slide(Element):
parent: Slides
parent: Presentation

@classmethod
def get_parent(cls, parent: Slides) -> Presentation:
return parent.parent

@property
def shapes(self) -> Shapes:
Expand All @@ -90,11 +94,11 @@ def title(self, text):

@property
def width(self) -> float:
return self.parent.parent.width
return self.parent.width

@property
def height(self) -> float:
return self.parent.parent.height
return self.parent.height


class Slides(Collection[Slide]):
Expand All @@ -120,7 +124,7 @@ def add(self, index: int | None = None, layout=None):
else:
slide = self.api.AddSlide(index, layout)

return Slide(slide, self)
return Slide(slide, self.parent)

@property
def active(self):
Expand Down
7 changes: 6 additions & 1 deletion src/pptxlib/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ def select(self):
def delete(self):
self.api.Delete()

@classmethod
def get_parent(cls, parent: Collection) -> Base:
return parent


SomeElement = TypeVar("SomeElement", bound=Element)

Expand All @@ -65,7 +69,8 @@ def __call__(self, index: int | None = None) -> SomeElement:
if index is None:
index = len(self)

return self.type(self.api(index), self) # type: ignore
parent = self.type.get_parent(self)
return self.type(self.api(index), parent) # type: ignore

def __iter__(self) -> Iterator[SomeElement]:
for index in range(len(self)):
Expand Down
37 changes: 37 additions & 0 deletions src/pptxlib/lines.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING

from pptxlib.core import Element

if TYPE_CHECKING:
from pptxlib.tables import Borders


@dataclass(repr=False)
class LineFormat(Element):
parent: Borders


# def get_borders(
# cell: Cell | CellRange,
# border_type: Literal["bottom","left","right","top"],
# width: float = 1,
# color: int | str | tuple[int, int, int] = 0,
# line_style: Literal["-", "--"] = "-",
# *,
# visible: bool = True,
# ):
# border_type_int = getattr(constants, "ppBorder" + border_type[0].upper() + border_type[1:])
# border = cell.api.Borders(border_type_int)
# border.Visible = visible

# if not visible:
# return

# border.Weight = width
# border.ForeColor.RGB = color

# if line_style == "--":
# border.DashStyle = constants.msoLineDash"
20 changes: 13 additions & 7 deletions src/pptxlib/shapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@


class Shape(Element):
parent: Shapes | Cell
parent: Slide | Cell

@classmethod
def get_parent(cls, parent: Shapes) -> Slide:
return parent.parent

@property
def text_range(self) -> DispatchBaseClass:
Expand All @@ -32,8 +36,10 @@ def text(self, text: str) -> None:

@property
def slide(self) -> Slide:
if isinstance(self.parent, Shapes):
return self.parent.parent
from pptxlib.app import Slide

if isinstance(self.parent, Slide):
return self.parent

raise NotImplementedError

Expand Down Expand Up @@ -189,7 +195,7 @@ class Shapes(Collection[Shape]):

@property
def title(self) -> Shape:
return Shape(self.api.Title, self)
return Shape(self.api.Title, Shape.get_parent(self))

def add(
self,
Expand All @@ -205,7 +211,7 @@ def add(
kind = getattr(constants, f"msoShape{kind}")

api = self.api.AddShape(kind, left, top, width, height)
shape = Shape(api, self)
shape = Shape(api, Shape.get_parent(self))
shape.text = text
shape.set_style(**kwargs)

Expand All @@ -228,7 +234,7 @@ def add_label(
if auto_size is False:
api.TextFrame.AutoSize = False

shape = Shape(api, self)
shape = Shape(api, Shape.get_parent(self))
shape.text = text
shape.set_style(**kwargs)

Expand All @@ -244,7 +250,7 @@ def add_table(
height: float = 100,
) -> Shape:
api = self.api.AddTable(num_rows, num_columns, left, top, width, height)
return Shape(api, self)
return Shape(api, Shape.get_parent(self))


# def add_picture(self, path=None, left=0, top=0, width=None, height=None, scale=1, **kwargs):
Expand Down
73 changes: 48 additions & 25 deletions src/pptxlib/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from win32com.client import constants

from pptxlib.core import Collection, Element
from pptxlib.lines import LineFormat
from pptxlib.shapes import Shape

if TYPE_CHECKING:
Expand Down Expand Up @@ -109,7 +110,7 @@ def __iter__(self) -> Iterator[Table]:
api = self.api(index + 1) # type: ignore

if api.HasTable:
yield Table(api.Table, Shape(api, self.parent.shapes))
yield Table(api.Table, Shape(api, self.parent))

def __len__(self) -> int:
return len(list(iter(self)))
Expand All @@ -131,12 +132,16 @@ def add(
height: float = 100,
) -> Table:
api = self.api.AddTable(num_rows, num_columns, left, top, width, height)
return Table(api.Table, Shape(api, self.parent.shapes))
return Table(api.Table, Shape(api, self.parent))


@dataclass(repr=False)
class Row(Element):
parent: Rows
parent: Table

@classmethod
def get_parent(cls, parent: Rows) -> Table:
return parent.parent

@property
def height(self) -> float:
Expand All @@ -146,6 +151,10 @@ def height(self) -> float:
def height(self, value: float) -> None:
self.api.Height = value

@property
def cells(self) -> CellRange:
return CellRange(self.api.Cells, self)


@dataclass(repr=False)
class Rows(Collection[Row]):
Expand All @@ -164,7 +173,11 @@ def height(self, value: list[float]) -> None:

@dataclass(repr=False)
class Column(Element):
parent: Columns
parent: Table

@classmethod
def get_parent(cls, parent: Columns) -> Table:
return parent.parent

@property
def width(self) -> float:
Expand All @@ -174,6 +187,10 @@ def width(self) -> float:
def width(self, value: float) -> None:
self.api.Width = value

@property
def cells(self) -> CellRange:
return CellRange(self.api.Cells, self)


@dataclass(repr=False)
class Columns(Collection[Column]):
Expand Down Expand Up @@ -230,27 +247,33 @@ def value(self):
def value(self, value):
self.text = value

def set_border(
self,
pos: str,
width: float = 1,
color: int | str | tuple[int, int, int] = 0,
line_style: Literal["-", "--"] = "-",
*,
visible: bool = True,
):
pos = getattr(constants, "ppBorder" + pos[0].upper() + pos[1:])
border = self.api.Borders(pos)
border.Visible = visible

if not visible:
return

border.Weight = width
border.ForeColor.RGB = color

if line_style == "--":
border.DashStyle = constants.msoLineDash
@property
def borders(self) -> Borders:
borders = Borders(self) # type: ignore
borders.parent = self.parent
return borders


@dataclass(repr=False)
class CellRange(Element):
parent: Row | Column

@property
def borders(self) -> Borders:
borders = Borders(self) # type: ignore
borders.parent = self.parent.parent
return borders


@dataclass(repr=False)
class Borders(Collection[LineFormat]):
parent: Table
type: ClassVar[type[Element]] = LineFormat

def __call__(self, type: Literal["bottom", "left", "right", "top"]) -> LineFormat: # noqa: A002
type_int = getattr(constants, "ppBorder" + type[0].upper() + type[1:])
api = self.api(type_int) # type: ignore
return LineFormat(api, self)


# from win32com.client import constants
Expand Down
12 changes: 11 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from pptxlib.app import PowerPoint, Presentation, Presentations, Slide, Slides
from pptxlib.shapes import Shapes
from pptxlib.tables import Table, Tables
from pptxlib.tables import Rows, Table, Tables


@pytest.fixture(scope="session")
Expand Down Expand Up @@ -74,3 +74,13 @@ def rows(table: Table):
@pytest.fixture
def columns(table: Table):
return table.columns


@pytest.fixture
def cell(table: Table):
return table.cell(1, 1)


@pytest.fixture
def cell_range(rows: Rows):
return rows(1).cells
32 changes: 27 additions & 5 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,21 +125,43 @@ def test_slide_title(slide: Slide):
assert slide.title == "Slide Title"


def test_repr_powerpoint(pp: PowerPoint):
def test_powerpoint_repr(pp: PowerPoint):
assert repr(pp) == "<PowerPoint>"


def test_repr_presentations(prs: Presentations):
def test_presentations_repr(prs: Presentations):
assert repr(prs) == "<Presentations>"


def test_repr_presentation(pr: Presentation):
def test_presentation_repr(pr: Presentation):
assert repr(pr).startswith("<Presentation [")


def test_repr_slides(slides: Slides):
def test_slides_repr(slides: Slides):
assert repr(slides) == "<Slides>"


def test_repr_slide(slide: Slide):
def test_slide_repr(slide: Slide):
assert repr(slide) == "<Slide [Slide1]>"


def test_presentations_parent(prs: Presentations):
assert prs.api.Parent.__class__.__name__ == "_Application"
assert prs.parent.__class__.__name__ == "PowerPoint"


def test_presentation_parent(pr: Presentation, prs: Presentations):
assert pr.api.Parent.__class__.__name__ == "Presentations"
assert pr.parent.__class__.__name__ == "Presentations"
assert prs(1).parent.__class__.__name__ == "Presentations"


def test_slides_parent(slides: Slides):
assert slides.api.Parent.__class__.__name__ == "_Presentation"
assert slides.parent.__class__.__name__ == "Presentation"


def test_slide_parent(slide: Slide, slides: Slides):
assert slide.api.Parent.__class__.__name__ == "_Presentation"
assert slide.parent.__class__.__name__ == "Presentation"
assert slides(1).parent.__class__.__name__ == "Presentation"
13 changes: 13 additions & 0 deletions tests/test_lines.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from pptxlib.tables import Cell, CellRange


def test_cell_border(cell: Cell):
line_format = cell.borders("left")
assert line_format.api.__class__.__name__ == "LineFormat"
assert line_format.__class__.__name__ == "LineFormat"


def test_cell_border_parent(cell: Cell):
line_format = cell.borders("left")
assert line_format.api.Parent.__class__.__name__ == "Borders"
assert line_format.parent.__class__.__name__ == "Borders"
Loading

0 comments on commit 4e1eb86

Please sign in to comment.