How to add a macro to an Excel document? - python

I recently began working with macros and VBA for work however my knowledge is currently very limited so please bear with me. I recently wrote a macro to apply a simple standardized format to all excel documents I send out to others. The macro has been working when I apply the macro from inside of excel, however I also have some automated reports I send out using python and I would like to use this macro on these reports as well to format them prior to sending.
My question is essentially, how do I add a macro to an existing excel document, how do I then execute that macro from python, and then save the resulting excel document?
I do not have much experience in using VBA code or interacting with this using python so I am not too sure where to start so any guidance would be much appreciated.
Below is the Macro I am looking to use
Sub Delphi_macro()
Columns("A:A").Select
Selection.Delete Shift:=xlToLeft
Cells.Select
With Selection
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
.WrapText = False
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlContext
.MergeCells = False
End With
With Selection
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlCenter
.WrapText = False
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlContext
.MergeCells = False
End With
ActiveSheet.ListObjects.Add(xlSrcRange, Range("a1").CurrentRegion, , xlYes).Name = _
"Table1"
Range("Table1[#All]").Select
ActiveSheet.ListObjects("Table1").TableStyle = "TableStyleLight9"
ActiveWindow.DisplayGridlines = False
With ActiveWindow
.SplitColumn = 0
.SplitRow = 1
End With
ActiveWindow.FreezePanes = True
ActiveWindow.SmallScroll Down:=0
Cells.Select
Cells.EntireColumn.AutoFit
End Sub

Add your Macro to a Module, and then Export the Module. After exporting the module you can use this python code to add it/import it and run it.
xl = win32com.client.Dispatch("Excel.Application")
xl.Workbooks.Add
wb = xl.ActiveWorkbook
wb.VBProject.VBComponents.Import "Full Path\VBA_Exported_Macro.bin"
xl.Application.Run("Module1.Delphi_macro")
xl.DisplayAlerts = False
wb.DoNotPromptForConvert = True
wb.CheckCompatibility = False
wb.SaveAs('final_outfile.xlsx', FileFormat=51, ConflictResolution=2) # Macro disapears here
xl.Application.Quit()
del xl
xl = None
In the code above this is creating a fresh Excel Workbook, adding the macro, running it and then Saves the file WITHOUT the macro. You can change that and instead open an existing workbook and save as Macro enabled excel to retain the macro.

Related

Create a Drop down list using XLWings in Python

I have tried this code, but this is for validation. I want a code to create a new drop down menu.
app = xw.App(visible=True)
wb = app.books.open('Test.xlsx')
sht = wb.sheets['Sheet1']
Formula1='"Dog,Cat,Bat"'
dv = sht.range('A1').api.Validation.Formula1
I have tried using openpyxl it is working but it doesn't save a file when the file is open.
xlwings is a wrapper of win32com, which is similar to VBA. With recording VBA macro for reference, the following code should work.
import xlwings as xw
app = xw.App(visible=True)
wb = app.books.open('Test.xlsx')
sht = wb.sheets['Sheet1']
Formula1='Dog,Cat,Bat' # remove the redundant "
# set up validation
sht.range('A1').api.Validation.Add(Type=3, Formula1=Formula1)
The exploring steps:
Record a macro for the default steps to create a list type data validation:
Sub Macro1()
'
' Macro1 Macro
'
'
Range("A1").Select
Application.WindowState = xlMaximized
With Selection.Validation
.Delete
.Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Operator:= _
xlBetween, Formula1:="Dog,Cat,Bat"
.IgnoreBlank = True
.InCellDropdown = True
.InputTitle = ""
.ErrorTitle = ""
.InputMessage = ""
.ErrorMessage = ""
.ShowInput = True
.ShowError = True
End With
End Sub
Indeed, we did only two key steps and kept the rest default:
select List from validation criteria dropdown list
type the source for list
So, the above code should be simplified as
' VBA
Range("A1").Validation.Add Type:=xlValidateList, Formula1:="Dog,Cat,Bat"

python Copy excel file Char into word file failure.(Use Clipboard) (Word.Application)

I am searching for a long time on net. But no use. Please help or try to give some ideas how to achieve this
python code Exec failure: process[word.Selection.Paste] is Invalid.
from win32com import client
excel = client.Dispatch("Excel.Application")
word = client.Dispatch("Word.Application")
doc = word.Documents.Open("D:/python/My_Work/exe/word-opt/word_file.docx")
book = excel.Workbooks.Open("D:/python/My_Work/exe/word-opt/excel_file.xlsx")
char1=sheet.ChartObjects(1).Copy
doc.Bookmarks("BookMark2").Range.Select
word.Selection.Paste
doc.Save()
doc.Close()
the Same VBA code Exec OK:
Sub test()
Set dataExcel = CreateObject("Excel.Application")
Set Workbook = dataExcel.Workbooks.Open(ThisWorkbook.Path & "\excel_file.xlsx")
Set Sheet = Workbook.Worksheets(1)
Dim WordApp As Word.Application
Dim WordDoc As Word.Document
Set WordApp = New Word.Application
WordApp.Visible = True
Set WordDoc = WordApp.Documents.Open(ThisWorkbook.Path & "\word_file.docx")
Sheet.ChartObjects(1).Copy
WordDoc.Bookmarks("BookMark2").Range.Select
WordApp.Selection.Paste
WordDoc.Save
WordDoc.Close
WordApp.Quit
Set WordDoc = Nothing
Set WordApp = Nothing
End Sub
ex: File [excel_file.xlsx] in Only a chart.

Unable to insert caluclated field in PIVOT TABLE created using win32COM python library

I am trying to insert a calculated field in PIVOT TABLE created using win32com python library. But when i execute my code excel gives me error "References, names and arrays are not supported in Pivot Table formulas"
import win32com.client
Excel = win32com.client.gencache.EnsureDispatch('Excel.Application')
win32c = win32com.client.constants
Wb = Excel.Workbooks.Open('MyWorkbook')
Excel.Visible = True
Ws = Wb.Sheets('PR Jan20')
Wb.Sheets.Add()
Wb.ActiveSheet.Name = 'PivotSheet'
WsP = Wb.Sheets('PivotSheet')
MaxR = Ws.UsedRange.Rows.Count
MaxC = Ws.UsedRange.Columns.Count
C1 = Ws.Cells(1,1)
C2 = Ws.Cells(MaxR, MaxC)
PivotSourceRange = Ws.Range(C1,C2)
PCache = Wb.PivotCaches().Create(SourceType=win32c.xlDatabase, SourceData=PivotSourceRange,Version=win32c.xlPivotTableVersion14)
PTable = PCache.CreatePivotTable(TableDestination=WsP.Range('B2'), TableName='RegisterPivot', DefaultVersion=win32c.xlPivotTableVersion14)
PTable.PivotFields('Party').Orientation = win32c.xlRowField
PTable.PivotFields('Party').Position = 1
PTable.AddDataField(PTable.PivotFields('Gross Kgs'))
PTable.AddDataField(PTable.PivotFields('Amount (RS.)'))
#till above this line code is working fine
#this below line is causing issue
PTable.CalculatedFields().Add('Average Purchase Rate', '= Amount (RS.) / Gross Kgs')
'Excel Error'
I have managed to resolve the above issue. The problem was with the column name "Amount (RS.)" i renamed the column to "Amount" and everything worked fine. I think VBA is not comfortable with () parenthesis in Pivot Field name.

Python + Win32: Paste header from one Excel workbook to the top of another

I want to format an Excel report automatically. I generate the raw data using Python for reports, but they need pretty formatting (colors, bold font, borders) on them before they go to a Project Manager.
My current approach is using the pywin32 package, copying the header from a Template file, and pasting it into my Output report.
I am having trouble with the .Paste() methods, for instance, if I try to specify the destination with .Paste(Destination=Range('A1:A100') it will throw an error. If I attempt output_worksheet.used_range.Paste() it will not recognize used_range as valid either.
Additionally my current code doesn't exit out of Excel.
Finally my code pastes the header into row 16 instead of starting from the top:
template_path = tests.path + r"\Box Tracking Report Regents Template.xlsx"
timestamp = datetime.datetime.now().strftime("%m-%d-%Y %I%M%p")
output_path = tests.path + r"\Box Tracking Report " + timestamp + ".xlsx"
# (... write my pandas dataframe in ...)
def paste_formatting(tab_name):
excel_instance = win32com.client.gencache.EnsureDispatch("Excel.Application")
template = excel_instance.Workbooks.Open(template_path)
template_workbook = excel_instance.Workbooks.Item(1)
template_worksheet = template_workbook.Worksheets(tab_name)
used_range = template_worksheet.UsedRange
used_range.Copy()
output_excel = excel_instance.Workbooks.Open(output_path)
output_workbook = excel_instance.Workbooks.Item(2)
output_worksheet=output_workbook.Worksheets(tab_name)
output_worksheet.Paste()
output_workbook.Close()
template_workbook.Close()
paste_formatting('Regents Box Tracking Report')
This is what I get:
With #chucklukowski's comment:
def paste_formatting(tab_name):
excel_instance = win32com.client.gencache.EnsureDispatch("Excel.Application")
template = excel_instance.Workbooks.Open(template_path)
template_workbook = excel_instance.Workbooks.Item(1)
template_worksheet = template_workbook.Worksheets(tab_name)
used_range = template_worksheet.UsedRange
used_range.Copy()
output_excel = excel_instance.Workbooks.Open(output_path)
output_workbook = excel_instance.Workbooks.Item(2)
output_worksheet=output_workbook.Worksheets(tab_name)
output_worksheet.Paste(output_worksheet.Range('A1'))
output_workbook.Save()
output_workbook.Close()
template_workbook.Close()
excel_instance.Quit()
del excel_instance
paste_formatting('Regents Box Tracking Report')
Still messy, but probably better than calling an Excel VBA macro from the template itself (which was going to be my last ditch approach).

Can I use Win32 COM to replace text inside a word document?

I have to perform a large number of replacements in some documents, and the thing is, I would like to be able to automate that task. Some of the documents contain common strings, and this would be pretty useful if it could be automated. From what I read so far, COM could be one way of doing this, but I don't know if text replacement is supported.
I'd like to be able to perform this task in python? Is it possible? Could you post a code snippet showing how to access the document's text?
Thanks!
I like the answers so far;
here's a tested example (slightly modified from here)
that replaces all occurrences of a string in a Word document:
import win32com.client
def search_replace_all(word_file, find_str, replace_str):
''' replace all occurrences of `find_str` w/ `replace_str` in `word_file` '''
wdFindContinue = 1
wdReplaceAll = 2
# Dispatch() attempts to do a GetObject() before creating a new one.
# DispatchEx() just creates a new one.
app = win32com.client.DispatchEx("Word.Application")
app.Visible = 0
app.DisplayAlerts = 0
app.Documents.Open(word_file)
# expression.Execute(FindText, MatchCase, MatchWholeWord,
# MatchWildcards, MatchSoundsLike, MatchAllWordForms, Forward,
# Wrap, Format, ReplaceWith, Replace)
app.Selection.Find.Execute(find_str, False, False, False, False, False, \
True, wdFindContinue, False, replace_str, wdReplaceAll)
app.ActiveDocument.Close(SaveChanges=True)
app.Quit()
f = 'c:/path/to/my/word.doc'
search_replace_all(f, 'string_to_be_replaced', 'replacement_str')
See if this gives you a start on word automation using python.
Once you open a document, you could do the following.
After the following code, you can Close the document & open another.
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "test"
.Replacement.Text = "test2"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchKashida = False
.MatchDiacritics = False
.MatchAlefHamza = False
.MatchControl = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
The above code replaces the text "test" with "test2" and does a "replace all".
You can turn other options true/false depending on what you need.
The simple way to learn this is to create a macro with actions you want to take, see the generated code & use it in your own example (with/without modified parameters).
EDIT: After looking at some code by Matthew, you could do the following
MSWord.Documents.Open(filename)
Selection = MSWord.Selection
And then translate the above VB code to Python.
Note: The following VB code is shorthand way of assigning property without using the long syntax.
(VB)
With Selection.Find
.Text = "test"
.Replacement.Text = "test2"
End With
Python
find = Selection.Find
find.Text = "test"
find.Replacement.Text = "test2"
Pardon my python knowledge. But, I hope you get the idea to move forward.
Remember to do a Save & Close on Document, after you are done with the find/replace operation.
In the end, you could call MSWord.Quit (to release Word object from memory).
If this mailing list post is right, accessing the document's text is a simple as:
MSWord = win32com.client.Dispatch("Word.Application")
MSWord.Visible = 0
MSWord.Documents.Open(filename)
docText = MSWord.Documents[0].Content
Also see How to: Search for and Replace Text in Documents. The examples use VB and C#, but the basics should apply to Python too.
Checkout this link: http://python.net/crew/pirx/spam7/
The links on the left side point to the documentation.
You can generalize this using the object model, which is found here:
http://msdn.microsoft.com/en-us/library/kw65a0we(VS.80).aspx
You can also achieve this using VBScript. Just type the code into a file named script.vbs, then open a command prompt (Start -> Run -> Cmd), then switch to the folder where the script is and type: cscript script.vbs
strFolder = "C:\Files"
Const wdFormatDocument = 0
'Select all files in strFolder
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colFiles = objWMIService.ExecQuery _
("ASSOCIATORS OF {Win32_Directory.Name='" & strFolder & "'} Where " _
& "ResultClass = CIM_DataFile")
'Start MS Word
Set objWord = CreateObject("Word.Application")
Const wdReplaceAll = 2
Const wdOrientLandscape = 1
For Each objFile in colFiles
If objFile.Extension = "doc" Then
strFile = strFolder & "\" & objFile.FileName & "." & objFile.Extension
strNewFile = strFolder & "\" & objFile.FileName & ".doc"
Wscript.Echo "Processing " & objFile.Name & "..."
Set objDoc = objWord.Documents.Open(strFile)
objDoc.PageSetup.Orientation = wdOrientLandscape
'Replace text - ^p in a string stands for new paragraph; ^m stands for page break
Set objSelection = objWord.Selection
objSelection.Find.Text = "String to replace"
objSelection.Find.Forward = TRUE
objSelection.Find.Replacement.Text = "New string"
objSelection.Find.Execute ,,,,,,,,,,wdReplaceAll
objDoc.SaveAs strNewFile, wdFormatDocument
objDoc.Close
Wscript.Echo "Ready"
End If
Next
objWord.Quit

Categories

Resources