Thanks for the rpptx binding round (#141, #142): the deck can now be edited from Python end to end. One
behaviour makes it awkward: after run.text = ..., every handle to the presentation's slides, shapes,
paragraphs and runs raises StaleElementError on next use, including the shape the run belongs to. Setting a
font, a paragraph property, a shape's position or a text frame inset leaves them valid, and in rdocx
Run.text leaves paragraph and run handles valid too. A loop that rewrites the runs of one text box has to
re-navigate from Presentation after every run.
import rpptx
from pptx import Presentation
from pptx.util import Inches
p = Presentation(); s = p.slides.add_slide(p.slide_layouts[6])
tf = s.shapes.add_textbox(Inches(1), Inches(1), Inches(4), Inches(1)).text_frame
tf.paragraphs[0].add_run().text = "alpha "; tf.paragraphs[0].add_run().text = "beta"
p.save("rs.pptx")
def fresh():
prs = rpptx.Presentation("rs.pptx"); slide = prs.slides[0]; shape = slide.shapes[0]
return prs, slide, shape
mutations = {
"Run.text = ...": lambda prs, sl, sh: setattr(sh.text_frame.paragraphs[0].runs[0], "text", "ALPHA "),
"Run.font.italic = True": lambda prs, sl, sh: setattr(sh.text_frame.paragraphs[0].runs[0].font, "italic", True),
"Paragraph.space_after = ...": lambda prs, sl, sh: setattr(sh.text_frame.paragraphs[0], "space_after", 12700),
"Shape.left = ...": lambda prs, sl, sh: setattr(sh, "left", sh.left + 12700),
"TextFrame.margin_left = 0": lambda prs, sl, sh: setattr(sh.text_frame, "margin_left", 0),
}
for label, fn in mutations.items():
prs, slide, shape = fresh()
run = shape.text_frame.paragraphs[0].runs[1]
fn(prs, slide, shape)
out = []
for name, probe in (("same shape", lambda: shape.left), ("same slide", lambda: len(slide.shapes)),
("second run", lambda: run.text)):
try:
probe(); out.append(f"{name}: valid")
except Exception as e:
out.append(f"{name}: {type(e).__name__}")
print(f"{label:<30} " + " | ".join(out))
Run.text = ... same shape: StaleElementError | same slide: StaleElementError | second run: StaleElementError
Run.font.italic = True same shape: valid | same slide: valid | second run: valid
Paragraph.space_after = ... same shape: valid | same slide: valid | second run: valid
Shape.left = ... same shape: valid | same slide: valid | second run: valid
TextFrame.margin_left = 0 same shape: valid | same slide: valid | second run: valid
The README documents stale handles "after structural changes"; replacing the text of an existing run does
not change the structure: TextRunMut::set_text (rpptx/src/lib.rs:7189) only sets the run's text, and
the revision is bumped by the binding (rpptx-py/src/text.rs:886). PR #149 records it ("a later
run.text = ... also advances the revision") next to the rule that in-place setters do not advance it. Could the setter keep handles valid, as
the font setters do?
Environment: main at 9a7ed714 (S75), release build, linux x86_64, python-pptx 1.0.2 for the fixture.
Thanks for the rpptx binding round (#141, #142): the deck can now be edited from Python end to end. One
behaviour makes it awkward: after
run.text = ..., every handle to the presentation's slides, shapes,paragraphs and runs raises
StaleElementErroron next use, including the shape the run belongs to. Setting afont, a paragraph property, a shape's position or a text frame inset leaves them valid, and in rdocx
Run.textleaves paragraph and run handles valid too. A loop that rewrites the runs of one text box has tore-navigate from
Presentationafter every run.The README documents stale handles "after structural changes"; replacing the text of an existing run does
not change the structure:
TextRunMut::set_text(rpptx/src/lib.rs:7189) only sets the run's text, andthe revision is bumped by the binding (
rpptx-py/src/text.rs:886). PR #149 records it ("a laterrun.text = ...also advances the revision") next to the rule that in-place setters do not advance it. Could the setter keep handles valid, asthe font setters do?
Environment:
mainat9a7ed714(S75), release build, linux x86_64, python-pptx 1.0.2 for the fixture.