Skip to content

Commit eca907e

Browse files
author
Steve Canny
committed
tbl: add _Body.add_table()
1 parent 09a4e9b commit eca907e

File tree

9 files changed

+133
-28
lines changed

9 files changed

+133
-28
lines changed

docx/oxml/parts.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"""
66

77
from docx.oxml.shared import OxmlBaseElement, qn
8+
from docx.oxml.table import CT_Tbl
89
from docx.oxml.text import CT_P
910

1011

@@ -30,6 +31,14 @@ def add_p(self):
3031
p = CT_P.new()
3132
return self._append_blocklevelelt(p)
3233

34+
def add_tbl(self):
35+
"""
36+
Return a new <w:tbl> element that has been added at the end of any
37+
existing body content.
38+
"""
39+
tbl = CT_Tbl.new()
40+
return self._append_blocklevelelt(tbl)
41+
3342
def clear_content(self):
3443
"""
3544
Remove all content child elements from this <w:body> element. Leave

docx/oxml/table.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,19 @@ def add_tr(self):
5151
tr = CT_Row.new()
5252
return self._append_tr(tr)
5353

54+
@classmethod
55+
def new(cls):
56+
"""
57+
Return a new ``<w:tbl>`` element, containing the required
58+
``<w:tblPr>`` and ``<w:tblGrid>`` child elements.
59+
"""
60+
tbl = OxmlElement('w:tbl')
61+
tblPr = CT_TblPr.new()
62+
tbl.append(tblPr)
63+
tblGrid = CT_TblGrid.new()
64+
tbl.append(tblGrid)
65+
return tbl
66+
5467
@property
5568
def tblGrid(self):
5669
tblGrid = self.find(qn('w:tblGrid'))
@@ -95,6 +108,13 @@ def gridCol_lst(self):
95108
"""
96109
return self.findall(qn('w:gridCol'))
97110

111+
@classmethod
112+
def new(cls):
113+
"""
114+
Return a new ``<w:tblGrid>`` element.
115+
"""
116+
return OxmlElement('w:tblGrid')
117+
98118
def _append_gridCol(self, gridCol):
99119
"""
100120
Return *gridCol* after appending it to end of gridCol sequence.
@@ -131,6 +151,19 @@ def new(cls):
131151
return OxmlElement('w:gridCol')
132152

133153

154+
class CT_TblPr(OxmlBaseElement):
155+
"""
156+
``<w:tblPr>`` element, child of ``<w:tbl>``, holds child elements that
157+
define table properties such as style and borders.
158+
"""
159+
@classmethod
160+
def new(cls):
161+
"""
162+
Return a new ``<w:tblPr>`` element.
163+
"""
164+
return OxmlElement('w:tblPr')
165+
166+
134167
class CT_Tc(OxmlBaseElement):
135168
"""
136169
``<w:tc>`` table cell element

docx/parts.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,25 @@ def __init__(self, body_elm):
4949
self._body = body_elm
5050

5151
def add_paragraph(self):
52+
"""
53+
Return a paragraph newly added to the end of body content.
54+
"""
5255
p = self._body.add_p()
5356
return Paragraph(p)
5457

58+
def add_table(self, rows, cols):
59+
"""
60+
Return a table having *rows* rows and *cols* cols, newly appended to
61+
the main document story.
62+
"""
63+
tbl = self._body.add_tbl()
64+
table = Table(tbl)
65+
for i in range(cols):
66+
table.add_column()
67+
for i in range(rows):
68+
table.add_row()
69+
return table
70+
5571
def clear_content(self):
5672
"""
5773
Return this |_Body| instance after clearing it of all content.

features/blk-add-table.feature

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ Feature: Add a table
77
Given a document containing a table
88
Then I can access the table
99

10-
@wip
1110
Scenario: Add a table
1211
Given a document
1312
When I add a table

tests/opc/test_oxml.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ class DescribeCT_Types(object):
102102

103103
def it_provides_access_to_default_child_elements(self):
104104
types = a_Types().element
105-
print(actual_xml(types))
106105
assert len(types.defaults) == 2
107106
for default in types.defaults:
108107
assert isinstance(default, CT_Default)

tests/oxml/test_parts.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ def it_can_add_a_p_to_itself(self):
2929
# exercise -----------------
3030
p = body.add_p()
3131
# verify -------------------
32-
print(body.xml)
3332
assert body.xml == after_body_bldr.xml()
3433
assert isinstance(p, CT_P)
3534

tests/oxml/test_text.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ def it_knows_its_paragraph_style(self):
3737
(a_p().with_child(pPr_bldr), 'foobar'),
3838
)
3939
for builder, expected_value in cases:
40-
print builder.with_nsdecls().xml()
4140
p = builder.with_nsdecls().element
4241
assert p.style == expected_value
4342

@@ -51,13 +50,9 @@ def it_can_set_its_paragraph_style(self):
5150
(4, a_p().with_child(pPr), 'barfoo', a_p().with_child(pPr2)),
5251
)
5352
for case_nmbr, before_bldr, new_style, after_bldr in cases:
54-
print before_bldr.with_nsdecls().xml()
55-
print 'case_nmbr, new_style => %d, %s' % (case_nmbr, new_style)
5653
p = before_bldr.with_nsdecls().element
5754
p.style = new_style
5855
expected_xml = after_bldr.with_nsdecls().xml()
59-
print expected_xml
60-
print p.xml
6156
assert p.xml == expected_xml
6257

6358

@@ -74,7 +69,6 @@ def it_knows_the_paragraph_style(self):
7469
(a_pPr().with_child(a_pStyle().with_val('foobar')), 'foobar'),
7570
)
7671
for builder, expected_value in cases:
77-
print builder.with_nsdecls().xml
7872
pPr = builder.with_nsdecls().element
7973
assert pPr.style == expected_value
8074

@@ -89,13 +83,9 @@ def it_can_set_the_paragraph_style(self):
8983
a_pPr().with_child(a_pStyle().with_val('barfoo'))),
9084
)
9185
for case_nmbr, before_bldr, new_style, after_bldr in cases:
92-
print '====\ncase %d: new_style => %s' % (case_nmbr, new_style)
93-
print 'before:\n%s' % before_bldr.with_nsdecls().xml()
9486
pPr = before_bldr.with_nsdecls().element
9587
pPr.style = new_style
9688
expected_xml = after_bldr.with_nsdecls().xml()
97-
print 'expected:\n%s' % expected_xml
98-
print 'actual:\n%s' % pPr.xml
9989
assert pPr.xml == expected_xml
10090

10191

tests/oxml/unitdata/table.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ class CT_TblGridColBuilder(BaseBuilder):
3131
__attrs__ = ('w:w',)
3232

3333

34+
class CT_TblPrBuilder(BaseBuilder):
35+
__tag__ = 'w:tblPr'
36+
__nspfxs__ = ('w',)
37+
__attrs__ = ()
38+
39+
3440
class CT_TcBuilder(BaseBuilder):
3541
__tag__ = 'w:tc'
3642
__nspfxs__ = ('w',)
@@ -49,6 +55,10 @@ def a_tblGrid():
4955
return CT_TblGridBuilder()
5056

5157

58+
def a_tblPr():
59+
return CT_TblPrBuilder()
60+
61+
5262
def a_tc():
5363
return CT_TcBuilder()
5464

tests/test_parts.py

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
from docx.text import Paragraph
1616

1717
from .oxml.unitdata.parts import a_body
18-
from .oxml.unitdata.table import a_tbl
18+
from .oxml.unitdata.table import (
19+
a_gridCol, a_tbl, a_tblGrid, a_tblPr, a_tc, a_tr
20+
)
1921
from .oxml.unitdata.text import a_p, a_sectPr
2022
from .unitutil import class_mock, function_mock, initializer_mock
2123

@@ -80,12 +82,18 @@ def serialize_part_xml_(self, request):
8082

8183
class Describe_Body(object):
8284

83-
def it_can_add_a_paragraph_to_itself(self, add_paragraph_fixture):
85+
def it_can_add_a_paragraph(self, add_paragraph_fixture):
8486
body, expected_xml = add_paragraph_fixture
8587
p = body.add_paragraph()
8688
assert body._body.xml == expected_xml
8789
assert isinstance(p, Paragraph)
8890

91+
def it_can_add_a_table(self, add_table_fixture):
92+
body, expected_xml = add_table_fixture
93+
table = body.add_table(rows=1, cols=1)
94+
assert body._body.xml == expected_xml
95+
assert isinstance(table, Table)
96+
8997
def it_can_clear_itself_of_all_content_it_holds(
9098
self, clear_content_fixture):
9199
body, expected_xml = clear_content_fixture
@@ -112,28 +120,34 @@ def it_provides_access_to_the_tables_it_contains(
112120
# fixtures -------------------------------------------------------
113121

114122
@pytest.fixture(params=[
115-
(False, False), (True, False), (False, True), (True, True)
123+
(0, False), (1, False), (0, True), (1, True)
116124
])
117125
def add_paragraph_fixture(self, request):
118-
has_p, has_sectPr = request.param
126+
p_count, has_sectPr = request.param
119127
# body element -----------------
120-
body_bldr = a_body().with_nsdecls()
121-
if has_p:
122-
body_bldr.with_child(a_p())
123-
if has_sectPr:
124-
body_bldr.with_child(a_sectPr())
128+
body_bldr = self._body_bldr(p_count=p_count, sectPr=has_sectPr)
125129
body_elm = body_bldr.element
126130
body = _Body(body_elm)
127131
# expected XML -----------------
128-
body_bldr = a_body().with_nsdecls()
129-
if has_p:
130-
body_bldr.with_child(a_p())
131-
body_bldr.with_child(a_p())
132-
if has_sectPr:
133-
body_bldr.with_child(a_sectPr())
132+
p_count += 1
133+
body_bldr = self._body_bldr(p_count=p_count, sectPr=has_sectPr)
134134
expected_xml = body_bldr.xml()
135135
return body, expected_xml
136136

137+
@pytest.fixture(params=[(0, False), (0, True), (1, False), (1, True)])
138+
def add_table_fixture(self, request):
139+
p_count, has_sectPr = request.param
140+
body_bldr = self._body_bldr(p_count=p_count, sectPr=has_sectPr)
141+
body = _Body(body_bldr.element)
142+
143+
tbl_bldr = self._tbl_bldr()
144+
body_bldr = self._body_bldr(
145+
p_count=p_count, tbl_bldr=tbl_bldr, sectPr=has_sectPr
146+
)
147+
expected_xml = body_bldr.xml()
148+
149+
return body, expected_xml
150+
137151
@pytest.fixture
138152
def body_with_paragraphs(self):
139153
body_elm = (
@@ -170,3 +184,39 @@ def clear_content_fixture(self, request):
170184
body_bldr.with_child(a_sectPr())
171185
expected_xml = body_bldr.xml()
172186
return body, expected_xml
187+
188+
def _body_bldr(self, p_count=0, tbl_bldr=None, sectPr=False):
189+
body_bldr = a_body().with_nsdecls()
190+
for i in range(p_count):
191+
body_bldr.with_child(a_p())
192+
if tbl_bldr is not None:
193+
body_bldr.with_child(tbl_bldr)
194+
if sectPr:
195+
body_bldr.with_child(a_sectPr())
196+
return body_bldr
197+
198+
def _tbl_bldr(self, rows=1, cols=1):
199+
tblPr_bldr = a_tblPr()
200+
201+
tblGrid_bldr = a_tblGrid()
202+
for i in range(cols):
203+
tblGrid_bldr.with_child(a_gridCol())
204+
205+
tbl_bldr = a_tbl()
206+
tbl_bldr.with_child(tblPr_bldr)
207+
tbl_bldr.with_child(tblGrid_bldr)
208+
for i in range(rows):
209+
tr_bldr = self._tr_bldr(cols)
210+
tbl_bldr.with_child(tr_bldr)
211+
212+
return tbl_bldr
213+
214+
def _tc_bldr(self):
215+
return a_tc().with_child(a_p())
216+
217+
def _tr_bldr(self, cols):
218+
tr_bldr = a_tr()
219+
for i in range(cols):
220+
tc_bldr = self._tc_bldr()
221+
tr_bldr.with_child(tc_bldr)
222+
return tr_bldr

0 commit comments

Comments
 (0)