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

Posted

技术标签:

【中文标题】LibreOffice Calc 上的 Python UNO,重新定位光标【英文标题】:Python UNO on LibreOffice Calc, rehoming a cursor 【发布时间】:2018-05-27 01:39:38 【问题描述】:

LibreOffice 5.3、python 3.53、VOID Linux

这更像是一个 uno 问题而不是 python 问题。下面的代码对 3 个单元格进行了简单的更新。工作表上配置了 3 个按钮,调用 dowriteonce()、dowritetwice() 和 dowritethrice(),它们都会更新和工作,就像您可能期望将数字和文本写入选定的单元格一样。

问题出在哪里,当用户在 UI 中编辑单元格时,通过执行该函数对该单元格的任何后续更新都会被阻止。因此,只需在计算 UI 中单击单元格 C4,就可以防止 writethrice() 函数更新单元格 C4。如果我删除内容并单击 UI 中的另一个单元格,比如 C5,那么一切都会再次正常工作,并且在单击按钮时 C4 会更新。

我想做的是在执行之前将 UI 编辑光标重新定位到未使用的单元格,以防止这种情况发生。用户复制粘贴会将活动光标留在不可预测的位置,如果我无法隔离光标,这将导致计算失败。

所以问题是,如何使用 Python 通过 UNO API 将 UI 编辑光标移动到命名单元格?或者,如果它更容易,只需暂时停用它。

Python:

import socket
import sys
import re
import uno
import unohelper

class ODSCursor(unohelper.Base):

    # predeclare class properties

    ctx=None
    desktop=None
    model=None
    activesheet=None
    counter=0 
    scooby="Scooby"

    # import namespaces

    def __init__(self):
        import socket
        import uno
        import unohelper
        import sys
        import re

    # initialize uno handle only once and get the first sheet

    @classmethod
    def sheet1(cls,*args):
        if cls.activesheet is not None:
                return (cls.activesheet)
        cls.ctx = uno.getComponentContext() 
        cls.desktop = cls.ctx.ServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", cls.ctx)
        cls.model = cls.desktop.getCurrentComponent()
        # cls.activesheet = cls.model.Sheets.getByName("Sheet1")
        cls.activesheet = cls.model.Sheets.getByIndex(0)
        return (cls.activesheet)

    @classmethod
    def writeonce(self,*args):
        self.counter += 1
        cell_b1 = self.activesheet.getCellRangeByName("B1") 
        cell_b1.String = self.counter

    @classmethod
    def writetwice(self,*args):
        self.counter += 1
        cell_b2 = self.activesheet.getCellRangeByName("B2") 
        cell_b2.String = self.counter 

    @classmethod
    def writescooby(self,*args):
        cell_c4 = self.activesheet.getCellRangeByName("C4") 
        cell_c4.String = self.scooby

### BUTTON BOUND FUNCTIONS ###

def dowriteonce(*args):
    Odc = ODSCursor()   # create the object
    Odc.sheet1()
    Odc.writeonce()

def dowritetwice(*args):
    Odc = ODSCursor() # create the object
    Odc.sheet1()
    Odc.writetwice()

def dowritethrice(*args):
    Odc = ODSCursor() # create the object
    Odc.sheet1()
    Odc.writescooby()

【问题讨论】:

【参考方案1】:

在以下代码中,在更改值之前取消选择单元格,然后再次选择。这样,即使用户处于编辑模式,也可以修改单元格。

Python 类的方法和变量似乎也有些混乱,所以我也更改了这些部分。

import uno
import unohelper

SCOOBY = "Scooby"

class ODSCursor(unohelper.Base):
    def __init__(self):
        self.ctx = None
        self.desktop = None
        self.document = None
        self.controller = None
        self.sheet = None
        self.counter = 0

    def sheet1(self):
        """Initialize uno handle only once and get the first sheet."""
        if self.sheet is not None:
            return self.sheet
        self.ctx = uno.getComponentContext()
        self.desktop = self.ctx.ServiceManager.createInstanceWithContext(
            "com.sun.star.frame.Desktop", self.ctx)
        self.document = self.desktop.getCurrentComponent()
        self.controller = self.document.getCurrentController()
        self.sheet = self.controller.getActiveSheet()
        return self.sheet

    def writeonce(self):
        self.writeval("B1", self.inc())

    def writetwice(self):
        self.writeval("B2", self.inc())

    def writescooby(self):
        self.writeval("C4", SCOOBY)

    def writeval(self, address, value):
        self.deselect()
        cell = self.sheet.getCellRangeByName(address)
        cell.String = value
        self.controller.select(cell)

    def deselect(self):
        """Select cell A1, then select nothing."""
        cell_a1 = self.sheet.getCellByPosition(0, 0)
        self.controller.select(cell_a1)
        emptyRanges = self.document.createInstance(
            "com.sun.star.sheet.SheetCellRanges")
        self.controller.select(emptyRanges)

    def inc(self):
        """Increment the counter and return the value."""
        self.counter += 1
        return self.counter

odsc = ODSCursor()


### BUTTON BOUND FUNCTIONS ###

def dowriteonce(dummy_oEvent):
    odsc.sheet1()
    odsc.writeonce()

def dowritetwice(dummy_oEvent):
    odsc.sheet1()
    odsc.writetwice()

def dowritethrice(dummy_oEvent):
    odsc.sheet1()
    odsc.writescooby()

【讨论】:

感谢您的回答!我是 python 新手,但我想知道:通过将 uno api 实例化为对象并将其存储在 init 下,您不是为每个按钮制作一份 API 副本吗?我在类变量中绑定所有内容的原因是不这样​​做。我不知道 uno 的内存密集程度如何,这就是为什么我认为要小心。还是我误解了 pythons init 函数,或者是否存在需要此功能的 uno api 依赖项?我现在正在调整你的方法,如果它符合我的需要,我会回答。再次感谢! 好的,我更改了代码,使其只创建一次 API 对象。不过,我担心的是,现在对象会一直保留在内存中,直到 LibreOffice 关闭。这对我来说似乎比每次都实例化值更糟糕。 无论如何,使用@classmethod 并不是一个好主意。另外,如果它真的是一个类方法,那么它应该用一个类而不是一个对象来调用。例如,如果类是这样定义的:class A: @classmethod def do_stuff(cls): pass。然后像这样调用方法:A.do_stuff(),而不是像对象方法:a = A(); a.do_stuff();

以上是关于LibreOffice Calc 上的 Python UNO,重新定位光标的主要内容,如果未能解决你的问题,请参考以下文章

LibreOffice (Calc) VBA 单元格总和(按索引)

用于 Libreoffice-calc 和 Openoffice-calc 的 Python“Hello World”[关闭]

LibreOffice Calc / OpenOffice Calc / Excel:如何显示负持续时间?

用于 CSV 导入的 Libreoffice Calc 模板

如何通过 LibreOffice 的 Calc 中的正则表达式语句捕获公司名称

Excel、Libreoffice/Openoffice Calc:计算“正确”答案