CALC: ayuda para convertir macro vba a python (filtro estándar)

Necesito ayuda para convertir una macro vba a python. @elmau

Sub ordenar_y_filtrar()
	CELDA_ORIGEN = "A1"
	
	doc = ThisComponent
	hoja = doc.CurrentController.ActiveSheet
	
	celda = hoja.getCellRangeByName(CELDA_ORIGEN)
	rango = obtener_rango_datos(celda)
	
	ordenar(rango, 3)
	filtrar_vacios(rango, 3, True)
	
End Sub

Function obtener_rango_datos(celda)
	sheet = celda.SpreadSheet
	cursor = sheet.createCursorByRange(celda)
	cursor.collapseToCurrentRegion()
	rango = sheet.getCellRangeByName(cursor.AbsoluteName)
	obtener_rango_datos = rango
End Function 

Sub ordenar(rango, campo)
Dim campos (0) As New com.sun.star.table.TableSortField

    campos(0).Field = campo
    campos(0).IsAscending = False
    campos(0).IsCaseSensitive = False
	campos(0).FieldType = com.sun.star.table.TableSortFieldType.AUTOMATIC
     
	como_ordenar = rango.createSortDescriptor()	
	como_ordenar(1).Name = "ContainsHeader"
    como_ordenar(1).Value = True
    como_ordenar(3).Name = "SortFields"
    como_ordenar(3).Value = campos
     
    rango.sort(como_ordenar)
End Sub

Sub filtrar_vacios(rango, campo, son_numeros)
Dim campos(0) As New com.sun.star.sheet.TableFilterField

	NOMBRE = "datostmp"
	
	doc = ThisComponent
    rangos = doc.DataBaseRanges()
    If rangos.hasByName(NOMBRE) Then
    	rangos.removeByName(NOMBRE)
    End If
    
    rangos.addNewByName(NOMBRE, rango.RangeAddress)
    data = rangos.getByName(NOMBRE)    
    data.AutoFilter = True
    
	como_filtrar = data.getFilterDescriptor()
	
	campos(0).Field = campo
    campos(0).Operator = com.sun.star.sheet.FilterOperator.NOT_EMPTY
	campos(0).IsNumeric = son_numeros
	como_filtrar.FilterFields = campos
	
	data.refresh()
End Sub

Intenté convertir la macro de este tema del foro, logré convertir todas las funciones, excepto la función: filtrar_vacios

Macro para ordenar de mayor a menor quitando celdas sin valor - #4 by elmau

mi intento:

from com.sun.star.table import TableSortField
from com.sun.star.table.TableSortFieldType import AUTOMATIC
from com.sun.star.sheet import TableFilterField

def ordenar_y_filtrar():
    CELDA_ORIGEN = 'A1'
    
    doc = XSCRIPTCONTEXT.getDocument()
    sheets = doc.Sheets
    hoja = sheets[0]

    celda = hoja[CELDA_ORIGEN]
    rango = obtener_rango_datos(celda)

    ordenar(rango, 8)
    filtrar_vacios(rango, 8, True)


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


def ordenar(rango, campo):
    campos = TableSortField()
    campos.Field = campo
    campos.IsAscending = False
    campos.IsCaseSensitive = False
    campos.FieldType = AUTOMATIC

    como_ordenar = rango.createSortDescriptor()
    como_ordenar[1].Name = 'ContainsHeader'
    como_ordenar[1].Value = True
    como_ordenar[3].Name = 'SortFields'
    como_ordenar[3].Value = uno.Any('[]com.sun.star.table.TableSortField', (campos,))

    uno.invoke(rango, 'sort', (como_ordenar,))


def filtrar_vacios(rango, campo, son_numeros):
    NOMBRE = 'datostmp'

    doc = XSCRIPTCONTEXT.getDocument()
    rangos = doc.DatabaseRanges
    if rangos.hasByName(NOMBRE):
        rangos.removeByName(NOMBRE)

    rangos.addNewByName(NOMBRE, rango.RangeAddress)
    data = rangos.getByName(NOMBRE)    
    data.AutoFilter = True

    campos = TableFilterField()
    campos.Field = campo
    campos.Operator = com.sun.star.sheet.FilterOperator.EMPTY
    campos.IsNumeric = son_numeros
    
    como_filtrar = data.createFilterDescriptor(True)
    como_filtrar.FilterFields = ((campos,))

    data.filter(como_filtrar)

¡Como hacer eso! Gracias

Y… ¿te muestra algún error?

Logré filtrar con el operador EQUAL, sin embargo, al intentar usar cualquier otro operador (EMPTY, GREATER_EQUAL …), el filtro no funciona y no muestra ningún mensaje de error.
ver las fotos
antes de aplicar cualquier filtro:
NORMAL

Filtro EQUAL, funciona:
OK

Filtro EMPTY, no funciona:
ERRO

Filtro EMPTY, no aplicado:

ejemplo de archivo de muestra
teste filter.ods (13,3,KB)

Aquí está el código que usé en la macro de este archivo:

import uno
from com.sun.star.table import TableSortField
from com.sun.star.table.TableSortFieldType import AUTOMATIC
from com.sun.star.sheet.FilterOperator import EMPTY, EQUAL, GREATER_EQUAL, GREATER, TOP_VALUES, BOTTOM_VALUES, NOT_EQUAL, NOT_EMPTY
from com.sun.star.sheet import TableFilterField


def ordenar_y_filtrar_equal(*args):
    CELDA_ORIGEN = 'A1'
    
    doc = XSCRIPTCONTEXT.getDocument()
    sheets = doc.Sheets
    hoja = sheets[0]

    celda = hoja[CELDA_ORIGEN]
    rango = obtener_rango_datos(celda)

    ordenar(rango, 2)
    filtrar_vacios(rango, 2, True, equal=True)


def ordenar_y_filtrar_empty(*args):
    CELDA_ORIGEN = 'A1'
    
    doc = XSCRIPTCONTEXT.getDocument()
    sheets = doc.Sheets
    hoja = sheets[0]

    celda = hoja[CELDA_ORIGEN]
    rango = obtener_rango_datos(celda)

    ordenar(rango, 2)
    filtrar_vacios(rango, 2, True, equal=False)


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


def ordenar(rango, campo):
    campos = TableSortField()
    campos.Field = campo
    campos.IsAscending = False
    campos.IsCaseSensitive = False
    campos.FieldType = AUTOMATIC

    como_ordenar = rango.createSortDescriptor()
    como_ordenar[1].Name = 'ContainsHeader'
    como_ordenar[1].Value = True
    como_ordenar[3].Name = 'SortFields'
    como_ordenar[3].Value = uno.Any('[]com.sun.star.table.TableSortField', (campos,))

    uno.invoke(rango, 'sort', (como_ordenar,))


def filtrar_vacios(rango, campo, son_numeros, equal):
    NOMBRE = 'datostmp'

    doc = XSCRIPTCONTEXT.getDocument()
    rangos = doc.DatabaseRanges
    if rangos.hasByName(NOMBRE):
        rangos.removeByName(NOMBRE)

    rangos.addNewByName(NOMBRE, rango.RangeAddress)
    data = rangos.getByName(NOMBRE)
    data.AutoFilter = True

    campos = TableFilterField()
    campos.Field = campo
    campos.IsNumeric = son_numeros
    if equal == True:
        campos.NumericValue = 5
        campos.Operator = EQUAL
    else:
        campos.Operator = EMPTY
        
    #campos.StringValue = 'texto'

    como_filtrar = rango.createFilterDescriptor(True)
    como_filtrar.ContainsHeader = True
    como_filtrar.FilterFields = ((campos,))

    rango.filter(como_filtrar)
    data.refresh()

complementar:
Operador GREATER_EQUAL, aplica el filtro a las celdas, pero al mirar las opciones de filtro, todas están marcadas:

Usando el módulo uno para acceder a la API UNO de LibreOffice, se requiere importar el módulo uno e inicializar el objeto XSCRIPTCONTEXT antes de ejecutar el código, que podría ser similar a este:

CELDA_ORIGEN = "A1"

doc = XSCRIPTCONTEXT.getDocument()
hoja = doc.getCurrentController().getActiveSheet()

celda = hoja.getCellRangeByName(CELDA_ORIGEN)
rango = obtener_rango_datos(celda)

ordenar(rango, 2)
filtrar_vacios(rango, 2, True)

def obtener_rango_datos(celda):
    sheet = celda.getSpreadSheet()
    cursor = sheet.createCursorByRange(celda)
    cursor.collapseToCurrentRegion()
    rango = sheet.getCellRangeByName(cursor.getRangeAddress().getAbsoluteName())
    return rango

def ordenar(rango, campo):
    campos = uno.createUnoStruct("[]com.sun.star.table.TableSortField", 1)
    campos[0] = uno.createUnoStruct("com.sun.star.table.TableSortField")
    campos[0].Field = campo
    campos[0].IsAscending = False
    campos[0].IsCaseSensitive = False
    campos[0].FieldType = uno.getConstantByName("com.sun.star.table.TableSortFieldType.AUTOMATIC")
    
    como_ordenar = rango.createSortDescriptor()
    como_ordenar[1].Name = "ContainsHeader"
    como_ordenar[1].Value = True
    como_ordenar[3].Name = "SortFields"
    como_ordenar[3].Value = campos
    
    rango.sort(como_ordenar)

def filtrar_vacios(rango, campo, son_numeros):
    campos = uno.createUnoStruct("[]com.sun.star.sheet.TableFilterField", 1)
    campos[0] = uno.createUnoStruct("com.sun.star.sheet.TableFilterField")
    campos[0].Field = campo
    campos[0].Operator = uno.getConstantByName("com.sun.star.sheet.FilterOperator.NOT_EMPTY")
    campos[0].IsNumeric = son_numeros

    NOMBRE = "datostmp"
    rangos = doc.DatabaseRanges
    if rangos.hasByName(NOMBRE):
        rangos.removeByName(NOMBRE)
    
    rangos.addNewByName(NOMBRE, rango.getRangeAddress())
    data = rangos.getByName(NOMBRE)
    data.AutoFilter = True
    
    como_filtrar = data.getFilterDescriptor()
    como_filtrar.FilterFields = campos
    
    data.refresh()

Como sugirió mostrar el mensaje de error:

<class ‘uno.com.sun.star.uno.RuntimeException’>: pyuno.getClass: []com.sun.star.sheet.TableFilterFieldis a SEQUENCE, expected EXCEPTION, STRUCT or INTERFACE

nota: en el código de python que publiqué, solo la función filtrar_vacios no funciona.

Podría ser este para filtrar_vacios:

def filtrar_vacios(rango, campo, son_numeros):
    import uno
    from com.sun.star.sheet.FilterOperator import NOT_EMPTY
    from com.sun.star.sheet.TableFilterField import TableFilterField
    
    campos = [TableFilterField() for _ in range(1)]
    
    NOMBRE = "datostmp"
    
    doc = XSCRIPTCONTEXT.getDocument()
    rangos = doc.DataBaseRanges
    if rangos.hasByName(NOMBRE):
        rangos.removeByName(NOMBRE)
    
    rangos.addNewByName(NOMBRE, rango.RangeAddress)
    data = rangos.getByName(NOMBRE)
    data.AutoFilter = True
    
    como_filtrar = data.getFilterDescriptor()
    campos[0].Field = campo
    campos[0].Operator = NOT_EMPTY
    campos[0].IsNumeric = son_numeros
    como_filtrar.FilterFields = campos
    
    data.refresh()
1 Like

tu código, muéstrame este error

com.sun.star.uno.RuntimeException: Error during invoking function ordenar_y_filtrar_empty in module vnd.sun.star.tdoc:/10/Scripts/python/Main.py (<class ‘ImportError’>: No module named ‘com’ (or ‘com.sun.star.sheet.TableFilterField.TableFilterField’ is unknown)
File “C:\Portable\LibreOffice.[Portable]\App\libreoffice\program\pythonscript.py”, line 915, in invoke
ret = self.func( *args )
File “vnd.sun.star.tdoc:/10/Scripts/python/Main.py”, line 33, in ordenar_y_filtrar_empty
File “vnd.sun.star.tdoc:/10/Scripts/python/Main.py”, line 96, in filtrar_vacios
File “C:\Portable\LibreOffice.[Portable]\App\libreoffice\program\uno.py”, line 425, in _uno_import
raise uno_import_exc
File “C:\Portable\LibreOffice.[Portable]\App\libreoffice\program\uno.py”, line 346, in _uno_import
return _builtin_import(name, *optargs, **kwargs)
)

Usas el módulo uno para acceder a la API UNO de LibreOffice, importando el módulo uno e inicializando el objeto XSCRIPTCONTEXT antes de ejecutar el código.

1 Like

El código que sugirió tiene un error en estas líneas:

from com.sun.star.sheet.TableFilterField import TableFilterField

from com.sun.star.sheet import TableFilterField


rangos = doc.DataBaseRanges

rangos = doc.DatabaseRanges

Después de arreglarlo, el filtro tampoco funciona con el operador EMPTY

El código correcto para la función es:

def filtrar_vacios(rango, campo, son_numeros):
    NOMBRE = 'datostmp'

    doc = XSCRIPTCONTEXT.getDocument()
    rangos = doc.DatabaseRanges
    if rangos.hasByName(NOMBRE):
        rangos.removeByName(NOMBRE)

    rangos.addNewByName(NOMBRE, rango.RangeAddress)
    data = rangos.getByName(NOMBRE)
    data.AutoFilter = True

    campos = TableFilterField()
    campos.Field = campo
    campos.Operator = EMPTY
    campos.IsNumeric = son_numeros

    como_filtrar = data.getFilterDescriptor()
    como_filtrar.FilterFields = ((campos,))

    data.refresh()

    return

Que filtra correctamente los vacíos.

image

1 Like

Gracias por tu ayuda, @Kyodake @elmau
Ahora el código funciona correctamente, pero solo para los operadores

EMPTY, NOT_EMPTY, EQUAL

En cuanto a los otros operadores:

NOT_EQUAL, GREATER, GREATER_EQUAL, LESS, LESS_EQUAL, TOP_VALUES, TOP_PERCENT, BOTTOM_VALUES, BOTTOM_PERCENT

el filtro solo funciona en las celdas. Cuando abre la ventana de filtro, se seleccionan todos los elementos, no solo los elementos de filtro.

ejemplo de código:

from com.sun.star.sheet.FilterOperator import EMPTY, NOT_EMPTY, EQUAL, NOT_EQUAL, GREATER, GREATER_EQUAL, LESS, LESS_EQUAL, TOP_VALUES, TOP_PERCENT, BOTTOM_VALUES, BOTTOM_PERCENT
def filtrar_vacios(rango, campo, son_numeros):
    NOMBRE = 'datostmp'

    doc = XSCRIPTCONTEXT.getDocument()
    rangos = doc.DatabaseRanges
    if rangos.hasByName(NOMBRE):
        rangos.removeByName(NOMBRE)

    rangos.addNewByName(NOMBRE, rango.RangeAddress)
    data = rangos.getByName(NOMBRE)
    data.AutoFilter = True

    campos = TableFilterField()
    campos.Field = campo

    #EMPTY, NOT_EMPTY, EQUAL
    #NOT_EQUAL, GREATER, GREATER_EQUAL, LESS, LESS_EQUAL, TOP_VALUES, TOP_PERCENT, BOTTOM_VALUES, BOTTOM_PERCENT
    campos.Operator = GREATER_EQUAL

    campos.IsNumeric = son_numeros
    campos.NumericValue = 5

    como_filtrar = data.getFilterDescriptor()
    como_filtrar.FilterFields = ((campos,))

    data.refresh()

    return

https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet.html#af9e5fd8fd26fc252748d97ebd68ea6b1

¿Este problema solo me está pasando a mí?
¿Hay algún problema en mi código o es un bug ?