Search code examples
pythonlibreofficeopenoffice.orguno

Python UNO on LibreOffice Calc, rehoming a cursor


LibreOffice 5.3, python 3.53, VOID Linux

This is more of an uno question than a python question. The code below does a simple update of 3 cells. 3 buttons configured on the sheet calling dowriteonce() dowritetwice() and dowritethrice(), and they all update and work like you might expect writing numbers and text to selected cells.

Where the problem comes in, is that when a cell is edited in the UI by a user, any subsequent update of that cell by means of executing the function is blocked. So simply clicking cell C4 in the calc UI, prevents the writethrice() function from updating cell C4. If I delete the content and click another cell in the UI, say C5, then everything works normally again and C4 updates when the button is clicked.

What I would like to do is relocate the UI edit-cursor to an unused cell prior to execution in order to prevent this. User copy-paste is going to leave the active cursor in unpredictable places and that will bork calculations if I can't isolate the cursor.

So the question is, how do I move the UI edit cursor to a named cell via the UNO API, with Python? Or if it is easier, just deactivate it temporarily.

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()

Solution

  • In the following code, cells are deselected before changing the values, then selected again. This way, cells can be modified even when left in edit mode by the user.

    There also seems to be some confusion about Python class methods and variables, so I changed those parts as well.

    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()