我正在使用 pywin32 包从 Python 控制 Excel 和 PowerPoint。我正在 Excel 中创建数据透视图,然后将这些图表复制到 ppt 幻灯片中。我如何检查特定任务是否已完成并且可以继续其他任务?当我立即运行代码时,通常会出现一些错误或 Excel 完全崩溃。我尝试在一些操作后添加一些“随机”延迟,然后效果更好。但我想等待完成特定操作所需的确切时间。
import win32com.client as win32
import sys
import time
import pandas as pd
#Pivot table config
rows = ['Temperature [°C]', 'Vdd [V]']
columns = ['Sample']
filters = []
xlsx_filepath = r"C:\Users\phrcka\Desktop\Excel_macro_test\DC_20230606_1407_test.xlsx"
ppt_filepath = r"C:\Users\phrcka\Desktop\Excel_macro_test\Python_test.pptx"
# construct the Powerpoint application object
pptApp = win32.Dispatch("PowerPoint.Application")
pptApp.Visible = True
#Open a template ppt file and make a copy
ppt_original = pptApp.Presentations.Open(ppt_filepath)
ppt_original.SaveAs(r"C:\Users\phrcka\Desktop\Excel_macro_test\Python_test_new.pptx")
ppt_original.Close()
#Open the copy
ppt = pptApp.Presentations.Open(r"C:\Users\phrcka\Desktop\Excel_macro_test\Python_test_new.pptx")
# construct the Excel application object
xlApp = win32.Dispatch('Excel.Application')
xlApp.Visible = True
# Create workbook
wb = xlApp.Workbooks.Open(xlsx_filepath)
# Create worksheets
ws_data = wb.Worksheets("Sheet1")
ws_pivot = wb.Worksheets("Demo")
#Loading data to dataframe
df = pd.read_excel(xlsx_filepath) # load the xslx file as data_frame
header_values = df.columns.values # extract the header from xlsx file
items_to_remove = rows + columns + filters + ['Corner']
values = list(filter(lambda x: x not in items_to_remove, header_values)) # Remove the fields which are already used in pivot chart. The rest values will be ploted.
def clear_pts(ws):
for pt in ws.PivotTables():
pt.TableRange2.Clear()
def insert_pt_field_set1(pt, rows, columns, filters, value):
pt.ClearTable() # Cleasr all pivot fields
xlApp.CalculateUntilAsyncQueriesDone()
#Pivot rows
i = 1
field_rows = {}
if len(rows) > 0:
for row in rows:
field_rows[row] = pt.PivotFields(row)
field_rows[row].Orientation = 1 # Orientation = 1 means Pivot rows in excel
field_rows[row].Position = i
i += 1
#Pivot columns
i = 1
field_columns = {}
if len(columns) > 0:
for column in columns:
field_columns[column] = pt.PivotFields(column)
field_columns[column].Orientation = 2 #Orientation = 2 means Pivot colums in excel
field_columns[column].Position = i
i += 1
#Pivot filters
i = 1
field_filters = {}
if len(filters) > 0:
for filter_string in filters:
field_filters[filters] = pt.PivotFields(filters)
field_filters[filters].Orientation = 3 #Orientation = 3 means Pivot filters in excel
field_filters[filters].Position = i
i += 1
#Pivot value
field_values = {}
field_values[value] = pt.PivotFields(value)
field_values[value].Orientation = 4 #Orientation = 4 means Pivot values in excel
field_values[value].Function = -4106 #Value set to Average
xlApp.CalculateUntilAsyncQueriesDone()
#Custom filters
field_rows['Temperature [°C]'].PivotItems("0").Visible = False
field_rows['Temperature [°C]'].PivotItems("45").Visible = False
def pivot_chart_create(ws_pivot):
ws_pivot.Activate()
ws_pivot.Cells(3,3).Select() # select cell within area of Pivot table
ws_pivot.Shapes.AddChart2(-1, 4).Select # create a Pivot chart from pivot table data: -1 means dafault style, 4 means line type of chart
def pivot_chart_adjust_and_copy(ws_pivot, rows, values):
ActiveChart = ws_pivot.ChartObjects(1).Chart
chart_settings(ActiveChart, rows, values)
ws_pivot.ChartObjects(1).Copy() # Copy the first chart which is on the sheet ws_pivot
def rgbToInt(rgb): # Convert RBG number to int
colorInt = rgb[0] + (rgb[1] * 256) + (rgb[2] * 256 * 256)
return colorInt
def chart_settings(ActiveChart, label_x, label_y): # adjust line colors and axis labeling
ActiveChart.FullSeriesCollection("SPEC_MAX").Format.Line.ForeColor.RGB = rgbToInt((255,0,0))
ActiveChart.FullSeriesCollection("SPEC_MAX").Format.Line.DashStyle = 10 # 10 = msoLineSysDash, 11 = msoLineSysDot
ActiveChart.FullSeriesCollection("SPEC_MAX").Format.Line.Weight = 3
ActiveChart.FullSeriesCollection("SPEC_MIN").Format.Line.ForeColor.RGB = rgbToInt((0, 176, 240))
ActiveChart.FullSeriesCollection("SPEC_MIN").Format.Line.DashStyle = 10 # 10 = msoLineSysDash, 11 = msoLineSysDot
ActiveChart.FullSeriesCollection("SPEC_MIN").Format.Line.Weight = 3
ActiveChart.FullSeriesCollection("SPEC_TYP").Format.Line.ForeColor.RGB = rgbToInt((0, 176, 80))
ActiveChart.FullSeriesCollection("SPEC_TYP").Format.Line.DashStyle = 10 # 10 = msoLineSysDash, 11 = msoLineSysDot
ActiveChart.FullSeriesCollection("SPEC_TYP").Format.Line.Weight = 3
ActiveChart.FullSeriesCollection("SIM_MAX").Format.Line.ForeColor.RGB = rgbToInt((255, 0, 0))
ActiveChart.FullSeriesCollection("SIM_MAX").Format.Line.DashStyle = 11 # 10 = msoLineSysDash, 11 = msoLineSysDot
ActiveChart.FullSeriesCollection("SIM_MAX").Format.Line.Weight = 3
ActiveChart.FullSeriesCollection("SIM_MIN").Format.Line.ForeColor.RGB = rgbToInt((0, 176, 240))
ActiveChart.FullSeriesCollection("SIM_MIN").Format.Line.DashStyle = 11 # 10 = msoLineSysDash, 11 = msoLineSysDot
ActiveChart.FullSeriesCollection("SIM_MIN").Format.Line.Weight = 3
ActiveChart.FullSeriesCollection("SIM_TYP").Format.Line.ForeColor.RGB = rgbToInt((0, 176, 80))
ActiveChart.FullSeriesCollection("SIM_TYP").Format.Line.DashStyle = 11 # 10 = msoLineSysDash, 11 = msoLineSysDot
ActiveChart.FullSeriesCollection("SIM_TYP").Format.Line.Weight = 3
ActiveChart.Axes(1, 1).HasTitle = True
merged_label_string_x = ', '.join(label_x)
ActiveChart.Axes(1, 1).AxisTitle.Text = 'Corner [' + merged_label_string_x +']' # Add an x label
ActiveChart.Axes(2, 1).HasTitle = True
ActiveChart.Axes(2, 1).AxisTitle.Text = label_y # Add an y label
# clear pivot tables on Report tab
clear_pts(ws_pivot)
# create pt cache connection
pt_cache = wb.PivotCaches().Create(1, ws_data.Range("A1").CurrentRegion) # https://docs.microsoft.com/en-us/office/vba/api/excel.xlpivottablesourcetype
# insert pivot table designer/editor
pt = pt_cache.CreatePivotTable(ws_pivot.Range("B3"), "Pivot_table")
ppt_slides_count = ppt.Slides.Count
initial_run = 1
for value in values:
print(' Processing value: %s' %value)
# xlApp.CalculateUntilAsyncQueriesDone()
time.sleep(1)
insert_pt_field_set1(pt, rows, columns, filters, value)
# xlApp.CalculateUntilAsyncQueriesDone()
time.sleep(1)
if initial_run == 1:
pivot_chart_create(ws_pivot)
initial_run +=1
pivot_chart_adjust_and_copy(ws_pivot, rows, value)
# xlApp.CalculateUntilAsyncQueriesDone()
time.sleep(1)
pptApp.ActiveWindow.View.GotoSlide(ppt_slides_count)
ppt.Slides.Add(ppt_slides_count + 1, 16) # Add new slide, often used layouts 16, 32, 29
ppt_slides_count +=1
pptApp.ActiveWindow.View.GotoSlide(ppt_slides_count)
ppt.Slides.Item(ppt_slides_count).Shapes.Paste().Select() # Paste the chart to ppt
pptApp.ActiveWindow.Selection.Cut()
ppt.Slides.Item(ppt_slides_count).Shapes(2).Select() # Shape 2 means second placeholder on the slide layout
pptApp.ActiveWindow.View.Paste()
ppt.Slides.Item(ppt_slides_count).Shapes(2).LinkFormat.BreakLink()
ppt.Slides(ppt_slides_count).Shapes.Placeholders.Item(1).TextFrame.TextRange.Text = value
ppt.Save()
我尝试使用 xlApp.CalculateUntilAsyncQueriesDone() 命令而不是延迟,但它没有解决问题。谢谢你的帮助
在提供的代码中,已经使用 xlApp.CalculateUntilAsyncQueriesDone() 进行异步计算处理,以等待所有数据透视表计算完成。但是,该错误可能是由于 Python 代码继续执行后异步查询仍在进行所致。 由于计算过程是异步的,因此您需要确保在将图表复制到 PowerPoint 幻灯片之前完成所有查询并更新结果。以下是如何实现此目标的示例:
# Create pivot chart in Excel'pivot_chart_create(ws_pivot)
# Wait for pivot table calculations to completewhile xlApp.CalculationState != 0:
time.sleep(1) # Wait for 1 second to check again
在此示例中,执行pivot_chart_create(ws_pivot)中的代码来创建数据透视图,然后使用while循环不断检查Excel的CalculationState。如果 CalculationState 不为 0(表示计算正在进行),则代码等待 1 秒,然后再次检查。计算完成后,代码将继续将图表复制到 PowerPoint 幻灯片。 通过在循环中不断检查 CalculationState,您可以确保在继续下一个任务之前所有数据透视表计算都已完成。这应该有助于避免 Excel 中的错误和崩溃。