运行 excel 来自 python 的宏

Running excel macro from python

我正在使用以下代码 运行 来自 Python 的 Excel 宏:

import pymysql
import datetime
import csv
import math
import os
import glob
import sys
import win32com.client
import numpy
from tkinter import *
from tkinter import ttk
import tkinter.messagebox

def run_macro():
    print('macro')

    #this if is here because if an executable is created, __file__ doesn't work
    if getattr(sys, 'frozen', False):
        name = (os.path.dirname(sys.executable) + '\Forecast template.xlsm')

    else:
        name = str(os.path.dirname(os.path.realpath(__file__)) + '\Forecast template.xlsm')

    print(name)

    #this part runs the macro from excel
    if os.path.exists(name):
        xl=win32com.client.Dispatch("Excel.Application")
        xl.Workbooks.Open(Filename=name, ReadOnly=1)
        xl.Application.Run("ThisWorkbook.LoopFilesInFolder")
        xl.Application.Quit() # Comment this out if your excel script closes
        del xl

    print('File refreshed!')

我似乎遇到了一些问题,在 运行 之后,我去打开任何 excel 文件,我只得到一个灰色的 window:

知道为什么会这样吗?另外,如何在代码中添加一些内容以在 Excel 中打开一个文件? (不是为了获取信息,而是为了在 Excel 中打开该文件)

额外的问题:如何让它不关闭所有打开的 Excel 文件?

编辑:我刚刚检查了宏,工作正常,问题似乎来自于我 运行 代码。

新编辑:

这是宏中的代码:

Sub LoopFilesInFolder()

    Dim wb1 As Workbook
    Dim wb2 As Workbook
    Dim path As String
    Dim file As String
    Dim extension As String
    Dim myFileName As String

    Application.ScreenUpdating = False
    Application.DisplayAlerts = False

    Set wb1 = ActiveWorkbook

    path = ActiveWorkbook.path & "\csvs\"

    extension = "*.csv"
    file = Dir(path & extension)

    Do While file <> ""

        Set wb2 = Workbooks.Open(Filename:=path & file)
        wb2.Activate

        'this section is for the avail heads file, basically it just opens it and copies the info to the template
        If wb2.Name = "avail_heads.csv" Then
            Range("A1").Select
            Range(Selection, Selection.End(xlDown)).Select
            Range(Selection, Selection.End(xlToRight)).Select
            Selection.Copy

            wb1.Activate

            Worksheets("raw data").Range("B88").PasteSpecial xlPasteValues
        End If

        'this section is for the forecast file, basically it just opens it and copies the info to the template
        If wb2.Name = "forecast.csv" Then
            Range("A1").Select
            Range(Selection, Selection.End(xlDown)).Select
            Range(Selection, Selection.End(xlToRight)).Select
            Selection.Copy

            wb1.Activate

            Worksheets("raw data").Range("B74").PasteSpecial xlPasteValues
        End If

        'this section is for the income file, basically it just opens it and copies the info to the template
        If wb2.Name = "income volume.csv" Then
            Range("A1").Select
            Range(Selection, Selection.End(xlDown)).Select
            Range(Selection, Selection.End(xlToRight)).Select
            Selection.Copy

            wb1.Activate

            Worksheets("raw data").Range("B3").PasteSpecial xlPasteValues
        End If

        'this section is for the outgoing volume file, basically it just opens it and copies the info to the template
        If wb2.Name = "outgoing_volume.csv" Then
            Range("A1").Select
            Range(Selection, Selection.End(xlDown)).Select
            Range(Selection, Selection.End(xlToRight)).Select
            Selection.Copy

            wb1.Activate

            Worksheets("raw data").Range("B36").PasteSpecial xlPasteValues
        End If

        'this section is for the required heads file, basically it just opens it and copies the info to the template
        If wb2.Name = "required_heads.csv" Then
            Range("A1").Select
            Range(Selection, Selection.End(xlDown)).Select
            Range(Selection, Selection.End(xlToRight)).Select
            Selection.Copy

            wb1.Activate

            Worksheets("raw data").Range("B102").PasteSpecial xlPasteValues
        End If

        wb2.Close
        file = Dir

    Loop

    'myFileName = ActiveWorkbook.path & "\forecast_for_w" & Format(Now, "ww") + 1

    myFileName = ActiveWorkbook.path & "\yoda_forecast"

    ActiveWorkbook.SaveAs Filename:=myFileName, FileFormat:=xlWorkbookNormal

    'MsgBox "Done!"

    Application.DisplayAlerts = True

End Sub

发送后需要将Visibility设置为True:

xl = win32com.client.Dispatch("Excel.Application")
xl.Application.Visible = True

此外,调用宏应该不需要 ThisWorkbook:

xl.Application.Run("LoopFilesInFolder")

为了能够再次关闭工作簿,您需要将其分配给一个变量:

wb = xl.Workbooks.Open(Filename=name, ReadOnly=1)
wb.Close(SaveChanges=False)

我写了包 xlwings 让事情变得更简单:

from xlwings import Workbook, Range
wb = Workbook(r'C:\path\to\workbook.xlsx')  # open a workbook if not open yet
Range('A1').value = 123  # Write a value to cell A1
wb.close()  # close the workbook again

运行 一个宏尚未实现,但有一个 issue 开放。同时,您可以通过执行(按照上面的说明)来解决此问题:

wb.xl_app.Run("macro_name")

我已经有将近一周的赏金了,我真的不知道以前有多少人遇到过这个问题。折腾了一个多星期,多次考虑跳出办公室window,我想通了问题出在哪里。

问题不在 python(某种程度上),但主要在 VBA 代码上,我刚刚添加了

Application.ScreenUpdating = True

在最后,问题停止了。不确定是 excel 宏完成后不更新的错误,还是宏完成后不允许屏幕更新的 python 错误。不过现在一切都好了。

谢谢!!

运行ning Excel 来自 Python

的宏

来自python的运行一个Excel宏,你几乎什么都不需要。在完成这项工作的脚本下面。从 Excel 内的宏更新数据的优点是您可以立即看到结果。您不必先保存或关闭工作簿。我用这个方法来更新实时股票报价。它快速且稳定。 这只是一个示例,但您可以使用 Excel.

中的宏执行任何操作
from os import system, path
import win32com.client as win32
from time import sleep

def isWorkbookOpen(xlPath, xlFileName):
    SeachXl = xlPath + "~$" + xlFileName
    if path.exists(SeachXl):
        return True
    else:
        return False

def xlRunMacro(macroLink):
    PathFile = macroLink[0]
    xlMacro = macroLink[1]
    isLinkReady = False

    # Create the link with the open existing workbook
    win32.pythoncom.CoInitialize()
    xl = win32.Dispatch("Excel.Application")
    try:
        wb = win32.GetObject(PathFile)
        isLinkReady = True
    except:
        NoteToAdd = 'Can not create the link with ' + PathFile
        print(NoteToAdd)
    if isLinkReady:
        # If the link with the workbook exist, then run the Excel macro
        try:
            xl.Application.Run(xlMacro)
        except:
            NoteToAdd = 'Running Excel Macro ' + xlMacro + ' failed !!!'
            print(NoteToAdd)
    del xl

def mainProgam(macroSettings):
    FullMacroLink = []
    PathFile = macroSettings[0] + macroSettings[1]
    FullMacroLink.append(PathFile)
    FullModuleSubrout = macroSettings[1] + '!' + macroSettings[2] + '.' + macroSettings[3]
    FullMacroLink.append(FullModuleSubrout)
    if isWorkbookOpen(macroSettings[0], macroSettings[1]) == False:
        # If the workbook is not open, Open workbook first.
        system(f'start excel.exe "{PathFile}"')
        # Give some time to start up Excel
        sleep(2)
    xlRunMacro(FullMacroLink)

def main():
    macroSettings = []
    # The settings below will execute the macro example
    xlPath = r'C:\test\'               # Change add your needs
    macroSettings.append(xlPath)
    workbookName = 'Example.xlsm'       # Change add your needs
    macroSettings.append(workbookName)
    xlModule = "Updates"                # Change add your needs
    macroSettings.append(xlModule)
    xlSubroutine = "UpdateCurrentTime"  # Change add your needs
    macroSettings.append(xlSubroutine)
    mainProgam(macroSettings)

if __name__ == "__main__":
    main()
    exit()

VBA Excel 宏

Option Explicit

Sub UpdateCurrentTime()
    Dim sht As Worksheet
    Set sht = ThisWorkbook.Sheets("Current-Time")
    With sht
        sht.Cells(2, 1).Value = Format(Now(), "hh:mm:ss")
    End With
End Sub

您也可以将它用作动态模块。在您的 python 项目中将上述模块另存为 RunExcelMacro.py。仅使用以下行后:

from RunExcelMacro import mainProgam
mainProgram(macroSettings)

它将完成工作,成功...