Excel import/export example
Short description
This example demonstrates how to read and write Excel files from an Add-on. The Excel file access is implemented using openpyxl.
For demonstration purposes, project keywords are read from or written to an Excel file, but the example can be used as a template for other items.
Prerequisite
Both example scripts check if a project has been opened and quit with an error message dialog if this is not the case:
if not hasattr(gom.app, 'project'):
gom.script.sys.execute_user_defined_dialog (file='no_project.gdlg')
quit(0)
Excel read example: import_project_keywords.py
Two packages are imported from openpyxl
:
from openpyxl import load_workbook
from openpyxl.utils.cell import coordinate_from_string, column_index_from_string
The current project keywords and their values are listed with:
print("-- Current keywords --")
for k in gom.app.project.project_keywords:
print(f"{k}='{gom.app.project.get(k)}'")
A dialog is used to request the Excel file to be opened:
RESULT=gom.script.sys.execute_user_defined_dialog (file='dialog.gdlg')
print("\nOpening", RESULT.file)
The workbook is opened from the Excel file and a worksheet object ws
is created:
wb = load_workbook(filename = RESULT.file)
ws = wb['Sheet']
As a tradeoff between simplicity and flexibility, the script allows to configure the cells which contain keywords, but assumes that the keyword descriptions and values are located at fixed positions in the adjacent cells:
# Cells containing the keywords
# - descriptions are expected in column+1
# - values are expected in column+2
layout = [
"A2",
"A3",
"A4",
#...
]
Cells can be accessed by their coordinates (e.g. “A1”) or by row and column. The method coordinate_from_string()
splits the coordinate into row and column (e.g. coordinate_from_string("A1") => ("A", 1)
). The method column_index_from_string()
converts a column name into
a column index (e.g. column_index_from_string("A") => 1
).
# Get keyword by cell name, e.g. cell="A1"
key = ws[cell].value
# Get column as a string
col, row = coordinate_from_string(cell)
# Get column index
col = column_index_from_string(col)
# Keyword description - next to keyword
desc = ws.cell(row=row, column=col+1).value
# Value - next to keyword description
val = ws.cell(row=row, column=col+2).value
In the array gom.app.project.project_keywords
, the keywords have the prefix user_
, but this prefix must be omitted for the method gom.script.sys.set_project_keywords()
.
The script distinguishes the following cases:
The keyword in the Excel file is new
The keyword in the Excel file already exists, but its value has changed
The keyword in the Excel file already exists, but its description has changed
The keyword in the Excel file already exists and remains unchanged
ukw = "user_" + key
if not ukw in gom.app.project.project_keywords:
print(f"New keyword {key}='{val}' added")
gom.script.sys.set_project_keywords(keywords={key:val}, keywords_description={key:desc})
else:
ex_val = gom.app.project.get(ukw)
ex_desc = gom.app.project.get(f'description({ukw})')
if (val == ex_val) and (desc == ex_desc):
print(f"Existing keyword {key}='{val}' - not changed")
else:
if val != ex_val:
print(f"Existing keyword {key}='{ex_val}' changed to '{val}'")
gom.script.sys.set_project_keywords(keywords={key:val})
if desc != ex_desc:
print(f"Existing keyword {key} - description '{ex_desc}' changed to '{desc}'")
gom.script.sys.set_project_keywords(keywords_description={key:desc})
The steps described above are repeated for all Excel cells listed in layout[]
:
for cell in layout:
# Get keyword, description and value
#...
# Convert data type, if required
#...
# Add or update project keywords
#...
Finally, the updated project keywords are listed:
print("\n-- Updated keywords --")
for k in gom.app.project.project_keywords:
print(f"{k}='{gom.app.project.get(k)}'")
Excel write example: export_project_keywords.py
Two packages are imported from openpyxl
:
from openpyxl import Workbook
from openpyxl.styles import Font
The following code creates a workbook and writes text into three cells of the worksheet as a table header:
# Create a workbook
wb = Workbook()
# Select the active worksheet
ws = wb.active
# Create table header
ws['A1'] = "Project Keyword"
ws['B1'] = "Description"
ws['C1'] = "Value"
Next, the table header is formatted and the column widths are adjusted:
# Change table header layout
for cell in ['A1', 'B1', 'C1']:
ws[cell].font = Font(bold=True, size=16)
ws.column_dimensions['A'].width = 30
ws.column_dimensions['B'].width = 70
ws.column_dimensions['C'].width = 50
The script iterates over all project keywords and gets the values and keyword descriptions. A type conversion has been implemented for date entries. The method ws.append([key, val, desc])
writes the variables key
, val
and desc
- which are combined into an array - to the next three cells of the worksheet.
for key in gom.app.project.project_keywords:
val = gom.app.project.get(key)
desc = gom.app.project.get(f'description({key})')
# Remove prefix 'user_' from key
key = key[5:]
print(f"{key} - {desc}: {val}")
# Special case - convert gom.Date to datetime-object
# and format Excel cell accordingly
if type(val) is gom.Date:
val = datetime.datetime(val.year, val.month, val.day)
ws.append([key, desc, val])
ws.cell(row=ws.max_row, column=2).number_format = "yyyy-mm-dd"
else:
ws.append([key, desc, val])
Specific cells can be selected with ws.cell(row=<row>, column=<column>)
. This can be used for
Changing the content:
ws.cell(row=ws.max_row+2, column=1).value = f'Exported from {gom.app.application_name}'
Setting the font:
ws.cell(row=ws.max_row, column=1).font = Font(size=8)
Adjusting the cell format:
ws.cell(row=ws.max_row, column=2).number_format = "yyyy-mm-dd"
etc.
Finally the prepared spreadsheet is written to a file. The file path has been requested previously with a dialog.
wb.save(RESULT1.file)
To handle the common case that the Excel file cannot been written by the script, because it it currently opened in the Excel software, a loop with exception handling has been implemented:
# Repeat until file was written successfully or
# user has cancelled.
while True:
try:
# File selection dialog
RESULT1=gom.script.sys.execute_user_defined_dialog (dialog={
#...
})
except gom.BreakError:
# User has cancelled
break
# ... create spreadsheet with project keywords ...
# Try to write Excel file, this will fail if the file is still open in Excel!
retry = False
try:
wb.save(RESULT1.file)
except PermissionError:
retry = True
# Notification dialog
RESULT=gom.script.sys.execute_user_defined_dialog (dialog={
#...
})
if not retry:
break