【问题标题】:Use Python to Inject Macros into Spreadsheets使用 Python 将宏注入电子表格
【发布时间】:2019-08-14 23:35:26
【问题描述】:

我有一个宏,我希望使用一堆现有的电子表格。唯一的问题是电子表格太多,手工完成太费时间了!

我已经编写了一个 Python 脚本来使用 pyWin32 访问所需的文件,但我似乎无法找到一种方法来使用它来添加宏。

A similar question here给出了这个答案(它不是Python,但看起来它仍然使用COM),但我的COM对象似乎没有一个名为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 

编辑:链接到引用的类似问题:Inject and execute Excel VBA code into spreadsheet received from external source

我还忘了提到,虽然这不是 Python,但我希望类似的对象成员可以通过 COM 对象提供给我。

【问题讨论】:

  • “A similar question here given this answer”这里缺少链接,不是吗?
  • 首先,您的示例代码甚至不是 Python;我很确定它是 Visual Basic。
  • 我已经修改了我的问题!

标签: python excel vbe vba


【解决方案1】:

这是转换后的代码。您可以使用win32comcomtypes 包。

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()

【讨论】:

  • 感谢您的帮助!为了彻底起见,我还必须在“Excel选项”->“信任中心”选项卡->“信任中心设置...”->宏设置选项卡中勾选“信任对VBA项目对象模块的访问”以能够运行此代码。
  • @dilbert.. 感谢分享代码并演示使用 win32comcomtypes 之间的代码差异。一般来说,您什么时候使用comtypeswin32com
  • @alpha_989,我对这些库的使用还不够彻底,无法区分它们。我更喜欢ComTypes的API。
【解决方案2】:

由于我也花了一些时间来解决这个问题,我将提供另一个示例,它应该适用于 Excel 2007/2010/2013 的xlsm 格式。与上面提供的示例没有太大区别,只是更简单一点,没有循环不同的文件并包含更多的 cmets。此外,宏的源代码是从文本文件中加载的,而不是在 Python 脚本中硬编码。

请记住根据您的需要调整脚本顶部的文件路径。

此外,请记住 Excel 2007/2010/2013 仅允许以 xlsm 格式存储带有宏的工作簿,而不能以 xlsx 格式存储。将宏插入xlsx 文件时,系统会提示您将其保存为其他格式,否则该宏将不会包含在文件中。

最后但并非最不重要的一点是,检查 Excel 从应用程序外部执行 VBA 代码的选项是否已激活(出于安全原因,默认情况下该选项已停用),否则,您将收到一条错误消息。 为此,请打开 Excel 并转到

文件->选项->信任中心->信任中心设置->宏设置->激活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

【讨论】:

    【解决方案3】:

    @Dirk 和@dilbert 提到的内容很棒。您还可以添加这段代码,以编程方式启用对“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)
    

    【讨论】:

      【解决方案4】:

      我想补充一下。我在尝试解析文件夹中的每个 .xlsm 文件时遇到了麻烦。我想出了如何使用 glob 并在 for 语句中格式化 with 语句。我是编程新手,所以如果我使用了不正确的术语,请见谅。希望这对其他人有帮助。谢谢。

      import glob
      import os, sys
      import win32com.client
      
      
      _file = os.path.abspath(sys.argv[0])
      path = os.path.dirname(_file)
      pathToMacro = path + r'\macro2.txt'
      myMacroName = 'TestMacro'
      
      for fname in glob.glob(path + "\*.xlsm"):
          with open (pathToMacro, "r") as myfile:
              print('reading macro into string from: ' + str(myfile))
              macro=myfile.read()
              excel = win32com.client.Dispatch("Excel.Application")
              excel.Visible = False
              workbook = excel.Workbooks.Open(Filename=fname)
              excelModule = workbook.VBProject.VBComponents.Add(1)
              excelModule.CodeModule.AddFromString(macro)
              excel.Application.Run('Module1.CleanDataPivot')
              excel.Workbooks(1).Close(SaveChanges=1)
              excel.Application.Quit()
              del excel
      

      现在我可以在文件夹中的每个 .xlsm 文件中运行一个宏,而无需打开 excel。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-10
        • 1970-01-01
        • 1970-01-01
        • 2012-07-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多