editing the position of a shape in PowerPoint - python

from pptx import Presentation
prs = Presentation(my_file)
print(prs.slides[1].shape[0])
#out:
#<pptx.shapes.picture.Picture at 0x2295816cf98>
I need to loop through my shapes and assign a custom height, width and vertical position :
height = 7002000
width = 12193200
i have my height + width values which I can set via assignment prs.slides[1].shape[0].height = height
with a simple loop.
one thing I can't find is the attribute to set the shape's position on the page, chiefly the Vertical Position
my correct value is set to -0.16cm which I'm trying to replicate.
I thought it might be under left or top but my correct presentation returns a value of 0

Note that you can use the provided convenience measurements like this:
from pptx.util import Cm
shape.left = Cm(5.5)
Which saves you doing the arithmetic to English Metric Units (EMU) yourself.

Found the answer finally - I had to use a combination of top and left on the shape attribute.
in my case I had to set my variables to
top = -57600
left = 0
I then access the shape method
for slide in prs.slides:
for shape in slide.shapes:
shape.left = left
shape.top = top

Related

How to label inner/outer of contour when slice/rasterize 3D objects to image stack?

For 3D printing, we slice the digital objects into image stacks in order to stack them layer by layer using a 3D printer. And when the slice is done, how to label the inner/outer to set the solid parts?
The STL model:
The Slices:
Sample of one image stack (sliced):
but the need is to keep or label the inner/outer of contours, say the inner is black so the 3D printer will print it and skip the white outer. The goal is filled inner of contours as the following image:
Try 1
import pyvista as pv
mesh = pv.read('./haus.stl')
slices = mesh.slice_along_axis(n=20, axis='z', progress_bar=True)
# show single slice with camera setting
slices[15].plot(cpos=[0, 1, 1], line_width=5, parallel_projection=True,)
# save slices (outcome is as step.3 image stack)
for i in range(20):
p = pv.Plotter(off_screen=True)
p.add_mesh(slices[i])
p.camera_position = 'zy'
p.enable_parallel_projection()
im_name = "im_slice_" + str(i) + ".jpg"
p.screenshot(im_name)
# Try voxelize (as ans from https://stackoverflow.com/questions/75300529)
voxels = pv.voxelize(mesh, density=mesh.length / 100)
# Try pv.Plane() (not test yet)
plane=pv.Plane()
plane.compute_implicit_distance(mesh, inplace=True)
np.sign(plane.point_data['implicit_distance'])
#i_resolution=?, j_resolution=?
# Try vtk (not test yet)
# https://stackoverflow.com/questions/68191368
voxelize model:
voxelize sliced:
but voxelize sliced doesn't seem very suitable. A very fine mesh needs to be built to restore the boundaries.
Try 2 VTK example
show STL:
Just add STL reader and Mapper:
filename = './haus.stl'
reader = vtkSTLReader()
reader.SetFileName(filename)
reader.Update()
stlMapper = vtk.vtkPolyDataMapper()
stlMapper.SetInputConnection(reader.GetOutputPort())
polydata = stlMapper
print("Get GetOrigin", polydata.GetCenter())
sphereSource = reader
slice result:
Try 2 is almost done with the job, but can not figure out the SetExtent/SetOrigin effect. The output image all fit to the contours' dimensions so each output image WXH is not identical.
Try 3 3D Silcer example
Only change some code as following:
inputModelFile = "./data/haus.stl"
outputDir = "./outputs/"
...
for i in range(80,140, 10):
imageio.imwrite(f"{outputDir}/image_{i:03}.jpg", 255 - outputLabelmapVolumeArray[i]) # Inverting Colors
The result seems acceptable, but need future to revise some code to match the resolution, position, spacing, etc. So, is there a more lean and more efficient way to automate similar work?
You may want to try out the combination of vtkFeatureEdges, vtkStripper and vtkTriangleFilter, eg:
from vedo import *
msh = Mesh('https://vedo.embl.es/examples/data/cow.vtk')
slices = []
for z in np.arange(-.50, .50, .15):
line = msh.clone().cut_with_plane(origin=(0,0,z), normal='z')
cap = line.cap(True)
slices.append(cap)
show(slices, msh.alpha(0.1), axes=1)

Appropriately positioning annotations on stacked barplot

I have annotated each bar on my stacked barplot but can't seem to get the annotations to be equivalent to the bar's position.
This is the code I have:
for i in ax_mult.patches:
width,height=i.get_width(),i.get_height()
x,z =i.get_xy()
ax_mult.annotate(str(i.get_height()),(i.get_x()+.30*width,i.get_height()+.1*height))
This is what I am getting
I guess your main problem was that you placed the text in y direction effectively at 1.1 * i.get_height(), without considering the initial offset i.get_y().
Try this:
for i in ax_mult.patches:
ix,iy=i.get_x(),i.get_y() ## gives you the bottom left of each patch
width,height=i.get_width(),i.get_height() ## the width & height of each patch
## to place the annotation at the center (0.5, 0.5):
ax.annotate(str(height),(ix+0.5*width, iy+0.5*height),ha="center",va="center")
## alternatively via ax.text():
# ax.text(ix+.5*width,iy+.5*height,height,ha="center",va="center" )
Note that you may need to "play around" with good offsets, especially in y-direction. The ha="center",va="center" parameters align the text exactly at the chosen coordinate (both horizontally: ha and vertically: va), which comes in handy if you'd like to put the labels e.g. aligned below the top end of the patch:
ax.annotate(str(height),(ix+0.5*width, iy+1.0*height),ha="center",va="top")
Or just above the top end of the patch:
ax.annotate(str(height),(ix+0.5*width, iy+1.0*height),ha="center",va="bottom")

how to find Y face of the cube in Maya with Python

sorry for such specific question guys , I think people only with knowledge of Maya will answer tho. In Maya I have cubes different sizes and I need to find with python which face of cube is pointing Y axis down. (Pivot is in center) Any tips will be appreciated
Thanks a lot :)
import re
from maya import cmds
from pymel.core.datatypes import Vector, Matrix, Point
obj = 'pCube1'
# Get the world transformation matrix of the object
obj_matrix = Matrix(cmds.xform(obj, query=True, worldSpace=True, matrix=True))
# Iterate through all faces
for face in cmds.ls(obj + '.f[*]', flatten=True):
# Get face normal in object space
face_normals_text = cmds.polyInfo(face, faceNormals=True)[0]
# Convert to a list of floats
face_normals = [float(digit) for digit in re.findall(r'-?\d*\.\d*', face_normals_text)]
# Create a Vector object and multiply with matrix to get world space
v = Vector(face_normals) * obj_matrix
# Check if vector faces downwards
if max(abs(v[0]), abs(v[1]), abs(v[2])) == -v[1]:
print face, v
If you just need a quick solution without vector math and Pymel or the the API, you can use cmds.polySelectConstraint to find the faces aligned with a normal. All you need to do is select all the faces, then use the constraint to get only the ones pointing the right way. This will select all the faces in a mesh that are pointing along a given axis:
import maya.cmds as cmds
def select_faces_by_axis (mesh, axis = (0,1,0), tolerance = 45):
cmds.select(mesh + ".f[*]")
cmds.polySelectConstraint(mode = 3, type = 8, orient = 2, orientaxis = axis, orientbound = (0, tolerance))
cmds.polySelectConstraint(dis=True) # remember to turn constraint off!
The axis is the x,y,z axis you want and tolerance is the slop in degrees you'll tolerate. To get the downward faces you'd do
select_faces_by_axis ('your_mesh_here', (0,0,-1))
or
select_faces_by_axis ('your_mesh_here', (0,0,-1), 1)
# this would get faces only within 1 degree of downard
This method has the advantage of operating mostly in Maya's C++, it's going to be faster than python-based methods that loop over all the faces in a mesh.
With pymel the code can be a bit more compact. Selecting the faces pointing downwards:
n=pm.PyNode("pCubeShape1")
s = []
for f in n.faces:
if f.getNormal(space='world')[1] < 0.0:
s.append(f)
pm.select(s)

Custom legend position with python-pptx

I would like to set the legend on a self defined, custom position.
My final goal would be to get the settings of an already existing chart and use the same settings for a new chart.
I read in the docs it's possible to set the legend like this:
(http://python-pptx.readthedocs.io/en/latest/api/enum/XlLegendPosition.html#xllegendposition)
from pptx.enum.chart import XL_LEGEND_POSITION
chart.has_legend = True
chart.legend.position = XL_LEGEND_POSITION.CUSTOM
But I get a ValueError:
ValueError: CUSTOM (-4161) not a member of XL_LEGEND_POSITION enumeration
Did I miss anything or how can I set the legend on a custom position?
The .CUSTOM member of the XL_LEGEND_POSITION is a reporting member only (roughly like "read-only"). It is intended as the value of the Legend.position property when the legend has been manually adjusted (dragged and dropped with the mouse using the UI). Unlike the other members of that enumeration, it is not "assignable" and could not by itself of course set the position to where you wanted it.
Custom placement of the legend is not yet supported by the python-pptx API. If you wanted to do it you'd have to manipulate the underlying XML with low-level lxml calls. You'd need to understand the relevant XML schema and semantics to know what to do with that XML to produce the result you were after. This sort of thing is commonly called a "workaround function" in python-pptx and python-docx (they work very similarly being based on the same architecture). A Google search on "python-pptx" OR "python-docx" workaround function will find you some examples used for other purposes that you may find helpful if you decide to take that approach.
I couldn't find a fully formed answer to this, so I thought it would be worth posting the workaround that I used:
from pptx.oxml.xmlchemy import OxmlElement
def SubElement(parent, tagname, **kwargs):
element = OxmlElement(tagname)
element.attrib.update(kwargs)
parent.append(element)
return element
def manuallySetLegendPosition(
chart,
x,
y,
w,
h
):
## Inside layout, add manualLayout
L = chart.legend._element.get_or_add_layout()
mL = L.get_or_add_manualLayout()
## Add xMode and yMode and set vals to edge
xM = SubElement(mL, 'c:xMode', val="edge")
xY = SubElement(mL, 'c:yMode', val="edge")
## Add x, value is between -1 and 1 as a proportion of the chart width
## point of reference on the legend is its centre, not top left
xE = SubElement(mL, 'c:x', val=str(x))
## Add y, same concept as above
yE = SubElement(mL, 'c:y', val=str(y))
## Add w, legend height as a proportion of chart height
wE = SubElement(mL, 'c:w', val=str(w))
## Add h, same concept as above
hE = SubElement(mL, 'c:h', val=str(h))

python pptx get table width

I work with python 2.7 and using python pptx.
I add a table to my slide, and need to get the table overall width.
I found here the _column attribute width, and try to use it, for example with that code
for col in table._column:
yield col.width
and get the following error:
AttributeError: 'Table' object has no attribute '_column'
I need to get the table width (or the columns width and sum it). ideas?
Thanks!
The property you want on Table is .columns, so:
for column in table.columns:
yield column.width
All the properties and a description of each is available in the API section of the documentation, for example this page describing the table object API:
http://python-pptx.readthedocs.io/en/latest/api/table.html
Building off Scanny's code and the pptx documentation we can define a function like this to print the dimensions of an entire existing python-pptx table object:
from pptx import Presentation
from pptx.util import Inches, Cm, Pt
def table_dims(table, measure = 'Inches'):
"""
Returns a dimensions tuple (width, height) of your pptx table
object in Inches, Cm, or Pt.
Defaults to Inches.
This value can then be piped into an Inches, Cm, or Pt call to
generate a new table of the same initial size.
"""
widths = []
heights = []
for column in table.columns:
widths.append(column.width)
for row in table.rows:
heights.append(row.height)
# Because the initial widths/heights are stored in a strange format, we'll convert them
if measure == 'Inches':
total_width = (sum(widths)/Inches(1))
total_height = (sum(heights)/Inches(1))
dims = (total_width, total_height)
return dims
elif measure == 'Cm':
total_width = (sum(widths)/Cm(1))
total_height = (sum(heights)/Cm(1))
dims = (total_width, total_height)
return dims
elif measure == 'Pt':
total_width = (sum(widths)/Pt(1))
total_height = (sum(heights)/Pt(1))
dims = (total_width, total_height)
return dims
else:
Exception('Invalid Measure Argument')
# Initialize the Presentation and Slides objects
prs = Presentation('path_to_existing.pptx')
slides = prs.slides
# Access a given slide's Shape Tree
shape_tree = slides['replace w/ the given slide index'].shapes
# Access a given table
table = shape_tree['replace w/ graphic frame index'].table
# Call our function defined above
slide_table_dims = table_dims(table)
print(slide_table_dims)

Categories