2017-01-21 18 views
1

私は、エクセルブックでモデラーが直接消費する必要がある出力を生成するシミュレーションをPythonで実行しています。エクセルのスプレッドシートテンプレートに自分のデータを直接出力するコードを生成しました。データをテンプレートに直接出力するために生成したコードは問題ありませんが、私が実行している問題は、モデラーに一連のブックが「リンク」されていることです。データをスプレッドシートに挿入すると、ユーザーが物理的にブックを開いて「リンクの編集」→「値の更新」を行わない限り、そのブックへのリンクは更新されません。 1つのブックがある場合、ユーザーは問題なくブックを開くことができます。実際には、リンクが更新される必要がある100以上のブックがあります。残念ながら、ワークブックをリンクする際のモデラーのアプローチを変更するために私ができることは何もありません。私ができるのは、そのアプローチに対応することだけです。更新リンクのためのPythonを使用してExcel

私の目標は、1)シミュレートされたデータの生成、2)生成されたデータのモデラーのワークブックへの挿入、3)ワークブック間のすべてのリンクの更新を可能にするPythonソリューションを作成することです。最終的には、合理化するために、私は3つのエンドツーエンドのPythonプログラムで3つすべてを実行できるようにしたいと考えています。私は(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でデバッグする方法と、次のワークブックに進むことができる唯一の方法私はこのマクロと終了、デバッグを実行する場合、プログラムは、それが再び同じエラーが発生するまで実行し続けます

sub UpdateLinkValues() 
    Application.AskToUpdateLinks = False 
end sub 

にマクロを変更した場合OOKです。私の最初の考えは、ワークブックを開いてマクロを実行しようとするタイミングの問題かもしれないということでした。私が発見した問題を回避するには、私はマクロとアプリケーションの可視性を変更することができるということです。

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

xl_app.Visible = True 

これは正常に動作しますが、私は開いているブックのそれぞれを持っていることのファンではありませんそれは長い時間がかかりますので近づいてください。私の質問は、誰もこのランタイムエラーが出てくる理由を知っていますか?あるいは、誰かがPythonでこの実行時エラーを例外として傍受する方法を知っていますか?私がこのエラーをPythonの例外として傍受することができれば、その特定のワークブックの代替ソリューションを使用することができます。

ありがとうございます!

+0

興味深いことに、マクロを ".xlsx"形式で保存することはできないため、* random *エラーとは別に作業していました。 ".xlsm"または ".xlsb"タイプが必要です。 – Parfait

答えて

0

初期化するCOMオブジェクト、つまりxl_appwbオブジェクトを使用してPythonに直接メソッドUpdateLinkを実行させることを検討してください。各ワークブックにマクロを作成して呼び出す必要はありません。UpdateLink()以下

はCOM例外を上げ、LinkSourcesとして何のリンクが値を返さないだろうしている場合ブックのtry/except/finallyブロックに包まれて、非常に表示されるエラー:

実行時エラー '無料のCPUに使用した後:1004' メソッド 『updatelink』オブジェクトのメソッド 『_Workbook』 はまた、(Set wb = NothingすぎVBAで良いのベストプラクティス)のオブジェクトの初期化を解除するようにしてください

を失敗しましたその他のリソースは、ガベージコレクションまでバックグラウンドプロセスとして残ります。別に

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 

- ExcelやMS Officeのアプリケーションと、デフォルトでVBAの船が、それは実際には別のコンポーネントですが。チェックするには、Tools \ References in VBA IDEの下にVBAが最初にチェックされた項目であり、何も組み込まれていないことがわかります。実際には、VBAはPythonで行っていることを正確に行います:Excel Object LibraryへのCOMインターフェイスの作成。したがって、ある意味ではVBAはExcelとPythonに関連しています!

関連する問題