| name | run2_openpyxl-cross-sheet |
| description | Using openpyxl to edit Excel workbooks with cross-sheet formulas while preserving all formatting |
Editing Excel Workbooks with openpyxl: Cross-Sheet References
Workflow Overview
- Load existing workbook with
load_workbook()
- Access sheets by name:
wb['SheetName']
- Modify specific cells
- Save the file
- Recalculate formulas with external tool (e.g., LibreOffice)
Loading Workbooks
from openpyxl import load_workbook
wb = load_workbook('file.xlsx')
task_sheet = wb['Task']
data_sheet = wb['Data']
sheet_names = wb.sheetnames
Cross-Sheet References in Formulas
Critical: Use ! Syntax, Not .
When writing cross-sheet references in openpyxl formulas:
- Use:
=INDEX(Data!$H$21:$M$40, ...) ← Excel/LibreOffice compatible
- Not:
=INDEX(Data.$H$21:$M$40, ...) ← Causes #NAME? errors
Correct Sheet Reference Patterns
formula = "=INDEX(Data!$H$21:$M$40, MATCH($D12, Data!$B$21:$B$40, 0), MATCH(H$9, Data!$H$4:$M$4, 0))"
formula = "=(SUM(Data!$H$12:$H$17) - SUM(Data!$H$19:$H$24)) / SUM(Data!$H$26:$H$31) * 100"
formula = "=ROUND(MIN(Data!$H$35:$H$40), 1)"
Cell Reference Anchoring
Copying Formulas with Mixed References
Use $ selectively so references adjust correctly when copying:
formula = f"=INDEX(Data!$H$21:$M$40, MATCH($D{row}, Data!$B$21:$B$40, 0), MATCH({col_letter}$9, Data!$H$4:$M$4, 0))"
When copied:
- Right (H → I):
H$9 becomes I$9 ✓
- Down (12 → 13):
$D12 becomes $D13 ✓
Setting Formulas in openpyxl
sheet['H12'] = "=INDEX(Data!$H$21:$M$40, MATCH($D12, Data!$B$21:$B$40, 0), MATCH(H$9, Data!$H$4:$M$4, 0))"
sheet.cell(row=12, column=8).value = "=INDEX(...)"
for row in range(12, 18):
for col in range(8, 13):
col_letter = chr(64 + col)
formula = f"=SUM({col_letter}12:{col_letter}17)"
sheet[f'{col_letter}{row}'] = formula
Formatting Preservation
Automatic Preservation
openpyxl automatically preserves when loading and saving:
- Cell colors and fill patterns (including yellow background)
- Font styles and colors (including blue text)
- Cell borders and alignment
- Number formatting
- Merged cells
sheet['H12'] = formula
sheet['D9'] = value
Checking Cell Properties
cell = sheet['H12']
print(cell.fill.start_color.rgb)
print(cell.font.color.rgb)
print(cell.number_format)
Saving and Recalculating
Save Changes
wb.save('file.xlsx')
Recalculate Formulas
openpyxl stores formulas as strings but doesn't calculate them. Use external tool:
python3 recalc.py file.xlsx
Handling Errors After Recalculation
Common Errors and Fixes
| Error | Cause | Fix |
|---|
| #NAME? | Sheet reference wrong (using . instead of !) | Change Data.$H$21 to Data!$H$21 |
| #REF! | Invalid cell reference | Verify range exists and sheet name is correct |
| #N/A | Value not found in MATCH | Check criteria matches data exactly |
| #DIV/0! | Division by zero | Add error handling: =IFERROR(formula, 0) |
Python Script Pattern
from openpyxl import load_workbook
wb = load_workbook('gdp.xlsx')
task = wb['Task']
for row in range(12, 18):
for col in range(8, 13):
col_letter = chr(64 + col)
formula = f"=INDEX(Data!$H$21:$M$40, MATCH($D{row}, Data!$B$21:$B$40, 0), MATCH({col_letter}$9, Data!$H$4:$M$4, 0))"
task[f'{col_letter}{row}'] = formula
for col in range(8, 13):
col_letter = chr(64 + col)
task[f'{col_letter}42'] = f"=ROUND(MIN({col_letter}35:{col_letter}40), 1)"
task[f'{col_letter}50'] = f"=ROUND((SUM({col_letter}12:{col_letter}17)-SUM({col_letter}19:{col_letter}24))/SUM({col_letter}26:{col_letter}31)*100, 1)"
wb.save('gdp.xlsx')
Performance Tips
- Batch operations: Loop through ranges instead of individual cells
- Avoid data_only=True during edit: It replaces formulas with values permanently
- Use cell coordinates: A1, H12, L40 notation is clearer than row/column numbers
- Verify before save: Print a few cells to confirm formulas are correct