G_24 Ответов: 1

Как открыть книгу excel в pywin32, не препятствуя функции сохранения openpyxl


Я написал программу для автоматизации создания pdf-счетов для заказов Amazon. Amazon создает рабочую книгу excel со всей информацией, необходимой для выставления счета. Мой код извлекает данные, затем добавляет к ним описание продукта и другие материалы из отдельной книги excel и отправляет их в предопределенный шаблон в третьей книге. Затем эта книга экспортируется в формате pdf и отправляется отдельным клиентам.

Вот полная вещь для справки, но проблема заключается в блоке 3, строка formXl = excel.Рабочая тетрадь.Открытый путь").

import openpyxl as xl # Manipulate Form
from datetime import date # Imports Date module
import win32com.client # Open excel save as pdf
from pywintypes import com_error # Show error if something wrong in conversion

#-------------------------------------------------------------------------------
# BLOCK 1: IMPORTS ALL THE WORKBOOKS NEEDED TO INPUT AND OUTPUT DATA

# Import the Amazon and descriptions spreadsheets with openpyxl
workbook = xl.load_workbook(r"C:\Users\t_man\Documents\ThatsArte Fatture XL\Descrizioni.xlsx", read_only=True, data_only=True)
descriptions = workbook.active
workbook = xl.load_workbook(r"C:\Users\t_man\Documents\ThatsArte Fatture XL\Amazon ordini 2020.xlsx", read_only=True, data_only=True)
fatture = workbook.active
workbook = xl.load_workbook(r"C:\Users\t_man\Documents\ThatsArte Fatture XL\Output Form.xlsx", read_only=False, data_only=True)
form = workbook.active

#-------------------------------------------------------------------------------
# BLOCK 2: GATHERS THE ORDERS AND PREPARES THEM FOR EXPORT

# Defines orders list, current date and starting invoice number

today = date.today()
invoice_num = int(1)
orders = []

for row in fatture.iter_rows(min_row=2, max_row=fatture.max_row, min_col=1, max_col=fatture.max_column, values_only=True):
# Create order - store values in key-value pairs
    order = {"Buyer Name":row[11],
             "Shipping Address":row[25],
             "Shipping Details": f"{row[28]}, {row[29]} {row[30]}",
             "Invoice num": invoice_num, # Set initial invoice number
             "Emission Date": today.strftime("%d/%m/%Y"),
             "Order ID":row[0],
             "Items Qty": int(1),
             "Product Code":row[13],
             "Unit Price":float(row[17]),
             "Total Price":float(row[17]),
             "Date String": today.strftime("%B %d, %Y")}

    # Appends description data
    for row in descriptions.iter_rows(min_row=2, max_row=descriptions.max_row, min_col=1, max_col=descriptions.max_column, values_only=True):
        if order["Product Code"] == row[0]:
            order.update({"Description": row[3],
                          "HTS Heading": row[1],
                          "FDA Code": row[2]})
            break
    orders.append(order)
    invoice_num += 1

# Merge all rows with the same order ID
for i in reversed(range(len(orders)-1)):
    if orders[i]["Order ID"] == orders[i+1]["Order ID"]:
        orders[i]["Total Price"] += orders[i+1]["Unit Price"]
        orders[i]["Items Qty"] += 1
        orders.remove(orders[i+1])

print(orders)

#-------------------------------------------------------------------------------
# BLOCK 3: SENDS ORDER TO FORM, SAVES AS PDF AND REPEATS FOR ALL ORDERS

# Open excel in background
print("Dispatching...")
excel = win32com.client.Dispatch("Excel.Application")
excel.Visible = False
formXl = excel.Workbooks.Open(r"C:\Users\t_man\Documents\ThatsArte Fatture XL\Output Form.xlsx")

for order in orders:
    # Send order to correct cells in excel output file with default template (do not overwrite)
    cells = ["A14", "A15", "A16", "C22", "C23", "C24", "A30", "B30", "I30", "J30", "B47", "C30", "E30", "G30"]
    i = 0
    for key in order:
        form[cells[i]] = order[key]
        i += 1
    # Checkpoint
    workbook.save(r"C:\Users\t_man\Documents\ThatsArte Fatture XL\Output Form.xlsx")

    # Save excel file as pdf named with order ID
    try:
        print("Start conversion to PDF: {}".format(order["Buyer Name"]))
        formXl.ActiveSheet.ExportAsFixedFormat(0, r"C:\Users\t_man\Documents\ThatsArte Fatture XL\Fatture\Fattura {}".format(order["Order ID"]))
    except com_error as e:
        print("Failed:" + e)
    else:
        print("Success.")
    # Checkpoint
    workbook.save(r"C:\Users\t_man\Documents\ThatsArte Fatture XL\Output Form.xlsx")
    # Overwrites the excel file by looping through again

# Close workbook and PDF
formXl.Close()
excel.Quit()


The issue I am having is that when the problematic line is executed, the excel file in question is opened in the foreground of my pc. This causes the workbook.save("path") lines to fail due to [Errno 13]: Permission Denied, which basically means sorry can't save the file cause it's open. workbook.save("path") works perfectly when the file is closed and excel successfully launches in the background when excel = win32com.client.Dispatch("Excel.Application") is executed. I have the same problem when trying to more test files, so I don't believe this will be fixed by changing file properties but rather the code. I thought this ought to be solved by excel.Visible = False but apparently not.

Есть ли способ открыть книгу в фоновом режиме, чтобы я мог продолжать перебирать заказы и экспортировать xlsx в формате pdf, не останавливаясь из-за ошибок разрешений?

Все остальное работает нормально.

Что я уже пробовал:

различные файлы. Как только чтение, так и чтение и запись.

Этот ответ на аналогичный вопрос из stack exchange

Изолируя проблемную линию и воспроизводя проблему, меняя одну переменную за раз.

1 Ответов

Рейтинг:
2

Richard MacCutchan

Я думаю, что проблема в том, что вы пытаетесь сохранить в том же месте, что и открытый файл, и Windows не позволит вам этого сделать. Большинство систем просто использовали бы Save без указания имени файла или Close, и пусть пользователь ответит, когда его спросят, следует ли сохранить файл. Я не использовал openpyxl, но уверен, что есть способ закрыть файл без необходимости указывать его назначение. Проверьте документацию, чтобы узнать, какие еще существуют способы закрыть текущий файл.


G_24

Привет, Ричард, спасибо за твой ответ. Openpyxl запрашивает путь для сохранения файла, в противном случае он выдает ошибку. На самом деле, когда он сохраняет файл по указанному пути, он не "сохраняет как" отдельный файл, а сохраняет уже существующий файл. Это работает просто отлично, когда файл, о котором идет речь, закрыт. Моя проблема касается win32, открывающего книгу на переднем плане, а не на заднем плане. Excel успешно отправляет сообщения в фоновом режиме, но excel.Рабочая тетрадь.Open() этого не делает. После того, как книга открыта, openpyxl функции экономии не получается.

Richard MacCutchan

Ну, это, по-видимому, проблема с библиотекой openpyxl. Я использовал пространство имен Microsoft interop для управления файлами Excel с помощью C#, и метод Save не требует имени файла. Я думаю, вам придется изменить свой код, чтобы он сохранил его в другом имени файла.