python-pptx duplicate slide PPT will be damaged - python

I found that when using the method of duplicate slide, if there is a chart on the page, PPT will be damaged, so I used this method to copy a slide with a chart and modify the title of the chart on one page, and the title of the chart on the other page is also modified inexplicably
def duplicate_slide(pres,index):
template = pres.slides[index]
blank_slide_layout = pres.slide_layouts[index]
copied_slide = pres.slides.add_slide(blank_slide_layout)
for shp in template.shapes:
el = shp.element
newel = copy.deepcopy(el)
copied_slide.shapes._spTree.insert_element_before(newel, 'p:extLst')
for _, value in six.iteritems(template.part.rels):
# Make sure we don't copy a notesSlide relation as that won't exist
if "notesSlide" not in value.reltype:
copied_slide.part.rels.add_relationship(
value.reltype, value._target, value.rId
)
return copied_slide

In the general case, duplicating a slide is not a simple as just cloning the slide XML, which is what your duplicate_slide() method does. That works for some simple cases, but not for slides with charts.
In particular, a chart is a separate package-part ("file") within the PPTX package (zip archive). If you just copy the relationships from one slide to the other, like you do here, then you have two slides pointing to the same chart-part. This is why changing the chart title in one slide changes it in the other as well, because the same single chart is displayed on both slides.
In order to get the behavior you seem to be looking for, you would need to also duplicate the chart part and form a relationship from the new slide to that new chart part.
That's not a simple enough process for me to just provide here a few lines of code to do it, but hopefully this explains for you why you are seeing the behavior you are.

Related

Coloring checkbuttons text in matplotlib

I need to add a checkbox to a python chart, to check the lines to be displayed or hidden.
I found the code below that works just fine, except one detail, that is annoyingly difficult for me to find out, I already have a few weeks since I am trying to do that: besides the checkboxes and the series names, I would like to also have the line colors along to the text, to be able to visually identify which line is which series.
Initially I tried to show the legend over the text, next to the checkboxes, but the legend would move if the window is resized, and that would not be feasible to implement.
My feeling is that there should be a way to add the line colors display within the function that creates the checkboxes, can anyone give me some suggestions on how to do that? The function is:
def func(label):
index = labels.index(label)
lines[index].set_visible(not lines[index].get_visible())
plt.draw()
The link with the full code is:
https://matplotlib.org/3.1.0/gallery/widgets/check_buttons.html
for idx, text in enumerate(check.labels):
text.set_color(lines[idx].get_color())
Add this bit of code after instantiating CheckButtons. It will color the text like you want it to.
Old, worse solution:
labels = [str(line.get_label()) + ", " + str(line.get_color()) for line in lines]

Adding a border to PDF from ArcMap export using Arcpy

I'm trying to add a border to a PDF exported from ArcMap, using arcpy. I've not been able to find the answer to this anywhere, nor does arcpy seem to have any documentation on this.
Oddly enough, the map layout from which I'm exporting already has a black border around it, but when I export to a PDF, there is no border. My code here:
#Export to PDF
currentMXD_Map = (r"myMap.mxd")
mxd_Map = arcpy.mapping.MapDocument(currentMXD_Map)
df_Map = arcpy.mapping.ListDataFrames(mxd_Map,"*")[0]
arcpy.mapping.ExportToPDF(mxd_Map, r"myMap.pdf", df_Map,
df_export_width=3300,
df_export_height=2550)
mxd_Map.save()
I would think arcpy.mapping has a method to add border to a PDF export (or in the map layout). What can I try next?
arcpy is not designed for map or layout authoring. It is designed to manipulate existing layouts or maps. Here's a quote from the documentation
The arcpy.mapping module was designed so that it can be used to modify
existing elements within already existing map documents (.mxd) or
layer files (.lyr). In other words, it helps with the automation of
existing features but it can't be used to author new objects.
The easiest way to "add" a border is to have a border already in your map layout with size set to 0 or positioned off screen and then to use arcpy to make it visible or move it where you want. It seems you already have the border so maybe it's not in the right place or is set to 0 width.
Either way, you can access the border element by giving it a name in arcmap and then accessing with ListLayoutElements.
First fill in the "Element Name" in the elements properties in arcmap. notice how I've set the height and width to 0 so that it won't be visible normally.
Then access the element with ListLayoutElements
#we want the first border element because we are assuming there is only one.
#iterate or change index depending on your scenario
borderElement = arcpy.mapping.ListLayoutElements(mxd, "GRAPHIC_ELEMENT", "border_element")[0]
borderElement.elementHeight = y
borderElement.elementWidth = x

Rearrange powerpoint slides automatically using python-pptx

We typically use powerpoint to facilitate our experiments. We use "sections" in powerpoint to keep groups of slides together for each experimental task. Moving the sections to counterbalance the task order of the experiment has been a lot of work!
I thought we might be able to predefine a counterbalance order (using a string of numbers representing the order of the sections) in a CSV which could be read from python. Then using python-pptx move the sections and save the file for each order. The problem I am having is understanding how to read sections from the python-pptx. If anyone has a better solution than python please let me know.
Thanks for your help!
As explained in the documentation (specifically this section):
Right now, adding a slide is the only operation on the slide collection. On the backlog at the time of writing is deleting a slide and moving a slide to a different position in the list. Copying a slide from one presentation to another turns out to be pretty hard to get right in the general case, so that probably won’t come until more of the backlog is burned down.
Or in other words it currently is not possible to move slides as you have suggested. The best work around I have used is to generate a new presentation and reorder the slides into this (since you can add slides).
For instance say I have slides in Presentation1.pptx of:
[0]
[1]
[2]
[3]
[4]
But I want:
[2]
[3]
[4]
[0]
[1]
Your code (in untested pseudocode of sorts) would be:
old_presentation = Presentation("Presentation1.pptx")
new_presentation = Presentation()
for slide in old_presentation.slides[2:]:
new_slide = new_presentation.slides.add_slide() # transfer the contents into new presentation for slides [2] [3] [4]
populate_new_slide_from_old_slide(slide, new_slide)
for slide in old_presentation.slides[:2]:
new_slide = new_presentation.slides.add_slide() # transfer the contents into new presentation for slides [0] [1]
populate_new_slide_from_old_slide(slide, new_slide)
new_presentation.save()
Where populate_new_slide_from_old_slide() would look like (pretty sure this would work as is, but again I didn't test it):
def populate_new_slide_from_old_slide(slide, new_slide):
shapes_to_transfer = slide.shapes
for shape in shapes_to_transfer:
new_shape = new_slide.shapes.add_shape(shape)
I believe that placeholders are shapes too so they should be transferred via this method!
Take heed I haven't coded .pptx for a while, so the actual implementation might be slightly different. As a concept though, this is currently the only way to do what you're asking. In my opinion if you are actively generating the data (as opposed to just reorganizing it after the fact) it would probably be simpler to just make a new_presentation object and plug your data into that. It seems odd to me to keep generating output in the old format and then converting it to the new format. For instance, when DVDs came out people started putting their movies on that (the sensible option) instead of making VHS and then porting the VHS to DVD through some arbitrary method (the very peculiar option I am trying to dissuade you from).
Would it be feasible - if all we're doing is reordering - to read the XML and rewrite it with the slide elements permuted?
Further - for the "delete" case - is it feasible to simply delete a slide element in the XML? (I realise this could leave dangling objects such as images in the file.)
The process of extracting the XML and rewriting it to a copy of the file is probably not too onerous.
I found a nice solution here:
class PresentationBuilder(object):
presentation = Presentation("presentation.pptx")
def move_slide(self, old_index, new_index):
xml_slides = self.presentation.slides._sldIdLst
slides = list(xml_slides)
xml_slides.remove(slides[old_index])
xml_slides.insert(new_index, slides[old_index])
So for example if you want to move the slide that is currently in position with index 5 to position with index 1, you use:
prs = PresentationBuilder()
prs.move_slide(5, 1)

openpyxl - Changing the scatterStyle of a chart to 'marker'

I use openpyxl to create scatterCharts in an .xlsx-file.
The default style of the chart is "line". I want to change this style to "marker".
http://openpyxl.readthedocs.io/en/default/charts/scatter.html
says, that it is the best way to do this by changing the style of the series.
I tried different things:
1st:
chart = openpyxl.chart.ScatterChart(scatterStyle='marker')
--> no Effect
2nd:
chart.scatterStyle = "marker"
--> no Effect, maby i have to place this line on a special place?
3rd:
series = openpyxl.chart.Series(yvalues, xvalues, title_from_data=True)
series.marker=openpyxl.chart.marker.Marker('x')
--> now i have lines with markers, so it seems, that i am on the right way. But i have not found a way to remove the lines.
I found a solution:
series.marker=openpyxl.chart.marker.Marker('x')
series.graphicalProperties.line.noFill=True
will add markers to the graph and remove the lines.
Additional information:
to find properties and methods of the objects like "series" you can use
dir(series)
to show all properties and methods of this object. There you find the "graphicalProperties"
and with
dir(series.graphicalProperties)
you can find "line" ... and so on

how to group objects in reportlab, so that they stay together across new pages

I'm generating some pdf files using reportlab. I have a certain section that is repeated. It contains of a header and a table:
Story.append(Paragraph(header_string, styleH))
Story.append(table)
How can I group the paragraph with the table (in latex I would put them into the same environment) so that in case of a page brake, the paragraph and table stay together? Currently the paragraph sometimes floats at the end of one page and the table starts on top of the next page.
You can try to put them together in a KeepTogether flowable, like so:
Story.append(KeepTogether([Paragraph(header_string, styleH), table])
However be aware that, last I checked, the implementation was not perfect and would still split up items too frequently. I know it does a good job of keeping a single flowable together that would otherwise split, like if you were to say:
Story.append(KeepTogether(Paragraph(header_string, styleH))
then that paragraph would not get split unless it was impossible for it not to be.
If KeepTogether doesn't work for you, I'd suggest creating a custom Flowable with your paragraph and table inside it and then during layout make sure your custom Flowable subclass does not allow itself to be split up.
this is the solution that I found going through the reportlab source code:
paragraph = Paragraph(header_string, styleH)
paragraph.keepWithNext = True
Story.append(paragraph)
Story.append(table)
Using a ParagraphStyle might actually be better so i figured i'd add it to this super old answer.
Found this in their changelog after seeing #memyself's answer.
* `KeepWithNext` improved:
Paragraph styles have long had an attribute keepWithNext, but this was
buggy when set to True. We believe this is fixed now. keepWithNext is important
for widows and orphans control; you typically set it to True on headings, to
ensure at least one paragraph appears after the heading and that you don't get
headings alone at the bottom of a column.
header = ParagraphStyle(name='Heading1', parent=normal, fontSize=14, leading=19,
spaceAfter=6, keepWithNext=1)

Categories