2017-01-21 4 views
1

Я запускаю симуляции в Python, которые генерируют выходные данные, которые нужно непосредственно потреблять модельером в своих книгах excel. Я создал код, который будет напрямую выводить мои данные в их шаблон Excel Excel. Код, который я сгенерировал для вывода данных непосредственно на их шаблон, хорош, но проблема, с которой я сталкиваюсь, заключается в том, что модельер имеет серию книг, которые «связаны» вместе. Если я вставляю свои данные в свою электронную таблицу, ссылки на эту книгу не обновляются, если пользователь физически не открывает книгу «Изменить ссылки» -> «Обновить значения». Если бы была одна книга, пользователь мог бы просто открыть книгу без проблем. В действительности, будет более 100 книг, которые нуждаются в обновлении ссылок. К сожалению, я ничего не могу поделать, чтобы изменить подход модельера при связывании книг - единственное, что я могу сделать, - это их подход.Обновление ссылок для таблицы 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, и единственный способ, которым я могу перейти к следующей работе ООК, если изменить макрос

sub UpdateLinkValues() 
    Application.AskToUpdateLinks = False 
end sub 

если запустить этот макрос и выхода отладки, программа будет продолжать работать, пока он снова не встречает ту же ошибку. Моя первая мысль заключалась в том, что, возможно, существует проблема с тем, что я открываю книгу и пытаюсь запустить макрос. Обходной, что я нашел, что я могу изменить макрос и приложение видимости:

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

и

xl_app.Visible = True 

Это прекрасно работает, но я не поклонник того, чтобы каждый из книг открытых и закрыть, потому что это занимает много времени. Мой вопрос в том, кто-нибудь знает, почему эта ошибка во время исполнения - с решением? Или, может быть, кто-нибудь знает, как перехватить эту ошибку во время выполнения в Python как исключение? Если я могу перехватить эту ошибку как исключение в python, тогда я мог бы использовать свое альтернативное решение для этих конкретных книг.

Заранее благодарен!

+0

Интересно, что это даже работало отдельно от случайной ошибки, поскольку вы не можете сохранять макросы в форматах «.xlsx». Вам нужны типы «.xlsm» или «.xlsb». – Parfait

ответ

0

Рассмотрите возможность запуска Python методом UpdateLink с инициализацией COM-объектов, а именно объектов xl_app и wb. Нет необходимости создавать макрос в каждой книге, а затем вызывать его.

Ниже 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 вы увидите, что VBA - это первый отмеченный элемент, ничего не встроенный. Фактически, VBA делает именно то, что вы делаете в Python: создание COM-интерфейса в библиотеке объектов Excel. Итак, в некотором смысле VBA так же, как и Excel, и Python!

 Смежные вопросы

  • Нет связанных вопросов^_^