1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Class Release: Load/save external variables

Discussion in 'Alibre Script' started by NateLiqGrav, Jul 5, 2019.

  1. NateLiqGrav

    NateLiqGrav Alibre Super User

    This lets you load variables from an external file and save variables to an external file using a special class called Settings. You can load all existing variables from the file and/or you can load/create them on the fly. The variables are not saved until you use SaveAll().

    Basically include requirements and the classes.
    There is an example of how this works included at the end of the code.

    Give it a try and let me know how it works for you.
    Code:
    #########################################################################################################################
    #########################################################################################################################
    ###  requirements
    #########################################################################################################################
    #########################################################################################################################
    
    Win = Windows()
    import os # for working with file paths
    import xml.etree.ElementTree as xml
    
    #########################################################################################################################
    #########################################################################################################################
    ###  classes
    #########################################################################################################################
    #########################################################################################################################
    
    class Settings:
        def __init__(self, xmlfile):
            self._xmlfile = xmlfile
            if os.path.isfile(self._xmlfile):
                print('Settings file found: ' + str(self._xmlfile))
                with open(self._xmlfile, 'r') as content_file:
                    content = content_file.read()
                self._xmlroot = xml.fromstring(content)
            else:
                print('WARNING: Settings file NOT FOUND: ' + str(self._xmlfile))
                if Win.QuestionDialog('WARNING: Settings file NOT FOUND: ' + str(self._xmlfile) + ' \nMake a new File?', 'Error'):
                    self._xmlroot = xml.Element("root")
                else:
                    sys.exit()
     
        def __getattr__(self, name):
            if name == '_xmlfile' or name == '_xmlroot':
                return self.__dict__[name]
            else:
                print('Retrieving '+ str(name))
                retval = None
                if self._xmlroot.findall('Settings/' + str(name)):
                    if self._xmlroot.find('Settings/' + str(name)).attrib.get('MyType', None):
                        MyType = str(self._xmlroot.find('Settings/' + str(name)).attrib['MyType'])
                        if MyType == 'int':
                            retval = int(self._xmlroot.find('Settings/' + str(name)).text)
                        elif MyType == 'float':
                            retval = float(self._xmlroot.find('Settings/' + str(name)).text)
                        elif MyType == 'bool':
                            retval = str(self._xmlroot.find('Settings/' + str(name)).text).lower() in ("yes", "true", "t", "1")
                        elif MyType == 'NoneType' and str(self._xmlroot.find('Settings/' + str(name)).text) == 'None':
                            retval = None
                        else:
                            retval = str(self._xmlroot.find('Settings/' + str(name)).text)
                    else:
                        retval = str(self._xmlroot.find('Settings/' + str(name)).text)
                    self.__dict__[name] = retval
                    return retval
                else:
                    print(str(name) + ' does not exist and will be created without value.')
                    self.__dict__[name] = None
                    return None
     
        def __setattr__(self, name, val):
            if name == '_xmlfile' or name == '_xmlroot':
                self.__dict__[name] = val
            elif name == 'LoadAll' or name == 'SaveAll':
                print('WARNING: CAN NOT USE ' + str(name) + ' as a variable because it is a class method')
            else:
                print('Updating ' + str(name))
                if self._xmlroot.findall('Settings'):
                    a = self._xmlroot.find('Settings')
                else:
                    a = xml.Element('Settings')
                    self._xmlroot.append(a)
                if self._xmlroot.findall('Settings/' + str(name)):
                    self._xmlroot.find('Settings/' + str(name)).text = str(val)
                    self._xmlroot.find('Settings/' + str(name)).attrib['MyType'] = str(type(val).__name__)
                else:
                    b = xml.SubElement(a, str(name), MyType = str(type(val).__name__))
                    b.text = str(val)
                self.__dict__[name] = val
     
        def LoadAll(self):
            if self._xmlroot.findall('Settings'):
                print('\nStart Loading All Settings:')
                a = self._xmlroot.find('Settings')
                for b in a:
                    if b.attrib.get('MyType', None):
                        MyType = str(b.attrib['MyType'])
                        if MyType == 'int':
                            retval = int(b.text)
                        elif MyType == 'float':
                            retval = float(b.text)
                        elif MyType == 'bool':
                            retval = str(b.text).lower() in ("yes", "true", "t", "1")
                        elif MyType == 'NoneType' and str(b.text) == 'None':
                            retval = None
                        else:
                            retval = str(b.text)
                    else:
                        retval = str(b.text)
                    self.__dict__[str(b.tag)] = retval
                    print('Loaded ' + str(type(retval).__name__) +':       ' + str(b.tag) + ' = ' + str(retval))
                print('Done Loading All.\n')
                return True
            else:
                print('\nCould not find any previous settings')
                return False
     
        def SaveAll(self):
            print('Saving All Settings.')
            if os.path.isfile(self._xmlfile):
                os.remove(self._xmlfile)
            with open(self._xmlfile, 'w') as content_file:
                content_file.write(xml.tostring(self._xmlroot))
    
    
    #########################################################################################################################
    #########################################################################################################################
    ###  main program
    #########################################################################################################################
    #########################################################################################################################
    # Your code goes down here. Everything below is just for example usage.
    
    # first you must create a new Settings instance by passing it a file name.
    MySettingsFile = r'C:\Users\Gamer\Documents\Alibre Script Library\A_Current Scripts\xml_test2.xml'
    vars = Settings(MySettingsFile)
    
    # optionally Load All variables saved in the xml files Settings section at once
    worked = vars.LoadAll()
    if not worked:
        print('Nothing was found in the settings')
        # set any default variables here
        vars.Boolval = False
        vars.makesomething = 125
    
    # or just use variables like normal
    if not vars.LibraryFolder:
        vars.LibraryFolder = r'C:\Users\Gamer\Documents'
    
    vars.Boolval = False
    vars.makesomething = 125
    
    # Make sure to save any changes
    vars.SaveAll()
    
     
    Last edited: Jul 7, 2019
    ajayre likes this.
  2. R-man

    R-man Senior Member

    Ok, here I am and I want to test out your idea. My knowledge and use of python is very basic so please excuse my very basic questions.

    1. It looks like my variables would be in an XML file. What does that look like? (Never made an xml file before.)

    2. Would all of your "requirements" and "classes" code be included fully in every script that needed access to the variables, or could it be somehow imported or included?

    3. Where you say "everything below is just for example usage" I see a few options, but I'm not clear about any. So assume a simple case where all my variables were in the XML file and I made all changes by directly editing that file (nothing on the fly, no reassignments, etc). In that case it looks like I would just need to initialize MySettingsFile and successfully run vars.LoadAll() prior to proceeding with my code. Right??

    Thanks in advance for your patience!
     
  3. NateLiqGrav

    NateLiqGrav Alibre Super User

    If the XML file is not found at the path it will ask if you want to create it. If you say no it just exits. I'm open to suggestions for improvement.

    It can be or you could also save it as a separate script (remove the example at the bottom first) and then import it your scripts. I will make an example of this tomorrow.

    Initialize by creating a Settings instance (in this case named vars) with a path to the XML file:
    Code:
    vars = Settings(r'C:\Path\to\your\file\setting.xml')
    
    The XML data is actually loaded into memory from the file when the class is instanced.

    To be frank vars.LoadAll() is visual fluff. ;) It does takes each variable from in the XML data in memory and assigns each their own attribute and prints out what it did.

    These can be used in the form:
    vars.AttributeName

    However after I wrote that I have added dynamic on the fly so everything is automatically created as attributes and you can just use it.
    Code:
    print(vars.MyDiameter2)
    TotalLength = vars.LengthA + vars.LengthB
    vars.Height = 101.276
    vars.MyString = "test"
    
    If you try to get a variable that didn't exist in the XML it creates it with None for the value.
    It will automatically change values and their types when they are assigned.

    You can manually edit the XML file or just make a script that assigns all the variables and does a vars.SaveAll() at the end. I would do a SaveAll() at least once to see the structure I'm using for the XML file.

    When saving it converts into string first.
    During loading it currently only converts back into the following types:
    int
    float
    bool
    NoneType
    str

    Anything else is defaulted into a string.

    Like I said I'll make an import example tomorrow.
     
  4. R-man

    R-man Senior Member

    I gather from your examples that in my code all reference to stored variables would be in the form "vars.Length". Is that correct?
     
  5. NateLiqGrav

    NateLiqGrav Alibre Super User

    Correct.
    SettingsClassInstanceName.AttributeName
     
  6. NateLiqGrav

    NateLiqGrav Alibre Super User

    It was a little more difficult than I initially thought to get it to import. I'm learning Python as I go and IronPython and AlibreScript have their own twists as well, so there is a little trial and error to this. I also fixed and cleaned up some other things. It's working but I'll need some time to test it and document it better. Sorry for the wait.
     
  7. R-man

    R-man Senior Member

    Considering that my scripts don't create or update parameters on the fly, I can get a similar result using what I call 'Hack1".

    File 'parameters.py' contains the assignment statements:
    Code:
    length = 44
    width  = 21
    height = 12
    My script file imports parameters.py:
    Code:
    # make *SURE* vars folder is in sys.path (but only one time)
    varspath = "C:\\Users\\dh\Documents\\RotoAlibre\\Alibre\\Hack1"
    if varspath not in sys.path:
      sys.path.insert(0, varspath)
    
    # import the parameters file
    import parameters as g  # 'g' can be anything
    
    # is it working?
    print g.length, g.width, g.height
    This enables me to refer to my parameters in a similar 'prefixed' format. This is not really a 'hack' but it is limited to using parameters like constants.

    Ideally I'd like to not require a prefix and have truly global parameters. I see some possibilities but it will take many more wasted hours to know if it's possible.
     
  8. NateLiqGrav

    NateLiqGrav Alibre Super User

    If that works for you then use that.

    I don't see a way. Plus if you could manage to make all variables global I think it would screw up built in functions and methods of AlibreScript/Python.
     
  9. R-man

    R-man Senior Member

    I have 'Hack2' ready. It's back in the thread "How do I access variable file from a script?"
     

Share This Page