Python UNO(libreoffice):如何为工作表启用自动过滤器

Posted

技术标签:

【中文标题】Python UNO(libreoffice):如何为工作表启用自动过滤器【英文标题】:Python UNO (libreoffice): How to enable auto filters for a sheet 【发布时间】:2017-01-11 12:38:56 【问题描述】:

我有一个程序,可以创建一个 CSV 文件。

现在我想使用一个 Python UNO 脚本,它可以做几件事:

1.) 在电子表格中打开 csv 文件

2.) 为所有列启用自动过滤

3.) 创建一个宏并将其添加到文档中

4.) 将文件保存为 ODS 文件

这个问题只涉及 2 个。)

1.) 正在工作

for 3.) 我可能会提出另一个问题 1.) 正在工作(使用 pyoo 和 unotools)

到目前为止我的步骤:

我手动启动:

libreoffice --accept='socket,host=localhost,port=2202;urp;' --norestore --nologo --nodefault

我的python脚本:

与pyoo

import pyoo
# Step 0) connect to UNO bridge
desktop = pyoo.Desktop('localhost', 2002)

# Step 1) open the doc and get the sheet
# This works only if the field separator is a comma.
# I don't know how for example to specify tab as separator instead
doc = desktop.open_spreadsheet('tst.csv')
# I see the spreadsheet opening
sheet = doc.sheets[0] # I get an object of type Sheet

# Step2) set autofilter for active sheet
# no idea how to do

# Step3) create a macro and add it to the document
# no idea how to do but will create another question as 
# soon as step2 is solved

# Step 4) save the sheet
doc.save("tst_pyoo.ods")

或者使用 unotools

import unotools
from unotools.component.calc import Calc
from unotools.unohelper import convert_path_to_url

# Step 0) connect to UNO bridge
context = unotools.connect(unotools.Socket('localhost', 2002))

# Step 1) open document
doc = Calc(ctx, convert_path_to_url('tst.csv')
# I see the spreadsheet opening
sheet = doc.get_sheet_by_index(0)
# I get an object of type unotools.component.calc.Spreadsheet

# Step2) set autofilter for active sheet
# no idea how to do

# Step3) create a macro and add it to the document
# no idea how to do but will create another question as 
# soon as step2 is solved

# Step 4)
doc.store_to_url(convert_path_to_url("tst_unotools.ods"))

提前感谢您的任何反馈

【参考方案1】:

这是使用直接 PyUNO 而不是包装库的代码。改编自http://www.imaccanici.org/en.libreofficeforum.org/node/5413.html

import os
import uno

class UnoObjs:
    "Initializes and stores UNO objects to connect to a document."""
    def __init__(self, filepath=None):
        localContext = uno.getComponentContext()
        resolver = localContext.ServiceManager.createInstanceWithContext(
            "com.sun.star.bridge.UnoUrlResolver", localContext )
        self.ctx = resolver.resolve(
            "uno:socket,host=localhost,port=2002;urp;"
            "StarOffice.ComponentContext")
        self.smgr = self.ctx.ServiceManager
        desktop = self.smgr.createInstanceWithContext(
            "com.sun.star.frame.Desktop", self.ctx)
        if filepath:
            fileUrl = uno.systemPathToFileUrl(os.path.realpath(filepath))
            self.document = desktop.loadComponentFromURL(
                fileUrl, "_default", 0, ())
        else:
            self.document = desktop.getCurrentComponent()

def add_autofilter(unoObjs):
    """This adds an autofilter by selecting only the filled spreadsheet area. 
    NOTE: If any cell in the header row of the selection is empty this will
    trigger a popup for interactive user action (must click Yes for the
    Autofilter column header message box)
    """
    dispatcher = unoObjs.smgr.createInstanceWithContext(
        "com.sun.star.frame.DispatchHelper", unoObjs.ctx)
    controller = unoObjs.document.getCurrentController()
    sheet = unoObjs.document.getSheets().getByIndex(0)
    # select a sufficiently big "guess" area, hopefully
    guessRange = sheet.getCellRangeByPosition(0, 0, 150, 10000)
    # look up the actual used area within the guess area
    cursor = sheet.createCursorByRange(guessRange)
    cursor.gotoEndOfUsedArea(False)
    lastcol = cursor.RangeAddress.EndColumn
    lastrow = cursor.RangeAddress.EndRow
    # select the filled part of the spreadsheet
    actualRange = sheet.getCellRangeByPosition(0, 0, lastcol, lastrow)
    controller.select(actualRange)
    # add autofilter
    dispatcher.executeDispatch(
        unoObjs.document.getCurrentController(), ".uno:DataFilterAutoFilter",
        "", 0, ())

add_autofilter(UnoObjs("tst.csv"))

诸如.uno:DataFilterAutoFilter 之类的调度程序调用很难找出参数。在大多数情况下,最好使用 UNO API 调用,例如 XTextCursor。但是,有几个选项可以找出调度程序调用:

使用宏记录器。 查看this one 之类的列表。 在 LibreOffice 源代码中查找调用。这是最可靠的,但有时仍难以确定论据。

关于调度程序调用,请参阅https://forum.openoffice.org/en/forum/viewtopic.php?f=20&t=61127。

【讨论】:

非常感谢。如果我对你的脚本做一个小的修改,它就会起作用:看起来,如果fileUrl = uno.systemPathToFileUrl(filepath) 传递了一个相对路径名,它就会失败。如果我在文件开头添加import os 并写fileUrl = uno.systemPathToFileUrl(os.path.realpath(filepath)),您的脚本就可以工作。有没有什么好方法(文档页面)可以查看所有可以使用 UNO 执行的操作?猜测字符串“.uno:DataFilterAutoFilter”并猜测它的参数很棘手,这是我找到答案的主要问题, 好吧,我原来确实有os.path.realpath,但为了缩短代码,我把它拿出来了。显然这毕竟是必要的。我编辑了答案以包含代码并解释调度程序调用。【参考方案2】:

是否可以用python修改.ods文件中直接包含的content.xml

我的目标也是在 Windows 10 上的 LibreOffice 中应用自动过滤器。 我找不到任何文档。

但是我发现如果你解压一个.ods文件,在content.xml文件中有如下几行xml

<table:database-ranges>
                <table:database-range table:name="__Anonymous_Sheet_DB__0" table:target-range-address="Sheet1.A1:Sheet1.B3" table:display-filter-buttons="true"/>
</table:database-ranges>

有没有办法使用 python 的 lxml 模块读取和修改 content.xml 并在 LibreOffice 中应用自动过滤器?

【讨论】:

以上是关于Python UNO(libreoffice):如何为工作表启用自动过滤器的主要内容,如果未能解决你的问题,请参考以下文章

Python libreoffice 使用 uno 设置边距值、最佳高度和打印文档

LibreOffice Calc 上的 Python UNO,重新定位光标

LibreOffice UNO:设置样式(可以使用 Java、VB、Python、C++、任何使用 UNO API 的语言提供)

无法在 python 中为 ubuntu 16.04 上的 libreoffice 导入 uno

如何让 LibreOffice 无头 Calc 计算以保存 uno 的新值?

Bootstrap Uno LibreOffice 异常