Curso sobre como trabajar con hojas de cálculo (Excel, Calc) usando openpyxl en Python (III)

tutoriales

Índice:

Después de ver un poco como eran los libros ahora nos vamos a centrar en las hojas o pestañas, que es donde se recogen los datos, fórmulas, gráficos, enlaces,...

Empezamos importando cosas que usaremos en este capítulo:

import io

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

import openpyxl
from openpyxl.drawing.image import Image

Vamos a empezar creando un notebook con algunos datos:

wb = openpyxl.Workbook()
ws = wb.active

for row in range(1, 101):
    for col in list("ABCDEFGHIJKL"):
        value = f"{col}{row}"
        ws[value] = value

Hemos insertado 100 filas y 12 columnas donde el valor de cada celda en la primera columna es A1, A2, ..., A100, en la segunda columna es B1, B2, ..., B100,...

Si guardamos el notebook y lo abrimos podemos ver cómo queda:

wb.save("new.xlsx")

Resultado del workbook generado con el código anterior.

Antes de seguir vamos a ver las cosas que tiene una hoja o pestaña:

for name in dir(ws):
    if not name.startswith("__"):
        print(name)

Lo anterior mostrará:

BREAK_COLUMN
BREAK_NONE
BREAK_ROW
HeaderFooter
ORIENTATION_LANDSCAPE
ORIENTATION_PORTRAIT
PAPERSIZE_A3
PAPERSIZE_A4
PAPERSIZE_A4_SMALL
PAPERSIZE_A5
PAPERSIZE_EXECUTIVE
PAPERSIZE_LEDGER
PAPERSIZE_LEGAL
PAPERSIZE_LETTER
PAPERSIZE_LETTER_SMALL
PAPERSIZE_STATEMENT
PAPERSIZE_TABLOID
SHEETSTATE_HIDDEN
SHEETSTATE_VERYHIDDEN
SHEETSTATE_VISIBLE
_WorkbookChild__title
_add_cell
_add_column
_add_row
_cells
_cells_by_col
_cells_by_row
_charts
_clean_merge_range
_comments
_current_row
_default_title
_drawing
_get_cell
_hyperlinks
_id
_images
_invalid_row
_move_cell
_move_cells
_parent
_path
_pivots
_print_area
_print_cols
_print_rows
_rel_type
_rels
_setup
_tables
active_cell
add_chart
add_data_validation
add_image
add_pivot
add_table
append
auto_filter
calculate_dimension
cell
col_breaks
column_dimensions
columns
conditional_formatting
data_validations
delete_cols
delete_rows
dimensions
encoding
evenFooter
evenHeader
firstFooter
firstHeader
formula_attributes
freeze_panes
insert_cols
insert_rows
iter_cols
iter_rows
legacy_drawing
max_column
max_row
merge_cells
merged_cell_ranges
merged_cells
mime_type
min_column
min_row
move_range
oddFooter
oddHeader
orientation
page_breaks
page_margins
page_setup
paper_size
parent
path
print_area
print_options
print_title_cols
print_title_rows
print_titles
protection
row_breaks
row_dimensions
rows
scenarios
selected_cell
set_printer_settings
sheet_format
sheet_properties
sheet_state
sheet_view
show_gridlines
show_summary_below
show_summary_right
tables
title
unmerge_cells
values
views

Como podéis ver tenemos muchas más cosas que con un Workbook ya que aquí es donde realmente se tiene información de datos, no solo metainformación, como en el libro.

Modificando propiedades generales de la hoja

Podemos modificar la hoja para definir el ancho de la columna, el valor por defecto de ancho, al altura de la fila,...

ws.sheet_format

Lo anterior mostrará:

<openpyxl.worksheet.dimensions.SheetFormatProperties object>
Parameters:
baseColWidth=8, defaultColWidth=None, defaultRowHeight=15.0, customHeight=None, zeroHeight=None, thickTop=None, thickBottom=None, outlineLevelRow=None, outlineLevelCol=None

Modificamos algunas cosas:

ws.sheet_format.baseColWidth = 4
ws.sheet_format.defaultRowHeight = 30
# etc

Guardamos y visualizamos:

wb.save("new.xlsx")

También tenemos una serie de propiedades. La mayoría de las cuales no sé muy bien para qué funcionan. Una referencia aquí. Por ejemplo, vamos a cambiar el color del lugar donde se visualiza la hoja o pestaña:

ws.sheet_properties

Lo anterior nos muestra:

<openpyxl.worksheet.properties.WorksheetProperties object>
Parameters:
codeName=None, enableFormatConditionsCalculation=None, filterMode=None, published=None, syncHorizontal=None, syncRef=None, syncVertical=None, transitionEvaluation=None, transitionEntry=None, tabColor=None, outlinePr=<openpyxl.worksheet.properties.Outline object>
Parameters:
applyStyles=None, summaryBelow=True, summaryRight=True, showOutlineSymbols=None, pageSetUpPr=<openpyxl.worksheet.properties.PageSetupProperties object>
Parameters:
autoPageBreaks=None, fitToPage=None
ws.sheet_properties.tabColor = "00cc00"

Como anteriormente, guardamos y visualizamos:

wb.save("new.xlsx")

Tab con color cambiado.

Podemos hacer que la hoja aparezca oculta o visible:

ws.sheet_state

Si el libro solo tiene una hoja esta no podrá ser invisible por lo que para que no dé error, en el caso de que queramos ocultarla, deberíamos tener dos o más hojas en el libro.

ws.sheet_state = "hidden"
wb.save("new.xlsx") # esto dará error

Lo anterior, como hemos comentado, dará un error:

ValueError: The only worksheet of a workbook cannot be hidden

Le añadimos una hora, lo guardamos y lo abrimos:

wb.create_sheet()
wb.save("new.xlsx")

Hola oculta

La volvemos a dejar visible:

ws.sheet_state = "visible"

Podemos controlar como queremos que se vea la hoja. Si queremos que se vean las fórmulas o los valores obtenidos al aplicar la fórmula, si queremos que se vea el cabecero con el nombre de las columnas (A, B, C, D,...), podemos indicar si queremos que se vea el grid, si queremos cambiar el color del grid,... Toqueteamos un poco para ver esto en acción:

ws.sheet_view

Lo anterior mostrará:

<openpyxl.worksheet.views.SheetView object>
Parameters:
windowProtection=None, showFormulas=None, showGridLines=None, showRowColHeaders=None, showZeros=None, rightToLeft=None, tabSelected=None, showRuler=None, showOutlineSymbols=None, defaultGridColor=None, showWhiteSpace=None, view=None, topLeftCell=None, colorId=None, zoomScale=None, zoomScaleNormal=None, zoomScaleSheetLayoutView=None, zoomScalePageLayoutView=None, zoomToFit=None, workbookViewId=0, pane=None, selection=[<openpyxl.worksheet.views.Selection object>
Parameters:
pane=None, activeCell='A1', activeCellId=None, sqref='A1']
# Definimos que la celda seleccionada sea la B3 en la hoja "Sheet"
ws.sheet_view.selection[0].activeCell = "B3"
wb.save("new.xlsx")

Si lo abrís veréis que la celda seleccionada es la B3.

Algunas utilidades

Para conocer el rango de celdas usadas en la hoja:

ws.calculate_dimension()

Lo anterior devolverá 'A1:L100'.

Podemos colocar una imagen donde queramos. Vamos a crear una con matplotlib y la vamos a añadir a la hoja (necesitaremos installar pillow):

# Creamos la imagen
fig, ax = plt.subplots()
ax.plot((1,2,3))
stream = io.BytesIO()
fig.savefig(stream, format="png")

# La pegamos
ws.add_image(Image(stream), "A10")

Y lo guardamos y visualizamos:

wb.save("new.xlsx")

Worksheet con imagen.

Podemos ocultar filas y/o columnas:

# Ocultar o mostrar celdas
ws.column_dimensions.group('A','D', hidden=True)
ws.row_dimensions.group(3, 4, hidden=True)

Guardamos y visualizamos:

wb.save('new.xlsx')

Celdas ocultas.

Como veis en la anterior figura no se ven varias columnas y filas. Para volverlo a dejar como estaba podéis hacer:

# Ocultar o mostrar celdas
ws.column_dimensions.group('A','D', hidden=False)
ws.row_dimensions.group(3, 4, hidden=False)

Podemos desplazar las celdas de la siguiente forma:

ws.move_range(ws.calculate_dimension(), rows=1, cols=1)

Indicamos el rango de celdas que queremos mover y después indicamos el número de filas y/o columnas hasta donde lo queremos desplazar.

wb.save('new.xlsx')

El resultado debajo donde se ve que hemos desplazado todas las celdas una columna y una fila hacia abajo y hacia la derecha, respectivamente:

Celdas desplazadas.

Podemos unir celdas. En este caso voy a unir las celdas B1 a M1 para poner un cabecero a esas celdas:

# Antes metemos un valor en la celda B1 que será el que tomen las celdas unidas
ws["B1"] = "Cabecero"
ws.merge_cells(start_row=1, start_column=2, end_row=1, end_column=13)

Indicamos desde donde hasta donde queremos unir.

[recordad que empezamos a contar por 1 y no por 0].

[CUIDADO: Al unir celdas se perderá el valor de todas menos la primera que será la que se use para las celdas unidas. Estos valores no se pueden volver a recuperar].

Si guardamos y abrimos el documento:

wb.save('new.xlsx')

Celdas unidas.

Vemos las celdas unidas con el texto 'Cabecero'.

También podemos añadir/eliminar filas y/o columnas con los métodos:

  • insert_rows: añade filas.
  • insert_cols: añade columnas.
  • delete_rows: elimina filas.
  • delete_cols: elimina columnas.

Vamos a eliminar, por ejemplo, la columna D, que actualmente contiene los valores (C1, C2, C3,..):

ws.delete_cols(4, amount=1)

En el método hemos indicado desde qué columna (por su índice empezando por 1) y cuantas queremos eliminar en cantidad.

Guardamos y visualizamos:

wb.save('new.xlsx')

Columna eliminada.

Resumiendo

Vemos que la hoja nos permite hacer muchas más cosas que el libro, lo cual tiene todo el sentido del mundo. Hemos aprendido a hacer las cosas para manejar nuestros datos dentro de una hoja, hemos visto como pegar imágenes, mover celdas, eliminar columnas, unir celdas,... Estamos listos para ver una unidad más pequeña del rompecabezas, la celda.