Use Python para inyectar macros en hojas de cálculo

Tengo una macro que me gustaría usar un montón de hojas de cálculo existentes. ¡El único problema es que hay tantas hojas de cálculo que sería demasiado lento hacerlo a mano!

He escrito una secuencia de comandos de Python para acceder a los archivos necesarios con pyWin32, pero parece que no encuentro la forma de usarlo para agregar la macro.

Una pregunta similar aquí dio esta respuesta (no es Python, pero parece que todavía usa COM), pero mi objeto COM no parece tener un miembro llamado VBProject:

Set objExcel = CreateObject("Excel.Application") objExcel.Visible = True objExcel.DisplayAlerts = False Set objWorkbook = objExcel.Workbooks.Open("C:\scripts\test.xls") Set xlmodule = objworkbook.VBProject.VBComponents.Add(1) strCode = _ "sub test()" & vbCr & _ " msgbox ""Inside the macro"" " & vbCr & _ "end sub" xlmodule.CodeModule.AddFromString strCode objWorkbook.SaveAs "c:\scripts\test.xls" objExcel.Quit 

EDITAR: enlace a la pregunta similar a la que se hace referencia: inyecte y ejecute el código Excel VBA en la hoja de cálculo recibida de una fuente externa

También olvidé mencionar que, aunque esto no es Python, esperaba que los miembros de objetos similares estuvieran disponibles para mí a través de los objetos COM.

Este es el código convertido. Puede utilizar los paquetes win32com o comtypes .

 import os import sys # Import System libraries import glob import random import re sys.coinit_flags = 0 # comtypes.COINIT_MULTITHREADED # USE COMTYPES OR WIN32COM #import comtypes #from comtypes.client import CreateObject # USE COMTYPES OR WIN32COM import win32com from win32com.client import Dispatch scripts_dir = "C:\\scripts" conv_scripts_dir = "C:\\converted_scripts" strcode = \ ''' sub test() msgbox "Inside the macro" end sub ''' #com_instance = CreateObject("Excel.Application", dynamic = True) # USING COMTYPES com_instance = Dispatch("Excel.Application") # USING WIN32COM com_instance.Visible = True com_instance.DisplayAlerts = False for script_file in glob.glob(os.path.join(scripts_dir, "*.xls")): print "Processing: %s" % script_file (file_path, file_name) = os.path.split(script_file) objworkbook = com_instance.Workbooks.Open(script_file) xlmodule = objworkbook.VBProject.VBComponents.Add(1) xlmodule.CodeModule.AddFromString(strcode.strip()) objworkbook.SaveAs(os.path.join(conv_scripts_dir, file_name)) com_instance.Quit() 

Como también me costó un poco de tiempo para hacerlo bien, daré otro ejemplo que se supone que funciona con el formato xlsm de xlsm . No hay mucha diferencia en el ejemplo proporcionado anteriormente, es solo un poco más simple sin el bucle sobre diferentes archivos y con más comentarios incluidos. Además, el código fuente de la macro se carga desde un archivo de texto en lugar de codificarlo en el script de Python.

Recuerde adaptar las rutas de archivo en la parte superior del script a sus necesidades.

Además, recuerde que Excel 2007/2010/2013 solo permite almacenar libros de trabajo con macros en formato xlsm , no en xlsx . Cuando inserte una macro en un archivo xlsx , se le pedirá que la guarde en un formato diferente o la macro no se incluirá en el archivo.

Y por último, pero no menos importante, verifique que la opción de Excel para ejecutar el código VBA desde fuera de la aplicación esté activada (que está desactivada por defecto por razones de seguridad), de lo contrario, aparecerá un mensaje de error. Para ello, abre Excel y ve a

Archivo -> Opciones -> Centro de confianza -> Configuración del centro de confianza -> Configuración de macros -> activar la marca de verificación en el Trust access to the VBA project object model .

 # necessary imports import os, sys import win32com.client # get directory where the script is located _file = os.path.abspath(sys.argv[0]) path = os.path.dirname(_file) # set file paths and macro name accordingly - here we assume that the files are located in the same folder as the Python script pathToExcelFile = path + '/myExcelFile.xlsm' pathToMacro = path + '/myMacro.txt' myMacroName = 'UsefulMacro' # read the textfile holding the excel macro into a string with open (pathToMacro, "r") as myfile: print('reading macro into string from: ' + str(myfile)) macro=myfile.read() # open up an instance of Excel with the win32com driver excel = win32com.client.Dispatch("Excel.Application") # do the operation in background without actually opening Excel excel.Visible = False # open the excel workbook from the specified file workbook = excel.Workbooks.Open(Filename=pathToExcelFile) # insert the macro-string into the excel file excelModule = workbook.VBProject.VBComponents.Add(1) excelModule.CodeModule.AddFromString(macro) # run the macro excel.Application.Run(myMacroName) # save the workbook and close excel.Workbooks(1).Close(SaveChanges=1) excel.Application.Quit() # garbage collection del excel 

Lo que mencionaron @Dirk y @dilbert funciona muy bien. También puede agregar este fragmento de código que habilitará de manera programática el acceso al `módulo de objeto VBA ‘

 import win32api import win32con key = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, "Software\\Microsoft\\Office\\16.0\\Excel" + "\\Security", 0, win32con.KEY_ALL_ACCESS) win32api.RegSetValueEx(key, "AccessVBOM", 0, win32con.REG_DWORD, 1)