Store data in components other than Writer

In Writer, data for each document can be stored with text field masters.

fieldMasters = self.document.getTextFieldMasters()
fieldName = "com.sun.star.text.FieldMaster.User." + varName
if fieldMasters.hasByName(fieldName):
    field = fieldMasters.getByName(fieldName)
    field.setPropertyValue("Content", stringVal)
else:
    xMaster = self.document.createInstance(
        "com.sun.star.text.fieldmaster.User")
    xMaster.Name = varName
    xMaster.Content = stringVal
    xUserField = self.document.createInstance(
        "com.sun.star.text.textfield.User")
    xUserField.attachTextFieldMaster(xMaster)

This works very well, and the values can even be managed manually by going to Insert → Fields → More Fields, Variables, User Field.

However, I have not found a way to store data for Calc or other components of LibO. My current solution is to save a separate Writer document and then store the values in it. But that means that when using Calc, two files must be saved and opened together (the Calc file and a Writer file), which seems less than ideal.

There is com.sun.star.comp.configuration.ConfigurationProvider, but this is global, and I need different data for each document.

Is there a way to store data in documents for components other than Writer?

When writing this question, it occurred to me that people such as @JohnSUN have shown how to embed documents. Would it work to embed the Writer document in the Calc file, so that there is only a single file to manage?

Why don’t you use the PropertyBag named .UserDefinedProperties accessible from ThisComponent.DocumentProperties in evry case, and manageable via the UI?
For Calc documents in specific an extra sheet, probably a hidden one, might be a better isdea, at least if many values ned to be stored.
See LibreOffice: PropertyBag Service Reference

Sounds promising; I’ll look into it.

BTW: The special propertytypes Date, DateTime, Duration follow concepts I partly don’t understand and partly deprecate for being unhandy - at least if accessed by user code.
If using UserDefinedProperties with user code, I would suggest to omit the specialized UX tools (datepicker and the like) and the related special types, and to do editing, interpretation and formatting completely by user code, based on the Number type which stores a number as Double (like in Calc) - or based on strings gotten by reasonable (ISO again!) formatting conversion .

As suggested by @Lupp, I tried PropertyBag. This ended up solving my problem nicely in the end.

Writing the code wasn’t easy. I could find few examples and no documentation except the bare API docs. Starting with XrayTool and MRI, it took some wrong turns before I came up with the following.

from com.sun.star.beans.PropertyAttribute import REMOVEABLE

class UserDefinedDocProps:
    def __init__(self, oDoc):
        oDocProps = oDoc.getDocumentProperties()
        self.userProps = oDocProps.getUserDefinedProperties()

    def userPropsInfo(self):
        return self.userProps.getPropertySetInfo()

    def store(self, varName, stringVal):
        if self.userPropsInfo().hasPropertyByName(varName):
            self.userProps.setPropertyValue(varName, stringVal)
        else:
            self.userProps.addProperty(varName, REMOVEABLE, stringVal)

    def get(self, varName):
        if self.userPropsInfo().hasPropertyByName(varName):
            stringVal = self.userProps.getPropertyValue(varName)
            return stringVal
        else:
            return ""

    def delete(self, varName):
        if self.userPropsInfo().hasPropertyByName(varName):
            self.userProps.removeProperty(varName)

Tricky things about the API that I encountered:

  • Updating an existing property is shown immediately when going to File → Properties → Custom Properties. However, newly added properties are not shown until the document is saved and reloaded. This is the only drawback I found with this approach compared with field masters. The newly added properties can be accessed by the API before saving, so this should not be a significant problem for my project.
  • To see if a property exists, a separate object must be obtained by calling getPropertySetInfo(). There are other ways this could be done instead, either (a) Enumerate and look through each name in the list (althoughenumerate() didn’t seem to return anything when I tried it) or (b) add a try / except block to catch UnknownPropertyException. If speed is a concern, then (b) may be faster than the code listed above.
  • Calling store() and then get() failed in my earlier attempts because I did not call getPropertySetInfo() in between, and the object does not get updated, so the call to obtain the object must be made each time.
  • removeProperty() fails unless the REMOVEABLE attribute is set.

Also as @Lupp commented, there are tricky things about certain types. However, strings are the only type I need, and my previous solution with field masters also used only strings. Then the code converts from strings to boolean or numeric if needed.