How to paste an Excel chart into PowerPoint placeholder using Python? - python

I have an excel file with a series of formatted charts on a tab called Charts. I have named the charts, Figure1, Figure2, Figure3, etc.
I have an existing PowerPoint template. The template has 2 placeholders per slide (so that it can accommodate 2 charts per slide).
I would like to paste Figure1 in the left placeholder of slide 3, and Figure2 in the right placeholder of slide 3. I want to do this in python as the data analysis is done in python and excel is used to share stored results with colleagues.
Attempt 1:
Attempt 1 uses win32com.client. I am following this example: How to copy chart from excel and paste it as chart into powerpoint (not image) using python
but I cannot get the syntax right to insert the chart into the placeholder. When I follow the syntax in the solution, nothing happens and I get a message
<bound method Paste of <COMObject >>
Current code:
xlApp = win32.Dispatch('Excel.Application')
wb = xlApp.Workbooks.Open(outputPath+'Chart Pack.xlsb')
pptApp = win32.Dispatch('PowerPoint.Application')
ppt = pptApp.Presentations.Open(template)
# attempt 1
wb.sheets('Charts').ChartObjects('Figure1').Copy
ppt.slides[2].Shapes.Paste
# attempt 2
wb.sheets('Charts').ChartObjects('Figure1').Copy
ppt.slides[2].placeholders[1].Paste
Attempt 2:
Attempt 2 uses python-pptx. I looked at the documentation here:
https://python-pptx.readthedocs.io/en/latest/user/placeholders-using.html
but the example involves creating an excel chart from scratch in PowerPoint (I am not sure why you would ever do that), and I can't figure out the syntax to insert an existing chart from excel.
Current code:
from pptx import Presentation
xlApp = win32.Dispatch('Excel.Application')
wb = xlApp.Workbooks.Open(outputPath+'Chart Pack.xlsb')
prs = Presentation(template)
slide = prs.slides[3]
for shape in slide.placeholders:
print('%d %s' % (shape.placeholder_format.idx, shape.name))
placeholder = prs.slides[3].placeholders[1]
placeholder.name
placeholder.placeholder_format.type
placeholder.insert_chart(wb.sheets('Charts').ChartObjects('Figure1').Copy)
Requirements:
I would like to paste the excel chart as an excel object (rather than as a picture) as my colleague likes to be able to click on data series to get the underlying values etc.
I would like to paste the chart inside a placeholder (rather than on top of it).
I don't want to generate a new chart in PowerPoint as most examples do. A colleague has prepared an excel dashboard and formatted a number of charts as desired, and doing it all from scratch would be laborious.
I don't want to do this in VBA. I would like to do it in python as it is part of a broader program.
I don't want to use something like plotnine, seaborn, matplotlib etc. As per 1, my colleague likes excel objects that he can click on to show underlying values. This can't be done in these programs.
Python should be able to do this. Any ideas?

You're very close! Copy and Paste are methods, so to call them you need to add brackets after them, e.g. Copy().
To get slide 2, you need to use the Item method of the Slides class: ppt.Slides.Item(2)
import win32com.client as win32
xlApp = win32.Dispatch('Excel.Application')
wb = xlApp.Workbooks.Open(outputPath+'Chart Pack.xlsb')
pptApp = win32.Dispatch('PowerPoint.Application')
ppt = pptApp.Presentations.Open(template)
slide_num = 3
LEFT_PLACEHOLDER = 3
RIGHT_PLACEHOLDER = 2
# Figure1
window.View.GotoSlide(slide_num)
wb.sheets('Charts').ChartObjects('Figure1').Copy()
ppt.Slides.Item(slide_num).Shapes.Paste().Select()
window.Selection.Cut()
ppt.Slides.Item(slide_num).Shapes(LEFT_PLACEHOLDER).Select()
window.View.Paste()
# Figure2
window.View.GotoSlide(slide_num)
wb.sheets('Charts').ChartObjects('Figure2').Copy()
ppt.Slides.Item(slide_num).Shapes.Paste().Select()
window.Selection.Cut()
ppt.Slides.Item(slide_num).Shapes(RIGHT_PLACEHOLDER).Select()
window.View.Paste()
EDIT: I was able to paste the chart directly into the placeholder after fiddling around a bit trying to implement the solution in this answer. It a bit hacky, but it works.

Related

How to create and edit timeline slicer using win32com python?

I am trying to create a timeline slicer using win32com python. I am currently using win32com to manipulate excel data but in the data, my client wants me to set the upper limit and lower limit of the timeline slicer to certain month. I have googled a lot and i have came to a conclusion that the only way I could do it is by coding it in VBA and implement it in python like here. I have no experience in VBA and I was wondering if there is a way to use win32com python instead of VBA win32com python.
Edit:
After using "Assign Macro" in Excel, this is the code regarding my timeslicer:
ActiveWorkbook.SlicerCaches("NativeTimeline_Goods_Receipt_Date").TimelineState. _
SetFilterDateRange "01/01/2020", "30/04/2020"
Now i need to change it into python and assign the start date & end date into variable. So far i have this:
from win32com.client import Dispatch
excel = win32.gencache.EnsureDispatch('Excel.Application')
test_wb = excel.Workbooks.Open(test_file)
date_sl = test_wb.SlicerCaches("NativeTimeline_Goods_Receipt_Date")
Apparently in Excel, there is a program within it that could record anything you click so if you want to manipulate the filter/slicer, you can right click the element, and then choose "Assign Macro". Then you can click away as it records your clicks. Once youre done, you can view it by again choosing "Assign Macro" and a pop-up window will be available and you can choose your_filter/slicer_name_Click and it will provid you the VBA code. All you have to do is change it so it fits python format.
Updated answer for converting the VBA into python code:
By referring to this link, i was able to convert the VBA into python code and adjust the date based on your choice of date.
So the VBA code is this:
ActiveWorkbook.SlicerCaches("NativeTimeline_Goods_Receipt_Date").TimelineState. _
SetFilterDateRange "01/01/2020", "30/04/2020"
And the python version of it is:
excel = win32.gencache.EnsureDispatch('Excel.Application')
excel.DisplayAlerts = False
excel.Visible = True
test_wb = excel.Workbooks.Open(test_file, None, False)
date_sl = test_wb.SlicerCaches("NativeTimeline_Goods_Receipt_Date")
date_sl.TimelineState.SetFilterDateRange("01/01/2020", "30/04/2020")
In my case, i need to change the date to set based on when i run the code and so on so i can just assign the date to a variable and substitute the hardcoded date with it.

How to split an Excel workbook by worksheet while preserving grouping

I am doing some excel reports for work and am given a book exported from SSRS daily. The book is nicely set up, with groupings applied to every sheet for an effect similar to pivot tables.
However the book comes with 32 sheets, and I eventually need to send out each sheet individually as a distinct report. Right now I am splitting them up manually, but I am wondering if there is a way to automate this while preserving the grouping.
I previously tried something like:
import xlrd
import pandas as pd
targetWorkbook = xlrd.open_workbook(r'report.xlsx', on_demand=True)
xlsxDoc = pd.ExcelFile('report.xlsx')
for sheet in targetWorkbook.sheet_names():
reportDF = pd.read_excel(xlsxDoc, sheet)
reportDF.to_excel("report - {}.xlsx".format(sheet))
However since I'm converting each sheet to a pandas datagrams, the grouping is lost.
There are multiple ways to read/interact with excel docs in python, but I can't find a clear way to pick out a sheet and save it as its own document without losing the grouping.
This is my full answer. I have used the Worksheets().Move() method. The main idea is to use win32com.client library.
This was tested and works on my Windows 10 system with Excel 2013 installed, and Python 3.7. The grouping format was moved intact with the worksheets. I am still working on getting the looping to work. I will revise my answer again when I get the looping to work.
My example has 3 worksheets, each with different grouping (subtotal) formats.
#
# Refined .Move() method, save new file using Active Worksheet property.
#
import win32com.client as win32
excel = win32.gencache.EnsureDispatch('Excel.Application')
wb0 = excel.Workbooks.Open(r'C:\python\so\original.xlsx')
excel.Visible = True
# Move sheet1.
wb0.Worksheets(1).Move()
excel.Application.ActiveWorkbook.SaveAs(r'C:\python\so\sheet1.xlsx')
# Move sheet2, which is now the front sheet.
wb0.Worksheets(1).Move()
excel.Application.ActiveWorkbook.SaveAs(r'C:\python\so\sheet2.xlsx')
# Save single remaining sheet as sheet3.
wb0.SaveAs(r'C:\python\so\sheet3.xlsx')
wb0.Close()
excel.Application.Quit()
You would also need to install pywin32, which is not a standard library item.
https://github.com/mhammond/pywin32
pip install pywin32

Python - Apply the formatting of an existing workbook to another workbook

I am relatively new to coding in Python, and here is my problem:
1- When I update data of an existing .xlsx file (using openpyxl), the outcome is a .xlsx that looses all the previous formatting. I've also tried with .xls (using xlwt and xlrd), but nothing changed.
2- So, I decided to keep this unformatted outcome file and apply all the formatting of a template .xls(x) file.
Is there an straight forward way to preserve the formatting at the step 1? if not, how can I implement step 2?
P.S: I've tried to handle styles with xlutils.styles... but I didn't manage to...
Thanks for your help!
You can take the format of a cell with xlrd and set it as the style of a cell you're writing in xlwt. The cell's xf_index attribute is an instance of the XFStyle object and can be dumped directly into the style argument of a write method, like so:
import xlwt,xlrd
readbook = xlrd.open_workbook('book.xls', formatting_info=True)
readsheet = readbook.sheet_by_index(0)
cell = sheet.cell(0,0)
writebook = xlwt.Workbook()
writesheet = writebook.add_sheet('sheet1')
writesheet.write(0,0,'First Cell', cell.xf_index)
From there you can either iterate to match each cell's format or you can write each format to rows or columns, as shown here: https://github.com/python-excel/xlwt/blob/master/xlwt/examples/row_styles.py

Pywin32 Excel Formatting

I want to write to an Excel sheet via pywin32. I can do it actually without problem. But I couldnt format a range of cells in sheet. I want to align the values centerly inside cells. And also i need to fill the cells with color. How can I do it?
Thanks in advance.
I've not specifically done this using python before, but I'm assuming you're using the COM automation interface to excel.
This page has an example that seems to cover both alignment and filling cells with colour in C#, so it should be fairly easy to adapt to python. Assuming you have a Worksheet object called sheet, and the Excel automation object is called Excel, I'm guessing it might look a bit like this:
//Format A1:D1 as center alignment,
sheet.Range("A1", "D1").VerticalAlignment = Excel.XlVAlign.xlVAlignCenter
sheet.Range("A1", "D1").HorizontalAlignment = Excel.XlHAlign.xlHAlignCenter
sheet.Range("A1", "D1").Interior.ColorIndex = Excel.XlColorIndex.Red
If you don't have access to the Excel.XlAlign and XlColorIndex constants from python then you can just replace them with the specific integers they represent, though I'm not entirey sure where you could get them from. Probably from a VBA Reference Site or similar. (Though that link I provided doesn't seem to allow you to expand each of the entries in the list, so you may need to look elsewhere)
EDIT: Just had a play about with excel automation via the python console, and it seems to work alright:
>>> from win32com.client import Dispatch
>>> xlApp = Dispatch("Excel.Application")
>>> xlWb = xlApp.Workbooks.Add()
>>> xlSht = xlWb.WorkSheets(1)
>>> xlSht.Range("A1", "D1").VerticalAlignment = 1
>>> xlSht.Range("A1", "D1").Interior.ColorIndex = 6
>>> # The background color of A1-D1 should now be yellow
>>> xlSht.Cells(1, 1).VerticalAlignment = 1
If you can't find any good reference on what the various alignment/colour constants are, then I'd just play about with python on the console like this, then open the resulting worksheet in excel and have a look at the results to figure things out.
You can find the official reference for the office 2003 automation API here
Specifically, you'll probably find the range documentation most usefull.

use python to generate graph in excel

I have been trying to generate data in Excel.
I generated .CSV file.
So up to that point it's easy.
But generating graph is quite hard in Excel...
I am wondering, is python able to generate data AND graph in excel?
If there are examples or code snippets, feel free to post it :)
Or a workaround can be use python to generate graph in graphical format like .jpg, etc or .pdf file is also ok..as long as workaround doesn't need dependency such as the need to install boost library.
Yes, Xlsxwriter[docs][pypi] has a lot of utility for creating excel charts in Python. However, you will need to use the xlsx file format, there is not much feedback for incorrect parameters, and you cannot read your output.
import xlsxwriter
import random
# Example data
# Try to do as much processing outside of initializing the workbook
# Everything beetween Workbook() and close() gets trapped in an exception
random_data = [random.random() for _ in range(10)]
# Data location inside excel
data_start_loc = [0, 0] # xlsxwriter rquires list, no tuple
data_end_loc = [data_start_loc[0] + len(random_data), 0]
workbook = xlsxwriter.Workbook('file.xlsx')
# Charts are independent of worksheets
chart = workbook.add_chart({'type': 'line'})
chart.set_y_axis({'name': 'Random jiggly bit values'})
chart.set_x_axis({'name': 'Sequential order'})
chart.set_title({'name': 'Insecure randomly jiggly bits'})
worksheet = workbook.add_worksheet()
# A chart requires data to reference data inside excel
worksheet.write_column(*data_start_loc, data=random_data)
# The chart needs to explicitly reference data
chart.add_series({
'values': [worksheet.name] + data_start_loc + data_end_loc,
'name': "Random data",
})
worksheet.insert_chart('B1', chart)
workbook.close() # Write to file
You have 2 options:
If you are on windows, you can use pywin32 (included in ActivePython) library to automate Excel using OLE automation.
from win32com.client import Dispatch
ex = Dispatch("Excel.Application")
# you can use the ex object to invoke Excel methods etc.
If all you want to just generate basic plots etc. you can use matplotlib.
I suggest you to try gnuplot while drawing graph from data files.
If you do decide to use matplotlib, check out my excel to python class PyWorkbooks to get the data. It lets you retrieve data efficiently and easily as numpy arrays (the native datatype for matplotlib).
https://sourceforge.net/projects/pyworkbooks/
#David Gao, I am looking at doing something similar. Currently I am looking at using the raw csv or converting it to json and just dropping it in a folder that is being read by jqplot.jquery plotting and graphing library. Then all I need to do is have the user or myself display the plot in any web browser.

Categories