How can I copy slide?
I created a template slide and I need to copy it and edit shapes of each copy separately.
Or how I can add my template slide to presentation.slide_layouts?
This is what I found on GitHub, and it works for me. I did change a couple of things for my project. You will need to import six and copy. I am using pptx-6.10
def duplicate_slide(pres, index):
template = pres.slides[index]
try:
blank_slide_layout = pres.slide_layouts[12]
except:
blank_slide_layout = pres.slide_layouts[len(pres.slide_layouts)]
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
Then you can create the copy with passing in your presentation and the slide index of your template:
copied_slide = duplicate_slide(pres, 4)
I am still working on editing the shapes from the copied slide, once I am further along in my project I can update
I wanted to present my workaround to copy slides. I use a template ppt and populate it. I know before populating the slides which slides of the template need to be copied and how often. What I then do is copying the slides and saving the new ppt with the copied slides. After saving I can open the ppt with the copied slides and use pptx to populate the slides.
import win32com.client
ppt_instance = win32com.client.Dispatch('PowerPoint.Application')
#open the powerpoint presentation headless in background
read_only = True
has_title = False
window = False
prs = ppt_instance.Presentations.open('path/ppt.pptx',read_only,has_title,window)
nr_slide = 1
insert_index = 1
prs.Slides(nr_slide).Copy()
prs.Slides.Paste(Index=insert_index)
prs.SaveAs('path/new_ppt.pptx')
prs.Close()
#kills ppt_instance
ppt_instance.Quit()
del ppt_instance
In this case the firste slide would be copied of the presentation and inserted after the first slide of the same presentation.
Hope this helps some of you!
Since I also found another usecase for the code shared by #d_bergeron, I just wanted to share it here.
In my case, I wanted to copy a slide from another presentation into the one I generated with python-pptx:
As argument I pass in the Presentation() object I created using python-pptx (prs = Presenation()).
from pptx import Presentation
import copy
def copy_slide_from_external_prs(prs):
# copy from external presentation all objects into the existing presentation
external_pres = Presentation("PATH/TO/PRES/TO/IMPORT/from.pptx")
# specify the slide you want to copy the contents from
ext_slide = external_pres.slides[0]
# Define the layout you want to use from your generated pptx
SLD_LAYOUT = 5
slide_layout = prs.slide_layouts[SLD_LAYOUT]
# create now slide, to copy contents to
curr_slide = prs.slides.add_slide(slide_layout)
# now copy contents from external slide, but do not copy slide properties
# e.g. slide layouts, etc., because these would produce errors, as diplicate
# entries might be generated
for shp in ext_slide.shapes:
el = shp.element
newel = copy.deepcopy(el)
curr_slide.shapes._spTree.insert_element_before(newel, 'p:extLst')
return prs
I am mainly posting it here, since I was looking for a way to copy an external slide into my presentation and ended up in this thread.
I was using n00by0815's answer and it worked great until I had to copy images. Here is my adapted version that handles images. This code creates a local copy of the image then adds it to the slide. I'm sure there's a cleaner way, but this works.
Here is another way to copy each slide onto a single PPTX slide for an entire presentation, and then you can use LibreOffice to convert each individual powerpoint into an image:
def get_slide_count(prs):
""" Get the number of slides in PPTX presentation """
slidecount = 0
for slide in prs.slides:
slidecount += 1
return slidecount
def delete_slide(prs, slide):
""" Delete a slide out of a powerpoint presentation"""
id_dict = { slide.id: [i, slide.rId] for i,slide in enumerate(prs.slides._sldIdLst) }
slide_id = slide.slide_id
prs.part.drop_rel(id_dict[slide_id][1])
del prs.slides._sldIdLst[id_dict[slide_id][0]]
def get_single_slide_pres(prs, slidetokeep):
for idx, slide in enumerate(prs.slides):
if idx < slidetokeep:
delete_slide(prs, slide)
elif (idx > slidetokeep):
delete_slide(prs, slide)
prs.save(str(slidetokeep + 1) + ".pptx")
pptxfilepath = "test.pptx"
prs = Presentation(pptxfilepath)
slidecount = get_slide_count(prs)
for i in range(slidecount):
prs_backup = Presentation(pptxfilepath)
get_single_slide_pres(prs_backup, i)
prs_backup = None
I edited #n00by0815 solution and came up with very elegant code, which also can copy images without errors:
# ATTENTNION: PPTX PACKAGE RUNS ONLY ON CERTAINS VERSION OF PYTHON (https://python-pptx.readthedocs.io/en/latest/user/install.html)
from pptx import Presentation
from pptx.util import Pt
from pptx.enum.text import PP_ALIGN
import copy
import os
DIR_PATH = os.path.dirname(os.path.realpath(__file__))
#modeled on https://stackoverflow.com/a/56074651/20159015 and https://stackoverflow.com/a/62921848/20159015
#this for some reason doesnt copy text properties (font size, alignment etc.)
def SlideCopyFromPasteInto(copyFromPres, slideIndex, pasteIntoPres):
# specify the slide you want to copy the contents from
slide_to_copy = copyFromPres.slides[slideIndex]
# Define the layout you want to use from your generated pptx
slide_layout = pasteIntoPres.slide_layouts.get_by_name("Blank") # names of layouts can be found here under step 3: https://www.geeksforgeeks.org/how-to-change-slide-layout-in-ms-powerpoint/
# it is important for slide_layout to be blank since you dont want these "Write your title here" or something like that textboxes
# alternative: slide_layout = pasteIntoPres.slide_layouts[copyFromPres.slide_layouts.index(slide_to_copy.slide_layout)]
# create now slide, to copy contents to
new_slide = pasteIntoPres.slides.add_slide(slide_layout)
# create images dict
imgDict = {}
# now copy contents from external slide, but do not copy slide properties
# e.g. slide layouts, etc., because these would produce errors, as diplicate
# entries might be generated
for shp in slide_to_copy.shapes:
if 'Picture' in shp.name:
# save image
with open(shp.name+'.jpg', 'wb') as f:
f.write(shp.image.blob)
# add image to dict
imgDict[shp.name+'.jpg'] = [shp.left, shp.top, shp.width, shp.height]
else:
# create copy of elem
el = shp.element
newel = copy.deepcopy(el)
# add elem to shape tree
new_slide.shapes._spTree.insert_element_before(newel, 'p:extLst')
# things added first will be covered by things added last => since I want pictures to be in foreground, I will add them after others elements
# you can change this if you want
# add pictures
for k, v in imgDict.items():
new_slide.shapes.add_picture(k, v[0], v[1], v[2], v[3])
os.remove(k)
return new_slide # this returns slide so you can instantly work with it when it is pasted in presentation
templatePres = Presentation(f"{DIR_PATH}/template.pptx")
outputPres = Presentation()
outputPres.slide_height, outputPres.slide_width = templatePres.slide_height, templatePres.slide_width
# this can sometimes cause problems. Alternative:
# outputPres = Presentation(f"{DIR_PATH}/template.pptx") and now delete all slides to have empty presentation
# if you just want to copy and paste slide:
SlideCopyFromPasteInto(templatePres,0,outputPres)
# if you want to edit slide that was just pasted in presentation:
pastedSlide = SlideCopyFromPasteInto(templatePres,0,outputPres)
pastedSlide.shapes.title.text = "My very cool title"
for shape in pastedSlide.shapes:
if not(shape.has_text_frame): continue
# easiest ways to edit text fields is to put some identifying text in them
if shape.text_frame.text == "personName": # there is a text field with "personName" written into it
shape.text_frame.text = "Brian"
if shape.text_frame.text == "personSalary":
shape.text_frame.text = str(brianSalary)
# stylizing text need to be done after you change it
shape.text_frame.paragraphs[0].font.size = Pt(80)
shape.text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER
outputPres.save(f'{DIR_PATH}/output.pptx')
Sorry for the delay, I was moved to another project. I was able to complete my ppt project using multiple template slides and copying them. At the end of building the presentation I delete the templates. To grab the shapes you will need to iterate through the slide.shapes and find the name of the shape that you are looking for. Once you have this returned you can then edit the shape as needed. I have added a version of the add_text function that I use to populate shape.text_frame.
def find_shape_by_name(shapes, name):
for shape in shapes:
if shape.name == name:
return shape
return None
def add_text(shape, text, alignment=None):
if alignment:
shape.vertical_anchor = alignment
tf = shape.text_frame
tf.clear()
run = tf.paragraphs[0].add_run()
run.text = text if text else ''
To find the shape "slide_title".
slide_title = find_shape_by_name(slide.shapes,'slide_title')
To add text to the shape.
add_text(slide_title,'TEST SLIDE')
Please let me know if you need any other assistance.
Related
I have written a python program to take images picked by the user and insert them into a word document. For each image, the program with create a 1x2 table. The top cell will be the image and the bottom cell will be the image name.
My issue is that when running the program, the created tables don't autofit the images. It leaves the image at full scale cutting off most of the image. (see image below)
What my Program does
If you just run word on it's own and create a 1x2 table and insert an image it will automatically set the scale of the image to fit the whole image. This is what I would like my program to do. (see image below)
What I want my program to do
I am using the docx python library. See below for Libraries used
Libraries
I found this article:
https://python-docx.readthedocs.io/en/latest/dev/analysis/features/table/table-props.html
and tried "table.allow_autofit = True" but this did not work. See code below
Create Table
I can manually set the image size as seen commented out in my code, but I would like to not have to do this.
EDIT-
Below is my entire function that I need help with. Please let me know if you need anymore. I didn't post all the code because it's a little long. I am using tkinter to ask the user to enter a title and select the files they would like to import.
def auto_gen():
doc = docx.Document()
section = doc.sections[0]
header = section.header
header_para = header.paragraphs[0]
header_para.style = doc.styles.add_style('Style Name', WD_STYLE_TYPE.PARAGRAPH)
font = header_para.style.font
font.size = Pt(20)
font.bold = True
header_para.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
header_para.text = eid.get()
f = filedialog.askopenfilenames()
for x in f:
caption = os.path.basename(x)
f_name, f_ext = os.path.splitext(caption)
table = doc.add_table(rows=2, cols=1)
table.style = 'Table Grid'
run1 = table.cell(0,0).paragraphs[0].add_run()
run2p = table.cell(1,0).paragraphs[0]
run2 = table.cell(1,0).paragraphs[0].add_run()
run1.add_picture(x)#, height = Inches(3.38), width = Inches(6))
run2.text = (f_name)
run2p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.add_paragraph()
output = eid.get() + '.docx'
doc.save(output)
I've been using python and reportlab to auto-generate long documents and want to use the PDF outline tree for easy navigation through the document. According to the docs, canvas.bookmarkPage comes with multiple options to adjust the document view after jumping to the destination page. The standard one is a simple page Fit to the window. From a user perspective, I would prefer FitH (as wide as possible, with destination at the top of the screen) or XYZ (keep user zoom level with destination at the top of the screen). When using any fit option except the basic Fit, the function call must be provided with the coordinates to arrange the view accordingly.
However, I could not find any explanations, examples, code snippets, or anything on how to figure this out, and it took me a good while to come up with a solution. So, I want to share this solution here and ask if this is really the best way to do it or if I overlooked something basic.
The key thing here is SmartParagraph which remembers its position after it was drawn. First, I used flowable.canv.absolutePosition(0,0) in the afterFlowable() method because this is where I needed this information to pass it to bookmarkPage(). However, the position was always reported as 0, 0, so apparently the flowable and/or the canvas have forgotten everything about the position when afterFlowable() is reached. So I thought there has to be some point in time when a Flowable knows its position and after investigating the source code I found out that after draw(), it still knows where it is.
So: SmartParagraph is a subclass of Paragraph that stores its position after it is drawn, so that later in the document building process this can be used by any external element for whatever.
The example will create a dummy pdf with 2 headings that do a nice FitH zoom and two headings that do the basic Fit zoom.
Does anyone have a better idea on how to solve this?
import typing
from reportlab.lib.styles import ParagraphStyle as PS
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus.flowables import Flowable
from reportlab.platypus import PageBreak, Spacer
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.doctemplate import SimpleDocTemplate
from reportlab.lib.units import cm
class SmartParagraph(Paragraph):
def __init__(self, text, *args, **kwds):
"""This paragraph remembers its position on the canvas"""
super(SmartParagraph, self).__init__(text, *args, **kwds)
self._pos: typing.Tuple[int, int] = None
def draw(self):
super(SmartParagraph, self).draw()
self._pos = self.canv.absolutePosition(0, 0)
def get_pos(self) -> typing.Tuple[int, int]:
return self._pos
class CustomDocTemplate(SimpleDocTemplate):
def __init__(self, filename, outline_levels: int = 4, **kwargs):
super(CustomDocTemplate, self).__init__(filename, **kwargs)
self._bookmark_keys = list()
if not isinstance(outline_levels, int) and outline_levels < 1:
raise ValueError("Outline levels must be integer and at least 1")
self._outline_levels = {f'Heading{level+1}': level for level in range(outline_levels)}
# Map of kind: Heading1 -> 0
# Heading 1 is level 0, I dont make the rules
def afterFlowable(self, flowable: Flowable):
"""Registers TOC entries."""
if isinstance(flowable, Paragraph):
flowable: Paragraph
text = flowable.getPlainText()
style = flowable.style.name
if style in self._outline_levels:
level = self._outline_levels[style]
else:
return
if text not in self._bookmark_keys:
key = text
self._bookmark_keys.append(key)
else:
# There might headings with identical text, yet they need a different key
# Keys are stored in a list and incremented if a duplicate is found
cnt = 1
while True:
key = text + str(cnt)
if key not in self._bookmark_keys:
self._bookmark_keys.append(key)
break
cnt += 1
if isinstance(flowable, SmartParagraph):
# Only smart paragraphs know their own position
x, y = flowable.get_pos()
y += flowable.style.fontSize + 15
self.canv.bookmarkPage(key, fit="FitH", top=y)
else:
# Dumb paragraphs need to show the whole page
self.canv.bookmarkPage(key)
self.canv.addOutlineEntry(title=text, key=key, level=level)
def _endBuild(self):
"""Override of parent function. Shows outline tree by default when opening PDF."""
super(CustomDocTemplate, self)._endBuild()
self.canv.showOutline()
story = list()
story.append(SmartParagraph('First Smart Heading', getSampleStyleSheet()['h1']))
story.append(Paragraph('Text in first heading'))
story.append(Spacer(1, 0.5 * cm))
story.append(SmartParagraph('First Sub Smart Heading', getSampleStyleSheet()['h2']))
story.append(Paragraph('Text in first sub heading'))
story.append(Spacer(1, 0.5 * cm))
story.append(Paragraph('Second Sub Dumb Heading', getSampleStyleSheet()['h2']))
story.append(Paragraph('Text in second sub heading'))
story.append(PageBreak())
story.append(Paragraph('Last Dumb Heading', getSampleStyleSheet()['h1']))
story.append(Paragraph('Text in last heading', PS('body')))
doc = CustomDocTemplate('mintoc.pdf')
doc.multiBuild(story)
I'm writing a script in python using Open Cascade Technology (using the pyOCCT package for Anaconda) to import STEP files, defeature them procedurally and re-export them. I want to preserve the product hierarchy, names and colours as much as possible. Currently the script can import STEP files, simplify all of the geometry while roughly preserving the hierarchy and re-export the step file. The problem is no matter how I approach the problem, I can't manage to make it preserve the colours of the STEP file in a few particular cases.
Here's the model I pass in to the script:
And here's the result of the simplification:
In this case, the simplification has worked correctly but the colours of some of the bodies were not preserved. The common thread is that the bodies that loose their colours are children of products which only have other bodies as their children (ie: they don't contain sub-products).
This seems to be related to the way that Open Cascade imports STEP files which are translated as follows:
Alright, now for some code:
from OCCT.STEPControl import STEPControl_Reader, STEPControl_Writer, STEPControl_AsIs
from OCCT.BRepAlgoAPI import BRepAlgoAPI_Defeaturing
from OCCT.TopAbs import TopAbs_FACE, TopAbs_SHAPE, TopAbs_COMPOUND
from OCCT.TopExp import TopExp_Explorer
from OCCT.ShapeFix import ShapeFix_Shape
from OCCT.GProp import GProp_GProps
from OCCT.BRepGProp import BRepGProp
from OCCT.TopoDS import TopoDS
from OCCT.TopTools import TopTools_ListOfShape
from OCCT.BRep import BRep_Tool
from OCCT.Quantity import Quantity_ColorRGBA
from OCCT.ShapeBuild import ShapeBuild_ReShape
from OCCT.STEPCAFControl import STEPCAFControl_Reader, STEPCAFControl_Writer
from OCCT.XCAFApp import XCAFApp_Application
from OCCT.XCAFDoc import XCAFDoc_DocumentTool, XCAFDoc_ColorGen, XCAFDoc_ColorSurf
from OCCT.XmlXCAFDrivers import XmlXCAFDrivers
from OCCT.TCollection import TCollection_ExtendedString
from OCCT.TDF import TDF_LabelSequence
from OCCT.TDataStd import TDataStd_Name
from OCCT.TDocStd import TDocStd_Document
from OCCT.TNaming import TNaming_NamedShape
from OCCT.Interface import Interface_Static
# DBG
def export_step(shape, path):
writer = STEPControl_Writer()
writer.Transfer( shape, STEPControl_AsIs )
writer.Write(path)
# DBG
def print_shape_type(label, shapeTool):
if shapeTool.IsFree_(label):
print("Free")
if shapeTool.IsShape_(label):
print("Shape")
if shapeTool.IsSimpleShape_(label):
print("SimpleShape")
if shapeTool.IsReference_(label):
print("Reference")
if shapeTool.IsAssembly_(label):
print("Assembly")
if shapeTool.IsComponent_(label):
print("Component")
if shapeTool.IsCompound_(label):
print("Compound")
if shapeTool.IsSubShape_(label):
print("SubShape")
# Returns a ListOfShape containing the faces to be removed in the defeaturing
# NOTE: For concisness I've simplified this algorithm and as such it *MAY* not produce exactly
# the same output as shown in the screenshots but should still do SOME simplification
def select_faces(shape):
exp = TopExp_Explorer(shape, TopAbs_FACE)
selection = TopTools_ListOfShape()
nfaces = 0
while exp.More():
rgb = None
s = exp.Current()
exp.Next()
nfaces += 1
face = TopoDS.Face_(s)
gprops = GProp_GProps()
BRepGProp.SurfaceProperties_(face, gprops)
area = gprops.Mass()
surf = BRep_Tool.Surface_(face)
if area < 150:
selection.Append(face)
#log(f"\t\tRemoving face with area: {area}")
return selection, nfaces
# Performs the defeaturing
def simplify(shape):
defeaturer = BRepAlgoAPI_Defeaturing()
defeaturer.SetShape(shape)
sel = select_faces(shape)
if sel[0].Extent() == 0:
return shape
defeaturer.AddFacesToRemove(sel[0])
defeaturer.SetRunParallel(True)
defeaturer.SetToFillHistory(False)
defeaturer.Build()
if (not defeaturer.IsDone()):
return shape# TODO: Handle errors
return defeaturer.Shape()
# Given the label of an entity it finds it's displayed colour. If the entity has no defined colour the parents are searched for defined colours as well.
def find_color(label, colorTool):
col = Quantity_ColorRGBA()
status = False
while not status and label != None:
try:
status = colorTool.GetColor(label, XCAFDoc_ColorSurf, col)
except:
break
label = label.Father()
return (col.GetRGB().Red(), col.GetRGB().Green(), col.GetRGB().Blue(), col.Alpha(), status, col)
# Finds all child shapes and simplifies them recursively. Returns true if there were any subshapes.
# For now this assumes all shapes passed into this are translated as "SimpleShape".
# "Assembly" entities should be skipped as we don't need to touch them, "Compound" entities should work with this as well, though the behaviour is untested.
# Use the print_shape_type(shapeLabel, shapeTool) method to identify a shape.
def simplify_subshapes(shapeLabel, shapeTool, colorTool, set_colours=None):
labels = TDF_LabelSequence()
shapeTool.GetSubShapes_(shapeLabel, labels)
#print_shape_type(shapeLabel, shapeTool)
#print(f"{shapeTool.GetShape_(shapeLabel).ShapeType()}")
cols = {}
for i in range(1, labels.Length()+1):
label = labels.Value(i)
currShape = shapeTool.GetShape_(label)
print(f"\t{currShape.ShapeType()}")
if currShape.ShapeType() == TopAbs_COMPOUND:
# This code path should never be taken as far as I understand
simplify_subshapes(label, shapeTool, colorTool, set_colours)
else:
''' See the comment at the bottom of the main loop for an explanation of the function of this block
col = find_color(label, colorTool)
#print(f"{name} RGBA: {col[0]:.5f} {col[1]:.5f} {col[2]:.5f} {col[3]:.5f} defined={col[4]}")
cols[label.Tag()] = col
if set_colours != None:
colorTool.SetColor(label, set_colours[label.Tag()][5], XCAFDoc_ColorSurf)'''
# Doing both of these things seems to result in colours being reset but the geometry doesn't get replaced
nshape = simplify(currShape)
shapeTool.SetShape(label, nshape) # This doesn't work
return labels.Length() > 0, cols
# Set up XCaf Document
app = XCAFApp_Application.GetApplication_()
fmt = TCollection_ExtendedString('MDTV-XCAF')
doc = TDocStd_Document(fmt)
app.InitDocument(doc)
shapeTool = XCAFDoc_DocumentTool.ShapeTool_(doc.Main())
colorTool = XCAFDoc_DocumentTool.ColorTool_(doc.Main())
# Import the step file
reader = STEPCAFControl_Reader()
reader.SetNameMode(True)
reader.SetColorMode(True)
Interface_Static.SetIVal_("read.stepcaf.subshapes.name", 1) # Tells the importer to import subshape names
reader.ReadFile("testcolours.step")
reader.Transfer(doc)
labels = TDF_LabelSequence()
shapeTool.GetShapes(labels)
# Simplify each shape that was imported
for i in range(1, labels.Length()+1):
label = labels.Value(i)
shape = shapeTool.GetShape_(label)
# Assemblies are just made of other shapes, so we'll skip this and simplify them individually...
if shapeTool.IsAssembly_(label):
continue
# This function call here is meant to be the fix for the bug described.
# The idea was to check if the TopoDS_Shape we're looking at is a COMPOUND and if so we would simplify and call SetShape()
# on each of the sub-shapes instead in an attempt to preserve the colours stored in the sub-shape's labels.
#status, loadedCols = simplify_subshapes(label, shapeTool, colorTool)
#if status:
#continue
shape = simplify(shape)
shapeTool.SetShape(label, shape)
# The code gets a bit messy here because this was another attempt at fixing the problem by building a dictionary of colours
# before the shapes were simplified and then resetting the colours of each subshape after simplification.
# This didn't work either.
# But the idea was to call this function once to generate the dictionary, then simplify, then call it again passing in the dictionary so it could be re-applied.
#if status:
# simplify_subshapes(label, shapeTool, colorTool, loadedCols)
shapeTool.UpdateAssemblies()
# Re-export
writer = STEPCAFControl_Writer()
Interface_Static.SetIVal_("write.step.assembly", 2)
Interface_Static.SetIVal_("write.stepcaf.subshapes.name", 1)
writer.Transfer(doc, STEPControl_AsIs)
writer.Write("testcolours-simplified.step")
There's a lot of stuff here for a minimum reproducible example but the general flow of the program is that we import the step file:
reader.ReadFile("testcolours.step")
reader.Transfer(doc)
Then we iterate through each label in the file (essentially every node in the tree):
labels = TDF_LabelSequence()
shapeTool.GetShapes(labels)
# Simplify each shape that was imported
for i in range(1, labels.Length()+1):
label = labels.Value(i)
shape = shapeTool.GetShape_(label)
We skip any labels marked as assemblies since they contain children and we only want to simplify individual bodies. We then call simplify(shape) which performs the simplification and returns a new shape, we then call shapeTool.SetShape() to bind the new shape to the old label.
The thing that doesn't work here is that as explained, Component3 and Component4 don't get marked as Assemblies and are treated as SimpleShapes and when they are simplified as one shape, the colours are lost.
One solution I attempted was to call a method simplify_subshapes() which would iterate through each of the subshapes, and do the same thing as the main loop, simplifying them and then calling SetShape(). This ended up being even worse as it resulted in those bodies not being simplified at all but still loosing their colours.
I also attempted to use the simplify_subshapes() method to make a dictionary of all the colours of the subshapes, then simplify the COMPOUND shape and then call the same method again to this time re-apply the colours to the subshapes using the dictionary (the code for this is commented out with an explanation as to what it did).
col = find_color(label, colorTool)
#print(f"{name} RGBA: {col[0]:.5f} {col[1]:.5f} {col[2]:.5f} {col[3]:.5f} defined={col[4]}")
cols[label.Tag()] = col
if set_colours != None:
colorTool.SetColor(label, set_colours[label.Tag()][5], XCAFDoc_ColorSurf)
As far as I see it the issue could be resolved either by getting open cascade to import Component3 and Component4 as Assemblies OR by finding a way to make SetShape() work as intended on subshapes.
Here's a link to the test file:
testcolours.step
I have script which adds a textbox with some text on an existing PPT. Now the textbox colour is made white to overwrite the existing text present in the slidemaster.
The issue is than a small part of textbox overlaps with another shape which is supposed to be on top. Is there an option in python-pptx to send the shape to back.
Below is the option which can be used using the powerpoint
Is the a way I can do this using python-pptx
here is my script
for pptfile in addressList:
prs = Presentation(pptfile)
slides = prs.slides
for i in range(2,len(slides)-1):
textContent = ""
slide = prs.slides[i]
# Text position
t_left = Inches(3.27)
t_top = Inches(7.05)
t_width = Inches(6.89)
t_height = Inches(0.27)
# Text
txBox = slide.shapes.add_textbox(t_left, t_top, t_width, t_height)
fill = txBox.fill
fill.solid()
fill.fore_color.rgb = RGBColor(255, 255, 255)
tf = txBox.text_frame.paragraphs[0]
tf.vertical_anchor = MSO_ANCHOR.TOP
tf.word_wrap = True
tf.margin_top = 0
tf.auto_size = MSO_AUTO_SIZE.SHAPE_TO_FIT_TEXT
run = tf.add_run()
run.text = "This is new text."
font = run.font
font.name = 'Univers LT Std 47 Cn Lt'
font.size = Pt(10)
font.bold = None
font.italic = None # cause value to be inherited from theme
font.color.rgb = RGBColor(157, 163, 163)
prs.save(pptfile)
print(pptfile," Done!")
This discussion in github might help you:
The z-order of shapes on a slide is determined solely by their document order in the slide part (e.g. slide1.xml). So the general gist would be to re-order that sequence of elements. The shapes in a slide are contained in the slide's "shape tree", a element with the same syntax as the group shape, just the different name. The object I expect you'll want to look at first is pptx.shapes.shapetree.SlideShapeTree and its parent BaseShapeTree, which is what you get from slide.shapes. The _spTree attribute on that object gives you the lxml object for the element, which would allow you to reorder shapes.
[...]
I believe the .addprevious() and .addnext() lxml methods actually move the XML element in question.
So you could do something like this to move a shape from ninth position to fourth:
# shape will be positioned relative to this one, hence the name "cursor"
cursor_sp = shapes[3]._element
cursor_sp.addprevious(shapes[8]._element)
See github question
Given a tensorflow event file, how can I extract images corresponding to a specific tag, and then save them to disk in a common format e.g. .png?
You could extract the images like so. The output format may depend on how the image is encoded in the summary, so the resulting write to disk may need to use another format besides .png
import os
import scipy.misc
import tensorflow as tf
def save_images_from_event(fn, tag, output_dir='./'):
assert(os.path.isdir(output_dir))
image_str = tf.placeholder(tf.string)
im_tf = tf.image.decode_image(image_str)
sess = tf.InteractiveSession()
with sess.as_default():
count = 0
for e in tf.train.summary_iterator(fn):
for v in e.summary.value:
if v.tag == tag:
im = im_tf.eval({image_str: v.image.encoded_image_string})
output_fn = os.path.realpath('{}/image_{:05d}.png'.format(output_dir, count))
print("Saving '{}'".format(output_fn))
scipy.misc.imsave(output_fn, im)
count += 1
And then an example invocation may look like:
save_images_from_event('path/to/event/file', 'tag0')
Note that this assumes the event file is fully written -- in the case that it's not, some error handling is probably necessary.
For those who can also do without code, there is an elegant way in the Tensorboard UI.
In the upper left corner, select the checkbox Show data download links
In the lower left corner, select the download icon which lets you download a svg file.
In the lower right corner, select data download links for the raw data, handy for those who want to do more sophisticated data analysis or data visualization
If you are using TensorFlow 2, this works nicely
from collections import defaultdict, namedtuple
from typing import List
import tensorflow as tf
TensorBoardImage = namedtuple("TensorBoardImage", ["topic", "image", "cnt"])
def extract_images_from_event(event_filename: str, image_tags: List[str]):
topic_counter = defaultdict(lambda: 0)
serialized_examples = tf.data.TFRecordDataset(event_filename)
for serialized_example in serialized_examples:
event = event_pb2.Event.FromString(serialized_example.numpy())
for v in event.summary.value:
if v.tag in image_tags:
if v.HasField('tensor'): # event for images using tensor field
s = v.tensor.string_val[2] # first elements are W and H
tf_img = tf.image.decode_image(s) # [H, W, C]
np_img = tf_img.numpy()
topic_counter[v.tag] += 1
cnt = topic_counter[v.tag]
tbi = TensorBoardImage(topic=v.tag, image=np_img, cnt=cnt)
yield tbi
Although, 'v' has an image field, it is empty.
I used
tf.summary.image("topic", img)
to add the images to the event file.