2017-01-21 108 views
1

我正在Python中運行模擬程序,該模擬程序生成的輸出需要由其建模工作人員在他們的Excel工作簿中直接使用。我生成的代碼將直接將我的數據輸出到他們的Excel電子表格模板中。我生成的代碼直接輸出到他們的模板是好的,但我遇到的問題是,建模者有一系列「鏈接」在一起的工作簿。如果我將數據插入到電子表格中,除非用戶以「編輯鏈接」 - >「更新值」的形式打開工作簿,否則指向該工作簿的鏈接不會更新。如果有一個工作簿,那麼用戶可以簡單地打開工作簿而不會出現問題。實際上,將會有超過100個需要更新鏈接的工作簿。不幸的是,我沒有辦法改變建模者鏈接工作簿的方法 - 我能做的唯一事情就是適應他們的方法。使用Python更新Excel電子表格的鏈接使用Python

我的目標是創建一個Python解決方案,它允許我1)生成模擬數據,2)將我生成的數據插入到建模者的工作簿中,3)更新工作簿之間的所有鏈接。最終,爲了簡化流程,我希望能夠在一個端到端的Python程序中完成所有三個任務。我已經解決了(1)和(2),並且我對(3)的解決方案几乎可行。我已經生成了以下功能的腳本:

from win32com.client import Dispatch 
import pandas as pd 
from openpyxl import load_workbook 
import os 
import time 

def run_macro(workbook_name, vba_sub, com_instance): 
    wb = com_instance.workbooks.open(workbook_name) 
    wb.RefreshAll() 
    xl_module = wb.VBProject.VBComponents.Add(1) 
    xl_module.CodeModule.AddFromString(vba_sub.strip()) 
    com_instance.Application.Run('UpdateLinkValues') 
    wb.Save() 
    wb.Close() 

    return True 

def main(): 
    dir_root = ("C:\\Model_Spreadsheets") 

    vba_sub = \ 
     ''' 
     sub UpdateLinkValues() 
      Application.AskToUpdateLinks = False 
      ActiveWorkbook.UpdateLink Name:=ActiveWorkbook.LinkSources 
     end sub 
     ''' 

    xl_app = Dispatch("Excel.Application") 
    xl_app.Visible = False 
    xl_app.DisplayAlerts = False 

    for root, dirs, files in os.walk(dir_root): 
     for fn in files: 
      if fn.endswith(".xlsx") and fn[0] is not "~": 
       run_macro(os.path.join(root, fn), vba_sub, xl_app) 
    xl_app.Quit() 


if __name__ == "__main__": 
    main() 

這個腳本是非常接近正確的解決方案,我正在尋找,但我碰上了VBA錯誤看似「隨機」:

run-time error '1004' method 'updatelink' method of object '_workbook' failed 

此錯誤每次我嘗試運行此腳本時都會出現,但每次都不會出現在同一個工作簿中 - 有時候,它會在第一個工作簿上出現,有時在第15次出現,等等......

我有一個選項在VBA中進行調試,以及我可以繼續進行下一步工作的唯一方法OOK是,如果我改變宏

sub UpdateLinkValues() 
    Application.AskToUpdateLinks = False 
end sub 

我辦這個宏和退出調試,程序將繼續運行,直到它再次遇到同樣的錯誤。我的第一個想法是,在打開工作簿並嘗試運行宏之間可能存在計時問題。我發現一個解決辦法是,我可以改變宏觀和應用可視性:

vba_sub = \ 
    ''' 
    sub UpdateLinkValues() 
     Application.AskToUpdateLinks = False 
    end sub 
    ''' 

xl_app.Visible = True 

這工作得很好,但我不是讓每個工作簿開放的粉絲並關閉,因爲它需要很長時間。我的問題是,有人知道爲什麼這個運行時錯誤即將出現 - 有解決方案嗎?或者,也許有人知道如何攔截Python中的這個運行時錯誤作爲異常嗎?如果我可以截取這個錯誤作爲Python中的異常,那麼我可以使用我的替代解決方案來處理這些特定的工作簿。

在此先感謝!

+0

有趣的是,由於無法將宏保存爲「.xlsx」格式,因此甚至與* random *錯誤區別開來。您需要「.xlsm」或「.xlsb」類型。 – Parfait

回答

0

考慮讓Python直接運行方法UpdateLink與您初始化的COM對象,即xl_appwb對象。不需要在每個工作簿中創建一個宏,然後調用它。

下面UpdateLink()被包裹在的情況下,工作簿中的try/except/finally塊已經沒有任何聯繫作爲LinkSources將返回值,提高COM異常,非常錯誤您收到:

運行時錯誤「 1004' 方法'updatelink _Workbook‘對象的方法’失敗

此外,一定要取消初始化對象(在VBA良好的最佳實踐過:Set wb = Nothing)使用,不佔用CPU後資源,否則它們將保留爲後臺進程直到垃圾收集。

def run_macro(workbook_name, com_instance): 
    wb = com_instance.workbooks.open(workbook_name) 
    com_instance.AskToUpdateLinks = False 
    try: 
     wb.UpdateLink(Name=wb.LinkSources()) 

    except Exception as e: 
     print(e) 

    finally: 
     wb.Close(True) 
     wb = None  
    return True 

def main(): 
    dir_root = ("C:\\Model_Spreadsheets") 

    xl_app = Dispatch("Excel.Application") 
    xl_app.Visible = False 
    xl_app.DisplayAlerts = False 

    for root, dirs, files in os.walk(dir_root): 
     for fn in files: 
      if fn.endswith(".xlsx") and fn[0] is not "~": 
       run_macro(os.path.join(root, fn), xl_app) 
    xl_app.Quit() 
    xl = None 

除了 - 儘管VBA船舶默認使用Excel和MS Office應用程序,它實際上是一個單獨的組件。要檢查,在VBA IDE的Tools \ References下,你會看到VBA是第一個選中的項目,沒有任何內建的。事實上,VBA完全是你在Python中做的事情:製作一個到Excel對象庫的COM接口。所以從某種意義上講,VBA與Excel和Python是相關的!