diff options
-rw-r--r-- | wizards/source/scriptforge/python/scriptforge.py | 221 |
1 files changed, 159 insertions, 62 deletions
diff --git a/wizards/source/scriptforge/python/scriptforge.py b/wizards/source/scriptforge/python/scriptforge.py index 2941c0db1e8b..93335098bf8b 100644 --- a/wizards/source/scriptforge/python/scriptforge.py +++ b/wizards/source/scriptforge/python/scriptforge.py @@ -304,42 +304,49 @@ class SFServices(object): A service instance is created by the CreateScriptService method It can have a mirror in the Basic world or be totally defined in Python - Every subclass must initialize 2 class properties: - servicename (e.g. ScriptForge.FileSystem, ScriptForge.Basic) + Every subclass must initialize 3 class properties: + servicename (e.g. 'ScriptForge.FileSystem', 'ScriptForge.Basic') + servicesynonyms (e.g. 'FileSystem', 'Basic') serviceimplementation: either 'python' or 'basic' - This is sufficient to register the set of services in the Python world + This is sufficient to register the service in the Python world The communication with Basic is managed by 2 ScriptForge() methods: InvokeSimpleScript(): low level invocation of a Basic script. This script must be located in a usual Basic module. The result is passed as-is - InvokeSBasicService(): the result comes back encapsulated with additional info + InvokeBasicService(): the result comes back encapsulated with additional info The result is interpreted in the method - The invoked script can be a property or a method of a Basic class module + The invoked script can be a property or a method of a Basic class or usual module It is up to every service method to determine which method to use For Basic services only: Each instance is identified by its - object reference: the real Basic object embedded as a UNO wrapper object - - objecttype ('SF_String', 'DICTIONARY', ...) + - object type ('SF_String', 'DICTIONARY', ...) + - class module: 1 for usual modules, 2 for class modules - name (form, control, ... name) - may be blank The role of the Service() superclass is mainly to propose a generic properties management Properties are got and set following next strategy: - 1. Property names are controlled strictly ('Value' and not 'value') + 1. Property names are controlled strictly ('Value' or 'value', not 'VALUE') 2. Getting a property value for the first time is always done via a Basic call 3. Next occurrences are fetched from the Python dictionary of the instance if the property - is read-only, otherwise via a Basic call + is read-only, otherwise via a Basic call 4. Read-only properties may be modified or deleted exceptionally by the class when self.internal == True. The latter must immediately be reset after use Each subclass must define its interface with the user scripts: 1. The properties - a dictionary named 'serviceProperties' with keys = (camel-cased) property names and value = boolean + Property names are proper-cased + Conventionally, camel-cased and lower-cased synonyms are supported where relevant + a dictionary named 'serviceproperties' with keys = (proper-cased) property names and value = boolean True = editable, False = read-only - a list named 'localProperties' reserved to properties for internal use + a list named 'localProperties' reserved to properties for internal use e.g. oDlg.Controls() is a method that uses '_Controls' to hold the list of available controls - serviceProperties are buffered in Python after their 1st get request to Basic - Only if there is a need to go to Basic at each get, then declare the property explicitly: + When + forceGetProperty = False # Standard bahaviour + read-only serviceproperties are buffered in Python after their 1st get request to Basic + Otherwise set it to True to force a recomputation at each property getter invocation + If there is a need to handle a specific property in a specific manner: @property def myProperty(self): return self.GetProperty('myProperty') @@ -347,7 +354,8 @@ class SFServices(object): a usual def: statement def myMethod(self, arg1, arg2 = ''): return self.Execute(self.vbMethod, 'myMethod', arg1, arg2) - Method names are camel-cased, arguments are lower-cased + Method names are proper-cased, arguments are lower-cased + Conventionally, camel-cased and lower-cased homonyms are supported where relevant All arguments must be present and initialized before the call to Basic, if any """ # Python-Basic protocol constants and flags @@ -359,6 +367,10 @@ class SFServices(object): # Basic class type moduleClass, moduleStandard = 2, 1 # + # Define the default behaviour for read-only properties: buffer their values in Python + forceGetProperty = False + # Empty dictionary for lower/camelcased homonyms or properties + propertysynonyms = {} # To operate dynamic property getting/setting it is necessary to # enumerate all types of properties and adapt __getattr__() and __setattr__() according to their type internal_attributes = ('objectreference', 'objecttype', 'name', 'internal', 'servicename', @@ -383,30 +395,46 @@ class SFServices(object): """ Executed for EVERY property reference if name not yet in the instance dict At the 1st get, the property value is always got from Basic + Due to the use of lower/camelcase synonyms, it is called for each variant of the same property + The method manages itself the buffering in __dict__ based on the official ProperCase property name """ + if name in self.propertysynonyms: # Reset real name if argument provided in lower or camel case + name = self.propertysynonyms[name] if self.serviceimplementation == 'basic': - if name in ('serviceProperties', 'localProperties', 'internal_attributes'): + if name in ('serviceproperties', 'localProperties', 'internal_attributes', 'propertysynonyms', + 'forceGetProperty'): pass - elif name in self.serviceProperties: - # Get Property from Basic - return self.GetProperty(name) + elif name in self.serviceproperties: + if self.forceGetProperty is False and self.serviceproperties[name] is False: # False = read-only + if name in self.__dict__: + return self.__dict__[name] + else: + # Get Property from Basic + prop = self.GetProperty(name) + self.__dict__[name] = prop + return prop + else: + return self.GetProperty(name) # Execute the usual attributes getter return super(SFServices, self).__getattribute__(name) def __setattr__(self, name, value): """ Executed for EVERY property assignment, including in __init__() !! - Setting a property requires for serviceProperties() to be executed in Basic + Setting a property requires for serviceproperties() to be executed in Basic + Management of __dict__ is automatically done in the final usual object.__setattr__ method """ if self.serviceimplementation == 'basic': - if name in ('serviceProperties', 'localProperties', 'internal_attributes'): + if name in ('serviceproperties', 'localProperties', 'internal_attributes', 'propertysynonyms'): pass elif name[0:2] == '__' or name in self.internal_attributes or name in self.localProperties: pass - elif name in self.serviceProperties: + elif name in self.serviceproperties or name in self.propertysynonyms: + if name in self.propertysynonyms: # Reset real name if argument provided in lower or camel case + name = self.propertysynonyms[name] if self.internal: # internal = True forces property local setting even if property is read-only pass - elif self.serviceProperties[name] is True: # True == Editable + elif self.serviceproperties[name] is True: # True == Editable self.SetProperty(name, value) else: raise AttributeError( @@ -420,11 +448,30 @@ class SFServices(object): return self.serviceimplementation + '/' + self.servicename + '/' + str(self.objectreference) + '/' + \ super(SFServices, self).__repr__() + @staticmethod + def _getAttributeSynonyms(dico): + """ + Returns a dictionary with key = name in lower case and in camelCase, value = real ProperCased name + Example: + d = dict(ConfigFolder = False, InstallFolder = False) + dh = _getHomonyms(d) + # dh == dict(configfolder = 'ConfigFolder', installfolder = 'InstallFolder', + configFolder = 'ConfigFolder', installFolder = 'InstallFolder') + """ + def camelCase(key): + return key[0].lower() + key[1:] + + lc = dict(zip(map(str.casefold, dico.keys()), dico.keys())) + cc = dict(zip(map(camelCase, dico.keys()), dico.keys())) + lc.update(cc) + return lc + def Dispose(self): if self.serviceimplementation == 'basic': - if self.objectreference >= 0: + if self.objectreference >= len(ScriptForge.servicesmodules): # Do not dispose predefined module objects self.Execute(self.vbMethod, 'Dispose') self.objectreference = -1 + dispose = Dispose def Execute(self, flags = 0, methodname = '', *args): if flags == 0: @@ -436,16 +483,21 @@ class SFServices(object): """ Get the given property from the Basic world """ - return self.EXEC(self.objectreference, self.vbGet, propertyname) + if self.serviceimplementation == 'basic': + return self.EXEC(self.objectreference, self.vbGet, propertyname) + getProperty, getproperty = GetProperty, GetProperty def Properties(self): - return list(self.serviceProperties) + return list(self.serviceproperties) + properties = Properties def SetProperty(self, propertyname, value): """ Set the given property to a new value in the Basic world """ - return self.EXEC(self.objectreference, self.vbLet, propertyname, value) + if self.serviceimplementation == 'basic': + return self.EXEC(self.objectreference, self.vbLet, propertyname, value) + setProperty, setproperty = SetProperty, SetProperty # ##################################################################################################################### @@ -465,6 +517,7 @@ class SFScriptForge: # Mandatory class properties for service registration serviceimplementation = 'python' servicename = 'ScriptForge.Basic' + servicesynonyms = ('basic', 'scriptforge.basic') # Basic helper functions invocation module = 'SF_PythonHelper' # Message box constants @@ -475,18 +528,22 @@ class SFScriptForge: def ConvertFromUrl(self, filename): return self.SIMPLEEXEC(self.module + '.PyConvertFromUrl', filename) + convertFromUrl, convertfromurl = ConvertFromUrl, ConvertFromUrl def ConvertToUrl(self, filename): return self.SIMPLEEXEC(self.module + '.PyConvertToUrl', filename) + convertToUrl, converttourl = ConvertToUrl, ConvertToUrl def CreateUnoService(self, unoservice): return self.SIMPLEEXEC(self.module + '.PyCreateUnoService', unoservice) + createUnoService, createunoservice = CreateUnoService, CreateUnoService def DateAdd(self, add, count, datearg): if isinstance(datearg, datetime.datetime): datearg = datearg.isoformat() dateadd = self.SIMPLEEXEC(self.module + '.PyDateAdd', add, count, datearg) return datetime.datetime.fromisoformat(dateadd) + dateAdd, dateadd = DateAdd, DateAdd def DateDiff(self, add, date1, date2, weekstart = 1, yearstart = 1): if isinstance(date1, datetime.datetime): @@ -494,36 +551,44 @@ class SFScriptForge: if isinstance(date2, datetime.datetime): date2 = date2.isoformat() return self.SIMPLEEXEC(self.module + '.PyDateDiff', add, date1, date2, weekstart, yearstart) + dateDiff, datediff = DateDiff, DateDiff def DatePart(self, add, datearg, weekstart = 1, yearstart = 1): if isinstance(datearg, datetime.datetime): datearg = datearg.isoformat() return self.SIMPLEEXEC(self.module + '.PyDatePart', add, datearg, weekstart, yearstart) + datePart, datepart = DatePart, DatePart def DateValue(self, datearg): if isinstance(datearg, datetime.datetime): datearg = datearg.isoformat() datevalue = self.SIMPLEEXEC(self.module + '.PyDateValue', datearg) return datetime.datetime.fromisoformat(datevalue) + dateValue, datevalue = DateValue, DateValue def Format(self, value, pattern = ''): if isinstance(value, datetime.datetime): value = value.isoformat() return self.SIMPLEEXEC(self.module + '.PyFormat', value, pattern) + format = Format @staticmethod def GetDefaultContext(): return ScriptForge.componentcontext + getDefaultContext, getdefaultcontext = GetDefaultContext, GetDefaultContext def GetGuiType(self): return self.SIMPLEEXEC(self.module + '.PyGetGuiType') + getGuiType, getguitype = GetGuiType, GetGuiType def GetSystemTicks(self): return self.SIMPLEEXEC(self.module + '.PyGetSystemTicks') + getSystemTicks, getsystemticks = GetSystemTicks, GetSystemTicks @staticmethod def GetPathSeparator(): return os.sep + getPathSeparator, getpathseparator = GetPathSeparator, GetPathSeparator class GlobalScope(object, metaclass = _Singleton): @classmethod # Mandatory because the GlobalScope class is normally not instantiated @@ -538,17 +603,21 @@ class SFScriptForge: if xpos < 0 or ypos < 0: return self.SIMPLEEXEC(self.module + '.PyInputBox', msg, title, default) return self.SIMPLEEXEC(self.module + '.PyInputBox', msg, title, default, xpos, ypos) + inputBox, inputbox = InputBox, InputBox def MsgBox(self, text, dialogtype = 0, dialogtitle = ''): return self.SIMPLEEXEC(self.module + '.PyMsgBox', text, dialogtype, dialogtitle) + msgBox, msgbox = MsgBox, MsgBox @staticmethod def Now(): return datetime.datetime.now() + now = Now @staticmethod def RGB(red, green, blue): return int('%02x%02x%02x' % (red, green, blue), 16) + rgb = RGB @staticmethod def StarDesktop(): @@ -559,9 +628,11 @@ class SFScriptForge: DESK = 'com.sun.star.frame.Desktop' desktop = smgr.createInstanceWithContext(DESK, ctx) return desktop + starDesktop, stardesktop = StarDesktop, StarDesktop def Xray(self, unoobject = None): return self.SIMPLEEXEC('XrayTool._main.xray', unoobject) + xray = Xray # ######################################################################### # SF_String CLASS @@ -575,6 +646,7 @@ class SFScriptForge: # Mandatory class properties for service registration serviceimplementation = 'basic' servicename = 'ScriptForge.String' + servicesynonyms = () # ######################################################################### # SF_FileSystem CLASS @@ -586,16 +658,16 @@ class SFScriptForge: # Mandatory class properties for service registration serviceimplementation = 'basic' servicename = 'ScriptForge.FileSystem' - serviceProperties = dict(FileNaming = True, ConfigFolder = False, ExtensionsFolder = False, HomeFolder = False, + servicesynonyms = ('filesystem', 'scriptforge.filesystem') + serviceproperties = dict(FileNaming = True, ConfigFolder = False, ExtensionsFolder = False, HomeFolder = False, InstallFolder = False, TemplatesFolder = False, TemporaryFolder = False, UserTemplatesFolder = False) + propertysynonyms = SFServices._getAttributeSynonyms(serviceproperties) + # Force for each property to get its value from Basic - due to FileNaming updatability + forceGetProperty = True # Open TextStream constants ForReading, ForWriting, ForAppending = 1, 2, 8 - @property - def ConfigFolder(self): - return self.GetProperty('ConfigFolder') - def BuildPath(self, foldername, name): return self.Execute(self.vbMethod, 'BuildPath', foldername, name) @@ -628,6 +700,7 @@ class SFScriptForge: def FileExists(self, filename): return self.Execute(self.vbMethod, 'FileExists', filename) + fileexists, fileExists = FileExists, FileExists def Files(self, foldername, filter = ''): return self.Execute(self.vbMethod + self.flgArrayRet, 'Files', foldername, filter) @@ -705,18 +778,21 @@ class SFScriptForge: # Mandatory class properties for service registration serviceimplementation = 'basic' servicename = 'ScriptForge.L10N' - serviceProperties = dict(Folder = False, Languages = False, Locale = False) + servicesynonyms = () + serviceproperties = dict(Folder = False, Languages = False, Locale = False) + propertysynonyms = SFServices._getAttributeSynonyms(serviceproperties) def AddText(self, context = '', msgid = '', comment = ''): return self.Execute(self.vbMethod, 'AddText', context, msgid, comment) + addText, addtext = AddText, AddText def ExportToPOTFile(self, filename, header = '', encoding= 'UTF-8'): return self.Execute(self.vbMethod, 'ExportToPOTFile', filename, header, encoding) + exportToPOTFile, exporttopotfile = ExportToPOTFile, ExportToPOTFile def GetText(self, msgid, *args): return self.Execute(self.vbMethod, 'GetText', msgid, *args) - - _ = GetText + _, gettext, getText = GetText, GetText, GetText # ######################################################################### # SF_Platform CLASS @@ -735,51 +811,63 @@ class SFScriptForge: # Mandatory class properties for service registration serviceimplementation = 'basic' servicename = 'ScriptForge.Platform' - serviceProperties = dict(Architecture = False, ComputerName = False, CPUCount = False, CurrentUser = False, + servicesynonyms = () + serviceproperties = dict(Architecture = False, ComputerName = False, CPUCount = False, CurrentUser = False, Locale = False, Machine = False, OfficeVersion = False, OSName = False, OSPlatform = False, OSRelease = False, OSVersion = False, Processor = False) + propertysynonyms = SFServices._getAttributeSynonyms(serviceproperties) # Python helper functions py = ScriptForge.pythonhelpermodule + '$' + '_SF_Platform' @property def Architecture(self): return self.SIMPLEEXEC(self.py, 'Architecture') + architecture = Architecture @property def ComputerName(self): return self.SIMPLEEXEC(self.py, 'ComputerName') + computerName, computername = ComputerName, ComputerName @property def CPUCount(self): return self.SIMPLEEXEC(self.py, 'CPUCount') + cpuCount, cpucount = CPUCount, CPUCount @property def CurrentUser(self): return self.SIMPLEEXEC(self.py, 'CurrentUser') + currentUser, currentuser = CurrentUser, CurrentUser @property def Machine(self): return self.SIMPLEEXEC(self.py, 'Machine') + machine = Machine @property def OSName(self): return self.SIMPLEEXEC(self.py, 'OSName') + osName, osname = OSName, OSName @property def OSPlatform(self): return self.SIMPLEEXEC(self.py, 'OSPlatform') + osPlatform, osplatform = OSPlatform, OSPlatform @property def OSRelease(self): return self.SIMPLEEXEC(self.py, 'OSRelease') + osRelease, osrelease = OSRelease, OSRelease @property def OSVersion(self): return self.SIMPLEEXEC(self.py, 'OSVersion') + osVersion, osversion = OSVersion, OSVersion @property def Processor(self): return self.SIMPLEEXEC(self.py, 'Processor') + processor = Processor # ######################################################################### # SF_TextStream CLASS @@ -792,34 +880,44 @@ class SFScriptForge: # Mandatory class properties for service registration serviceimplementation = 'basic' servicename = 'ScriptForge.TextStream' - serviceProperties = dict(AtEndOfStream = False, Encoding = False, FileName = False, - IOMode = False, Line = False, NewLine = True) + servicesynonyms = () + serviceproperties = dict(AtEndOfStream = False, Encoding = False, FileName = False, IOMode = False, + Line = False, NewLine = True) + propertysynonyms = SFServices._getAttributeSynonyms(serviceproperties) @property def AtEndOfStream(self): return self.GetProperty('AtEndOfStream') + atEndOfStream, atendofstream = AtEndOfStream, AtEndOfStream @property def Line(self): return self.GetProperty('Line') + line = Line def CloseFile(self): return self.Execute(self.vbMethod, 'CloseFile') + closeFile, closefile = CloseFile, CloseFile def ReadAll(self): return self.Execute(self.vbMethod, 'ReadAll') + readAll, readall = ReadAll, ReadAll def ReadLine(self): return self.Execute(self.vbMethod, 'ReadLine') + readLine, readline = ReadLine, ReadLine def SkipLine(self): return self.Execute(self.vbMethod, 'SkipLine') + skipLine, skipline = SkipLine, SkipLine def WriteBlankLines(self, lines): return self.Execute(self.vbMethod, 'WriteBlankLines', lines) + writeBlankLines, writeblanklines = WriteBlankLines, WriteBlankLines def WriteLine(self, line): return self.Execute(self.vbMethod, 'WriteLine', line) + writeLine, writeline = WriteLine, WriteLine # ######################################################################### # SF_Timer CLASS @@ -831,39 +929,31 @@ class SFScriptForge: # Mandatory class properties for service registration serviceimplementation = 'basic' servicename = 'ScriptForge.Timer' - serviceProperties = dict(Duration = False, IsStarted = False, IsSuspended = False, + servicesynonyms = () + serviceproperties = dict(Duration = False, IsStarted = False, IsSuspended = False, SuspendDuration = False, TotalDuration = False) - - @property - def Duration(self): - return self.GetProperty('Duration') - - @property - def IsStarted(self): - return self.GetProperty('IsStarted') - - @property - def SuspendDuration(self): - return self.GetProperty('SuspendDuration') - - @property - def TotalDuration(self): - return self.GetProperty('TotalDuration') + propertysynonyms = SFServices._getAttributeSynonyms(serviceproperties) + # Force for each property to get its value from Basic + forceGetProperty = True def Continue(self): return self.Execute(self.vbMethod, 'Continue') def Restart(self): return self.Execute(self.vbMethod, 'Restart') + restart = Restart def Start(self): return self.Execute(self.vbMethod, 'Start') + start = Start def Suspend(self): return self.Execute(self.vbMethod, 'Suspend') + suspend = Suspend def Terminate(self): return self.Execute(self.vbMethod, 'Terminate') + terminate = Terminate # ##############################################False####################################################################### @@ -872,11 +962,17 @@ class SFScriptForge: def CreateScriptService(service, *args): """ A service being the name of a collection of properties and methods, - this method returns the Python object mirror of the Basic object implementing - the requested service - As an exception to above, 'Basic' is accepted as a shortcut to the Basic service - which is implemented in Python + this method returns either + - the Python object mirror of the Basic object implementing the requested service + - the Python object implementing the service itself + + A service may be designated by its official name, stored in its class.servicename + or by one of its synonyms stored in its class.servicesynonyms list + If the service is not identified, the service creation is delegated to Basic, that might raise an error + if still not identified there + :param service: the name of the service as a string 'library.service' - cased exactly + or one of its synonyms :param args: the arguments to pass to the service constructor :return: the service as a Python object """ @@ -889,12 +985,11 @@ def CreateScriptService(service, *args): """ Synonyms within service names implemented in Python or predefined are resolved here :param servicename: The short name of the service - :return: The official service name + :return: The official service name if found, the argument otherwise """ - if servicename.lower() in ('basic', 'scriptforge.basic'): - return 'ScriptForge.Basic' - if servicename.lower() in ('filesystem', 'scriptforge.filesystem'): - return 'ScriptForge.FileSystem' + for cls in SFServices.__subclasses__(): + if servicename.lower() in cls.servicesynonyms: + return cls.servicename return servicename # @@ -915,6 +1010,8 @@ def CreateScriptService(service, *args): serv = ScriptForge.InvokeBasicService('SF_Services', SFServices.vbMethod, 'CreateScriptService', service, *args) return serv +createScriptSerive, createscriptservive = CreateScriptService, CreateScriptService + # ##################################################################################################################### # Services shortcuts ### |