How to set manually chart_title position?
Hello,
I've been using python-pptx 1.0.2 for several weeks now and find it very helpful. I've been able to do or find workarounds to everything I need to do except set manually the position of a chart title. I would like to align it to the left of the chart object.
I was trying to use something like the layout attribute that I found here: https://stackoverflow.com/questions/48325832/custom-legend-position-with-python-pptx but the chart_title element doesn't appear to have this property as I get the error: AttributeError: 'CT_Title' object has no attribute 'get_or_add_layout'.
Does anyone have any suggestions on a workaround?
Thanks, Matthew
@rileym99 the general gist is:
- figure out if PowerPoint can do it and then how how it does it. This involves using the PowerPoint UI to do it and inspecting before and after XML.
- Manipulate the XML in your document to produce the necessary elements and attributes using the best tools available.
If there is an existing property or method like .get_or_add_layout() that's of course the first choice. Where those are not available you'll need to use lower-level lxml methods to achieve the XML changes.
If you have a search around on "python-pptx workaround function" as a start you should be able to find ways folks have done that. You may want to dig into the internals that python-pptx itself uses to make methods like get_or_add_layout() like OxmlElement() and so on, many of which are in oxml/xmlchemy.py and nearby.
Thank you for your help @scanny - I've been able to implement this. I've put some sample code below in case anybody else has this request.
from pptx.oxml.xmlchemy import OxmlElement
# start with an existing python-pptx chart object
chart_title = chart.chart_title
__set_chart_title_position(chart_title, 0, 0.025) # 0.025 seems to match the default position
def __set_chart_title_position(title, x, y):
"""
Manually sets the chart title position
:param title: a chart_title object
:param x: value between 0 and 1 as a proportion of the chart width, from top left corner
:param y: value between 0 and 1 as a proportion of the chart height, from top left corner
"""
# Remove the existing layout
cte = title._element
cte = __remove_xml_element(cte, 'c:layout')
# Inside layout, add manualLayout
L = __add_xml_element(cte, 'c:layout')
mL = __add_xml_element(L, 'c:manualLayout')
# Add xMode and yMode and set vals to edge and x, y co-ordinates from top left corner
xM = __add_xml_element(mL, 'c:xMode', val="edge")
xY = __add_xml_element(mL, 'c:yMode', val="edge")
xE = __add_xml_element(mL, 'c:x', val=str(x))
yE = __add_xml_element(mL, 'c:y', val=str(y))
def __remove_xml_element(parent, tag_name, **kwargs):
"""
Removes an xml element from a parent xml element
:param parent: the parent element from which to remove the element
:tag_name: the xml tag names, e.g. c:layout]
Output: a reference to the parent element
"""
tag = tag_name.split(":")[-1]
for child in parent.getchildren():
if tag in str(child.tag):
parent.remove(child)
return parent
def __add_xml_element(parent, tag_name, **kwargs):
"""
Adds a new xml element to the presentation
:param parent: the parent element to which to add the new element
:tag_name: the xml tag names, e.g. c:layout
Output: a reference to the newly created element
"""
element = OxmlElement(tag_name)
element.attrib.update(kwargs)
parent.append(element)
return element