Как мы можем создать несколько файлов PDF, используя разные экземпляры win32com? - PullRequest
3 голосов
/ 20 февраля 2020

Я пытался создать несколько файлов PDF с помощью DispatchEx, но когда я пытаюсь проверить свой код, он создает только первый файл PDF, в то время как все другие запросы не выполняются со странными ошибками. Что я делаю неправильно и / или как я могу эффективно обрабатывать вызовы нескольких клиентов одновременно, чтобы генерировать соответствующие им pdf-файлы, которые они запрашивают?

Вот эта часть моего кода:

            rundate = "Quote_{:%d%m%Y_%H%M%S%f}".format(datetime.now())
            pythoncom.CoInitialize()
            FILENAME = "D:/packages/abc.pptx"
            APPLICATION = win32com.client.DispatchEx("PowerPoint.Application")
            APPLICATION.Visible = True
            path_ppt = shutil.copy(FILENAME, "D:/{0}.pptx".format(rundate))
            PRESENTATION = APPLICATION.Presentations.Open(path_ppt)
            Slide1 = PRESENTATION.Slides(1)
            Shape1 = Slide1.Shapes(1)
            print(Shape1.AlternativeText)
            for shape in Slide1.Shapes:
                if shape.AlternativeText:
                    print(shape.AlternativeText)
                if shape.HasTextFrame:
                    shape.TextFrame.TextRange.Replace(FindWhat="#abc",ReplaceWhat="THAILAND", WholeWords=False)
                if shape.AlternativeText == "1":
                    shape.Fill.UserPicture("D:/1.jpg")
                if shape.AlternativeText == "2":
                    shape.Fill.UserPicture("D:/2.jpg")
                if shape.AlternativeText == "3":
                    shape.Fill.UserPicture("D:/3.jpg")
            PATH_TO_PDF = "{0}{1}{2}".format(r'd:/',rundate,'.pdf')
            PRESENTATION.SaveAs(PATH_TO_PDF, 32)
            APPLICATION.Quit()
            PRESENTATION.Close()
            PRESENTATION =  None
            APPLICATION = None
            os.remove(path_ppt)

PS - Код успешно создает столько копий ppt (используя shutil), сколько запросов отправлено ему, но win32com выдает ошибку, когда несколько запросов выполняются за короткий промежуток времени, например shape.Al альтернативный текст не найден, объект не существует и т. Д. c .

Ответы [ 2 ]

1 голос
/ 20 февраля 2020

Вы можете решить проблему с выходом APPLICATION, запустив этот код в отдельном процессе. (Игнорирование риска использования жестко закодированного имени файла при одновременных вызовах).

Создайте еще один модуль в пакете проекта

powerpointer.py

import shutil
import sys
import subprocess as subp

def do_powerpoint(filename):
   """run external copy of self to do powerpoint stuff"""
   # sys.executable is the python.exe you are using, __file__ is the
   # path to this module's source and filename you pass in
   return subp.call([sys.executable, __file__, filename])

def _do_powerpoint(filename):
    rundate = "Quote_{:%d%m%Y_%H%M%S%f}".format(datetime.now())
    pythoncom.CoInitialize()
    APPLICATION = win32com.client.DispatchEx("PowerPoint.Application")
    APPLICATION.Visible = True # I think False is better so you dont see it pop up
    path_ppt = shutil.copy(filename, "D:/{0}.pptx".format(rundate))
    PRESENTATION = APPLICATION.Presentations.Open(path_ppt)
    Slide1 = PRESENTATION.Slides(1)
    Shape1 = Slide1.Shapes(1)
    print(Shape1.AlternativeText)
    for shape in Slide1.Shapes:
        if shape.AlternativeText:
            print(shape.AlternativeText)
        if shape.HasTextFrame:
            shape.TextFrame.TextRange.Replace(FindWhat="#abc",ReplaceWhat="THAILAND", WholeWords=False)
        if shape.AlternativeText == "1":
            shape.Fill.UserPicture("D:/1.jpg")
        if shape.AlternativeText == "2":
            shape.Fill.UserPicture("D:/2.jpg")
        if shape.AlternativeText == "3":
            shape.Fill.UserPicture("D:/3.jpg")
    PATH_TO_PDF = "{0}{1}{2}".format(r'd:/',rundate,'.pdf')
    PRESENTATION.SaveAs(PATH_TO_PDF, 32)
    PRESENTATION.Close()
    APPLICATION.Quit()
    os.remove(path_ppt)

if __name__ == "__main__":
    _do_powerpoint(sys.argv[1]) # will raise error if no parameters

Теперь, в вашем основном коде, import powerpointer и при необходимости вызовите powerpointer.do_powerpoint(filename). Он будет запускать свой собственный модуль в виде скрипта, и в этом случае у вас будет только 1 объект приложения.

0 голосов
/ 21 февраля 2020

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

1) Удалить

APPLICATION.Visible = True 

2) Изменить

APPLICATION.Presentations.Open(path_ppt) 

на

PRESENTATION = APPLICATION.Presentations.Open(path_ppt, WithWindow=False, ReadOnly=False) 

3) Удалить

APPLICATION.Quit()
PRESENTATION.Close()
PRESENTATION =  None
APPLICATION = None
os.remove(path_ppt) 

Проблема заключалась в том, что если я использовал

APPLICATION.Quit()
PRESENTATION.Close()
PRESENTATION =  None
APPLICATION = None
os.remove(path_ppt) 

в коде и совершал одновременные вызовы, он давал "вызов" была отклонена по ошибке вызываемого абонента. Когда я удалил эти строки кода, он смог опубликовать sh столько PDF-файлов, сколько было сгенерировано powerpoints, но экземпляры PowerPoint все еще были там и копии файлов тоже, пожирая системные ресурсы. Использование WithWindow = False помогает автоматически закрывать экземпляр PowerPoint после публикации PDF. Единственная оставшаяся проблема - это оставленные копии файлов PowerPoint, которые можно удалить в конце дня, когда система простаивает на стороне вашего клиента.

PS - я использую ExportAsFixedFormat вместо SaveAs для PDF PRESENTATION.ExportAsFixedFormat(PATH_TO_PDF, 32, PrintRange=None)

...