Calc. Copiar formato de celdas con macro en python

Hola
He logrado seleccionar un rango de celdas y quiero copiar en otra parte de la misma hoja sólo el formato.
Sólo he logrado llegar a seleccionar las celdas a copiar su formato pero no logro siquiera el copiar contenido.
El código hasta ahora es:

import uno

def main():

    documento = XSCRIPTCONTEXT.getDocument()
    fila = 0
    col = 0
    celda = documento.Sheets[0][(fila, col)]
    valor = celda.getString()
    fila += 1
    while valor:        # busco la primera celda vacía
        fila += 1
        celda = documento.Sheets[0][(fila, col)]
        valor = celda.getString()
    fila -= 1
    ncol = col + 5      # vuelvo a la fila anterior y defino el extremo derecho a seleccionar
    HojaActiva = documento.getCurrentController().getActiveSheet()
    Rango = HojaActiva.getCellRangeByPosition(col, fila, ncol, fila)
    SeleccionCeldas = documento.getCurrentController().select(Rango)

    # Y hasta aquí he llegado. He probado varios métodos pero "ni pa'trás"

    return

Te vas a simplificar la vida, por mucho, si usas estilos en vez de intentar copiar el formato, por que solo tienes que hacer:

rango.CellStyle = "MiEstilo"

Y no tienes que seleccionar el rango previamente, para la mayor parte del trabajo con macros en celdas y rangos no es necesario seleccionarlos primero.

No es necesario le pases una tupla para obtener la celda, puedes pasarle los indices directamente:

celda = doc.Sheets[0][fila, col]

La hoja cero, no siempre es la activa, solo asegurate de usar la hoja que realmente quieres:

documento.Sheets[0]
documento.CurrentController.ActiveSheet

Ahora, para obtener la región actual, que es lo que parece haces, mejor usa un cursor:

def region_actual(celda):
    cursor = celda.Spreadsheet.createCursorByRange(celda)
    cursor.collapseToCurrentRegion()
    rango = celda.Spreadsheet[cursor.AbsoluteName]
    return rango

y solo la llamas:

    celda = doc.Sheets[0][0, 0]
    rango = region_actual(celda)
1 Like

Gracias.
Lo intento.
Una pregunta. No entiendo cómo se selecciona una región, p.e. “A3:C7”, pasando sólo la referencia a una celda.

Otra. El copiar formato de un conjunto de celdas es porque en 4 celdas se aplican 4 estilos. En este caso, ¿debería hacer cuatro llamadas a “región_actual” con las cuatro referencias y aplicar en cada caso el estilo correspondiente? En una de estas celdas se aplica un formato condicional que copiando formato se replica pero aplicando estilo no logro que lo asuma.

Un saludo.

¿Conoces el concepto de región actual? a partir de cualquier celda dentro de un rango, puedes seleccionar todas las celdas adyacentes ( collapseToCurrentRegion)

region_actual

claro, por que el formato y el formato condicional son dos cosas diferentes.

Si tienes cuatro estilos, aplicalos en tantas celdas o rangos como los necesites y estableces el formato condicional donde se requiera.

Como consejo, piensa en tareas pequeñas. La tareas que se hacen manualmente en la interfaz como usuario, no siempre deben ser de la misma forma al automatizarlas, aunque el resultado final sea el mismo.

Gracias por la ayuda.
Ya casi consigo lo que quería. Le daré un par de vueltas más.
Un saludo

He conseguido mi propósito inicial, aunque no de forma satisfactoria. Copio el código y después comento.

import uno

def CopiarFila(void):
    
    documento = XSCRIPTCONTEXT.getDocument()
    Hojas = documento.Sheets
    HojaResumen = Hojas.getByName("Tablas")   # Obtengo la referencia a la hoja denominada Tablas, que contiene un intervalo con nombre
    HojaActiva = documento.CurrentController.ActiveSheet   # Obtengo la referencia a la hoja activa

    fila = 7
    col = 0 
    celda = HojaActiva.getCellByPosition(col, fila)    # Me sitúo en la fila, columna donde comienzan los datos de detalle de la hoja
    CeldaActiva = IrCeldaLibre(celda, documento)    #Busco la posición de primera celda vacía de la columna
    origen = HojaResumen.getCellRangeByName("FilasParaCopiar")  # Obtengo la selección de la hoja 'Tablas' para copiarlas
    HojaActiva.copyRange(CeldaActiva.CellAddress, origen.RangeAddress)  #Procedo a copiarlas
    
    return  
   
    
def IrCeldaLibre(celda, docu):
    
    HojaAct = docu.CurrentController.ActiveSheet
    cursor = HojaAct.createCursorByRange(celda)
    cursor.collapseToCurrentRegion()
    columna = 0
    fila = cursor.RangeAddress.EndRow +1
    CeldaLibre = HojaAct.getCellByPosition(columna, fila)
    
    return CeldaLibre

El problema me surge al definir el intervalo con nombre incluyendo una “combinación de celdas”. Si defino el intervalo méramente con celdas simples copia sin problema, estilo, fórmula, formato condicional y validez, pero siempre y cuando no haga una combinación de celdas.
¿Alguna luz al final del túnel?
Gracias
Añado. He probado con un intervalo con nombre de tres celdas unidas, definiendo el intervalo con las tres celda y ni por esas.

Agrega un archivo de ejemplo, con el problema y lo miro…

Por otra parte, ya te mostre como acceder a hojas y celdas directamente como indices, no se requiere: getCellRangeByName o getCellByPosition.

MacroCopiaPython.ods (15,5 KB)

Un saludo

pero no veo lo que tienes “claramente” y lo que quieres “claramente”, por favor, no dejes que adivine o intente deducir.

Nada más lejos de mi intención el confundirte.

Primero, limpiando las hojas de información innecesaria me llevé por delante un intervalo con nombre de donde se valida una celda. El objetivo, copiar unas celdas, de una fila en una hoja de tablas de datos, donde alguna de ellas tienen activada la validación, otra una fórmula, y varios estilos aplicados. Si lanzo la rutina de la macro y en intervalo con nombre FilasParaCopiar NO incluye celdas combinadas, la macro funciona. Desde que incluyo una combinación de celdas, en el archivo adjunto las celdas H1, J1 y K1, de la hoja Tablas, la macro no funciona.
Me explico:

  1. Para qué. Copiar un conjunto de celdas con sus formatos , fórmulas, reglas de validación, etc. de forma que se agreguen filas para que una persona usuaria añada líneas para introducir datos

  2. Por qué. A la persona que usa esta hoja le supera el simple hecho de copiar por si misma un conjunto de celdas, así como seleccionar y arrastrar para copiar.

Subo de nuevo el archivo con lo que eliminé en el primero.
MacroCopiaPython.ods (17,1 KB)

No combines esas celdas… de verdad, no hay necesidad de complicar el tema. Sobre todo, por lo que mencionas en el punto 2.

De todos modos dices: El objetivo, copiar unas celdas, de una fila en una hoja de tablas de datos, donde alguna de ellas tienen activada la validación, otra una fórmula, y varios estilos aplicados.

pero sigues sin detallar los nombres de las hojas y rangos origen y la hoja y rango destino. Reitero, no dejes NADA a la interpretación.

Siento explicarme mal.
No entiendo lo de detallar los rangos origen y destino. No sé si dices que no lo explicito en el código o en mi comentario
Por si es en el comentario,

  • rango origen: las celdas definidas en el intervalo con nombre FilasParaCopiar
  • hoja origen: Tablas
  • rango destino: la primera fila vacía por debajo de las cabeceras de las columnas de los datos a ingresar
  • hoja destino: la hoja actual, en la que se están ingresando los datos.

Tienes razón en por qué complicar las cosas, pero por la naturaleza de los datos, y por el contenido que hay en las 5 primeras filas de la hoja, que no incluí por no ser relevantes para el problema planteado, no puedo agrandar el ancho de la columna de Observaciones y por eso agrupé tres celdas.
Tal vez sea mejor reordenar la información que tengo en la hoja para no necesitar la combinación de celdas.
En cuanto al acceso a una hoja por su índice, ¿cómo puedo parametrizar ese índice? Conozco el nombre de la hoja pero no su posición en el conjunto de hojas, sí su nombre.

sheet = Hojas['Tablas']

sheet = Hojas[‘Tablas’]

¡Oido cocina!
Gracias

Me parece que esto no da más de sí.
Marco como solucionado resaltando que el método

hoja.copyRange(celdaDestino.CellAddress, origen.RangeAddress)

no funciona si el rango de origen contiene celdas combinadas.
Al final selecciono las tres celdas a combinar en la hoja destino, situadas a continuación de las celdas copiadas, utilizo el método merge() y la asignación de estilo mediante .CellStyle = "estilo" para, sin copiar las tres celdas para el campo observaciones, sí lograr el objetivo deseado.

Gracias Mauricio por los aportes.

copyRange si copia celdas combinadas

    origen = sheet['test_origen']
    destino = sheet['A20']
    sheet.copyRange(destino.CellAddress, origen.RangeAddress)

Tienes razón. Cuando he declarado el “origen” mediante

HojaResumen['FilasParaCopiar']

en vez de

HojaResumen.getCellRangeByName("FilasParaCopiar")

SÍ me ha copiado las celdas combinadas.
De nuevo, gracias.